summaryrefslogtreecommitdiff
path: root/src/plugins/dbghelpplug.c
diff options
context:
space:
mode:
authorFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2008-01-19 09:20:47 +0000
committerFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2008-01-19 09:20:47 +0000
commit92480d918c768b02c6ec4a2daf9700bf73efb59d (patch)
tree7c8c29f14a4928e34fa278de7f086cdb1cfb73d5 /src/plugins/dbghelpplug.c
parentafccb2b182162f5bae930f14b97e86ee15a8dd11 (diff)
downloadhercules-92480d918c768b02c6ec4a2daf9700bf73efb59d.tar.gz
hercules-92480d918c768b02c6ec4a2daf9700bf73efb59d.tar.bz2
hercules-92480d918c768b02c6ec4a2daf9700bf73efb59d.tar.xz
hercules-92480d918c768b02c6ec4a2daf9700bf73efb59d.zip
* 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://rathena.svn.sourceforge.net/svnroot/rathena/trunk@12089 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/plugins/dbghelpplug.c')
-rw-r--r--src/plugins/dbghelpplug.c1895
1 files changed, 1895 insertions, 0 deletions
diff --git a/src/plugins/dbghelpplug.c b/src/plugins/dbghelpplug.c
new file mode 100644
index 000000000..567291bc5
--- /dev/null
+++ b/src/plugins/dbghelpplug.c
@@ -0,0 +1,1895 @@
+/*
+ * 2008 January 19
+ *
+ * The author disclaims copyright to this source code. In place of
+ * a legal notice, here is a blessing:
+ *
+ * May you do good and not evil.
+ * May you find forgiveness for yourself and forgive others.
+ * May you share freely, never taking more than you give.
+ *
+ ********************************************************************
+ *
+ * DONE:
+ * - Command line
+ * - Windows version
+ * - Exception
+ * - Registers
+ * - Stacktrace (frame number starting at 0)
+ * + Functions:
+ * - address
+ * - name
+ * - offset in the function
+ * - line number and source file
+ * - source code of the line (external source)
+ * - function parameters
+ * - local function variables
+ * + Variables/parameters:
+ * - variable name
+ * - variable type (char/wchar, integers, floats, enums, arrays,
+ * pointers, structs, unions, ...)
+ * - readability of memory
+ * - value of char/wchar
+ * - value of integers
+ * - value of floats
+ * - value of enums (hex number)
+ * - values of arrays
+ * - address of pointers
+ * - value of simple pointers (not pointing to another pointer)
+ * - show (char*) and (wchar*) as nul-terminated strings
+ * - show chars/wchar escaped
+ * + Modules:
+ * - base address
+ * - file
+ * - version
+ * - size
+ *
+ * TODO:
+ * + Variables/parameters:
+ * - structure members
+ * - union members
+ * - Portability to MinGW
+ *
+ * $Id$
+ */
+
+#ifdef _WIN32
+
+
+/////////////////////////////////////////////////////////////////////
+// Include files
+//
+
+#include <assert.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define _NO_CVCONST_H
+#include <dbghelp.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+
+/////////////////////////////////////////////////////////////////////
+// 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 <symname not found>");
+ }
+ 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, "<basetype not found>");
+ 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, "<int%d>", 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, "<float%d>", length*8);
+ break;
+ default: fprintf(log_file, "<TODO name of basetype %d %d>", basetype, length); break;
+ }
+ }
+ break;
+ case SymTagPointerType:
+ {
+ DWORD subtype;
+ DWORD subtag;
+
+ if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &subtype) )
+ fprintf(log_file, "<type not found>*");
+ else if( !SymGetTypeInfo_(hProcess, modBase, subtype, TI_GET_SYMTAG, &subtag) )
+ fprintf(log_file, "<symtag not found>*");
+ 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, "<child type not found>");
+ break;
+ }
+ if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
+ {
+ fprintf(log_file, "<child symtag not found>");
+ 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, "[<count not found>]");
+ else
+ fprintf(log_file, "[%u]", childCount);
+ if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) )
+ {
+ fprintf(log_file, "<child type not found>");
+ break;
+ }
+ if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
+ {
+ fprintf(log_file, "<child symtag not found>");
+ 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, "<unknown udtkind %d> ", udtkind); break;
+ }
+ }
+ if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pSymname ) )
+ {
+ fprintf(log_file, "%ls", pSymname);
+ LocalFree( pSymname );
+ }
+ else
+ fprintf(log_file, "<TODO typename of tag %d>", 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, "<bytes:");
+ for( i = 0; i < length; ++i )
+ {
+ fprintf(log_file, "%02X", p[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, "<unexpected length %I64u>", 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, "<unexpected length %I64u>", 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, "<unexpected length %I64u>", 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, "<unexpected length %I64u>", 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, "<invalid memory>");
+ 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, "<invalid memory>");
+ 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, "<unknown data length>");
+ 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, "<invalid memory>");
+ return;
+ }
+
+ switch( symtag )
+ {
+ case SymTagBaseType:
+ {
+ if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) )
+ {
+ fprintf(log_file, "<basetype not found>");
+ 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, "<TODO value of basetype %d>", 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, "<unexpected child basetype %d>", 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, "<child type not found>");
+ Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
+ break;
+ }
+ if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
+ {
+ fprintf(log_file, "<child symtag not found>");
+ Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
+ break;
+ }
+ if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_COUNT, &count) )
+ {
+ fprintf(log_file, "<count not found>");
+ 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, "<child count not found>");
+ 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, "<children not found>");
+ 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, "<child offset not found>");
+ continue;
+ }
+
+ // Get the real "TypeId" of the child.
+ if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_TYPEID, &childTypeid) )
+ {
+ fprintf(log_file, "<child typeid not found>");
+ 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, "<child symname not found>");
+ 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, "<symtag not found>");
+}
+
+
+/// 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, "<value optimized out>");
+ 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, "<unsupported symtag %d>", 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 <unknown function>");
+
+ // 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 */