/** * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * * Copyright (C) 2012-2018 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules 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 3 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, see . */ // Ported from eAthena Dev Team's version @ http://eathena-project.googlecode.com/svn/trunk/src/plugins/dbghelpplug.c // Currently supported dbghelp 5.1 #include "common/hercules.h" #include "common/sysinfo.h" #include "common/HPMDataCheck.h" #include #include /** * Plugin basic information **/ HPExport struct hplugin_info pinfo = { "Debug Help", SERVER_TYPE_ALL, "0.5", HPM_VERSION, }; ///////////////////////////////////////////////////////////////////// // Include files // #include #define WIN32_LEAN_AND_MEAN #include #define _NO_CVCONST_H #include #include #include #include ///////////////////////////////////////////////////////////////////// // Types from Cvconst.h (DIA SDK) // #ifdef _NO_CVCONST_H typedef enum _BasicType { btNoType = 0, btVoid = 1, btChar = 2, btWChar = 3, btInt = 6, btUInt = 7, btFloat = 8, btBCD = 9, btBool = 10, btLong = 13, btULong = 14, btCurrency = 25, btDate = 26, btVariant = 27, btComplex = 28, btBit = 29, btBSTR = 30, btHresult = 31 } BasicType; typedef enum _UdtKind { UdtStruct, UdtClass, UdtUnion } UdtKind; /* typedef enum _SymTag { SymTagNull = 0, SymTagExe = 1, SymTagCompiland = 2, SymTagCompilandDetails = 3, SymTagCompilandEnv = 4, SymTagFunction = 5, SymTagBlock = 6, SymTagData = 7, SymTagAnnotation = 8, SymTagLabel = 9, SymTagPublicSymbol = 10, SymTagUDT = 11, SymTagEnum = 12, SymTagFunctionType = 13, SymTagPointerType = 14, SymTagArrayType = 15, SymTagBaseType = 16, SymTagTypedef = 17, SymTagBaseClass = 18, SymTagFriend = 19, SymTagFunctionArgType = 20, SymTagFuncDebugStart = 21, SymTagFuncDebugEnd = 22, SymTagUsingNamespace = 23, SymTagVTableShape = 24, SymTagVTable = 25, SymTagCustom = 26, SymTagThunk = 27, SymTagCustomType = 28, SymTagManagedType = 29, SymTagDimension = 30 } SymTag; */ #endif /* _NO_CVCONST_H */ ///////////////////////////////////////////////////////////////////// // dbghelp function prototypes // typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)( HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam ); typedef BOOL (WINAPI *SYMINITIALIZE)( HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess ); typedef DWORD (WINAPI *SYMSETOPTIONS)( DWORD SymOptions ); typedef DWORD (WINAPI *SYMGETOPTIONS)( VOID ); typedef BOOL (WINAPI *SYMCLEANUP)( HANDLE hProcess ); typedef BOOL (WINAPI *SYMGETTYPEINFO)( HANDLE hProcess, DWORD64 ModBase, ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType, PVOID pInfo ); typedef BOOL (WINAPI *SYMGETLINEFROMADDR)( HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line ); typedef BOOL (WINAPI *SYMENUMSYMBOLS)( HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, PVOID UserContext ); typedef BOOL (WINAPI *SYMSETCONTEXT)( HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame, PIMAGEHLP_CONTEXT Context ); typedef BOOL (WINAPI *SYMFROMADDR)( HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol ); typedef BOOL (WINAPI *STACKWALK)( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress ); typedef PVOID (WINAPI *SYMFUNCTIONTABLEACCESS)( HANDLE hProcess, DWORD AddrBase ); typedef DWORD (WINAPI *SYMGETMODULEBASE)( HANDLE hProcess, DWORD dwAddr ); ///////////////////////////////////////////////////////////////////// // Custom info /// Internal structure used to pass some data around typedef struct _InternalData { // PrintStacktrace FILE* log_file; STACKFRAME* pStackframe; HANDLE hProcess; DWORD nr_of_frame; // PrintFunctionDetail BOOL as_arg_list; BOOL log_params; BOOL log_locals; BOOL log_globals; DWORD nr_of_var; // PrintDataInfo ULONG64 modBase; } InterData; /// dbghelp dll filename #define DBGHELP_DLL "dbghelp.dll" // Default report filename, used when the module path is unavailable #define DBG_DEFAULT_FILENAME "hercules" // Extended information printed in the console #define DBG_EXTENDED_INFORMATION \ "Please report the crash in our Issues tracker:\n" \ "https://github.com/HerculesWS/Hercules/issues\n" // Print object children? // WARNING: This will generate huge dump files! //#define DBG_PRINT_CHILDREN ///////////////////////////////////////////////////////////////////// // Global variables HANDLE dbghelp_dll = INVALID_HANDLE_VALUE; MINIDUMPWRITEDUMP MiniDumpWriteDump_ = NULL; SYMINITIALIZE SymInitialize_ = NULL; SYMSETOPTIONS SymSetOptions_ = NULL; SYMGETOPTIONS SymGetOptions_ = NULL; SYMCLEANUP SymCleanup_ = NULL; SYMGETTYPEINFO SymGetTypeInfo_ = NULL; SYMGETLINEFROMADDR SymGetLineFromAddr_ = NULL; SYMENUMSYMBOLS SymEnumSymbols_ = NULL; SYMSETCONTEXT SymSetContext_ = NULL; SYMFROMADDR SymFromAddr_ = NULL; STACKWALK StackWalk_ = NULL; SYMFUNCTIONTABLEACCESS SymFunctionTableAccess_ = NULL; SYMGETMODULEBASE SymGetModuleBase_ = NULL; ///////////////////////////////////////////////////////////////////// // Code /// Writes the minidump to file. The callback function will at the /// same time write the list of modules to the log file. /// /// @param file Filename of the minidump /// @param ptrs Exception info /// @param module_callback Callback for MiniDumpWriteDump /// @param log_file Log file static VOID Dhp__WriteMinidumpFile( const char* file, PEXCEPTION_POINTERS ptrs, MINIDUMP_CALLBACK_ROUTINE module_callback, FILE* log_file) { // open minidump file HANDLE minidump_file = CreateFileA( file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( minidump_file != INVALID_HANDLE_VALUE ) { MINIDUMP_EXCEPTION_INFORMATION expt_info; MINIDUMP_CALLBACK_INFORMATION dump_cb_info; expt_info.ThreadId = GetCurrentThreadId(); expt_info.ExceptionPointers = ptrs; expt_info.ClientPointers = FALSE; dump_cb_info.CallbackRoutine = module_callback; dump_cb_info.CallbackParam = (void*)log_file; if( module_callback != NULL && log_file != NULL ) fprintf(log_file, "\n\nLoaded modules:\n"); MiniDumpWriteDump_( GetCurrentProcess(), GetCurrentProcessId(), minidump_file, MiniDumpNormal, ptrs ? &expt_info : NULL, NULL, &dump_cb_info ); CloseHandle(minidump_file); } } /// Prints module information to the log file. /// Used as a callback to MiniDumpWriteDump. /// /// @param data Log file /// @param callback_input /// @param callback_output /// @return static BOOL CALLBACK Dhp__PrintModuleInfoCallback( void* data, CONST PMINIDUMP_CALLBACK_INPUT callback_input, PMINIDUMP_CALLBACK_OUTPUT callback_output) { if( data != NULL && callback_input != NULL && callback_input->CallbackType == ModuleCallback) { FILE* log_file = (FILE*)data; MINIDUMP_MODULE_CALLBACK module = callback_input->Module; fprintf(log_file, "0x%p", module.BaseOfImage); fprintf(log_file, " %ws", module.FullPath, log_file); fprintf(log_file, " (%d.%d.%d.%d, %d bytes)\n", HIWORD(module.VersionInfo.dwFileVersionMS), LOWORD(module.VersionInfo.dwFileVersionMS), HIWORD(module.VersionInfo.dwFileVersionLS), LOWORD(module.VersionInfo.dwFileVersionLS), module.SizeOfImage); } return TRUE; } /// Prints details about the current process, platform and exception /// information to the log file. /// /// @param exception Exception info /// @param context Exception context /// @param log_file Log file static VOID Dhp__PrintProcessInfo( EXCEPTION_RECORD* exception, CONTEXT* context, FILE* log_file) { LPSTR cmd_line; fprintf(log_file, "\nProcess information:\n"); // print the command line cmd_line = GetCommandLineA(); if( cmd_line ) fprintf(log_file, "Command line: %s\n", cmd_line); // Print system information if( sysinfo ) { fprintf(log_file, "Platform: %s\n CPU: %s\nApplication architecture: %s\nCompiler: %s\n%s: %s\n", sysinfo->osversion(), sysinfo->cpu(), (sysinfo->is64bit())?"x64":"x86", sysinfo->compiler(), sysinfo->vcstype(), sysinfo->vcsrevision_src()); } // print the exception code if( exception ) { fprintf(log_file, "\nException:\n" "0x%x", exception->ExceptionCode); switch( exception->ExceptionCode ) { #define PRINT(x) case x: fprintf(log_file, " "#x); break PRINT(EXCEPTION_ACCESS_VIOLATION); PRINT(EXCEPTION_DATATYPE_MISALIGNMENT); PRINT(EXCEPTION_BREAKPOINT); PRINT(EXCEPTION_SINGLE_STEP); PRINT(EXCEPTION_ARRAY_BOUNDS_EXCEEDED); PRINT(EXCEPTION_FLT_DENORMAL_OPERAND); PRINT(EXCEPTION_FLT_DIVIDE_BY_ZERO); PRINT(EXCEPTION_FLT_INEXACT_RESULT); PRINT(EXCEPTION_FLT_INVALID_OPERATION); PRINT(EXCEPTION_FLT_OVERFLOW); PRINT(EXCEPTION_FLT_STACK_CHECK); PRINT(EXCEPTION_FLT_UNDERFLOW); PRINT(EXCEPTION_INT_DIVIDE_BY_ZERO); PRINT(EXCEPTION_INT_OVERFLOW); PRINT(EXCEPTION_PRIV_INSTRUCTION); PRINT(EXCEPTION_IN_PAGE_ERROR); PRINT(EXCEPTION_ILLEGAL_INSTRUCTION); PRINT(EXCEPTION_NONCONTINUABLE_EXCEPTION); PRINT(EXCEPTION_STACK_OVERFLOW); PRINT(EXCEPTION_INVALID_DISPOSITION); PRINT(EXCEPTION_GUARD_PAGE); PRINT(EXCEPTION_INVALID_HANDLE); #undef PRINT } // print where the fault occured fprintf(log_file, " at location 0x%p", exception->ExceptionAddress); // if the exception was an access violation, print additional information if( exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && exception->NumberParameters >= 2 ) fprintf(log_file, " %s location 0x%p", exception->ExceptionInformation[0] ? "writing to" : "reading from", exception->ExceptionInformation[1]); fprintf(log_file, "\n"); } // print the register info if( context ) { #if defined(_M_IX86) fprintf(log_file, "\nRegisters:\n"); if( context->ContextFlags & CONTEXT_INTEGER ) { fprintf(log_file, "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n", context->Eax, context->Ebx, context->Ecx, context->Edx, context->Esi, context->Edi); } if( context->ContextFlags & CONTEXT_CONTROL ) { fprintf(log_file, "eip=%08x esp=%08x ebp=%08x iopl=%1x %s %s %s %s %s %s %s %s %s %s\n", context->Eip, context->Esp, context->Ebp, (context->EFlags >> 12) & 3, // IOPL level value (context->EFlags & 0x00100000) ? "vip" : " ", // VIP (virtual interrupt pending) (context->EFlags & 0x00080000) ? "vif" : " ", // VIF (virtual interrupt flag) (context->EFlags & 0x00000800) ? "ov" : "nv", // VIF (virtual interrupt flag) (context->EFlags & 0x00000400) ? "dn" : "up", // OF (overflow flag) (context->EFlags & 0x00000200) ? "ei" : "di", // IF (interrupt enable flag) (context->EFlags & 0x00000080) ? "ng" : "pl", // SF (sign flag) (context->EFlags & 0x00000040) ? "zr" : "nz", // ZF (zero flag) (context->EFlags & 0x00000010) ? "ac" : "na", // AF (aux carry flag) (context->EFlags & 0x00000004) ? "po" : "pe", // PF (parity flag) (context->EFlags & 0x00000001) ? "cy" : "nc"); // CF (carry flag) } if( context->ContextFlags & CONTEXT_SEGMENTS ) { fprintf(log_file, "cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x", context->SegCs, context->SegSs, context->SegDs, context->SegEs, context->SegFs, context->SegGs); if( context->ContextFlags & CONTEXT_CONTROL ) fprintf(log_file, " efl=%08x", context->EFlags); fprintf(log_file, "\n"); } else if( context->ContextFlags & CONTEXT_CONTROL ) fprintf(log_file, " efl=%08x\n", context->EFlags); #else /* defined(_M_IX86) */ //TODO add more processors #endif } } /// Prints the typename of the symbol. /// /// @param typeIndex Type index of the symbol /// @param symtag Symbol tag /// @param withParens If brackets are printed around the typename /// @param interData Inter data static VOID Dhp__PrintTypeName( DWORD typeIndex, DWORD symtag, BOOL withParens, InterData* interData) { // inter data FILE* log_file; HANDLE hProcess; ULONG64 modBase; // assert( interData != NULL ); log_file = interData->log_file; hProcess = interData->hProcess; modBase = interData->modBase; if( withParens ) fprintf(log_file, "("); switch( symtag ) { case SymTagEnum: { WCHAR* pwszTypeName; if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pwszTypeName) ) { fprintf(log_file, "enum %ls", pwszTypeName); LocalFree(pwszTypeName); } else fprintf(log_file, "enum "); } break; case SymTagBaseType: { DWORD basetype; ULONG64 length = 0; SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length); if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) ) { fprintf(log_file, ""); break; } switch( basetype ) { case btVoid: fprintf(log_file, "void"); break; case btChar: fprintf(log_file, "char"); break; case btWChar: fprintf(log_file, "wchar"); break; case btULong: fprintf(log_file, "unsigned "); // next case btLong: fprintf(log_file, "long"); break; case btUInt: fprintf(log_file, "unsigned "); // next case btInt: if( length == sizeof(char) ) fprintf(log_file, "char"); else if( length == sizeof(short) ) fprintf(log_file, "short"); else if( length == sizeof(int) ) fprintf(log_file, "int"); else if( length == sizeof(long long) ) fprintf(log_file, "long long"); else fprintf(log_file, "", length*8); break; case btFloat: if( length == sizeof(float) ) fprintf(log_file, "float"); else if( length == sizeof(double) ) fprintf(log_file, "double"); else if( length == sizeof(long double) ) fprintf(log_file, "long double"); else fprintf(log_file, "", length*8); break; default: fprintf(log_file, "", basetype, length); break; } } break; case SymTagPointerType: { DWORD subtype; DWORD subtag; if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &subtype) ) fprintf(log_file, "*"); else if( !SymGetTypeInfo_(hProcess, modBase, subtype, TI_GET_SYMTAG, &subtag) ) fprintf(log_file, "*"); else { Dhp__PrintTypeName(subtype, subtag, FALSE, interData); fprintf(log_file, "*"); } } break; case SymTagArrayType: { DWORD childTypeIndex; DWORD childSymtag; // print root type childTypeIndex = typeIndex; childSymtag = symtag; for( ; ; ) { if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) ) { fprintf(log_file, ""); break; } if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) ) { fprintf(log_file, ""); break; } if( childSymtag != SymTagArrayType ) { Dhp__PrintTypeName(childTypeIndex, childSymtag, FALSE, interData); break; } // next dimension } // print dimensions childTypeIndex = typeIndex; childSymtag = symtag; for( ; childSymtag == SymTagArrayType ; ) { DWORD childCount; if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_COUNT, &childCount) ) fprintf(log_file, "[]"); else fprintf(log_file, "[%u]", childCount); if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) ) { fprintf(log_file, ""); break; } if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) ) { fprintf(log_file, ""); break; } // next dimension } } break; default: { WCHAR* pSymname; DWORD udtkind; if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_UDTKIND, &udtkind) ) { switch( (UdtKind)udtkind ) { case UdtStruct: fprintf(log_file, "struct "); break; case UdtClass: fprintf(log_file, "class "); break; case UdtUnion: fprintf(log_file, "union "); break; default: fprintf(log_file, " ", udtkind); break; } } if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pSymname ) ) { fprintf(log_file, "%ls", pSymname); LocalFree( pSymname ); } else fprintf(log_file, "", symtag); break; } break; } if( withParens ) fprintf(log_file, ")"); } /// Prints the bytes in the target location. /// /// @param log_file Log file /// @param p Pointer to the data /// @param length Length of the data in bytes static VOID Dhp__PrintValueBytes( FILE* log_file, BYTE* p, ULONG64 length) { ULONG64 i; fprintf(log_file, ""); } /// Prints a wide string/char value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueWideChars( FILE* log_file, WCHAR* wstr, ULONG64 length, BOOL isString) { ULONG64 i; char* buf; char delim; length /= sizeof(WCHAR); delim = ( isString || length > 1 ? '\"' : '\'' ); fprintf(log_file, "%c", delim); buf = (char *)LocalAlloc(LMEM_FIXED, MB_CUR_MAX+1); buf[MB_CUR_MAX] = '\0'; for( i = 0; i < length; ++i ) { int n; switch( wstr[i] ) { case L'\"': fprintf(log_file, "\\\""); break; case L'\'': fprintf(log_file, "\\\'"); break; case L'\\': fprintf(log_file, "\\\\"); break; case L'\a': fprintf(log_file, "\\a"); break; case L'\b': fprintf(log_file, "\\b"); break; case L'\f': fprintf(log_file, "\\f"); break; case L'\n': fprintf(log_file, "\\n"); break; case L'\r': fprintf(log_file, "\\r"); break; case L'\t': fprintf(log_file, "\\t"); break; case L'\v': fprintf(log_file, "\\v"); break; default: if( iswprint(wstr[i]) && (n=wctomb(buf, wstr[i])) > 0 ) { buf[n] = '\0'; fprintf(log_file, "%s", buf); } else fprintf(log_file, "\\u%04X", wstr[i]); break; } } LocalFree(buf); fprintf(log_file, "%c", delim); } /// Prints a string/char value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueChars( FILE* log_file, char* str, ULONG64 length, BOOL isString) { ULONG64 i; char delim; length /= sizeof(char); delim = ( isString || length > 1 ? '\"' : '\'' ); fprintf(log_file, "%c", delim); for( i = 0; i < length; ++i ) { switch( str[i] ) { case '\"': fprintf(log_file, "\\\""); break; case '\'': fprintf(log_file, "\\\'"); break; case '\\': fprintf(log_file, "\\\\"); break; case '\a': fprintf(log_file, "\\a"); break; case '\b': fprintf(log_file, "\\b"); break; case '\f': fprintf(log_file, "\\f"); break; case '\n': fprintf(log_file, "\\n"); break; case '\r': fprintf(log_file, "\\r"); break; case '\t': fprintf(log_file, "\\t"); break; case '\v': fprintf(log_file, "\\v"); break; default: if( isprint((unsigned char)str[i]) ) fprintf(log_file, "%c", str[i]); else fprintf(log_file, "\\x%02X", (unsigned char)str[i]); break; } } fprintf(log_file, "%c", delim); } /// Prints a float value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueFloat( FILE* log_file, VOID* p, ULONG64 length) { if( length == sizeof(float) ) fprintf(log_file, "%f", *(float*)p); else if( length == sizeof(double) ) fprintf(log_file, "%lf", *(double*)p); else if( length == sizeof(long double) ) fprintf(log_file, "%Lf", *(long double*)p); else { fprintf(log_file, "", length); Dhp__PrintValueBytes(log_file, (BYTE*)p, length); } } /// Prints a hex value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueHex( FILE* log_file, VOID* p, ULONG64 length) { if( length == sizeof(UINT32) ) fprintf(log_file, "0x%I32X", *(UINT32*)p); else if( length == sizeof(UINT64) ) fprintf(log_file, "0x%I64X", *(UINT64*)p); else if( length == sizeof(char) ) fprintf(log_file, "0x%X", *(unsigned char*)p); else if( length == sizeof(short) ) fprintf(log_file, "0x%X", *(unsigned short*)p); else if( length == sizeof(int) ) fprintf(log_file, "0x%x", *(unsigned int*)p); else if( length == sizeof(long) ) fprintf(log_file, "0x%lX", *(unsigned long*)p); else if( length == sizeof(long long) ) fprintf(log_file, "0x%llX", *(unsigned long long*)p); else { fprintf(log_file, "", length); Dhp__PrintValueBytes(log_file, (BYTE*)p, length); } } /// Prints an unsigned integer value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueUnsigned( FILE* log_file, VOID* p, ULONG64 length) { if( length == sizeof(INT32) ) fprintf(log_file, "%I32u", *(INT32*)p); else if( length == sizeof(INT64) ) fprintf(log_file, "%I64u", *(INT64*)p); else if( length == sizeof(char) ) fprintf(log_file, "%u", *(unsigned char*)p); else if( length == sizeof(short) ) fprintf(log_file, "%u", *(unsigned short*)p); else if( length == sizeof(int) ) fprintf(log_file, "%u", *(unsigned int*)p); else if( length == sizeof(long) ) fprintf(log_file, "%lu", *(unsigned long*)p); else if( length == sizeof(long long) ) fprintf(log_file, "%llu", *(unsigned long long*)p); else { fprintf(log_file, "", length); Dhp__PrintValueBytes(log_file, (BYTE*)p, length); } } /// Prints a signed integer value. /// /// @param log_file Log file /// @param p Pointer to the value /// @param length Length of the value in bytes static VOID Dhp__PrintValueSigned( FILE* log_file, VOID* p, ULONG64 length) { if( length == sizeof(INT32) ) fprintf(log_file, "%I32d", *(INT32*)p); else if( length == sizeof(INT64) ) fprintf(log_file, "%I64d", *(INT64*)p); else if( length == sizeof(char) ) fprintf(log_file, "%d", *(signed char*)p); else if( length == sizeof(short) ) fprintf(log_file, "%d", *(signed short*)p); else if( length == sizeof(int) ) fprintf(log_file, "%d", *(signed int*)p); else if( length == sizeof(long) ) fprintf(log_file, "%ld", *(signed long*)p); else if( length == sizeof(long long) ) fprintf(log_file, "%lld", *(signed long long*)p); else { fprintf(log_file, "", length); Dhp__PrintValueBytes(log_file, (BYTE*)p, length); } } /// Prints a nul-terminated wide string value. /// Checks if the memory can be read from. /// /// @param log_file Log file /// @param str Target string static VOID Dhp__PrintValueCWideString( FILE* log_file, WCHAR* str) { ULONG64 length = 0; // check if memory is readable __try { while( str[length] != L'\0' ) ++length; } __except( EXCEPTION_EXECUTE_HANDLER ) { if( length ) Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE); // print readable part fprintf(log_file, ""); return; } // print string Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE); } /// Prints a nul-terminated string value. /// Checks if the memory can be read from. /// /// @param log_file Log file /// @param str Target string static VOID Dhp__PrintValueCString( FILE* log_file, char* str) { ULONG64 length = 0; assert( log_file != NULL ); // check if memory is readable __try { while( str[length] != '\0' ) ++length; } __except( EXCEPTION_EXECUTE_HANDLER ) { if( length ) Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE); // print readable part fprintf(log_file, ""); return; } // print string Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE); } // forward declaration of Dhp__PrintDataContents static VOID Dhp__PrintDataContents(DWORD typeIndex, PVOID pVariable, InterData* interData); /// Prints the value of the data symbol. /// Checks if the memory can be read from. /// /// @param typeIndex Type index of the symbol /// @param symtag Symbol tag /// @param pVariable Address to the symbol contents /// @param pInterData Inter data static VOID Dhp__PrintDataValue( DWORD typeIndex, DWORD symtag, PVOID pVariable, InterData* pInterData) { // inter data FILE* log_file; DWORD64 modBase; HANDLE hProcess; // ULONG64 length = 0; DWORD basetype; assert( pInterData != NULL ); log_file = pInterData->log_file; modBase = pInterData->modBase; hProcess = pInterData->hProcess; if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length) ) { fprintf(log_file, ""); return; } // check if memory is readable __try { BYTE* p = (BYTE*)pVariable; ULONG i; BYTE b = 0; for( i = 0; i < length; ++i ) b += p[i]; // add to make sure it's not optimized out in release mode // Don't continue if there's no valid data if( b == 0 ) { fprintf(log_file, ""); return; } } __except( EXCEPTION_EXECUTE_HANDLER ) { fprintf(log_file, ""); return; } switch( symtag ) { case SymTagBaseType: { if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } switch( basetype ) { case btInt: case btLong: Dhp__PrintValueSigned(log_file, pVariable, length); break; case btUInt: case btULong: Dhp__PrintValueUnsigned(log_file, pVariable, length); break; case btFloat: Dhp__PrintValueFloat(log_file, pVariable, length); break; case btChar: { if( length == sizeof(char) ) fprintf(log_file, "%u ", *(unsigned char*)pVariable); Dhp__PrintValueChars(log_file, (char*)pVariable, length, FALSE); } break; case btWChar: { if( length == sizeof(WCHAR) ) fprintf(log_file, "%u ", *(WCHAR*)pVariable); Dhp__PrintValueWideChars(log_file, (WCHAR*)pVariable, length, FALSE); } break; case btVoid: if( length > 0 ) Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; default: fprintf(log_file, "", basetype); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } } break; case SymTagEnum: Dhp__PrintValueHex(log_file, pVariable, length); break; case SymTagPointerType: { DWORD childTypeIndex; DWORD childSymtag; fprintf(log_file, "0x%p", *(void**)pVariable); if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) && SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) && childSymtag != SymTagPointerType ) { DWORD childBasetype; // child isn't a pointer, print the contents fprintf(log_file, " "); if( childSymtag == SymTagBaseType && SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_BASETYPE, &childBasetype) && (childBasetype == btChar || childBasetype == btWChar) ) { // string or wide string if( childBasetype == btChar ) Dhp__PrintValueCString(log_file, *(char**)pVariable); else if( childBasetype == btWChar ) Dhp__PrintValueCWideString(log_file, *(WCHAR**)pVariable); else fprintf(log_file, "", childBasetype); break; } Dhp__PrintDataValue(childTypeIndex, childSymtag, *(PVOID*)pVariable, pInterData); } } break; case SymTagArrayType: { DWORD childTypeIndex; DWORD childSymtag; DWORD count; DWORD i; if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } LocalFree(&childSymtag); if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_COUNT, &count) ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } // print values fprintf(log_file, "{"); for( i = 0; i < count; ++i ) { BYTE* pData = pVariable; pData += i*(length/count); if( i > 0 ) fprintf(log_file, ","); Dhp__PrintDataValue(childTypeIndex, childSymtag, pData, pInterData); } fprintf(log_file, "}"); } break; default: #ifdef DBG_PRINT_CHILDREN { TI_FINDCHILDREN_PARAMS *children = NULL; size_t childrenSize; DWORD childCount = 0; DWORD i; // count children if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_CHILDRENCOUNT, &childCount) || !childCount ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); return; } // Prepare to get an array of "TypeIds", representing each of the children. // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this. childrenSize = sizeof(TI_FINDCHILDREN_PARAMS)+childCount*sizeof(ULONG); children = (TI_FINDCHILDREN_PARAMS*)LocalAlloc(LMEM_ZEROINIT, childrenSize); children->Count = childCount; // Get the array of TypeIds, one for each child type if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_FINDCHILDREN, children) || !children ) { fprintf(log_file, ""); Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); LocalFree(children); return; } // Iterate through each of the children fprintf(log_file, "\n{"); for( i = 0; i < childCount; ++i ) { DWORD childOffset; WCHAR *childName = NULL; DWORD childTypeId; DWORD_PTR pData; fprintf(log_file, "\n\t"); // Get the offset of the child member, relative to its parent if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_OFFSET, &childOffset) ) { fprintf(log_file, ""); continue; } // Calculate the address of the member pData = (DWORD_PTR)pVariable; pData += childOffset; if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_SYMNAME, &childName) ) { fprintf(log_file, ""); continue; } fprintf(log_file, "%ws=", childName); LocalFree(childName); if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_TYPEID, &childTypeId) ) { fprintf(log_file, ""); continue; } // print contents of the child Dhp__PrintDataContents(childTypeId, (PVOID)pData, pInterData); } fprintf(log_file, "\n}"); LocalFree(children); } #endif Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); break; } } /// Prints the contents of the data symbol. (type and value) /// /// @param typeIndex Type index of the symbol /// @param pVariable Address of the symbol contents /// @param pInterData Inter data static VOID Dhp__PrintDataContents( DWORD typeIndex, PVOID pVariable, InterData* pInterData) { // inter data FILE* log_file; HANDLE hProcess; DWORD64 modBase; // DWORD symtag; assert( pInterData != NULL ); log_file = pInterData->log_file; hProcess = pInterData->hProcess; modBase = pInterData->modBase; if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMTAG, &symtag) ) { // print type Dhp__PrintTypeName(typeIndex, symtag, TRUE, pInterData); // print value Dhp__PrintDataValue(typeIndex, symtag, pVariable, pInterData); } else fprintf(log_file, ""); } /// Prints information about the data symbol. /// /// @param pSymInfo Symbol info /// @param pInterData Inter data static VOID Dhp__PrintDataInfo( PSYMBOL_INFO pSymInfo, InterData* pInterData) { // inter data FILE* log_file; STACKFRAME* pStackframe; BOOL as_arg_list; BOOL log_params; BOOL log_locals; BOOL log_globals; int nr_of_var; // my data DWORD_PTR pVariable = 0; enum{ UNKNOWN, PARAM, LOCAL, GLOBAL } scope = UNKNOWN; assert( pSymInfo != NULL ); assert( pInterData != NULL ); assert( pSymInfo->Tag == SymTagData ); log_file = pInterData->log_file; pStackframe = pInterData->pStackframe; as_arg_list = pInterData->as_arg_list; log_params = pInterData->log_params; log_locals = pInterData->log_locals; log_globals = pInterData->log_globals; nr_of_var = pInterData->nr_of_var; // Determine the scope and address of the variable if( pSymInfo->Flags & SYMFLAG_REGREL ) { pVariable = pStackframe->AddrFrame.Offset; pVariable += (DWORD_PTR)pSymInfo->Address; if( pSymInfo->Flags & SYMFLAG_PARAMETER ) scope = PARAM; // parameter else if( pSymInfo->Flags & SYMFLAG_LOCAL ) { scope = LOCAL; // local #if defined(_M_IX86) if( (LONG64)pSymInfo->Address > 0) scope = PARAM; // parameter as local (bug in DBGHELP 5.1) #endif } } else if( pSymInfo->Flags & SYMFLAG_REGISTER ) { scope = (pSymInfo->Flags & SYMFLAG_PARAMETER) ? PARAM : LOCAL; // register, optimized out(?) } else { pVariable = (DWORD_PTR)pSymInfo->Address; scope = GLOBAL; // It must be a global variable } // check if we should to log the variable if( (scope == PARAM && log_params) || (scope == LOCAL && log_locals) || (scope == GLOBAL && log_globals) ) { // print prefix and name if( as_arg_list ) fprintf(log_file, "%s%s=", (nr_of_var ? ", " : ""), pSymInfo->Name); else fprintf(log_file, "\t%s = ", pSymInfo->Name); // print value if( !(pSymInfo->Flags & SYMFLAG_REGREL) && (pSymInfo->Flags & SYMF_REGISTER) ) fprintf(log_file, ""); else { pInterData->modBase = pSymInfo->ModBase; Dhp__PrintDataContents(pSymInfo->TypeIndex, (PVOID)pVariable, pInterData); } // print postfix if( !as_arg_list ) fprintf(log_file, "\n"); pInterData->nr_of_var = ++nr_of_var; } } /// Prints information about the symbol. /// /// @param pSymInfo Symbol info /// @param pInterData Inter data static VOID Dhp__PrintSymbolInfo( PSYMBOL_INFO pSymInfo, InterData* pInterData) { assert( pSymInfo != NULL ); assert( pInterData != NULL ); switch( pSymInfo->Tag ) { case SymTagData: Dhp__PrintDataInfo( pSymInfo, pInterData ); break; default: /*fprintf(pInterData->log_file, "", pSymInfo->Tag);*/ break; } } /// Prints the details of one symbol to the log file. /// Used as a callback for SymEnumSymbols. /// /// @param pSymInfo Symbol info /// @param symSize Size of the symbol info structure /// @param pData Inter data static BOOL WINAPI Dhp__EnumSymbolsCallback( PSYMBOL_INFO pSymInfo, ULONG symSize, PVOID pData) { if( pSymInfo == NULL ) return TRUE; // try other symbols if( pData == NULL ) { printf("Dhp__EnumSymbolsCallback: pData is NULL\n"); return FALSE; } Dhp__PrintSymbolInfo(pSymInfo, (InterData*)pData); return TRUE; } /// Prints the source code of the target line. /// Searches for the target file in the original path, /// in the last src folder of the original path (relative) /// and in the current directory. /// /// @param filename Original source file /// @param line Target line /// @param log_file Log file static VOID Dhp__PrintSourceLine( FILE* log_file, char* filename, DWORD line) { char path[MAX_PATH*3]; char pathBuffer[MAX_PATH+1]; char* p; assert( filename != NULL ); assert( log_file != NULL ); // generate search paths strcpy(path, filename); // original path p = strrchr(path, '\\'); if( p ) { memcpy(p, ";\0", 2); p = strstr(filename, "\\src\\"); if( p ) { while( strstr(p+1, "\\src\\") ) p = strstr(p+1, "\\src\\"); strcat(path, p+1); // last src folder path p = strrchr(path, '\\'); memcpy(p, ";\0", 2); } filename = strrchr(filename, '\\')+1; } else *path = '\0'; // no path strcat(path, "."); // current directoy // search for file and line if( SearchPathA(path, filename, NULL, MAX_PATH, pathBuffer, NULL) ) { char code[1024+1]; DWORD i = 1; FILE* fp; fp = fopen(pathBuffer, "rt"); if( fp == NULL ) return; code[1024] = '\0'; while( fgets(code, 1024, fp) ) { if( i == line ) {// found line char* term = strchr(code, '\n'); if( term && term != code && term[-1] == '\r' ) --term; if( term ) *term = '\0'; fprintf(log_file, "%d\t%s\n", line, code); break; } if( strchr(code, '\n') ) ++i; } fclose(fp); } } /// Prints details of one function to the log file. /// /// @param interData Inter data static VOID Dhp__PrintFunctionDetails( InterData* pInterData) { // inter data HANDLE hProcess; STACKFRAME* pStackframe; FILE* log_file; int nr_of_frame; // PSYMBOL_INFO pSymbolInfo; DWORD64 funcDisplacement=0; IMAGEHLP_STACK_FRAME imagehlpStackFrame; IMAGEHLP_LINE imagehlpLine; DWORD lineDisplacement=0; assert( pInterData != NULL ); hProcess = pInterData->hProcess; pStackframe = pInterData->pStackframe; log_file = pInterData->log_file; nr_of_frame = pInterData->nr_of_frame; // frame info fprintf(log_file, "#%d 0x%p", nr_of_frame, (void*)(DWORD_PTR)pStackframe->AddrPC.Offset); // restrict symbol enumeration to this frame only ZeroMemory(&imagehlpStackFrame, sizeof(IMAGEHLP_STACK_FRAME)); imagehlpStackFrame.InstructionOffset = pStackframe->AddrPC.Offset; SymSetContext_(hProcess, &imagehlpStackFrame, 0); // function name and displacement pSymbolInfo = (PSYMBOL_INFO)LocalAlloc(LMEM_FIXED, sizeof(SYMBOL_INFO)+1024); pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbolInfo->MaxNameLen = 1024; if( SymFromAddr_(hProcess, pStackframe->AddrPC.Offset, &funcDisplacement, pSymbolInfo) == TRUE ) { fprintf(log_file, " in %.1024s+0x%I64X (", pSymbolInfo->Name, funcDisplacement); // log all function parameters pInterData->as_arg_list = TRUE; pInterData->log_params = TRUE; pInterData->log_locals = FALSE; pInterData->log_globals = FALSE; pInterData->nr_of_var = 0; if( !SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData) ) fprintf(log_file, ""); fprintf(log_file, ")"); } else fprintf(log_file, "in "); // find the source line for this function. imagehlpLine.SizeOfStruct = sizeof(IMAGEHLP_LINE); if( SymGetLineFromAddr_(hProcess, pStackframe->AddrPC.Offset, &lineDisplacement, &imagehlpLine) != 0 ) { char* filename = imagehlpLine.FileName; DWORD line = imagehlpLine.LineNumber; fprintf(log_file, " at %s:%d\n", filename, line); Dhp__PrintSourceLine(log_file, filename, line); } else fprintf(log_file, "\n"); // log all function local variables pInterData->as_arg_list = FALSE; pInterData->log_params = FALSE; pInterData->log_locals = TRUE; pInterData->log_globals = FALSE; pInterData->nr_of_var = 0; if( !SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData) ) fprintf(log_file, "!!failed enumerating symbols!!"); pInterData->nr_of_frame = ++nr_of_frame; LocalFree(pSymbolInfo); } /// Walks over the stack and prints all relevant information to the log file. /// /// @param context Exception context /// @param log_file Log file static VOID Dhp__PrintStacktrace( CONTEXT *context, FILE *log_file) { HANDLE hProcess = GetCurrentProcess(); STACKFRAME stackframe; DWORD machine; CONTEXT ctx; InterData interData; int skip = 0; int i; assert( log_file != NULL ); fprintf(log_file, "\nStacktrace:\n"); // Use thread information - if not supplied. if( context == NULL ) { // If no context is supplied, skip 1 frame skip = 1; ctx.ContextFlags = CONTEXT_FULL; if( GetThreadContext(GetCurrentThread(), &ctx) ) context = &ctx; } if( context == NULL ) return; // Write the stack trace ZeroMemory(&stackframe, sizeof(STACKFRAME)); stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Mode = AddrModeFlat; stackframe.AddrFrame.Mode = AddrModeFlat; #if defined(_M_IX86) machine = IMAGE_FILE_MACHINE_I386; stackframe.AddrPC.Offset = context->Eip; stackframe.AddrStack.Offset = context->Esp; stackframe.AddrFrame.Offset = context->Ebp; #else /* defined(_M_IX86) */ #error FIXME add more processors some compilers don't stop on #error, this line makes sure it errors out #endif interData.hProcess = hProcess; interData.log_file = log_file; interData.pStackframe = &stackframe; interData.nr_of_frame = 0; for( i = 0; ; ++i ) { if( !StackWalk_(machine, hProcess, GetCurrentThread(), &stackframe, context, NULL, SymFunctionTableAccess_, SymGetModuleBase_, NULL)) { break; } if( i >= skip ) { // Check that the address is not zero. // Sometimes StackWalk returns TRUE with a frame of zero. if( stackframe.AddrPC.Offset != 0 ) Dhp__PrintFunctionDetails(&interData); } } } typedef BOOL (WINAPI *ISDEBUGGERPRESENT)(void); /// Checks if a debugger is attached to this process /// /// @return TRUE is a debugger is present static BOOL Dhp__IsDebuggerPresent(void) { HANDLE kernel32_dll; ISDEBUGGERPRESENT IsDebuggerPresent_; BOOL result; kernel32_dll = LoadLibraryA("kernel32.dll"); if( kernel32_dll == NULL ) return FALSE; IsDebuggerPresent_ = (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent"); if( IsDebuggerPresent_ ) result = IsDebuggerPresent_(); else result = FALSE; FreeLibrary(kernel32_dll); return result; } /// Loads the dbghelp.dll library. /// /// @return TRUE is sucessfull static BOOL Dhp__LoadDbghelpDll(void) { dbghelp_dll = LoadLibraryA(DBGHELP_DLL); if( dbghelp_dll != INVALID_HANDLE_VALUE ) { DWORD opts; // load the functions MiniDumpWriteDump_ = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump"); SymInitialize_ = (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize"); SymSetOptions_ = (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions"); SymGetOptions_ = (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions"); SymCleanup_ = (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup"); SymGetTypeInfo_ = (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo"); SymGetLineFromAddr_ = (SYMGETLINEFROMADDR)GetProcAddress(dbghelp_dll, "SymGetLineFromAddr"); SymEnumSymbols_ = (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols"); SymSetContext_ = (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext"); SymFromAddr_ = (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr"); StackWalk_ = (STACKWALK)GetProcAddress(dbghelp_dll, "StackWalk"); SymFunctionTableAccess_ = (SYMFUNCTIONTABLEACCESS)GetProcAddress(dbghelp_dll, "SymFunctionTableAccess"); SymGetModuleBase_ = (SYMGETMODULEBASE)GetProcAddress(dbghelp_dll, "SymGetModuleBase"); if( MiniDumpWriteDump_ && SymInitialize_ && SymSetOptions_ && SymGetOptions_ && SymCleanup_ && SymGetTypeInfo_ && SymGetLineFromAddr_ && SymEnumSymbols_ && SymSetContext_ && SymFromAddr_ && StackWalk_ && SymFunctionTableAccess_ && SymGetModuleBase_ ) { // initialize the symbol loading code opts = SymGetOptions_(); // Set the 'load lines' option to retrieve line number information. // Set the 'deferred loads' option to map the debug info in memory only when needed. SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS); // Initialize the dbghelp DLL with the default path and automatic // module enumeration (and loading of symbol tables) for this process. if( !SymInitialize_(GetCurrentProcess(), NULL, TRUE) ) { printf("Failed to initialize symbols. Error: %u\n", GetLastError()); return FALSE; } return TRUE; } } if( dbghelp_dll ) { FreeLibrary(dbghelp_dll); dbghelp_dll = NULL; } return FALSE; } /// Unloads the dbghelp.dll library. static VOID Dhp__UnloadDbghlpDll(void) { if( !SymCleanup_(GetCurrentProcess()) ) printf("Failed to cleanup symbols! Error: %u\n", GetLastError()); FreeLibrary(dbghelp_dll); dbghelp_dll = NULL; } /// Creates the report and minidump files. /// Puts the resulting pathnames in the arguments. /// The buffers must be at least MAX_PATH+1 in size. /// /// @param out_lpszLogFileName Buffer for the report filename /// @param out_lpszDmpFileName Buffer for the minidump filename /// @return TRUE if the files were created static BOOL Dhp__CreateFiles( char* out_logFileName, char* out_dmpFileName) { #define LEN_TIMESTAMP 14 // "YYYYMMDDhhmmss" #define LEN_EXT 4 // ".rpt" or ".dmp" char baseFileName[MAX_PATH+1]; char timestamp[LEN_TIMESTAMP+1]; FILE* fp; time_t now; // Generate base filename for the report/minidump ZeroMemory(baseFileName, sizeof(baseFileName)); if( GetModuleFileName(NULL, baseFileName, MAX_PATH-LEN_TIMESTAMP-LEN_EXT) ) { char* pTerm = strrchr(baseFileName, '\\'); if( pTerm == NULL ) pTerm = baseFileName; pTerm = strrchr(pTerm, '.'); if( pTerm ) *pTerm = '\0'; // remove extension } else if( GetTempPathA(MAX_PATH-6-LEN_TIMESTAMP-LEN_EXT, baseFileName) ) {// in temp folder strcat(baseFileName, DBG_DEFAULT_FILENAME); } else {// in current folder strcpy(baseFileName, DBG_DEFAULT_FILENAME); } time(&now); #if 0 szTimestamp[0] = '\0'; #else strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", localtime(&now)); #endif timestamp[LEN_TIMESTAMP] = '\0'; sprintf(out_logFileName, "%s%s.rpt", baseFileName, timestamp); fp = fopen(out_logFileName, "w"); if( fp == NULL ) return FALSE; // failed to create log file fclose(fp); sprintf(out_dmpFileName, "%s%s.dmp", baseFileName, timestamp); fp = fopen(out_dmpFileName, "w"); if( fp == NULL) return FALSE; // failed to create dump file fclose(fp); return TRUE; // success #undef LEN_EXT #undef LEN_TIMESTAMP } /// Unhandled exception handler. Where the magic starts... ;D /// /// @param ptrs Exception information /// @return What to do with the exception LONG WINAPI Dhp__UnhandledExceptionFilter(PEXCEPTION_POINTERS ptrs) { char szLogFileName[MAX_PATH+1]; char szDmpFileName[MAX_PATH+1]; FILE* log_file; // check if the crash handler was already loaded (crash while handling the crash) if( dbghelp_dll != INVALID_HANDLE_VALUE ) return EXCEPTION_CONTINUE_SEARCH; // don't log anything if we're running inside a debugger ... if( Dhp__IsDebuggerPresent() == TRUE ) return EXCEPTION_CONTINUE_SEARCH; // ... or if we can't load dbghelp.dll ... if( Dhp__LoadDbghelpDll() == FALSE ) return EXCEPTION_CONTINUE_SEARCH; // ... or if we can't create the log files if( Dhp__CreateFiles(szLogFileName, szDmpFileName) == FALSE ) return EXCEPTION_CONTINUE_SEARCH; // open log file log_file = fopen(szLogFileName, "wt"); // print information about the process Dhp__PrintProcessInfo( ptrs ? ptrs->ExceptionRecord : NULL, ptrs ? ptrs->ContextRecord : NULL, log_file); // print the stacktrace Dhp__PrintStacktrace( ptrs ? ptrs->ContextRecord : NULL, log_file); // write the minidump file and use the callback to print the list of modules to the log file Dhp__WriteMinidumpFile( szDmpFileName, ptrs, Dhp__PrintModuleInfoCallback, log_file); fclose(log_file); Dhp__UnloadDbghlpDll(); // inform the user fprintf(stderr, "\n" "This application has halted due to an unexpected error.\n" "A crash report and minidump file were saved to disk, you can find them here:\n" "%s\n" "%s\n" DBG_EXTENDED_INFORMATION "\n" "NOTE: The crash report and minidump files can contain sensitive information\n" "(filenames, partial file content, usernames and passwords etc.)\n", szLogFileName, szDmpFileName); // terminate the application return EXCEPTION_EXECUTE_HANDLER; } ///////////////////////////////////////////////////////////////////// // Plugin /// Previous exception filter. static LPTOP_LEVEL_EXCEPTION_FILTER previousFilter; /** * Initializes plugin * Installs a new unhandled exception filter (Dhp__UnhandledExceptionFilter) **/ HPExport void plugin_init (void) { previousFilter = SetUnhandledExceptionFilter(Dhp__UnhandledExceptionFilter); } /** * Finalizes plugin * Uninstalls the handler **/ HPExport void plugin_final (void) { SetUnhandledExceptionFilter(previousFilter); }