⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 crash_reporter_win32.cpp

📁 这是一款2d游戏引擎
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*  $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 + -