summaryrefslogtreecommitdiff
path: root/source/win_dbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/win_dbg.c')
-rw-r--r--source/win_dbg.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/source/win_dbg.c b/source/win_dbg.c
new file mode 100644
index 0000000..8dad6df
--- /dev/null
+++ b/source/win_dbg.c
@@ -0,0 +1,329 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "win_local.h"
+#include <dbghelp.h>
+
+typedef DWORD (WINAPI *SETSYMOPTIONS)( DWORD );
+typedef BOOL (WINAPI *SYMGETMODULEINFO64)( HANDLE, DWORD64,
+ PIMAGEHLP_MODULE64 );
+typedef BOOL (WINAPI *SYMINITIALIZE)( HANDLE, PSTR, BOOL );
+typedef BOOL (WINAPI *SYMCLEANUP)( HANDLE );
+typedef BOOL (WINAPI *ENUMERATELOADEDMODULES64)( HANDLE,
+ PENUMLOADED_MODULES_CALLBACK64, PVOID );
+typedef BOOL (WINAPI *STACKWALK64)( DWORD, HANDLE, HANDLE, LPSTACKFRAME64,
+ PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64,
+ PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64 );
+typedef BOOL (WINAPI *SYMFROMADDR)( HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO );
+typedef PVOID (WINAPI *SYMFUNCTIONTABLEACCESS64)( HANDLE, DWORD64 );
+typedef DWORD64 (WINAPI *SYMGETMODULEBASE64)( HANDLE, DWORD64 );
+
+typedef HINSTANCE (WINAPI *SHELLEXECUTE)( HWND, LPCSTR, LPCSTR,
+ LPCSTR, LPCSTR, INT );
+
+PRIVATE SETSYMOPTIONS pSymSetOptions;
+PRIVATE SYMGETMODULEINFO64 pSymGetModuleInfo64;
+PRIVATE SYMINITIALIZE pSymInitialize;
+PRIVATE SYMCLEANUP pSymCleanup;
+PRIVATE ENUMERATELOADEDMODULES64 pEnumerateLoadedModules64;
+PRIVATE STACKWALK64 pStackWalk64;
+PRIVATE SYMFROMADDR pSymFromAddr;
+PRIVATE SYMFUNCTIONTABLEACCESS64 pSymFunctionTableAccess64;
+PRIVATE SYMGETMODULEBASE64 pSymGetModuleBase64;
+PRIVATE SHELLEXECUTE pShellExecute;
+
+PRIVATE HANDLE processHandle, threadHandle;
+
+PRIVATE FILE *crashReport;
+
+PRIVATE CHAR moduleName[MAX_PATH];
+
+PRIVATE BOOL CALLBACK EnumModulesCallback(
+ PTSTR ModuleName,
+ DWORD64 ModuleBase,
+ ULONG ModuleSize,
+ PVOID UserContext )
+{
+ IMAGEHLP_MODULE64 moduleInfo;
+ DWORD64 pc = ( DWORD64 )UserContext;
+ BYTE buffer[4096];
+ PBYTE data;
+ UINT numBytes;
+ VS_FIXEDFILEINFO *info;
+ char version[64];
+ char *symbols;
+
+ strcpy( version, "unknown" );
+ if( GetFileVersionInfo( ModuleName, 0, sizeof( buffer ), buffer ) ) {
+ if( VerQueryValue( buffer, "\\", &data, &numBytes ) ) {
+ info = ( VS_FIXEDFILEINFO * )data;
+ sprintf( version, "%u.%u.%u.%u",
+ HIWORD( info->dwFileVersionMS ),
+ LOWORD( info->dwFileVersionMS ),
+ HIWORD( info->dwFileVersionLS ),
+ LOWORD( info->dwFileVersionLS ) );
+ }
+ }
+
+ symbols = "failed";
+ moduleInfo.SizeOfStruct = sizeof( moduleInfo );
+ if( pSymGetModuleInfo64( processHandle, ModuleBase, &moduleInfo ) ) {
+ ModuleName = moduleInfo.ModuleName;
+ switch( moduleInfo.SymType ) {
+ case SymCoff: symbols = "COFF"; break;
+ case SymExport: symbols = "export"; break;
+ case SymNone: symbols = "none"; break;
+ case SymPdb: symbols = "PDB"; break;
+ default: symbols = "unknown"; break;
+ }
+ }
+
+ fprintf( crashReport, "%p %p %s (version %s, symbols %s) ",
+ ModuleBase, ModuleBase + ModuleSize, ModuleName, version, symbols );
+ if( pc >= ModuleBase && pc < ModuleBase + ModuleSize ) {
+ strncpy( moduleName, ModuleName, sizeof( moduleName ) - 1 );
+ moduleName[ sizeof( moduleName ) - 1 ] = 0;
+ fprintf( crashReport, "*\n" );
+ } else {
+ fprintf( crashReport, "\n" );
+ }
+
+ return TRUE;
+}
+
+DWORD Sys_ExceptionHandler( DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo ) {
+ STACKFRAME64 stackFrame;
+ PCONTEXT context;
+ SYMBOL_INFO *symbol;
+ int count, ret, i, len;
+ DWORD64 offset;
+ BYTE buffer[sizeof( SYMBOL_INFO ) + 256 - 1];
+ IMAGEHLP_MODULE64 moduleInfo;
+ char path[MAX_PATH];
+ char execdir[MAX_PATH];
+ char *p;
+ HMODULE helpModule, shellModule;
+ SYSTEMTIME systemTime;
+ static const char monthNames[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ OSVERSIONINFO vinfo;
+
+#if USE_CLIENT
+ Win_Shutdown();
+#endif
+
+ ret = MessageBox( NULL, APPLICATION " has encountered an unhandled "
+ "exception and needs to be terminated.\n"
+ "Would you like to generate a crash report?",
+ "Unhandled Exception",
+ MB_ICONERROR | MB_YESNO
+#if !USE_CLIENT
+ | MB_SERVICE_NOTIFICATION
+#endif
+ );
+ if( ret == IDNO ) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ helpModule = LoadLibrary( "dbghelp.dll" );
+ if( !helpModule ) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+#define GPA( x, y ) \
+ do { \
+ p ## y = ( x )GetProcAddress( helpModule, #y ); \
+ if( !p ## y ) { \
+ return EXCEPTION_CONTINUE_SEARCH; \
+ } \
+ } while( 0 )
+
+ GPA( SETSYMOPTIONS, SymSetOptions );
+ GPA( SYMGETMODULEINFO64, SymGetModuleInfo64 );
+ GPA( SYMCLEANUP, SymCleanup );
+ GPA( SYMINITIALIZE, SymInitialize );
+ GPA( ENUMERATELOADEDMODULES64, EnumerateLoadedModules64 );
+ GPA( STACKWALK64, StackWalk64 );
+ GPA( SYMFROMADDR, SymFromAddr );
+ GPA( SYMFUNCTIONTABLEACCESS64, SymFunctionTableAccess64 );
+ GPA( SYMGETMODULEBASE64, SymGetModuleBase64 );
+
+ pSymSetOptions( SYMOPT_LOAD_ANYTHING|SYMOPT_DEBUG|SYMOPT_FAIL_CRITICAL_ERRORS );
+ processHandle = GetCurrentProcess();
+ threadHandle = GetCurrentThread();
+
+ GetModuleFileName( NULL, execdir, sizeof( execdir ) - 1 );
+ execdir[sizeof( execdir ) - 1] = 0;
+ p = strrchr( execdir, '\\' );
+ if( !p ) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ *p = 0;
+ len = p - execdir;
+ if( len + 24 >= MAX_PATH ) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ memcpy( path, execdir, len );
+ memcpy( path + len, "\\Q2PRO_CrashReportXX.txt", 25 );
+ for( i = 0; i < 100; i++ ) {
+ path[len+18] = '0' + i / 10;
+ path[len+19] = '0' + i % 10;
+ if( !Sys_GetPathInfo( path, NULL ) ) {
+ break;
+ }
+ }
+ crashReport = fopen( path, "w" );
+ if( !crashReport ) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ pSymInitialize( processHandle, execdir, TRUE );
+
+ GetSystemTime( &systemTime );
+ fprintf( crashReport, "Crash report generated %s %u %u, %02u:%02u:%02u UTC\n",
+ monthNames[(systemTime.wMonth - 1) % 12], systemTime.wDay, systemTime.wYear,
+ systemTime.wHour, systemTime.wMinute, systemTime.wSecond );
+ fprintf( crashReport, "by " APPLICATION " " VERSION ", built " __DATE__", " __TIME__ "\n" );
+
+ vinfo.dwOSVersionInfoSize = sizeof( vinfo );
+ if( GetVersionEx( &vinfo ) ) {
+ fprintf( crashReport, "\nWindows version: %u.%u (build %u) %s\n",
+ vinfo.dwMajorVersion, vinfo.dwMinorVersion, vinfo.dwBuildNumber, vinfo.szCSDVersion );
+ }
+
+ strcpy( moduleName, "unknown" );
+
+ context = exceptionInfo->ContextRecord;
+
+ fprintf( crashReport, "\nLoaded modules:\n" );
+ pEnumerateLoadedModules64( processHandle, EnumModulesCallback,
+#ifdef _WIN64
+ ( PVOID )context->Rip
+#else
+ ( PVOID )context->Eip
+#endif
+ );
+
+ fprintf( crashReport, "\nException information:\n" );
+ fprintf( crashReport, "Code: %08x\n", exceptionCode );
+ fprintf( crashReport, "Address: %p (%s)\n",
+#ifdef _WIN64
+ context->Rip,
+#else
+ context->Eip,
+#endif
+ moduleName );
+
+ fprintf( crashReport, "\nThread context:\n" );
+#ifdef _WIN64
+ fprintf( crashReport, "RIP: %p RBP: %p RSP: %p\n",
+ context->Rip, context->Rbp, context->Rsp );
+ fprintf( crashReport, "RAX: %p RBX: %p RCX: %p\n",
+ context->Rax, context->Rbx, context->Rcx );
+ fprintf( crashReport, "RDX: %p RSI: %p RDI: %p\n",
+ context->Rdx, context->Rsi, context->Rdi );
+#else
+ fprintf( crashReport, "EIP: %p EBP: %p ESP: %p\n",
+ context->Eip, context->Ebp, context->Esp );
+ fprintf( crashReport, "EAX: %p EBX: %p ECX: %p\n",
+ context->Eax, context->Ebx, context->Ecx );
+ fprintf( crashReport, "EDX: %p ESI: %p EDI: %p\n",
+ context->Edx, context->Esi, context->Edi );
+#endif
+
+ memset( &stackFrame, 0, sizeof( stackFrame ) );
+#ifdef _WIN64
+ stackFrame.AddrPC.Offset = context->Rip;
+ stackFrame.AddrFrame.Offset = context->Rbp;
+ stackFrame.AddrStack.Offset = context->Rsp;
+#else
+ stackFrame.AddrPC.Offset = context->Eip;
+ stackFrame.AddrFrame.Offset = context->Ebp;
+ stackFrame.AddrStack.Offset = context->Esp;
+#endif
+ stackFrame.AddrPC.Mode = AddrModeFlat;
+ stackFrame.AddrFrame.Mode = AddrModeFlat;
+ stackFrame.AddrStack.Mode = AddrModeFlat;
+
+ fprintf( crashReport, "\nStack trace:\n" );
+ count = 0;
+ symbol = ( SYMBOL_INFO * )buffer;
+ symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
+ symbol->MaxNameLen = 256;
+ while( pStackWalk64(
+#ifdef _WIN64
+ IMAGE_FILE_MACHINE_AMD64,
+#else
+ IMAGE_FILE_MACHINE_I386,
+#endif
+ processHandle,
+ threadHandle,
+ &stackFrame,
+ context,
+ NULL,
+ pSymFunctionTableAccess64,
+ pSymGetModuleBase64,
+ NULL ) )
+ {
+ fprintf( crashReport, "%d: %p %p %p %p ",
+ count,
+ stackFrame.Params[0],
+ stackFrame.Params[1],
+ stackFrame.Params[2],
+ stackFrame.Params[3] );
+
+ moduleInfo.SizeOfStruct = sizeof( moduleInfo );
+ if( pSymGetModuleInfo64( processHandle, stackFrame.AddrPC.Offset, &moduleInfo ) ) {
+ if( moduleInfo.SymType != SymNone && moduleInfo.SymType != SymExport &&
+ pSymFromAddr( processHandle, stackFrame.AddrPC.Offset, &offset, symbol ) )
+ {
+ fprintf( crashReport, "%s!%s+%#x\n",
+ moduleInfo.ModuleName,
+ symbol->Name, offset );
+ } else {
+ fprintf( crashReport, "%s!%#x\n",
+ moduleInfo.ModuleName,
+ stackFrame.AddrPC.Offset );
+ }
+ } else {
+ fprintf( crashReport, "%#x\n",
+ stackFrame.AddrPC.Offset );
+ }
+ count++;
+ }
+
+ fclose( crashReport );
+
+ shellModule = LoadLibrary( "shell32.dll" );
+ if( shellModule ) {
+ pShellExecute = ( SHELLEXECUTE )GetProcAddress( shellModule, "ShellExecuteA" );
+ if( pShellExecute ) {
+ pShellExecute( NULL, "open", path, NULL, execdir, SW_SHOW );
+ }
+ }
+
+ pSymCleanup( processHandle );
+
+ ExitProcess( 1 );
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+