📄 crash_reporter_win32.cpp
字号:
/* $Id: crash_reporter_win32.cpp,v 1.8 2003/09/18 09:15:01 mbn Exp $
**
** ClanLib Game SDK
** Copyright (C) 2003 The ClanLib Team
** For a total list of contributers see the file CREDITS.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**
*/
#include "Core/precomp.h"
#include "API/Core/System/crash_reporter.h"
#include "API/Core/System/log.h"
#include "crash_reporter_win32.h"
#ifdef _MSC_VER
#pragma warning (disable:4535)
#endif
/////////////////////////////////////////////////////////////////////////////
// CL_CrashReporter_Win32 Construction:
CL_CrashReporter_Win32::CL_CrashReporter_Win32()
{
#ifdef HAS_PDB_SUPPORT
if (moduleImagehlp == 0)
{
moduleImagehlp = LoadLibrary( "imagehlp.dll" );
if ( moduleImagehlp == 0 )
{
CL_Log::log("debug", "Unable to load imagehlp.dll. Crash reporter disabled.");
return;
}
ptrSymCleanup = (TypeSymCleanup) GetProcAddress( moduleImagehlp, "SymCleanup" );
ptrSymFunctionTableAccess = (TypeSymFunctionTableAccess) GetProcAddress( moduleImagehlp, "SymFunctionTableAccess" );
ptrSymGetLineFromAddr = (TypeSymGetLineFromAddr) GetProcAddress( moduleImagehlp, "SymGetLineFromAddr" );
ptrSymGetModuleBase = (TypeSymGetModuleBase) GetProcAddress( moduleImagehlp, "SymGetModuleBase" );
ptrSymGetModuleInfo = (TypeSymGetModuleInfo) GetProcAddress( moduleImagehlp, "SymGetModuleInfo" );
ptrSymGetOptions = (TypeSymGetOptions) GetProcAddress( moduleImagehlp, "SymGetOptions" );
ptrSymGetSymFromAddr = (TypeSymGetSymFromAddr) GetProcAddress( moduleImagehlp, "SymGetSymFromAddr" );
ptrSymInitialize = (TypeSymInitialize) GetProcAddress( moduleImagehlp, "SymInitialize" );
ptrSymSetOptions = (TypeSymSetOptions) GetProcAddress( moduleImagehlp, "SymSetOptions" );
ptrStackWalk = (TypeStackWalk) GetProcAddress( moduleImagehlp, "StackWalk" );
ptrUnDecorateSymbolName = (TypeUnDecorateSymbolName) GetProcAddress( moduleImagehlp, "UnDecorateSymbolName" );
ptrSymLoadModule = (TypeSymLoadModule) GetProcAddress( moduleImagehlp, "SymLoadModule" );
if (
ptrSymCleanup == 0 ||
ptrSymFunctionTableAccess == 0 ||
ptrSymGetLineFromAddr == 0 ||
ptrSymGetModuleBase == 0 ||
ptrSymGetModuleInfo == 0 ||
ptrSymGetOptions == 0 ||
ptrSymGetSymFromAddr == 0 ||
ptrSymInitialize == 0 ||
ptrSymSetOptions == 0 ||
ptrStackWalk == 0 ||
ptrUnDecorateSymbolName == 0 ||
ptrSymLoadModule == 0)
{
CL_Log::log("debug", "Unable to find all symbols in imagehlp.dll. Crash reporter disabled.");
FreeLibrary( moduleImagehlp );
moduleImagehlp = 0;
return;
}
// Setup search path for pdb (symbol information) files:
#define TTBUFLEN 65536 // for a temp buffer
char tt[TTBUFLEN];
if ( GetCurrentDirectoryA( TTBUFLEN, tt ) )
userSearchPath += tt + std::string( ";" );
// dir with executable
if ( GetModuleFileNameA( 0, tt, TTBUFLEN ) )
{
char *p;
for ( p = tt + strlen( tt ) - 1; p >= tt; -- p )
{
// locate the rightmost path separator
if ( *p == '\\' || *p == '/' || *p == ':' )
break;
}
// if we found one, p is pointing at it; if not, tt only contains
// an exe name (no path), and p points before its first byte
if ( p != tt ) // path sep found?
{
if ( *p == ':' ) // we leave colons in place
++ p;
*p = '\0'; // eliminate the exe name and last path sep
userSearchPath += tt + std::string( ";" );
}
}
// environment variable _NT_SYMBOL_PATH
if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) )
userSearchPath += tt + std::string( ";" );
// environment variable _NT_ALTERNATE_SYMBOL_PATH
if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) )
userSearchPath += tt + std::string( ";" );
// environment variable SYSTEMROOT
if ( GetEnvironmentVariableA( "SYSTEMROOT", tt, TTBUFLEN ) )
userSearchPath += tt + std::string( ";" );
if ( userSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon
userSearchPath = userSearchPath.substr( 0, userSearchPath.size() - 1 );
}
if (moduleDbghlp == 0)
{
moduleDbghlp = LoadLibrary( "dbghelp.dll" );
if ( moduleDbghlp == 0 )
{
CL_Log::log("debug", "Unable to load dbghlp.dll. Crash reporter disabled.");
return;
}
ptrMiniDumpWriteDump = (TypeMiniDumpWriteDump) GetProcAddress( moduleDbghlp, "MiniDumpWriteDump" );
if (ptrMiniDumpWriteDump == 0)
{
CL_Log::log("debug", "Unable to find all symbols in dbghlp.dll. Crash reporter disabled.");
FreeLibrary( moduleDbghlp );
moduleDbghlp = 0;
return;
}
}
#ifdef HAS_SE_TRANSLATOR
ptrOldFilter = _set_se_translator(&CL_CrashReporter_Win32::unhandledExceptionFilter);
#else
ptrOldFilter = SetUnhandledExceptionFilter(&CL_CrashReporter_Win32::unhandledExceptionFilter);
#endif
#endif
}
CL_CrashReporter_Win32::~CL_CrashReporter_Win32()
{
#ifdef HAS_PDB_SUPPORT
#ifdef HAS_SE_TRANSLATOR
_set_se_translator(ptrOldFilter);
#else
// Dont do this since exception filter seem to be on process level and not thread level:
// ptrOldFilter = SetUnhandledExceptionFilter(ptrOldFilter);
#endif
#endif
// if (moduleImagehlp == 0) return;
// FreeLibrary( moduleImagehlp );
// moduleImagehlp = 0;
}
/////////////////////////////////////////////////////////////////////////////
// CrashReporter Implementation:
#ifdef HAS_PDB_SUPPORT
struct DumpParams
{
HANDLE hProcess;
HANDLE hThread;
int threadId;
PEXCEPTION_POINTERS exceptionInfo;
unsigned int exceptionCode;
};
#ifdef HAS_SE_TRANSLATOR
void CL_CrashReporter_Win32::unhandledExceptionFilter(unsigned int exceptionCode, PEXCEPTION_POINTERS exceptionInfo)
#else
LONG CL_CrashReporter_Win32::unhandledExceptionFilter(PEXCEPTION_POINTERS exceptionInfo)
#endif
{
#ifndef HAS_SE_TRANSLATOR
unsigned int exceptionCode = GetExceptionCode();
#endif
// Ignore those bloody breakpoints!
if (exceptionCode == EXCEPTION_BREAKPOINT) return;
DumpParams dumpParams;
dumpParams.hProcess = GetCurrentProcess();
dumpParams.hThread = GetCurrentThread();
dumpParams.threadId = GetCurrentThreadId();
dumpParams.exceptionInfo = exceptionInfo;
dumpParams.exceptionCode = exceptionCode;
DWORD threadId;
HANDLE hThread = CreateThread(0, 0, &CL_CrashReporter_Win32::dumpStack, &dumpParams, 0, &threadId);
WaitForSingleObject(hThread, INFINITE);
// if (ptrOldFilter) ptrOldFilter(exceptionCode, exceptionInfo);
abort();
#ifndef HAS_SE_TRANSLATOR
return EXCEPTION_CONTINUE_SEARCH;
#endif
}
DWORD WINAPI CL_CrashReporter_Win32::dumpStack(LPVOID lpThreadParameter)
{
DumpParams *dumpParams = (DumpParams *) lpThreadParameter;
HANDLE hProcess = dumpParams->hProcess;
HANDLE hThread = dumpParams->hThread;
PEXCEPTION_POINTERS exceptionInfo = dumpParams->exceptionInfo;
// Get path to executable:
TCHAR szDllName[_MAX_PATH];
TCHAR szDrive[_MAX_DRIVE];
TCHAR szDir[_MAX_DIR];
TCHAR szFilename[256];
TCHAR szExt[256];
GetModuleFileName(0, szDllName, _MAX_PATH);
_splitpath(szDllName, szDrive, szDir, szFilename, szExt);
TCHAR fileName[1024];
// Produce minidump file:
_snprintf(fileName, 1024, "%s%scoredump-%d.dmp", szDrive, szDir, dumpParams->threadId);
HANDLE hFile = CreateFile(
fileName,
GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
MINIDUMP_EXCEPTION_INFORMATION exInfo;
exInfo.ThreadId = dumpParams->threadId;
exInfo.ExceptionPointers = exceptionInfo;
exInfo.ClientPointers = 0;
ptrMiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &exInfo, 0, 0);
CloseHandle(hFile);
// why oh why does SymInitialize() want a writeable string?
char tt[TTBUFLEN];
strncpy( tt, userSearchPath.c_str(), TTBUFLEN );
tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator
ptrSymInitialize(hProcess, tt, FALSE);
DWORD symOptions;
symOptions = ptrSymGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions &= ~SYMOPT_UNDNAME;
ptrSymSetOptions( symOptions );
#define MAXNAMELEN 1024 // max name length for found symbols
#define IMGSYMLEN ( sizeof IMAGEHLP_SYMBOL )
IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN );
char undName[MAXNAMELEN]; // undecorated name
char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans
IMAGEHLP_MODULE Module;
IMAGEHLP_LINE Line;
memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN );
pSym->SizeOfStruct = IMGSYMLEN;
pSym->MaxNameLength = MAXNAMELEN;
memset( &Line, '\0', sizeof Line );
Line.SizeOfStruct = sizeof Line;
memset( &Module, '\0', sizeof Module );
Module.SizeOfStruct = sizeof Module;
DWORD offsetFromSymbol; // tells us how far from the symbol we were
offsetFromSymbol = 0;
// Enumerate modules and tell imagehlp.dll about them.
enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() );
STACKFRAME stackframe; // in/out stackframe
memset( &stackframe, 0, sizeof(stackframe) );
stackframe.AddrPC.Offset = exceptionInfo->ContextRecord->Eip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = exceptionInfo->ContextRecord->Ebp;
stackframe.AddrFrame.Mode = AddrModeFlat;
// normally, call ImageNtHeader() and use machine info from PE header
DWORD imageType = IMAGE_FILE_MACHINE_I386;
_snprintf(fileName, 1024, "%s%scoredump-%d.txt", szDrive, szDir, dumpParams->threadId);
hFile = CreateFile(
fileName,
GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
char stringBuffer[1024];
DWORD bytesWritten;
char *exceptionStr = "Unknown exception.";
switch (dumpParams->exceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
exceptionStr = "EXCEPTION_ACCESS_VIOLATION, The thread attempted to read from or write to a virtual address for which it does not have the appropriate access.";
break;
case EXCEPTION_BREAKPOINT:
exceptionStr = "EXCEPTION_BREAKPOINT, A breakpoint was encountered.";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
exceptionStr = "EXCEPTION_DATATYPE_MISALIGNMENT, The thread attempted to read or write data that is misaligned on hardware that does not provide alignment.";
break;
case EXCEPTION_SINGLE_STEP:
exceptionStr = "EXCEPTION_SINGLE_STEP, A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
exceptionStr = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED, The thread attempted to access an array element that is out of bounds, and the underlying hardware supports bounds checking.";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
exceptionStr = "EXCEPTION_FLT_DENORMAL_OPERAND, One of the operands in a floating-point operation is denormal.";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
exceptionStr = "EXCEPTION_FLT_DIVIDE_BY_ZERO, The thread attempted to divide a floating-point value by a floating-point divisor of zero.";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
exceptionStr = "EXCEPTION_FLT_INEXACT_RESULT, The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
exceptionStr = "EXCEPTION_FLT_INVALID_OPERATION, This exception represents any floating-point exception not included in this list.";
break;
case EXCEPTION_FLT_OVERFLOW:
exceptionStr = "EXCEPTION_FLT_OVERFLOW, The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
break;
case EXCEPTION_FLT_STACK_CHECK:
exceptionStr = "EXCEPTION_FLT_STACK_CHECK, The stack overflowed or underflowed as the result of a floating-point operation.";
break;
case EXCEPTION_FLT_UNDERFLOW:
exceptionStr = "EXCEPTION_FLT_UNDERFLOW, The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
exceptionStr = "EXCEPTION_INT_DIVIDE_BY_ZERO, The thread attempted to divide an integer value by an integer divisor of zero.";
break;
case EXCEPTION_INT_OVERFLOW:
exceptionStr = "EXCEPTION_INT_OVERFLOW, The result of an integer operation caused a carry out of the most significant bit of the result.";
break;
case EXCEPTION_PRIV_INSTRUCTION:
exceptionStr = "EXCEPTION_PRIV_INSTRUCTION, The thread attempted to execute an instruction whose operation is not allowed in the current machine mode.";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
exceptionStr = "EXCEPTION_NONCONTINUABLE_EXCEPTION, The thread attempted to continue execution after a noncontinuable exception occurred.";
break;
}
_snprintf(stringBuffer, 1024, "Exception in thread %d: %s\r\n", dumpParams->threadId, exceptionStr);
WriteFile(hFile, stringBuffer, strlen(stringBuffer), &bytesWritten, 0);
_snprintf(stringBuffer, 1024, "-- Begin stack trace --\r\n");
WriteFile(hFile, stringBuffer, strlen(stringBuffer), &bytesWritten, 0);
_snprintf(stringBuffer, 1024, " Nr Flags PC Return Frame Stack\r\n");
WriteFile(hFile, stringBuffer, strlen(stringBuffer), &bytesWritten, 0);
int frameNum = 0;
while (true)
{
// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
// assume that either you are done, or that the stack is so hosed that the next
// deeper frame could not be found.
if (!ptrStackWalk(
imageType,
hProcess,
hThread,
&stackframe,
exceptionInfo->ContextRecord,
0,
ptrSymFunctionTableAccess,
ptrSymGetModuleBase,
0 ) )
break;
_snprintf(
stringBuffer,
1024,
"%3d %c%c %08lx %08lx %08lx %08lx ",
frameNum,
stackframe.Far? 'F': '.',
stackframe.Virtual? 'V': '.',
stackframe.AddrPC.Offset,
stackframe.AddrReturn.Offset,
stackframe.AddrFrame.Offset,
stackframe.AddrStack.Offset );
WriteFile(hFile, stringBuffer, strlen(stringBuffer), &bytesWritten, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -