From 19df0af514e1f7cc7b945b7921e81f1219049bce Mon Sep 17 00:00:00 2001 From: FlavioJS Date: Sat, 19 Jan 2008 09:20:47 +0000 Subject: [PATCH] * Added plugin dbghelpplug to generate more extensive crash reports in windows. - see header of src/plugins/dbghelpplug.c to know it's capabilities - VS8 project file supplied at vcproj-8/dbghelpplug.vcproj - a dll compiled in release mode is supplied at plugins/dbghelpplug.dll git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@12089 54d463be-8e91-2dee-dedb-b68131a5f0ec --- Changelog-Trunk.txt | 5 + plugins/dbghelpplug.dll | Bin 0 -> 118784 bytes src/plugins/dbghelpplug.c | 1895 +++++++++++++++++++++++++++++++++++ src/plugins/dbghelpplug.rc | 54 + vcproj-8/dbghelpplug.vcproj | 211 ++++ 5 files changed, 2165 insertions(+) create mode 100644 plugins/dbghelpplug.dll create mode 100644 src/plugins/dbghelpplug.c create mode 100644 src/plugins/dbghelpplug.rc create mode 100644 vcproj-8/dbghelpplug.vcproj diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index a2aff186c0..401e72a190 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -4,6 +4,11 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. +2008/01/19 + * Added plugin dbghelpplug to generate more extensive crash reports in windows. [FlavioJS] + - see header of src/plugins/dbghelpplug.c to know it's capabilities + - VS8 project file supplied at vcproj-8/dbghelpplug.vcproj + - a dll compiled in release mode is supplied at plugins/dbghelpplug.dll 2008/01/17 * Made battle_check_range fail whenever the distance between src/target is greater than the viewing range. [Skotlex] diff --git a/plugins/dbghelpplug.dll b/plugins/dbghelpplug.dll new file mode 100644 index 0000000000000000000000000000000000000000..1ae871543fa686817ab6199dc5871e596c76c190 GIT binary patch literal 118784 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

QYjcJCM3Wd7Z+ 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

e_5i+7;StA9$&|*Ix9@ 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 + +#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 "athena" + +// Extended information printed in the console +#define DBG_EXTENDED_INFORMATION \ + "Please report the crash in the bug tracker:\n" \ + "http://www.eathena.ws/board/index.php?autocom=bugtracker\n" + + +///////////////////////////////////////////////////////////////////// +// 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) +{ + OSVERSIONINFOA oi; + LPSTR cmd_line; + + fprintf(log_file, + "\nProcess info:\n"); + + // print the command line + cmd_line = GetCommandLineA(); + if( cmd_line ) + fprintf(log_file, + "Cmd line: %s\n", + cmd_line); + + // print information about the OS + oi.dwOSVersionInfoSize = sizeof(oi); + GetVersionExA(&oi); + fprintf(log_file, + "Platform: Windows OS version %d.%d build %d %s\n", + oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, oi.szCSDVersion); + + // 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, + context->EFlags); + 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 +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 +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 +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 +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 +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 +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 +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, TRUE); // print readable part + fprintf(log_file, ""); + return; + } + + // print string + Dhp__PrintValueWideChars(log_file, str, length, 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, TRUE); // print readable part + fprintf(log_file, ""); + return; + } + + // print string + Dhp__PrintValueChars(log_file, str, length, 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; + BOOL isValid = TRUE; + + 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; + for( i = 0; i < length; ++i ) + b = p[i]; + } + __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; + } + 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: +#if 0 + {//## TODO show children of structs/unions + TI_FINDCHILDREN_PARAMS* children; + DWORD childCount; + DWORD i; + + // count children + if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_CHILDRENCOUNT, &childCount) ) + { + fprintf(log_file, ""); + Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); + break; + } + + // 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. + children = (TI_FINDCHILDREN_PARAMS*)LocalAlloc(LMEM_FIXED, sizeof(TI_FINDCHILDREN_PARAMS)+childCount*sizeof(ULONG)); + children->Count = childCount; + children->Start= 0; + + // Get the array of TypeIds, one for each child type + if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_FINDCHILDREN, &children) ) + { + fprintf(log_file, ""); + Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length); + LocalFree(children); + return; + } + + // Iterate through each of the children + fprintf(log_file, "{"); + for( i = 0; i < childCount; ++i ) + { + DWORD childOffset; + DWORD childTypeid; + WCHAR* childName; + DWORD_PTR pData; + + if( i > 0 ) fprintf(log_file, ","); + + // 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; + } + + // Get the real "TypeId" of the child. + if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_TYPEID, &childTypeid) ) + { + fprintf(log_file, ""); + continue; + } + + // Calculate the address of the member + pData = (DWORD_PTR)pVariable; + pData += childOffset; + + // print name of the child + if( !SymGetTypeInfo_(hProcess, modBase, childTypeid, TI_GET_SYMNAME, &childName) ) + { + fprintf(log_file, ""); + continue; + } + fprintf(log_file, "%ws=", childName); + LocalFree(childName); + + // print contents of the child + Dhp__PrintDataContents(childTypeid, (PVOID)pData, interData); + } + fprintf(log_file, "}"); + + 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; + SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData); + + 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; + SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData); + + 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() +{ + 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() +{ + 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. + SymInitialize_(GetCurrentProcess(), NULL, TRUE); + + return TRUE; + } + } + + if( dbghelp_dll ) + { + FreeLibrary(dbghelp_dll); + dbghelp_dll = NULL; + } + + return FALSE; +} + + +/// Unloads the dbghelp.dll library. +static VOID +Dhp__UnloadDbghlpDll() +{ + SymCleanup_(GetCurrentProcess()); + + 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; +} + + + +///////////////////////////////////////////////////////////////////// +// DLL stuff +#if !defined(DBG_EMBEDDED) + + +/// Previous exception filter. +static LPTOP_LEVEL_EXCEPTION_FILTER previousFilter; + + +#if defined(__GNUC__) +// GNU : define DLL load/unload functions +static void Dhp__OnStartup(void) __attribute__((constructor)); +static void Dhp__OnExit(void) __attribute__((destructor)); +#endif /* defined(__GNUC__) */ + + +/// Installs as the unhandled exception handler. +void Dhp__OnStartup(void) +{ + // Install the unhandled exception filter function + previousFilter = SetUnhandledExceptionFilter(Dhp__UnhandledExceptionFilter); +} + + +/// Uninstalls the handler. +void Dhp__OnExit(void) +{ + SetUnhandledExceptionFilter(previousFilter); +} + + +#if !defined(__GNUC__) +// Windows : invoke DLL load/unload functions +BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + switch( dwReason ) + { + case DLL_PROCESS_ATTACH: Dhp__OnStartup(); break; + case DLL_PROCESS_DETACH: Dhp__OnExit(); break; + } + return TRUE; +} +#endif /* !defined(__GNUC__) */ + + + +#endif /* !defined(DBG_EMBEDDED) */ + + + +#endif /* _WIN32 */ diff --git a/src/plugins/dbghelpplug.rc b/src/plugins/dbghelpplug.rc new file mode 100644 index 0000000000..c136d10120 --- /dev/null +++ b/src/plugins/dbghelpplug.rc @@ -0,0 +1,54 @@ +// BC++ has all the necessary preprocessor macros defined automatically +#if !defined(__BORLANDC__) +#include "winres.h" +#endif // !defined(__BORLANDC__) + +#define VER_FILEVERSION 1,0,0,1 +#define VER_FILEVERSION_STR "1.0.0.1\0" + +#define VER_PRODUCTVERSION 1,0,0,1 +#define VER_PRODUCTVERSION_STR "1.0.0.1\0" + +#if defined(DEBUG) || defined(_DEBUG) +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#define VER_COMMENTS_STR "Registers as the unhandled exception handler. Generates reports and minidumps.\0" +#define VER_COMPANYNAME_STR "eAthena\0" +#define VER_FILEDESCRIPTION_STR "Stackdump Plugin by eAthena\0" +#define VER_INTERNALNAME_STR "dbghelpplug\0" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2008\0" +#define VER_ORIGINALFILENAME_STR "dbghelpplug.dll\0" +#define VER_PRODUCTNAME_STR "dbghelpplug\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VER_DEBUG +FILEOS (VOS_NT|VOS__WINDOWS32) +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "Comments", VER_COMMENTS_STR + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", VER_INTERNALNAME_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/vcproj-8/dbghelpplug.vcproj b/vcproj-8/dbghelpplug.vcproj new file mode 100644 index 0000000000..59d53f5c5d --- /dev/null +++ b/vcproj-8/dbghelpplug.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +