root/src/plugins/dbghelpplug.c @ 5

Revision 1, 51.6 kB (checked in by jinshiro, 17 years ago)
Line 
1/*
2 * 2008 January 19
3 *
4 * The author disclaims copyright to this source code.  In place of
5 * a legal notice, here is a blessing:
6 *
7 *    May you do good and not evil.
8 *    May you find forgiveness for yourself and forgive others.
9 *    May you share freely, never taking more than you give.
10 *
11 ********************************************************************
12 *
13 * DONE:
14 * - Command line
15 * - Windows version
16 * - Exception
17 * - Registers
18 * - Stacktrace (frame number starting at 0)
19 * + Functions:
20 *   - address
21 *   - name
22 *   - offset in the function
23 *   - line number and source file
24 *   - source code of the line (external source)
25 *   - function parameters
26 *   - local function variables
27 * + Variables/parameters:
28 *   - variable name
29 *   - variable type (char/wchar, integers, floats, enums, arrays,
30 *     pointers, structs, unions, ...)
31 *   - readability of memory
32 *   - value of char/wchar
33 *   - value of integers
34 *   - value of floats
35 *   - value of enums (hex number)
36 *   - values of arrays
37 *   - address of pointers
38 *   - value of simple pointers (not pointing to another pointer)
39 *   - show (char*) and (wchar*) as nul-terminated strings
40 *   - show chars/wchar escaped
41 * + Modules:
42 *   - base address
43 *   - file
44 *   - version
45 *   - size
46 *
47 * TODO:
48 * + Variables/parameters:
49 *   - structure members
50 *   - union members
51 *   - globals
52 * - Portability to MinGW
53 *
54 * $Id: dbghelpplug.c 12143 2008-01-27 00:34:24Z FlavioJS $
55 */
56
57#ifdef _WIN32
58
59
60/////////////////////////////////////////////////////////////////////
61// Include files
62//
63
64#include <assert.h>
65
66#define WIN32_LEAN_AND_MEAN
67#include <windows.h>
68
69#define _NO_CVCONST_H
70#include <dbghelp.h>
71
72#include <stdio.h>
73#include <stdlib.h>
74#include <time.h>
75
76
77/////////////////////////////////////////////////////////////////////
78// Types from Cvconst.h (DIA SDK)
79//
80
81#ifdef _NO_CVCONST_H
82
83typedef enum _BasicType
84{ 
85   btNoType   = 0,
86   btVoid     = 1,
87   btChar     = 2,
88   btWChar    = 3,
89   btInt      = 6,
90   btUInt     = 7,
91   btFloat    = 8,
92   btBCD      = 9,
93   btBool     = 10,
94   btLong     = 13,
95   btULong    = 14,
96   btCurrency = 25,
97   btDate     = 26,
98   btVariant  = 27,
99   btComplex  = 28,
100   btBit      = 29,
101   btBSTR     = 30,
102   btHresult  = 31
103} BasicType;
104
105typedef enum _UdtKind
106{
107    UdtStruct,
108    UdtClass,
109    UdtUnion
110} UdtKind;
111
112typedef enum _SymTag { 
113   SymTagNull             = 0,
114   SymTagExe              = 1,
115   SymTagCompiland        = 2,
116   SymTagCompilandDetails = 3,
117   SymTagCompilandEnv     = 4,
118   SymTagFunction         = 5,
119   SymTagBlock            = 6,
120   SymTagData             = 7,
121   SymTagAnnotation       = 8,
122   SymTagLabel            = 9,
123   SymTagPublicSymbol     = 10,
124   SymTagUDT              = 11,
125   SymTagEnum             = 12,
126   SymTagFunctionType     = 13,
127   SymTagPointerType      = 14,
128   SymTagArrayType        = 15,
129   SymTagBaseType         = 16,
130   SymTagTypedef          = 17,
131   SymTagBaseClass        = 18,
132   SymTagFriend           = 19,
133   SymTagFunctionArgType  = 20,
134   SymTagFuncDebugStart   = 21,
135   SymTagFuncDebugEnd     = 22,
136   SymTagUsingNamespace   = 23,
137   SymTagVTableShape      = 24,
138   SymTagVTable           = 25,
139   SymTagCustom           = 26,
140   SymTagThunk            = 27,
141   SymTagCustomType       = 28,
142   SymTagManagedType      = 29,
143   SymTagDimension        = 30
144} SymTag;
145
146#endif /* _NO_CVCONST_H */
147
148
149/////////////////////////////////////////////////////////////////////
150// dbghelp function prototypes
151//
152
153typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
154        HANDLE hProcess,
155        DWORD ProcessId,
156        HANDLE hFile,
157        MINIDUMP_TYPE DumpType,
158        CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
159        CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
160        CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
161);
162typedef BOOL (WINAPI *SYMINITIALIZE)(
163        HANDLE  hProcess,
164        PSTR    UserSearchPath,
165        BOOL    fInvadeProcess
166);
167typedef DWORD (WINAPI *SYMSETOPTIONS)(
168        DWORD   SymOptions
169);
170typedef DWORD (WINAPI *SYMGETOPTIONS)(
171        VOID
172);
173typedef BOOL (WINAPI *SYMCLEANUP)(
174        HANDLE  hProcess
175);
176typedef BOOL (WINAPI *SYMGETTYPEINFO)(
177        HANDLE                      hProcess,
178        DWORD64                     ModBase,
179        ULONG                       TypeId,
180        IMAGEHLP_SYMBOL_TYPE_INFO   GetType,
181        PVOID                       pInfo
182);
183typedef BOOL (WINAPI *SYMGETLINEFROMADDR)(
184        HANDLE          hProcess,
185        DWORD           dwAddr,
186        PDWORD          pdwDisplacement,
187        PIMAGEHLP_LINE  Line
188);
189typedef BOOL (WINAPI *SYMENUMSYMBOLS)(
190        HANDLE                          hProcess,
191        ULONG64                         BaseOfDll,
192        PCSTR                           Mask,
193        PSYM_ENUMERATESYMBOLS_CALLBACK  EnumSymbolsCallback,
194        PVOID                           UserContext
195);
196typedef BOOL (WINAPI *SYMSETCONTEXT)(
197        HANDLE                  hProcess,
198        PIMAGEHLP_STACK_FRAME   StackFrame,
199        PIMAGEHLP_CONTEXT       Context
200);
201typedef BOOL (WINAPI *SYMFROMADDR)(
202        HANDLE          hProcess,
203        DWORD64         Address,
204        PDWORD64        Displacement,
205        PSYMBOL_INFO    Symbol
206);
207typedef BOOL (WINAPI *STACKWALK)(
208        DWORD                           MachineType,
209        HANDLE                          hProcess,
210        HANDLE                          hThread,
211        LPSTACKFRAME                    StackFrame,
212        PVOID                           ContextRecord,
213        PREAD_PROCESS_MEMORY_ROUTINE    ReadMemoryRoutine,
214        PFUNCTION_TABLE_ACCESS_ROUTINE  FunctionTableAccessRoutine,
215        PGET_MODULE_BASE_ROUTINE        GetModuleBaseRoutine,
216        PTRANSLATE_ADDRESS_ROUTINE      TranslateAddress
217);
218typedef PVOID (WINAPI *SYMFUNCTIONTABLEACCESS)(
219        HANDLE  hProcess,
220        DWORD   AddrBase
221);
222typedef DWORD (WINAPI *SYMGETMODULEBASE)(
223        HANDLE  hProcess,
224        DWORD   dwAddr
225);
226
227
228/////////////////////////////////////////////////////////////////////
229// Custom info
230
231/// Internal structure used to pass some data around
232typedef struct _InternalData {
233        // PrintStacktrace
234        FILE* log_file;
235        STACKFRAME* pStackframe;
236        HANDLE hProcess;
237        DWORD nr_of_frame;
238
239        // PrintFunctionDetail
240        BOOL as_arg_list;
241        BOOL log_params;
242        BOOL log_locals;
243        BOOL log_globals;
244        DWORD nr_of_var;
245
246        // PrintDataInfo
247        ULONG64 modBase;
248} InterData;
249
250/// dbghelp dll filename
251#define DBGHELP_DLL "dbghelp.dll"
252
253// Default report filename, used when the module path is unavailable
254#define DBG_DEFAULT_FILENAME "athena"
255
256// Extended information printed in the console
257#define DBG_EXTENDED_INFORMATION \
258                "Please report the crash in the bug tracker:\n" \
259                "http://www.eathena.ws/board/index.php?autocom=bugtracker\n"
260
261
262/////////////////////////////////////////////////////////////////////
263// Global variables
264
265HANDLE dbghelp_dll = INVALID_HANDLE_VALUE;
266MINIDUMPWRITEDUMP MiniDumpWriteDump_ = NULL;
267SYMINITIALIZE SymInitialize_ = NULL;
268SYMSETOPTIONS SymSetOptions_ = NULL;
269SYMGETOPTIONS SymGetOptions_ = NULL;
270SYMCLEANUP SymCleanup_ = NULL;
271SYMGETTYPEINFO SymGetTypeInfo_ = NULL;
272SYMGETLINEFROMADDR SymGetLineFromAddr_ = NULL;
273SYMENUMSYMBOLS SymEnumSymbols_ = NULL;
274SYMSETCONTEXT SymSetContext_ = NULL;
275SYMFROMADDR SymFromAddr_ = NULL;
276STACKWALK StackWalk_ = NULL;
277SYMFUNCTIONTABLEACCESS SymFunctionTableAccess_ = NULL;
278SYMGETMODULEBASE SymGetModuleBase_ = NULL;
279
280
281
282/////////////////////////////////////////////////////////////////////
283// Code
284
285
286/// Writes the minidump to file. The callback function will at the
287/// same time write the list of modules to the log file.
288///
289/// @param file Filename of the minidump
290/// @param ptrs Exception info
291/// @param module_callback Callback for MiniDumpWriteDump
292/// @param log_file Log file
293static VOID
294Dhp__WriteMinidumpFile(
295        const char*                 file,
296        PEXCEPTION_POINTERS         ptrs,
297        MINIDUMP_CALLBACK_ROUTINE   module_callback,
298        FILE*                       log_file)
299{
300        // open minidump file
301        HANDLE minidump_file = CreateFileA(
302                file,
303                GENERIC_WRITE,
304                0,
305                NULL,
306                CREATE_ALWAYS,
307                FILE_ATTRIBUTE_NORMAL,
308                NULL
309        );
310
311        if( minidump_file != INVALID_HANDLE_VALUE )
312        {
313                MINIDUMP_EXCEPTION_INFORMATION expt_info;
314                MINIDUMP_CALLBACK_INFORMATION dump_cb_info;
315
316                expt_info.ThreadId = GetCurrentThreadId();
317                expt_info.ExceptionPointers = ptrs;
318                expt_info.ClientPointers = FALSE;
319
320                dump_cb_info.CallbackRoutine = module_callback;
321                dump_cb_info.CallbackParam = (void*)log_file;
322
323                if( module_callback != NULL && log_file != NULL )
324                        fprintf(log_file, "\n\nLoaded modules:\n");
325
326                MiniDumpWriteDump_(
327                        GetCurrentProcess(),
328                        GetCurrentProcessId(),
329                        minidump_file,
330                        MiniDumpNormal,
331                        ptrs ? &expt_info : NULL,
332                        NULL,
333                        &dump_cb_info
334                );
335
336                CloseHandle(minidump_file);
337        }
338}
339
340
341/// Prints module information to the log file.
342/// Used as a callback to MiniDumpWriteDump.
343///
344/// @param data Log file
345/// @param callback_input
346/// @param callback_output
347/// @return
348static BOOL CALLBACK
349Dhp__PrintModuleInfoCallback(
350        void*                           data,
351        CONST PMINIDUMP_CALLBACK_INPUT  callback_input,
352        PMINIDUMP_CALLBACK_OUTPUT       callback_output)
353{
354        if( data != NULL &&
355                callback_input != NULL &&
356                callback_input->CallbackType == ModuleCallback)
357        {
358                FILE* log_file = (FILE*)data;
359                MINIDUMP_MODULE_CALLBACK module = callback_input->Module;
360
361                fprintf(log_file, "0x%p", module.BaseOfImage);
362
363                fprintf(log_file, "  %ws", module.FullPath, log_file);
364
365                fprintf(log_file, "  (%d.%d.%d.%d, %d bytes)\n",
366                        HIWORD(module.VersionInfo.dwFileVersionMS),
367                        LOWORD(module.VersionInfo.dwFileVersionMS),
368                        HIWORD(module.VersionInfo.dwFileVersionLS),
369                        LOWORD(module.VersionInfo.dwFileVersionLS),
370                        module.SizeOfImage);
371        }
372
373        return TRUE;
374}
375
376
377/// Prints details about the current process, platform and exception
378/// information to the log file.
379///
380/// @param exception Exception info
381/// @param context Exception context
382/// @param log_file Log file
383static VOID
384Dhp__PrintProcessInfo(
385        EXCEPTION_RECORD*   exception,
386        CONTEXT*            context,
387        FILE*               log_file)
388{
389        OSVERSIONINFOA oi;
390        LPSTR cmd_line;
391
392        fprintf(log_file,
393                "\nProcess info:\n");
394
395        // print the command line
396        cmd_line = GetCommandLineA();
397        if( cmd_line )
398        fprintf(log_file,
399                "Cmd line: %s\n",
400                cmd_line);
401
402        // print information about the OS
403        oi.dwOSVersionInfoSize = sizeof(oi);
404        GetVersionExA(&oi);
405        fprintf(log_file,
406                "Platform: Windows OS version %d.%d build %d %s\n",
407                oi.dwMajorVersion, oi.dwMinorVersion, oi.dwBuildNumber, oi.szCSDVersion);
408
409        // print the exception code
410        if( exception )
411        {
412                fprintf(log_file,
413                        "\nException:\n"
414                        "0x%x",
415                        exception->ExceptionCode);
416                switch( exception->ExceptionCode )
417                {
418#define PRINT(x) case x: fprintf(log_file, " "#x); break
419                PRINT(EXCEPTION_ACCESS_VIOLATION);
420                PRINT(EXCEPTION_DATATYPE_MISALIGNMENT);
421                PRINT(EXCEPTION_BREAKPOINT);
422                PRINT(EXCEPTION_SINGLE_STEP);
423                PRINT(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
424                PRINT(EXCEPTION_FLT_DENORMAL_OPERAND);
425                PRINT(EXCEPTION_FLT_DIVIDE_BY_ZERO);
426                PRINT(EXCEPTION_FLT_INEXACT_RESULT);
427                PRINT(EXCEPTION_FLT_INVALID_OPERATION);
428                PRINT(EXCEPTION_FLT_OVERFLOW);
429                PRINT(EXCEPTION_FLT_STACK_CHECK);
430                PRINT(EXCEPTION_FLT_UNDERFLOW);
431                PRINT(EXCEPTION_INT_DIVIDE_BY_ZERO);
432                PRINT(EXCEPTION_INT_OVERFLOW);
433                PRINT(EXCEPTION_PRIV_INSTRUCTION);
434                PRINT(EXCEPTION_IN_PAGE_ERROR);
435                PRINT(EXCEPTION_ILLEGAL_INSTRUCTION);
436                PRINT(EXCEPTION_NONCONTINUABLE_EXCEPTION);
437                PRINT(EXCEPTION_STACK_OVERFLOW);
438                PRINT(EXCEPTION_INVALID_DISPOSITION);
439                PRINT(EXCEPTION_GUARD_PAGE);
440                PRINT(EXCEPTION_INVALID_HANDLE);
441#undef PRINT
442                }
443
444                // print where the fault occured
445                fprintf(log_file, " at location 0x%p", exception->ExceptionAddress);
446
447                // if the exception was an access violation, print additional information
448                if( exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && exception->NumberParameters >= 2 )
449                        fprintf(log_file, " %s location 0x%p", exception->ExceptionInformation[0] ? "writing to" : "reading from", exception->ExceptionInformation[1]);
450
451                fprintf(log_file, "\n");
452        }
453
454        // print the register info
455        if( context )
456        {
457#if defined(_M_IX86)
458                fprintf(log_file,
459                        "\nRegisters:\n");
460                if( context->ContextFlags & CONTEXT_INTEGER )
461                {
462                        fprintf(log_file,
463                                "eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n",
464                                context->Eax, context->Ebx, context->Ecx,
465                                context->Edx, context->Esi, context->Edi);
466                }
467                if( context->ContextFlags & CONTEXT_CONTROL )
468                {
469                        fprintf(log_file,
470                                "eip=%08x esp=%08x ebp=%08x iopl=%1x %s %s %s %s %s %s %s %s %s %s\n",
471                                context->Eip, context->Esp, context->Ebp, 
472                                (context->EFlags >> 12) & 3,    //  IOPL level value
473                                context->EFlags & 0x00100000 ? "vip" : "   ",   //  VIP (virtual interrupt pending)
474                                context->EFlags & 0x00080000 ? "vif" : "   ",   //  VIF (virtual interrupt flag)
475                                context->EFlags & 0x00000800 ? "ov"  : "nv",    //  VIF (virtual interrupt flag)
476                                context->EFlags & 0x00000400 ? "dn"  : "up",    //  OF (overflow flag)
477                                context->EFlags & 0x00000200 ? "ei"  : "di",    //  IF (interrupt enable flag)
478                                context->EFlags & 0x00000080 ? "ng"  : "pl",    //  SF (sign flag)
479                                context->EFlags & 0x00000040 ? "zr"  : "nz",    //  ZF (zero flag)
480                                context->EFlags & 0x00000010 ? "ac"  : "na",    //  AF (aux carry flag)
481                                context->EFlags & 0x00000004 ? "po"  : "pe",    //  PF (parity flag)
482                                context->EFlags & 0x00000001 ? "cy"  : "nc");   //  CF (carry flag)
483                }
484                if( context->ContextFlags & CONTEXT_SEGMENTS )
485                {
486                        fprintf(log_file,
487                                "cs=%04x  ss=%04x  ds=%04x  es=%04x  fs=%04x  gs=%04x",
488                                context->SegCs,
489                                context->SegSs,
490                                context->SegDs,
491                                context->SegEs,
492                                context->SegFs,
493                                context->SegGs,
494                                context->EFlags);
495                        if( context->ContextFlags & CONTEXT_CONTROL )
496                                fprintf(log_file,
497                                        "             efl=%08x",
498                                        context->EFlags);
499                        fprintf(log_file, "\n");
500                }
501                else if( context->ContextFlags & CONTEXT_CONTROL )
502                        fprintf(log_file,
503                                        "                                                                       efl=%08x\n",
504                                        context->EFlags);
505#else /* defined(_M_IX86) */
506                //TODO add more processors
507#endif
508        }
509}
510
511
512/// Prints the typename of the symbol.
513///
514/// @param typeIndex Type index of the symbol
515/// @param symtag Symbol tag
516/// @param withParens If brackets are printed around the typename
517/// @param interData Inter data
518static VOID
519Dhp__PrintTypeName(
520        DWORD       typeIndex,
521        DWORD       symtag,
522        BOOL        withParens,
523        InterData*  interData)
524{
525        // inter data
526        FILE* log_file;
527        HANDLE hProcess;
528        ULONG64 modBase;
529        //
530        assert( interData != NULL );
531        log_file = interData->log_file;
532        hProcess = interData->hProcess;
533        modBase  = interData->modBase;
534
535        if( withParens )
536                fprintf(log_file, "(");
537
538        switch( symtag )
539        {
540        case SymTagEnum:
541                {                       
542                        WCHAR* pwszTypeName;
543
544                        if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pwszTypeName) )
545                        {
546                                fprintf(log_file, "enum %ls", pwszTypeName);
547                                LocalFree(pwszTypeName);
548                        }
549                        else
550                                fprintf(log_file, "enum <symname not found>");
551                }
552                break;
553        case SymTagBaseType:
554                {
555                        DWORD basetype;
556                        ULONG64 length = 0;
557
558                        SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length);
559                        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) )
560                        {
561                                fprintf(log_file, "<basetype not found>");
562                                break;
563                        }
564                        switch( basetype )
565                        {
566                        case btVoid: fprintf(log_file, "void"); break;
567                        case btChar: fprintf(log_file, "char"); break;
568                        case btWChar: fprintf(log_file, "wchar"); break;
569                        case btULong: fprintf(log_file, "unsigned ");   // next
570                        case btLong: fprintf(log_file, "long"); break;
571                        case btUInt: fprintf(log_file, "unsigned ");    // next
572                        case btInt:
573                                if( length == sizeof(char) ) fprintf(log_file, "char");
574                                else if( length == sizeof(short) ) fprintf(log_file, "short");
575                                else if( length == sizeof(int) ) fprintf(log_file, "int");
576                                else if( length == sizeof(long long) ) fprintf(log_file, "long long");
577                                else fprintf(log_file, "<int%d>", length*8);
578                                break;
579                        case btFloat:
580                                if( length == sizeof(float) ) fprintf(log_file, "float");
581                                else if( length == sizeof(double) ) fprintf(log_file, "double");
582                                else if( length == sizeof(long double) ) fprintf(log_file, "long double");
583                                else fprintf(log_file, "<float%d>", length*8);
584                                break;
585                        default: fprintf(log_file, "<TODO name of basetype %d %d>", basetype, length); break;
586                        }
587                }
588                break;
589        case SymTagPointerType:
590                {
591                        DWORD subtype;
592                        DWORD subtag;
593
594                        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &subtype) )
595                                fprintf(log_file, "<type not found>*");
596                        else if( !SymGetTypeInfo_(hProcess, modBase, subtype, TI_GET_SYMTAG, &subtag) )
597                                fprintf(log_file, "<symtag not found>*");
598                        else
599                        {
600                                Dhp__PrintTypeName(subtype, subtag, FALSE, interData);
601                                fprintf(log_file, "*");
602                        }
603                }
604                break;
605        case SymTagArrayType:
606                {
607                        DWORD childTypeIndex;
608                        DWORD childSymtag;
609
610                        // print root type
611                        childTypeIndex = typeIndex;
612                        childSymtag = symtag;
613                        for( ; ; )
614                        {
615                                if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) )
616                                {
617                                        fprintf(log_file, "<child type not found>");
618                                        break;
619                                }
620                                if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
621                                {
622                                        fprintf(log_file, "<child symtag not found>");
623                                        break;
624                                }
625                                if( childSymtag != SymTagArrayType )
626                                {
627                                        Dhp__PrintTypeName(childTypeIndex, childSymtag, FALSE, interData);
628                                        break;
629                                }
630                                // next dimension
631                        }
632                        // print dimensions
633                        childTypeIndex = typeIndex;
634                        childSymtag = symtag;
635                        for( ; childSymtag == SymTagArrayType ; )
636                        {
637                                DWORD childCount;
638                                if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_COUNT, &childCount) )
639                                        fprintf(log_file, "[<count not found>]");
640                                else
641                                        fprintf(log_file, "[%u]", childCount);
642                                if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_TYPE, &childTypeIndex) )
643                                {
644                                        fprintf(log_file, "<child type not found>");
645                                        break;
646                                }
647                                if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
648                                {
649                                        fprintf(log_file, "<child symtag not found>");
650                                        break;
651                                }
652                                // next dimension
653                        }
654                }
655                break;
656        default:
657                {
658                        WCHAR* pSymname;
659                        DWORD udtkind;
660
661                        if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_UDTKIND, &udtkind) )
662                        {
663                                switch( (UdtKind)udtkind )
664                                {
665                                case UdtStruct: fprintf(log_file, "struct "); break;
666                                case UdtClass: fprintf(log_file, "class "); break;
667                                case UdtUnion: fprintf(log_file, "union "); break;
668                                default: fprintf(log_file, "<unknown udtkind %d> ", udtkind); break;
669                                }
670                        }
671                        if( SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_SYMNAME, &pSymname ) )
672                        {
673                                fprintf(log_file, "%ls", pSymname);
674                                LocalFree( pSymname );
675                        }
676                        else
677                                fprintf(log_file, "<TODO typename of symtag %d>", symtag); break;
678                }
679                break;
680        }
681
682        if( withParens )
683                fprintf(log_file, ")");
684}
685
686
687/// Prints the bytes in the target location.
688///
689/// @param log_file Log file
690/// @param p Pointer to the data
691/// @param length Length of the data in bytes
692static VOID
693Dhp__PrintValueBytes(
694        FILE*   log_file,
695        BYTE*   p,
696        ULONG64 length)
697{
698        ULONG64 i;
699
700        fprintf(log_file, "<bytes:");
701        for( i = 0; i < length; ++i )
702        {
703                fprintf(log_file, "%02X", p[i]);
704        }
705        fprintf(log_file, ">");
706}
707
708
709/// Prints a wide string/char value.
710///
711/// @param log_file Log file
712/// @param p Pointer to the value
713/// @param length Length of the value in bytes
714static VOID
715Dhp__PrintValueWideChars(
716        FILE*   log_file,
717        WCHAR*  wstr,
718        ULONG64 length,
719        BOOL    isString)
720{
721        ULONG64 i;
722        char* buf;
723        char delim;
724
725        length /= sizeof(WCHAR);
726        delim = ( isString || length > 1 ? '\"' : '\'' );
727        fprintf(log_file, "%c", delim);
728        buf = (char *)LocalAlloc(LMEM_FIXED, MB_CUR_MAX+1);
729        buf[MB_CUR_MAX] = '\0';
730        for( i = 0; i < length; ++i )
731        {
732                int n;
733                switch( wstr[i] )
734                {
735                case L'\"': fprintf(log_file, "\\\""); break;
736                case L'\'': fprintf(log_file, "\\\'"); break;
737                case L'\\': fprintf(log_file, "\\\\"); break;
738                case L'\a': fprintf(log_file, "\\a"); break;
739                case L'\b': fprintf(log_file, "\\b"); break;
740                case L'\f': fprintf(log_file, "\\f"); break;
741                case L'\n': fprintf(log_file, "\\n"); break;
742                case L'\r': fprintf(log_file, "\\r"); break;
743                case L'\t': fprintf(log_file, "\\t"); break;
744                case L'\v': fprintf(log_file, "\\v"); break;
745                default:
746                        if( iswprint(wstr[i]) && (n=wctomb(buf, wstr[i])) > 0 )
747                        {
748                                buf[n] = '\0';
749                                fprintf(log_file, "%s", buf);
750                        }
751                        else fprintf(log_file, "\\u%04X", wstr[i]);
752                        break;
753                }
754        }
755        LocalFree(buf);
756        fprintf(log_file, "%c", delim);
757}
758
759
760/// Prints a string/char value.
761///
762/// @param log_file Log file
763/// @param p Pointer to the value
764/// @param length Length of the value in bytes
765static VOID
766Dhp__PrintValueChars(
767        FILE*   log_file,
768        char*   str,
769        ULONG64 length,
770        BOOL    isString)
771{
772        ULONG64 i;
773        char delim;
774
775        length /= sizeof(char);
776        delim = ( isString || length > 1 ? '\"' : '\'' );
777        fprintf(log_file, "%c", delim);
778        for( i = 0; i < length; ++i )
779        {
780                switch( str[i] )
781                {
782                case '\"': fprintf(log_file, "\\\""); break;
783                case '\'': fprintf(log_file, "\\\'"); break;
784                case '\\': fprintf(log_file, "\\\\"); break;
785                case '\a': fprintf(log_file, "\\a"); break;
786                case '\b': fprintf(log_file, "\\b"); break;
787                case '\f': fprintf(log_file, "\\f"); break;
788                case '\n': fprintf(log_file, "\\n"); break;
789                case '\r': fprintf(log_file, "\\r"); break;
790                case '\t': fprintf(log_file, "\\t"); break;
791                case '\v': fprintf(log_file, "\\v"); break;
792                default:
793                        if( isprint((unsigned char)str[i]) ) fprintf(log_file, "%c", str[i]);
794                        else fprintf(log_file, "\\x%02X", (unsigned char)str[i]);
795                        break;
796                }
797        }
798        fprintf(log_file, "%c", delim);
799}
800
801
802/// Prints a float value.
803///
804/// @param log_file Log file
805/// @param p Pointer to the value
806/// @param length Length of the value in bytes
807static VOID
808Dhp__PrintValueFloat(
809        FILE*   log_file,
810        VOID*   p,
811        ULONG64 length)
812{
813        if( length == sizeof(float) ) fprintf(log_file, "%f", *(float*)p);
814        else if( length == sizeof(double) ) fprintf(log_file, "%lf", *(double*)p);
815        else if( length == sizeof(long double) ) fprintf(log_file, "%Lf", *(long double*)p);
816        else
817        {
818                fprintf(log_file, "<unexpected length %I64u>", length);
819                Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
820        }
821}
822
823
824/// Prints a hex value.
825///
826/// @param log_file Log file
827/// @param p Pointer to the value
828/// @param length Length of the value in bytes
829static VOID
830Dhp__PrintValueHex(
831        FILE*   log_file,
832        VOID*   p,
833        ULONG64 length)
834{
835        if( length == sizeof(UINT32) ) fprintf(log_file, "0x%I32X", *(UINT32*)p);
836        else if( length == sizeof(UINT64) ) fprintf(log_file, "0x%I64X", *(UINT64*)p);
837        else if( length == sizeof(char) ) fprintf(log_file, "0x%X", *(unsigned char*)p);
838        else if( length == sizeof(short) ) fprintf(log_file, "0x%X", *(unsigned short*)p);
839        else if( length == sizeof(int) ) fprintf(log_file, "0x%x", *(unsigned int*)p);
840        else if( length == sizeof(long) ) fprintf(log_file, "0x%lX", *(unsigned long*)p);
841        else if( length == sizeof(long long) ) fprintf(log_file, "0x%llX", *(unsigned long long*)p);
842        else
843        {
844                fprintf(log_file, "<unexpected length %I64u>", length);
845                Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
846        }
847}
848
849
850/// Prints an unsigned integer value.
851///
852/// @param log_file Log file
853/// @param p Pointer to the value
854/// @param length Length of the value in bytes
855static VOID
856Dhp__PrintValueUnsigned(
857        FILE*   log_file,
858        VOID*   p,
859        ULONG64 length)
860{
861        if( length == sizeof(INT32) ) fprintf(log_file, "%I32u", *(INT32*)p);
862        else if( length == sizeof(INT64) ) fprintf(log_file, "%I64u", *(INT64*)p);
863        else if( length == sizeof(char) ) fprintf(log_file, "%u", *(unsigned char*)p);
864        else if( length == sizeof(short) ) fprintf(log_file, "%u", *(unsigned short*)p);
865        else if( length == sizeof(int) ) fprintf(log_file, "%u", *(unsigned int*)p);
866        else if( length == sizeof(long) ) fprintf(log_file, "%lu", *(unsigned long*)p);
867        else if( length == sizeof(long long) ) fprintf(log_file, "%llu", *(unsigned long long*)p);
868        else
869        {
870                fprintf(log_file, "<unexpected length %I64u>", length);
871                Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
872        }
873}
874
875
876/// Prints a signed integer value.
877///
878/// @param log_file Log file
879/// @param p Pointer to the value
880/// @param length Length of the value in bytes
881static VOID
882Dhp__PrintValueSigned(
883        FILE*   log_file,
884        VOID*   p,
885        ULONG64 length)
886{
887        if( length == sizeof(INT32) ) fprintf(log_file, "%I32d", *(INT32*)p);
888        else if( length == sizeof(INT64) ) fprintf(log_file, "%I64d", *(INT64*)p);
889        else if( length == sizeof(char) ) fprintf(log_file, "%d", *(signed char*)p);
890        else if( length == sizeof(short) ) fprintf(log_file, "%d", *(signed short*)p);
891        else if( length == sizeof(int) ) fprintf(log_file, "%d", *(signed int*)p);
892        else if( length == sizeof(long) ) fprintf(log_file, "%ld", *(signed long*)p);
893        else if( length == sizeof(long long) ) fprintf(log_file, "%lld", *(signed long long*)p);
894        else
895        {
896                fprintf(log_file, "<unexpected length %I64u>", length);
897                Dhp__PrintValueBytes(log_file, (BYTE*)p, length);
898        }
899}
900
901
902/// Prints a nul-terminated wide string value.
903/// Checks if the memory can be read from.
904///
905/// @param log_file Log file
906/// @param str Target string
907static VOID
908Dhp__PrintValueCWideString(
909        FILE*   log_file,
910        WCHAR*   str)
911{
912        ULONG64 length = 0;
913
914        // check if memory is readable
915        __try
916        {
917                while( str[length] != L'\0' )
918                        ++length;
919        }
920        __except( EXCEPTION_EXECUTE_HANDLER )
921        {
922                if( length ) Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE);       // print readable part
923                fprintf(log_file, "<invalid memory>");
924                return;
925        }
926
927        // print string
928        Dhp__PrintValueWideChars(log_file, str, length*sizeof(WCHAR), TRUE);
929}
930
931
932/// Prints a nul-terminated string value.
933/// Checks if the memory can be read from.
934///
935/// @param log_file Log file
936/// @param str Target string
937static VOID
938Dhp__PrintValueCString(
939        FILE*   log_file,
940        char*   str)
941{
942        ULONG64 length = 0;
943
944        assert( log_file != NULL );
945
946        // check if memory is readable
947        __try
948        {
949                while( str[length] != '\0' )
950                        ++length;
951        }
952        __except( EXCEPTION_EXECUTE_HANDLER )
953        {
954                if( length ) Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE);    // print readable part
955                fprintf(log_file, "<invalid memory>");
956                return;
957        }
958
959        // print string
960        Dhp__PrintValueChars(log_file, str, length*sizeof(char), TRUE);
961}
962
963
964// forward declaration of Dhp__PrintDataContents
965static VOID Dhp__PrintDataContents(DWORD typeIndex, PVOID pVariable, InterData*  interData);
966
967
968/// Prints the value of the data symbol.
969/// Checks if the memory can be read from.
970///
971/// @param typeIndex Type index of the symbol
972/// @param symtag Symbol tag
973/// @param pVariable Address to the symbol contents
974/// @param pInterData Inter data
975static VOID
976Dhp__PrintDataValue(
977        DWORD       typeIndex,
978        DWORD       symtag,
979        PVOID       pVariable,
980        InterData*  pInterData)
981{
982        // inter data
983        FILE* log_file;
984        DWORD64 modBase;
985        HANDLE hProcess;
986        //
987        ULONG64 length = 0;
988        DWORD basetype;
989        BOOL isValid = TRUE;
990
991        assert( pInterData != NULL );
992        log_file = pInterData->log_file;
993        modBase  = pInterData->modBase;
994        hProcess = pInterData->hProcess;
995
996        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_LENGTH, &length) )
997        {
998                fprintf(log_file, "<unknown data length>");
999                return;
1000        }
1001
1002        // check if memory is readable
1003        __try
1004        {
1005                BYTE* p = (BYTE*)pVariable;
1006                ULONG i;
1007                BYTE b = 0;
1008                for( i = 0; i < length; ++i )
1009                        b += p[i];      // add to make sure it's not optimized out in release mode
1010        }
1011        __except( EXCEPTION_EXECUTE_HANDLER )
1012        {
1013                fprintf(log_file, "<invalid memory>");
1014                return;
1015        }
1016
1017        switch( symtag )
1018        {
1019        case SymTagBaseType:
1020                {
1021                        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &basetype) )
1022                        {
1023                                fprintf(log_file, "<basetype not found>");
1024                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1025                                break;
1026                        }
1027                        switch( basetype )
1028                        {
1029                        case btInt:
1030                        case btLong:
1031                                Dhp__PrintValueSigned(log_file, pVariable, length);
1032                                break;
1033                        case btUInt:
1034                        case btULong:
1035                                Dhp__PrintValueUnsigned(log_file, pVariable, length);
1036                                break;
1037                        case btFloat:
1038                                Dhp__PrintValueFloat(log_file, pVariable, length);
1039                                break;
1040                        case btChar:
1041                                {
1042                                        if( length == sizeof(char) ) fprintf(log_file, "%u ", *(unsigned char*)pVariable);
1043                                        Dhp__PrintValueChars(log_file, (char*)pVariable, length, FALSE);
1044                                }
1045                                break;
1046                        case btWChar:
1047                                {
1048                                        if( length == sizeof(WCHAR) ) fprintf(log_file, "%u ", *(WCHAR*)pVariable);
1049                                        Dhp__PrintValueWideChars(log_file, (WCHAR*)pVariable, length, FALSE);
1050                                }
1051                                break;
1052                        case btVoid:
1053                                if( length > 0 ) Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1054                                break;
1055                        default:
1056                                fprintf(log_file, "<TODO value of basetype %d>", basetype);
1057                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1058                                break;
1059                        }
1060                }
1061                break;
1062        case SymTagEnum:
1063                Dhp__PrintValueHex(log_file, pVariable, length);
1064                break;
1065        case SymTagPointerType:
1066                {
1067                        DWORD childTypeIndex;
1068                        DWORD childSymtag;
1069
1070                        fprintf(log_file, "0x%p", *(void**)pVariable);
1071                        if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) &&
1072                                SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) && 
1073                                childSymtag != SymTagPointerType )
1074                        {
1075                                DWORD childBasetype;
1076
1077                                // child isn't a pointer, print the contents
1078                                fprintf(log_file, " ");
1079                                if( childSymtag == SymTagBaseType &&
1080                                        SymGetTypeInfo_(hProcess, modBase, childTypeIndex, TI_GET_BASETYPE, &childBasetype) &&
1081                                        (childBasetype == btChar || childBasetype == btWChar) )
1082                                {
1083                                        // string or wide string
1084                                        if( childBasetype == btChar ) Dhp__PrintValueCString(log_file, *(char**)pVariable);
1085                                        else if( childBasetype == btWChar ) Dhp__PrintValueCWideString(log_file, *(WCHAR**)pVariable);
1086                                        else fprintf(log_file, "<unexpected child basetype %d>", childBasetype);
1087                                        break;
1088                                }
1089                                Dhp__PrintDataValue(childTypeIndex, childSymtag, *(PVOID*)pVariable, pInterData);
1090                        }
1091                }
1092                break;
1093        case SymTagArrayType:
1094                {
1095                        DWORD childTypeIndex;
1096                        DWORD childSymtag;
1097                        DWORD count;
1098                        DWORD i;
1099
1100                        if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_TYPE, &childTypeIndex) )
1101                        {
1102                                fprintf(log_file, "<child type not found>");
1103                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1104                                break;
1105                        }
1106                        if( !SymGetTypeInfo_( hProcess, modBase, childTypeIndex, TI_GET_SYMTAG, &childSymtag) )
1107                        {
1108                                fprintf(log_file, "<child symtag not found>");
1109                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1110                                break;
1111                        }
1112                        if( !SymGetTypeInfo_( hProcess, modBase, typeIndex, TI_GET_COUNT, &count) )
1113                        {
1114                                fprintf(log_file, "<count not found>");
1115                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1116                                break;
1117                        }
1118                        // print values
1119                        fprintf(log_file, "{");
1120                        for( i = 0; i < count; ++i )
1121                        {
1122                                BYTE* pData = pVariable;
1123                                pData += i*(length/count);
1124                                if( i > 0 ) fprintf(log_file, ",");
1125                                Dhp__PrintDataValue(childTypeIndex, childSymtag, pData, pInterData);
1126                        }
1127                        fprintf(log_file, "}");
1128                }
1129                break;
1130        default:
1131#if 0
1132                {//## TODO show children of structs/unions
1133                        TI_FINDCHILDREN_PARAMS* children;
1134                        DWORD childCount;
1135                        DWORD i;
1136
1137                        // count children
1138                        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_CHILDRENCOUNT, &childCount) )
1139                        {
1140                                fprintf(log_file, "<child count not found>");
1141                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1142                                break;
1143                        }
1144
1145                        // Prepare to get an array of "TypeIds", representing each of the children.
1146                        // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
1147                        // TI_FINDCHILDREN_PARAMS struct has.  Use derivation to accomplish this.
1148                        children = (TI_FINDCHILDREN_PARAMS*)LocalAlloc(LMEM_FIXED, sizeof(TI_FINDCHILDREN_PARAMS)+childCount*sizeof(ULONG));
1149                        children->Count = childCount;
1150                        children->Start= 0;
1151
1152                        // Get the array of TypeIds, one for each child type
1153                        if( !SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_FINDCHILDREN, &children) )
1154                        {
1155                                fprintf(log_file, "<children not found>");
1156                                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1157                                LocalFree(children);
1158                                return;
1159                        }
1160
1161                        // Iterate through each of the children
1162                        fprintf(log_file, "{");
1163                        for( i = 0; i < childCount; ++i )
1164                        {
1165                                DWORD childOffset;
1166                                DWORD childTypeid;
1167                                WCHAR* childName;
1168                                DWORD_PTR pData;
1169
1170                                if( i > 0 ) fprintf(log_file, ",");
1171
1172                                // Get the offset of the child member, relative to its parent
1173                                if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_OFFSET, &childOffset) )
1174                                {
1175                                        fprintf(log_file, "<child offset not found>");
1176                                        continue;
1177                                }
1178
1179                                // Get the real "TypeId" of the child.
1180                                if( !SymGetTypeInfo_(hProcess, modBase, children->ChildId[i], TI_GET_TYPEID, &childTypeid) )
1181                                {
1182                                        fprintf(log_file, "<child typeid not found>");
1183                                        continue;
1184                                }
1185
1186                                // Calculate the address of the member
1187                                pData = (DWORD_PTR)pVariable;
1188                                pData += childOffset;
1189
1190                                // print name of the child
1191                                if( !SymGetTypeInfo_(hProcess, modBase, childTypeid, TI_GET_SYMNAME, &childName) )
1192                                {
1193                                        fprintf(log_file, "<child symname not found>");
1194                                        continue;
1195                                }
1196                                fprintf(log_file, "%ws=", childName);
1197                                LocalFree(childName);
1198
1199                                // print contents of the child
1200                                Dhp__PrintDataContents(childTypeid, (PVOID)pData, interData);
1201                        }
1202                        fprintf(log_file, "}");
1203
1204                        LocalFree(children);
1205                }
1206#endif
1207                Dhp__PrintValueBytes(log_file, (BYTE*)pVariable, length);
1208                break;
1209        }
1210}
1211
1212
1213/// Prints the contents of the data symbol. (type and value)
1214///
1215/// @param typeIndex Type index of the symbol
1216/// @param pVariable Address of the symbol contents
1217/// @param pInterData Inter data
1218static VOID
1219Dhp__PrintDataContents(
1220        DWORD typeIndex,
1221        PVOID pVariable,
1222        InterData*  pInterData)
1223{
1224        // inter data
1225        FILE* log_file;
1226        HANDLE hProcess;
1227        DWORD64 modBase;
1228        //
1229        DWORD symtag;
1230
1231        assert( pInterData != NULL );
1232        log_file = pInterData->log_file;
1233        hProcess = pInterData->hProcess;
1234        modBase  = pInterData->modBase;
1235
1236        if( SymGetTypeInfo_(hProcess, modBase, typeIndex, TI_GET_SYMTAG, &symtag) )
1237        {
1238                // print type
1239                Dhp__PrintTypeName(typeIndex, symtag, TRUE, pInterData);
1240                // print value
1241                Dhp__PrintDataValue(typeIndex, symtag, pVariable, pInterData);
1242        }
1243        else
1244                fprintf(log_file, "<symtag not found>");
1245}
1246
1247
1248/// Prints information about the data symbol.
1249///
1250/// @param pSymInfo Symbol info
1251/// @param pInterData Inter data
1252static VOID
1253Dhp__PrintDataInfo(
1254        PSYMBOL_INFO    pSymInfo,
1255        InterData*      pInterData)
1256{
1257        // inter data
1258        FILE* log_file;
1259        STACKFRAME* pStackframe;
1260        BOOL as_arg_list;
1261        BOOL log_params;
1262        BOOL log_locals;
1263        BOOL log_globals;
1264        int nr_of_var;
1265        // my data
1266        DWORD_PTR pVariable = 0;
1267        enum{ UNKNOWN, PARAM, LOCAL, GLOBAL } scope = UNKNOWN;
1268
1269        assert( pSymInfo != NULL );
1270        assert( pInterData != NULL );
1271        assert( pSymInfo->Tag == SymTagData );
1272        log_file    = pInterData->log_file;
1273        pStackframe = pInterData->pStackframe;
1274        as_arg_list = pInterData->as_arg_list;
1275        log_params  = pInterData->log_params;
1276        log_locals  = pInterData->log_locals;
1277        log_globals = pInterData->log_globals;
1278        nr_of_var   = pInterData->nr_of_var;
1279
1280        // Determine the scope and address of the variable
1281    if( pSymInfo->Flags & SYMFLAG_REGREL )
1282        {
1283                pVariable = pStackframe->AddrFrame.Offset;
1284                pVariable += (DWORD_PTR)pSymInfo->Address;
1285                if( pSymInfo->Flags & SYMFLAG_PARAMETER )
1286                        scope = PARAM;  // parameter
1287                else if( pSymInfo->Flags & SYMFLAG_LOCAL )
1288                {
1289                        scope = LOCAL;  // local
1290#if defined(_M_IX86)
1291                        if( (LONG64)pSymInfo->Address  > 0) scope = PARAM;      // parameter as local (bug in DBGHELP 5.1)
1292#endif
1293                }
1294        }
1295        else if( pSymInfo->Flags & SYMFLAG_REGISTER )
1296        {
1297                scope = ( pSymInfo->Flags & SYMFLAG_PARAMETER ? PARAM : LOCAL );        // register, optimized out(?)
1298        }
1299        else
1300        {
1301                pVariable = (DWORD_PTR)pSymInfo->Address;
1302                scope = GLOBAL; // It must be a global variable
1303        }
1304
1305        // check if we should to log the variable
1306        if( (scope == PARAM && log_params) ||
1307                (scope == LOCAL && log_locals) ||
1308                (scope == GLOBAL && log_globals) )
1309        {
1310                // print prefix and name
1311                if( as_arg_list )
1312                        fprintf(log_file, "%s%s=", (nr_of_var ? ", " : ""), pSymInfo->Name);
1313                else
1314                        fprintf(log_file, "\t%s = ", pSymInfo->Name);
1315
1316                // print value
1317                if( !(pSymInfo->Flags & SYMFLAG_REGREL) && (pSymInfo->Flags & SYMF_REGISTER) )
1318                        fprintf(log_file, "<value optimized out>");
1319                else
1320                {
1321                        pInterData->modBase = pSymInfo->ModBase;
1322                        Dhp__PrintDataContents(pSymInfo->TypeIndex, (PVOID)pVariable, pInterData);
1323                }
1324
1325                // print postfix
1326                if( !as_arg_list )
1327                        fprintf(log_file, "\n");
1328                pInterData->nr_of_var = ++nr_of_var;
1329        }
1330}
1331
1332
1333/// Prints information about the symbol.
1334///
1335/// @param pSymInfo Symbol info
1336/// @param pInterData Inter data
1337static VOID
1338Dhp__PrintSymbolInfo(
1339        PSYMBOL_INFO    pSymInfo,
1340        InterData*      pInterData)
1341{
1342        assert( pSymInfo != NULL );
1343        assert( pInterData != NULL );
1344
1345        switch( pSymInfo->Tag ) 
1346        {
1347        case SymTagData: Dhp__PrintDataInfo( pSymInfo, pInterData ); break; 
1348        default: /*fprintf(pInterData->log_file, "<unsupported symtag %d>", pSymInfo->Tag);*/ break;
1349        }
1350}
1351
1352
1353/// Prints the details of one symbol to the log file.
1354/// Used as a callback for SymEnumSymbols.
1355///
1356/// @param pSymInfo Symbol info
1357/// @param symSize Size of the symbol info structure
1358/// @param pData Inter data
1359static BOOL WINAPI
1360Dhp__EnumSymbolsCallback(
1361        PSYMBOL_INFO    pSymInfo,
1362        ULONG           symSize,
1363        PVOID           pData)
1364{
1365        if( pSymInfo == NULL )
1366                return TRUE;    // try other symbols
1367
1368        if( pData == NULL )
1369        {
1370                printf("Dhp__EnumSymbolsCallback: pData is NULL\n");
1371                return FALSE;
1372        }
1373
1374        Dhp__PrintSymbolInfo(pSymInfo, (InterData*)pData);
1375        return TRUE;
1376}
1377
1378
1379/// Prints the source code of the target line.
1380/// Searches for the target file in the original path,
1381/// in the last src folder of the original path (relative)
1382/// and in the current directory.
1383///
1384/// @param filename Original source file
1385/// @param line Target line
1386/// @param log_file Log file
1387static VOID
1388Dhp__PrintSourceLine(
1389        FILE* log_file,
1390        char* filename,
1391        DWORD line)
1392{
1393        char path[MAX_PATH*3];
1394        char pathBuffer[MAX_PATH+1];
1395        char* p;
1396
1397        assert( filename != NULL );
1398        assert( log_file != NULL );
1399
1400        // generate search paths
1401        strcpy(path, filename); // original path
1402        p = strrchr(path, '\\');
1403        if( p )
1404        {
1405                memcpy(p, ";\0", 2);
1406                p = strstr(filename, "\\src\\");
1407                if( p )
1408                {
1409                        while( strstr(p+1, "\\src\\") )
1410                                p = strstr(p+1, "\\src\\");
1411                        strcat(path, p+1);      // last src folder path
1412                        p = strrchr(path, '\\');
1413                        memcpy(p, ";\0", 2);
1414                }
1415                filename = strrchr(filename, '\\')+1;
1416        }
1417        else
1418                *path = '\0';   // no path
1419        strcat(path, ".");      // current directoy
1420
1421        // search for file and line
1422        if( SearchPathA(path, filename, NULL, MAX_PATH, pathBuffer, NULL) )
1423        {
1424                char code[1024+1];
1425                DWORD i = 1;
1426                FILE* fp;
1427
1428                fp = fopen(pathBuffer, "rt");
1429                if( fp == NULL )
1430                        return;
1431
1432                code[1024] = '\0';
1433                while( fgets(code, 1024, fp) )
1434                {
1435                        if( i == line )
1436                        {// found line
1437                                char* term = strchr(code, '\n');
1438                                if( term && term != code && term[-1] == '\r' ) --term;
1439                                if( term ) *term = '\0';
1440                                fprintf(log_file, "%d\t%s\n", line, code);
1441                                break;
1442                        }
1443                        if( strchr(code, '\n') )
1444                                ++i;
1445                }
1446                fclose(fp);
1447        }
1448}
1449
1450
1451/// Prints details of one function to the log file.
1452///
1453/// @param interData Inter data
1454static VOID
1455Dhp__PrintFunctionDetails(
1456        InterData*  pInterData)
1457{
1458        // inter data
1459        HANDLE hProcess;
1460        STACKFRAME* pStackframe;
1461        FILE* log_file;
1462        int nr_of_frame;
1463        //
1464        PSYMBOL_INFO pSymbolInfo;
1465        DWORD64 funcDisplacement=0;
1466        IMAGEHLP_STACK_FRAME imagehlpStackFrame;
1467        IMAGEHLP_LINE imagehlpLine;
1468        DWORD lineDisplacement=0;
1469
1470        assert( pInterData != NULL );
1471        hProcess    = pInterData->hProcess;
1472        pStackframe = pInterData->pStackframe;
1473        log_file    = pInterData->log_file;
1474        nr_of_frame = pInterData->nr_of_frame;
1475
1476        // frame info
1477        fprintf(log_file,
1478                "#%d  0x%p",
1479                nr_of_frame, (void*)(DWORD_PTR)pStackframe->AddrPC.Offset);
1480
1481        // restrict symbol enumeration to this frame only
1482        ZeroMemory(&imagehlpStackFrame, sizeof(IMAGEHLP_STACK_FRAME));
1483        imagehlpStackFrame.InstructionOffset = pStackframe->AddrPC.Offset;
1484        SymSetContext_(hProcess, &imagehlpStackFrame, 0);
1485
1486        // function name and displacement
1487        pSymbolInfo = (PSYMBOL_INFO)LocalAlloc(LMEM_FIXED, sizeof(SYMBOL_INFO)+1024);
1488        pSymbolInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
1489        pSymbolInfo->MaxNameLen = 1024;
1490        if( SymFromAddr_(hProcess, pStackframe->AddrPC.Offset, &funcDisplacement, pSymbolInfo) == TRUE )
1491        {
1492                fprintf(log_file,
1493                        " in %.1024s+0x%I64X (",
1494                        pSymbolInfo->Name, funcDisplacement);
1495
1496                // log all function parameters
1497                pInterData->as_arg_list = TRUE;
1498                pInterData->log_params = TRUE;
1499                pInterData->log_locals = FALSE;
1500                pInterData->log_globals = FALSE;
1501                pInterData->nr_of_var = 0;
1502                SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData);
1503
1504                fprintf(log_file,
1505                        ")");
1506        }
1507        else
1508                fprintf(log_file,
1509                        "in <unknown function>");
1510
1511        // find the source line for this function.
1512        imagehlpLine.SizeOfStruct = sizeof(IMAGEHLP_LINE);
1513        if( SymGetLineFromAddr_(hProcess, pStackframe->AddrPC.Offset, &lineDisplacement, &imagehlpLine) != 0 )
1514        {
1515                char* filename = imagehlpLine.FileName;
1516                DWORD line = imagehlpLine.LineNumber;
1517
1518                fprintf(log_file,
1519                        " at %s:%d\n",
1520                        filename, line);
1521
1522                Dhp__PrintSourceLine(log_file, filename, line);
1523        }
1524        else
1525                fprintf(log_file,
1526                        "\n");
1527
1528        // log all function local variables
1529        pInterData->as_arg_list = FALSE;
1530        pInterData->log_params = FALSE;
1531        pInterData->log_locals = TRUE;
1532        pInterData->log_globals = FALSE;
1533        pInterData->nr_of_var = 0;
1534        SymEnumSymbols_(hProcess, 0, 0, Dhp__EnumSymbolsCallback, pInterData);
1535
1536        pInterData->nr_of_frame = ++nr_of_frame;
1537        LocalFree(pSymbolInfo);
1538}
1539
1540
1541/// Walks over the stack and prints all relevant information to the log file.
1542///
1543/// @param context Exception context
1544/// @param log_file Log file
1545static VOID
1546Dhp__PrintStacktrace(
1547        CONTEXT *context,
1548        FILE *log_file)
1549{
1550        HANDLE hProcess = GetCurrentProcess();
1551        STACKFRAME stackframe;
1552        DWORD machine;
1553        CONTEXT ctx;
1554        InterData interData;
1555        int skip = 0;
1556        int i;
1557
1558        assert( log_file != NULL );
1559
1560        fprintf(log_file,
1561                "\nStacktrace:\n");
1562
1563        // Use thread information - if not supplied.
1564        if( context == NULL )
1565        {
1566                // If no context is supplied, skip 1 frame
1567                skip = 1;
1568
1569                ctx.ContextFlags = CONTEXT_FULL;
1570                if( GetThreadContext(GetCurrentThread(), &ctx) )
1571                        context = &ctx;
1572        }
1573
1574        if( context == NULL )
1575                return;
1576
1577        // Write the stack trace
1578        ZeroMemory(&stackframe, sizeof(STACKFRAME));
1579        stackframe.AddrPC.Mode = AddrModeFlat;
1580        stackframe.AddrStack.Mode = AddrModeFlat;
1581        stackframe.AddrFrame.Mode = AddrModeFlat;
1582#if defined(_M_IX86)
1583        machine = IMAGE_FILE_MACHINE_I386;
1584        stackframe.AddrPC.Offset = context->Eip;
1585        stackframe.AddrStack.Offset = context->Esp;
1586        stackframe.AddrFrame.Offset = context->Ebp;
1587#else /* defined(_M_IX86) */
1588#error FIXME add more processors
1589some compilers don't stop on #error, this line makes sure it errors out
1590#endif
1591
1592        interData.hProcess = hProcess;
1593        interData.log_file = log_file;
1594        interData.pStackframe = &stackframe;
1595        interData.nr_of_frame = 0;
1596        for( i = 0; ; ++i )
1597        {
1598                if( !StackWalk_(machine, hProcess, GetCurrentThread(),
1599                        &stackframe, context, NULL, SymFunctionTableAccess_,
1600                        SymGetModuleBase_, NULL))
1601                {
1602                        break;
1603                }
1604
1605                if( i >= skip )
1606                {
1607                        // Check that the address is not zero.
1608                        // Sometimes StackWalk returns TRUE with a frame of zero.
1609                        if( stackframe.AddrPC.Offset != 0 )
1610                                Dhp__PrintFunctionDetails(&interData);
1611                }
1612        }
1613}
1614
1615
1616typedef BOOL (WINAPI *ISDEBUGGERPRESENT)(void);
1617/// Checks if a debugger is attached to this process
1618///
1619/// @return TRUE is a debugger is present
1620static BOOL
1621Dhp__IsDebuggerPresent()
1622{
1623        HANDLE kernel32_dll;
1624        ISDEBUGGERPRESENT IsDebuggerPresent_;
1625        BOOL result;
1626
1627        kernel32_dll = LoadLibraryA("kernel32.dll");
1628        if( kernel32_dll == NULL )
1629                return FALSE;
1630
1631        IsDebuggerPresent_ = (ISDEBUGGERPRESENT)GetProcAddress(kernel32_dll, "IsDebuggerPresent");
1632        if( IsDebuggerPresent_ )
1633                result = IsDebuggerPresent_();
1634        else
1635                result = FALSE;
1636
1637        FreeLibrary(kernel32_dll);
1638
1639        return result;
1640}
1641
1642
1643/// Loads the dbghelp.dll library.
1644///
1645/// @return TRUE is sucessfull
1646static BOOL
1647Dhp__LoadDbghelpDll()
1648{
1649        dbghelp_dll = LoadLibraryA(DBGHELP_DLL);
1650        if( dbghelp_dll != INVALID_HANDLE_VALUE )
1651        {
1652                DWORD opts;
1653
1654                // load the functions
1655                MiniDumpWriteDump_      =      (MINIDUMPWRITEDUMP)GetProcAddress(dbghelp_dll, "MiniDumpWriteDump");
1656                SymInitialize_          =          (SYMINITIALIZE)GetProcAddress(dbghelp_dll, "SymInitialize");
1657                SymSetOptions_          =          (SYMSETOPTIONS)GetProcAddress(dbghelp_dll, "SymSetOptions");
1658                SymGetOptions_          =          (SYMGETOPTIONS)GetProcAddress(dbghelp_dll, "SymGetOptions");
1659                SymCleanup_             =             (SYMCLEANUP)GetProcAddress(dbghelp_dll, "SymCleanup");
1660                SymGetTypeInfo_         =         (SYMGETTYPEINFO)GetProcAddress(dbghelp_dll, "SymGetTypeInfo");
1661                SymGetLineFromAddr_     =     (SYMGETLINEFROMADDR)GetProcAddress(dbghelp_dll, "SymGetLineFromAddr");
1662                SymEnumSymbols_         =         (SYMENUMSYMBOLS)GetProcAddress(dbghelp_dll, "SymEnumSymbols");
1663                SymSetContext_          =          (SYMSETCONTEXT)GetProcAddress(dbghelp_dll, "SymSetContext");
1664                SymFromAddr_            =            (SYMFROMADDR)GetProcAddress(dbghelp_dll, "SymFromAddr");
1665                StackWalk_              =              (STACKWALK)GetProcAddress(dbghelp_dll, "StackWalk");
1666                SymFunctionTableAccess_ = (SYMFUNCTIONTABLEACCESS)GetProcAddress(dbghelp_dll, "SymFunctionTableAccess");
1667                SymGetModuleBase_       =       (SYMGETMODULEBASE)GetProcAddress(dbghelp_dll, "SymGetModuleBase");
1668
1669                if( MiniDumpWriteDump_ &&
1670                        SymInitialize_  && SymSetOptions_  && SymGetOptions_ &&
1671                        SymCleanup_     && SymGetTypeInfo_ && SymGetLineFromAddr_ &&
1672                        SymEnumSymbols_ && SymSetContext_  && SymFromAddr_ && StackWalk_ &&
1673                        SymFunctionTableAccess_ && SymGetModuleBase_ )
1674                {
1675                        // initialize the symbol loading code
1676                        opts = SymGetOptions_();
1677
1678                        // Set the 'load lines' option to retrieve line number information.
1679                        // Set the 'deferred loads' option to map the debug info in memory only when needed.
1680                        SymSetOptions_(opts | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
1681
1682                        // Initialize the dbghelp DLL with the default path and automatic
1683                        // module enumeration (and loading of symbol tables) for this process.
1684                        SymInitialize_(GetCurrentProcess(), NULL, TRUE);
1685
1686                        return TRUE;
1687                }
1688        }
1689
1690        if( dbghelp_dll )
1691        {
1692                FreeLibrary(dbghelp_dll);
1693                dbghelp_dll = NULL;
1694        }
1695
1696        return FALSE;
1697}
1698
1699
1700/// Unloads the dbghelp.dll library.
1701static VOID
1702Dhp__UnloadDbghlpDll()
1703{
1704        SymCleanup_(GetCurrentProcess());
1705
1706        FreeLibrary(dbghelp_dll);
1707        dbghelp_dll = NULL;
1708}
1709
1710
1711/// Creates the report and minidump files.
1712/// Puts the resulting pathnames in the arguments.
1713/// The buffers must be at least MAX_PATH+1 in size.
1714///
1715/// @param out_lpszLogFileName Buffer for the report filename
1716/// @param out_lpszDmpFileName Buffer for the minidump filename
1717/// @return TRUE if the files were created
1718static BOOL
1719Dhp__CreateFiles(
1720        char*   out_logFileName,
1721        char*   out_dmpFileName)
1722{
1723#define LEN_TIMESTAMP 14        // "YYYYMMDDhhmmss"
1724#define LEN_EXT 4       // ".rpt" or ".dmp"
1725        char baseFileName[MAX_PATH+1];
1726        char timestamp[LEN_TIMESTAMP+1];
1727        FILE* fp;
1728        time_t now;
1729
1730        // Generate base filename for the report/minidump
1731        ZeroMemory(baseFileName, sizeof(baseFileName));
1732        if( GetModuleFileName(NULL, baseFileName, MAX_PATH-LEN_TIMESTAMP-LEN_EXT) )
1733        {
1734                char* pTerm = strrchr(baseFileName, '\\');
1735                if( pTerm == NULL ) pTerm = baseFileName;
1736                pTerm = strrchr(pTerm, '.');
1737                if( pTerm ) *pTerm = '\0';      // remove extension
1738        }
1739        else if( GetTempPathA(MAX_PATH-6-LEN_TIMESTAMP-LEN_EXT, baseFileName) )
1740        {// in temp folder
1741                strcat(baseFileName, DBG_DEFAULT_FILENAME);
1742        }
1743        else
1744        {// in current folder
1745                strcpy(baseFileName, DBG_DEFAULT_FILENAME);
1746        }
1747
1748        time(&now);
1749#if 0
1750        szTimestamp[0] = '\0';
1751#else
1752        strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", localtime(&now));
1753#endif
1754        timestamp[LEN_TIMESTAMP] = '\0';
1755       
1756        sprintf(out_logFileName, "%s%s.rpt", baseFileName, timestamp);
1757        fp = fopen(out_logFileName, "w");
1758        if( fp == NULL )
1759                return FALSE;   // failed to create log file
1760        fclose(fp);
1761
1762        sprintf(out_dmpFileName, "%s%s.dmp", baseFileName, timestamp);
1763        fp = fopen(out_dmpFileName, "w");
1764        if( fp == NULL)
1765                return FALSE;   // failed to create dump file
1766        fclose(fp);
1767
1768        return TRUE;    // success
1769#undef LEN_EXT
1770#undef LEN_TIMESTAMP
1771}
1772
1773
1774/// Unhandled exception handler. Where the magic starts... ;D
1775///
1776/// @param ptrs Exception information
1777/// @return What to do with the exception
1778LONG WINAPI
1779Dhp__UnhandledExceptionFilter(PEXCEPTION_POINTERS ptrs)
1780{
1781        char szLogFileName[MAX_PATH+1];
1782        char szDmpFileName[MAX_PATH+1];
1783        FILE* log_file;
1784
1785        // check if the crash handler was already loaded (crash while handling the crash)
1786        if( dbghelp_dll != INVALID_HANDLE_VALUE )
1787                return EXCEPTION_CONTINUE_SEARCH;
1788
1789        // don't log anything if we're running inside a debugger ...
1790        if( Dhp__IsDebuggerPresent() == TRUE )
1791                return EXCEPTION_CONTINUE_SEARCH;
1792
1793        // ... or if we can't load dbghelp.dll ...
1794        if( Dhp__LoadDbghelpDll() == FALSE )
1795                return EXCEPTION_CONTINUE_SEARCH;
1796
1797        // ... or if we can't create the log files
1798        if( Dhp__CreateFiles(szLogFileName, szDmpFileName) == FALSE )
1799                return EXCEPTION_CONTINUE_SEARCH;
1800
1801        // open log file
1802        log_file = fopen(szLogFileName, "wt");
1803
1804        // print information about the process
1805        Dhp__PrintProcessInfo(
1806                ptrs ? ptrs->ExceptionRecord : NULL,
1807                ptrs ? ptrs->ContextRecord : NULL,
1808                log_file);
1809
1810        // print the stacktrace
1811        Dhp__PrintStacktrace(
1812                ptrs ? ptrs->ContextRecord : NULL,
1813                log_file);
1814
1815        // write the minidump file and use the callback to print the list of modules to the log file
1816        Dhp__WriteMinidumpFile(
1817                szDmpFileName,
1818                ptrs,
1819                Dhp__PrintModuleInfoCallback,
1820                log_file);
1821
1822        fclose(log_file);
1823
1824        Dhp__UnloadDbghlpDll();
1825
1826        // inform the user
1827        fprintf(stderr,
1828                "\n"
1829                "This application has halted due to an unexpected error.\n"
1830                "A crash report and minidump file were saved to disk, you can find them here:\n"
1831                "%s\n"
1832                "%s\n"
1833                DBG_EXTENDED_INFORMATION
1834                "\n"
1835                "NOTE: The crash report and minidump files can contain sensitive information\n"
1836                "(filenames, partial file content, usernames and passwords etc.)\n",
1837                szLogFileName,
1838                szDmpFileName);
1839
1840        // terminate the application
1841        return EXCEPTION_EXECUTE_HANDLER;
1842}
1843
1844
1845
1846/////////////////////////////////////////////////////////////////////
1847// DLL stuff
1848#if !defined(DBG_EMBEDDED)
1849
1850
1851/// Previous exception filter.
1852static LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
1853
1854
1855#if defined(__GNUC__)
1856// GNU : define DLL load/unload functions
1857static void Dhp__OnStartup(void) __attribute__((constructor));
1858static void Dhp__OnExit(void) __attribute__((destructor));
1859#endif /* defined(__GNUC__) */
1860
1861
1862/// Installs as the unhandled exception handler.
1863void Dhp__OnStartup(void)
1864{
1865        // Install the unhandled exception filter function
1866        previousFilter = SetUnhandledExceptionFilter(Dhp__UnhandledExceptionFilter);
1867}
1868
1869
1870/// Uninstalls the handler.
1871void Dhp__OnExit(void)
1872{
1873        SetUnhandledExceptionFilter(previousFilter);
1874}
1875
1876
1877#if !defined(__GNUC__)
1878// Windows : invoke DLL load/unload functions
1879BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1880{
1881        switch( dwReason )
1882        {
1883                case DLL_PROCESS_ATTACH: Dhp__OnStartup(); break;
1884                case DLL_PROCESS_DETACH: Dhp__OnExit(); break;
1885        }
1886        return TRUE;
1887}
1888#endif /* !defined(__GNUC__) */
1889
1890
1891
1892#endif /* !defined(DBG_EMBEDDED) */
1893
1894
1895
1896#endif /* _WIN32 */
Note: See TracBrowser for help on using the browser.