1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/crashrpt.cpp 
   3 // Purpose:     helpers for structured exception handling (SEH) 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13     The code generating the crash reports in this file is heavily based on 
  14     Matt Pietrek's column from the March 2002 issue of MSDN Magazine. Note 
  15     that this code is not currently used by default, however. In any case, 
  16     all bugs are my alone. 
  19 // ============================================================================ 
  21 // ============================================================================ 
  23 // ---------------------------------------------------------------------------- 
  25 // ---------------------------------------------------------------------------- 
  27 // For compilers that support precompilation, includes "wx.h". 
  28 #include "wx/wxprec.h" 
  34 #if wxUSE_ON_FATAL_EXCEPTION 
  40    We have two possibilities here: one, a priori more interesting, is to 
  41    generate the crash report ourselves and include the values of all the 
  42    variables in the dump. Unfortunately my code to do it doesn't work in 
  43    "real life" situations i.e. it works in small examples but invariably 
  44    gets confused by something in big programs which makes quite useless. 
  46    The other possibility is to let dbghelp.dll to do the work for us and 
  47    analyze its results later using a debugger with knowledge about crash 
  48    dumps, such as (free!) WinDbg. This also has another advantage of not 
  49    needing to ship the .pdb file (containing debug info) to the user. So 
  50    this is the default now, but I keep the old code just in case, and if 
  51    you really want you can still use it. 
  53 #define wxUSE_MINIDUMP 1 
  56     #include "wx/longlong.h" 
  57 #endif // wxUSE_MINIDUMP 
  59 #include "wx/datetime.h" 
  61 #include "wx/dynload.h" 
  63 #include "wx/msw/crashrpt.h" 
  65 #include "wx/msw/wrapwin.h" 
  67 #include "wx/msw/private.h" 
  69 // we need to determine whether we have the declarations for the function in 
  70 // debughlp.dll version 5.81 (at least) and we check for DBHLPAPI to test this 
  73 //  - VC6 version of imagehlp.h doesn't define it 
  75 //  - testing for compiler version doesn't work as you can install and use 
  76 //    the new SDK headers with VC6 
  78 // in any case, the user may override by defining wxUSE_DBGHELP himself 
  81         #define wxUSE_DBGHELP 1 
  83         #define wxUSE_DBGHELP 0 
  89 // ---------------------------------------------------------------------------- 
  90 // types of imagehlp.h functions 
  91 // ---------------------------------------------------------------------------- 
  95 typedef BOOL (WINAPI 
*MiniDumpWriteDump_t
)(HANDLE
, DWORD
, HANDLE
, 
  97                                            CONST PMINIDUMP_EXCEPTION_INFORMATION
, 
  98                                            CONST PMINIDUMP_USER_STREAM_INFORMATION
, 
  99                                            CONST PMINIDUMP_CALLBACK_INFORMATION
); 
 100 #else // !wxUSE_MINIDUMP 
 101 typedef DWORD (WINAPI 
*SymSetOptions_t
)(DWORD
); 
 102 typedef BOOL (WINAPI 
*SymInitialize_t
)(HANDLE
, LPSTR
, BOOL
); 
 103 typedef BOOL (WINAPI 
*StackWalk_t
)(DWORD
, HANDLE
, HANDLE
, LPSTACKFRAME
, 
 104                                    LPVOID
, PREAD_PROCESS_MEMORY_ROUTINE
, 
 105                                    PFUNCTION_TABLE_ACCESS_ROUTINE
, 
 106                                    PGET_MODULE_BASE_ROUTINE
, 
 107                                    PTRANSLATE_ADDRESS_ROUTINE
); 
 108 typedef BOOL (WINAPI 
*SymFromAddr_t
)(HANDLE
, DWORD64
, PDWORD64
, PSYMBOL_INFO
); 
 109 typedef LPVOID (WINAPI 
*SymFunctionTableAccess_t
)(HANDLE
, DWORD
); 
 110 typedef DWORD (WINAPI 
*SymGetModuleBase_t
)(HANDLE
, DWORD
); 
 111 typedef BOOL (WINAPI 
*SymGetLineFromAddr_t
)(HANDLE
, DWORD
, 
 112                                             PDWORD
, PIMAGEHLP_LINE
); 
 113 typedef BOOL (WINAPI 
*SymSetContext_t
)(HANDLE
, PIMAGEHLP_STACK_FRAME
, 
 115 typedef BOOL (WINAPI 
*SymEnumSymbols_t
)(HANDLE
, ULONG64
, PCSTR
, 
 116                                         PSYM_ENUMERATESYMBOLS_CALLBACK
, PVOID
); 
 117 typedef BOOL (WINAPI 
*SymGetTypeInfo_t
)(HANDLE
, DWORD64
, ULONG
, 
 118                                         IMAGEHLP_SYMBOL_TYPE_INFO
, PVOID
); 
 119 #endif // wxUSE_MINIDUMP 
 121 // ---------------------------------------------------------------------------- 
 123 // ---------------------------------------------------------------------------- 
 127 // Stolen from CVCONST.H in the DIA 2.0 SDK 
 130     BASICTYPE_NOTYPE 
= 0, 
 140     BASICTYPE_ULONG 
= 14, 
 141     BASICTYPE_CURRENCY 
= 25, 
 143     BASICTYPE_VARIANT 
= 27, 
 144     BASICTYPE_COMPLEX 
= 28, 
 147     BASICTYPE_HRESULT 
= 31 
 154     SYMBOL_TAG_FUNCTION 
= 5, 
 156     SYMBOL_TAG_PUBLIC 
= 10,             // appears in .DBGs 
 159     SYMBOL_TAG_FUNCTION_TYPE
, 
 160     SYMBOL_TAG_POINTER_TYPE
, 
 161     SYMBOL_TAG_ARRAY_TYPE
, 
 162     SYMBOL_TAG_BASE_TYPE
, 
 167 #endif // wxUSE_MINIDUMP 
 169 #endif // wxUSE_DBGHELP 
 171 // ---------------------------------------------------------------------------- 
 173 // ---------------------------------------------------------------------------- 
 175 // low level wxBusyCursor replacement: we use Win32 API directly here instead 
 176 // of going through wxWindows calls as this could be dangerous 
 182         HCURSOR hcursorBusy 
= ::LoadCursor(NULL
, IDC_WAIT
); 
 183         m_hcursorOld 
= ::SetCursor(hcursorBusy
); 
 190             ::SetCursor(m_hcursorOld
); 
 195     HCURSOR m_hcursorOld
; 
 198 // the real crash report generator 
 199 class wxCrashReportImpl
 
 202     wxCrashReportImpl(const wxChar 
*filename
); 
 204     bool Generate(int flags
); 
 208         if ( m_hFile 
!= INVALID_HANDLE_VALUE 
) 
 210             ::CloseHandle(m_hFile
); 
 216     // formatted output to m_hFile 
 217     void Output(const wxChar 
*format
, ...); 
 219     // output end of line 
 220     void OutputEndl() { Output(_T("\r\n")); } 
 225     // translate exception code to its symbolic name 
 226     static wxString 
GetExceptionString(DWORD dwCode
); 
 228     // return the type from "type index" 
 229     static BasicType 
GetBasicType(DWORD64 modBase
, DWORD typeIndex
); 
 231     // return the name for the type index 
 232     static wxString 
GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
); 
 234     // return the string representation of the variable value 
 235     static wxString 
FormatSimpleValue(BasicType bt
, 
 239     // return string representation of a struct field (which may itself be a 
 240     // struct, of course) 
 241     static wxString 
FormatField(DWORD64 modBase
, 
 246     // show the name and value of the given symbol 
 247     static wxString 
FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME 
*sf
); 
 249     // show value described by SYMBOL_INFO located at pVariable 
 250     static wxString 
FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
); 
 252     // show value of possibly complex (user-defined) type 
 253     static wxString 
FormatUDT(DWORD64 modBase
, 
 258     // outputs information about the given symbol 
 259     void OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME 
*sf
); 
 261     // map address to module (and also section:offset), retunr true if ok 
 262     static bool GetLogicalAddress(PVOID addr
, 
 268     // callback used with SymEnumSymbols() to process all variables 
 269     static BOOL CALLBACK 
EnumerateSymbolsCallback(PSYMBOL_INFO  pSymInfo
, 
 274     // show the general information about exception which should be always 
 277     // returns the module of the handle where the crash occured 
 278     HANDLE 
OutputBasicContext(EXCEPTION_RECORD 
*pExceptionRecord
, CONTEXT 
*pCtx
); 
 280     // output the call stack and local variables values 
 281     void OutputStack(const CONTEXT 
*pCtx
, int flags
); 
 283     // output the global variables values 
 284     void OutputGlobals(HANDLE hModuleCrash
); 
 287     // the current stack frame (may be NULL) 
 288     STACKFRAME 
*m_sfCurrent
; 
 289 #endif // !wxUSE_MINIDUMP 
 291     // load all the functions we need from dbghelp.dll, return true if all ok 
 292     bool BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
); 
 295     // dynamically loaded dbghelp.dll functions 
 296     #define DECLARE_SYM_FUNCTION(func) static func ## _t func 
 299     DECLARE_SYM_FUNCTION(MiniDumpWriteDump
); 
 300 #else // !wxUSE_MINIDUMP 
 301     DECLARE_SYM_FUNCTION(SymSetOptions
); 
 302     DECLARE_SYM_FUNCTION(SymInitialize
); 
 303     DECLARE_SYM_FUNCTION(StackWalk
); 
 304     DECLARE_SYM_FUNCTION(SymFromAddr
); 
 305     DECLARE_SYM_FUNCTION(SymFunctionTableAccess
); 
 306     DECLARE_SYM_FUNCTION(SymGetModuleBase
); 
 307     DECLARE_SYM_FUNCTION(SymGetLineFromAddr
); 
 308     DECLARE_SYM_FUNCTION(SymSetContext
); 
 309     DECLARE_SYM_FUNCTION(SymEnumSymbols
); 
 310     DECLARE_SYM_FUNCTION(SymGetTypeInfo
); 
 311 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP 
 313     #undef DECLARE_SYM_FUNCTION 
 314 #endif // wxUSE_DBGHELP 
 316     // the handle of the report file 
 320 // ---------------------------------------------------------------------------- 
 322 // ---------------------------------------------------------------------------- 
 324 // global pointer to exception information, only valid inside OnFatalException 
 325 extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS 
*wxGlobalSEInformation 
= NULL
; 
 328 // flag telling us whether the application wants to handle exceptions at all 
 329 static bool gs_handleExceptions 
= false; 
 331 // the file name where the report about exception is written 
 332 static wxChar gs_reportFilename
[MAX_PATH
]; 
 334 // ============================================================================ 
 336 // ============================================================================ 
 340 #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0 
 343 DEFINE_SYM_FUNCTION(MiniDumpWriteDump
); 
 344 #else // !wxUSE_MINIDUMP 
 345 DEFINE_SYM_FUNCTION(SymSetOptions
); 
 346 DEFINE_SYM_FUNCTION(SymInitialize
); 
 347 DEFINE_SYM_FUNCTION(StackWalk
); 
 348 DEFINE_SYM_FUNCTION(SymFromAddr
); 
 349 DEFINE_SYM_FUNCTION(SymFunctionTableAccess
); 
 350 DEFINE_SYM_FUNCTION(SymGetModuleBase
); 
 351 DEFINE_SYM_FUNCTION(SymGetLineFromAddr
); 
 352 DEFINE_SYM_FUNCTION(SymSetContext
); 
 353 DEFINE_SYM_FUNCTION(SymEnumSymbols
); 
 354 DEFINE_SYM_FUNCTION(SymGetTypeInfo
); 
 355 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP 
 357 #undef DEFINE_SYM_FUNCTION 
 359 #endif // wxUSE_DBGHELP 
 361 // ---------------------------------------------------------------------------- 
 363 // ---------------------------------------------------------------------------- 
 365 wxCrashReportImpl::wxCrashReportImpl(const wxChar 
*filename
) 
 367 #if wxUSE_DBGHELP && !wxUSE_MINIDUMP 
 369 #endif // wxUSE_DBGHELP 
 371     m_hFile 
= ::CreateFile
 
 376                     NULL
,                       // default security 
 378                     FILE_FLAG_WRITE_THROUGH
, 
 379                     NULL                        
// no template file 
 383 void wxCrashReportImpl::Output(const wxChar 
*format
, ...) 
 386     va_start(argptr
, format
); 
 390     wxString s 
= wxString::FormatV(format
, argptr
); 
 391     ::WriteFile(m_hFile
, s
, s
.length() * sizeof(wxChar
), &cbWritten
, 0); 
 401 wxCrashReportImpl::GetLogicalAddress(PVOID addr
, 
 407     MEMORY_BASIC_INFORMATION mbi
; 
 409     if ( !::VirtualQuery(addr
, &mbi
, sizeof(mbi
)) ) 
 412     DWORD hMod 
= (DWORD
)mbi
.AllocationBase
; 
 414     if ( !::GetModuleFileName((HMODULE
)hMod
, szModule
, len
) ) 
 417     // Point to the DOS header in memory 
 418     PIMAGE_DOS_HEADER pDosHdr 
= (PIMAGE_DOS_HEADER
)hMod
; 
 420     // From the DOS header, find the NT (PE) header 
 421     PIMAGE_NT_HEADERS pNtHdr 
= (PIMAGE_NT_HEADERS
)(hMod 
+ pDosHdr
->e_lfanew
); 
 423     PIMAGE_SECTION_HEADER pSection 
= IMAGE_FIRST_SECTION( pNtHdr 
); 
 425     DWORD rva 
= (DWORD
)addr 
- hMod
; // RVA is offset from module load address 
 427     // Iterate through the section table, looking for the one that encompasses 
 428     // the linear address. 
 429     const DWORD nSections 
= pNtHdr
->FileHeader
.NumberOfSections
; 
 430     for ( DWORD i 
= 0; i 
< nSections
; i
++, pSection
++ ) 
 432         DWORD sectionStart 
= pSection
->VirtualAddress
; 
 433         DWORD sectionEnd 
= sectionStart
 
 434                     + max(pSection
->SizeOfRawData
, pSection
->Misc
.VirtualSize
); 
 436         // Is the address in this section? 
 437         if ( (rva 
>= sectionStart
) && (rva 
<= sectionEnd
) ) 
 439             // Yes, address is in the section.  Calculate section and offset, 
 440             // and store in the "section" & "offset" params, which were 
 441             // passed by reference. 
 443             offset 
= rva 
- sectionStart
; 
 449     // failed to map to logical address... 
 453 /* static */ BasicType
 
 454 wxCrashReportImpl::GetBasicType(DWORD64 modBase
, DWORD typeIndex
) 
 456     const HANDLE hProcess 
= GetCurrentProcess(); 
 458     // try the index we have 
 460     if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_BASETYPE
, &bt
) ) 
 465     // if failed, try to get the "real" typeid first 
 467     if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_TYPEID
, &typeId
) 
 469          (typeId 
!= typeIndex 
&& 
 470             SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_BASETYPE
, &bt
)) ) 
 475     return BASICTYPE_NOTYPE
; 
 478 /* static */ wxString
 
 479 wxCrashReportImpl::FormatSimpleValue(BasicType bt
, 
 486     // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) 
 489         s
.Printf(_T("%#04x"), *(PBYTE
)pAddress
); 
 491     else if ( length 
== 2 ) 
 493         s
.Printf(_T("%#06x"), *(PWORD
)pAddress
); 
 495     else if ( length 
== 4 ) 
 497         bool handled 
= false; 
 499         if ( bt 
== BASICTYPE_FLOAT 
) 
 501             s
.Printf(_T("%f"), *(PFLOAT
)pAddress
); 
 505         else if ( bt 
== BASICTYPE_CHAR 
) 
 507             static const size_t NUM_CHARS 
= 32; 
 509             const char * const pc 
= *(PSTR 
*)pAddress
; 
 510             if ( !::IsBadStringPtrA(pc
, NUM_CHARS
) ) 
 512                 s 
<< _T('"') << wxString(pc
, wxConvLibc
, NUM_CHARS
) << _T('"'); 
 520             // treat just as an opaque DWORD 
 521             s
.Printf(_T("%#x"), *(PDWORD
)pAddress
); 
 524     else if ( length 
== 8 ) 
 526         if ( bt 
== BASICTYPE_FLOAT 
) 
 528             s
.Printf(_T("%lf"), *(double *)pAddress
); 
 530         else // opaque 64 bit value 
 532             s
.Printf(_T("%#" wxLongLongFmtSpec 
_T("x")), *(PDWORD 
*)pAddress
); 
 540 wxString 
wxCrashReportImpl::GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
) 
 554         s 
= wxConvCurrent
->cWC2WX(pwszTypeName
); 
 556         ::LocalFree(pwszTypeName
); 
 562 // this is called for the struct members/base classes 
 564 wxCrashReportImpl::FormatField(DWORD64 modBase
, 
 571     // avoid infinite recursion 
 577     const HANDLE hProcess 
= GetCurrentProcess(); 
 580     SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_SYMTAG
, &dwTag
); 
 585         case SYMBOL_TAG_BASECLASS
: 
 586             s 
= FormatUDT(modBase
, dwTypeIndex
, pVariable
, level
); 
 589         case SYMBOL_TAG_FUNCTION
: 
 594             // try to treat all the rest as data even though it's not clear if 
 595             // it's really a good idea... 
 597             // Get the offset of the child member, relative to its parent 
 598             DWORD dwMemberOffset 
= 0; 
 599             SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, 
 600                            TI_GET_OFFSET
, &dwMemberOffset
); 
 602             // Get the real "TypeId" of the child.  We need this for the 
 603             // SymGetTypeInfo(TI_GET_LENGTH) call below. 
 605             if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, 
 606                                  TI_GET_TYPEID
, &typeId
) ) 
 608                 typeId 
= dwTypeIndex
; 
 611             // Get the size of the child member 
 613             SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_LENGTH
, &size
); 
 615             // Calculate the address of the member 
 616             DWORD_PTR dwFinalOffset 
= (DWORD_PTR
)pVariable 
+ dwMemberOffset
; 
 618             BasicType basicType 
= GetBasicType(modBase
, dwTypeIndex
); 
 620             s 
= FormatSimpleValue(basicType
, size
, (PVOID
)dwFinalOffset
); 
 627         // don't show if no value -- what for? 
 631     return wxString(_T('\t'), level 
+ 1) + 
 632                 GetSymbolName(modBase
, dwTypeIndex
) + 
 633                     _T(" = ") + s 
+ _T("\r\n"); 
 636 // If it's a user defined type (UDT), recurse through its members until we're 
 637 // at fundamental types. 
 639 wxCrashReportImpl::FormatUDT(DWORD64 modBase
, 
 646     s 
= GetSymbolName(modBase
, dwTypeIndex
) + _T(" {\r\n"); 
 648     const HANDLE hProcess 
= GetCurrentProcess(); 
 650     // Determine how many children this type has. 
 651     DWORD dwChildrenCount 
= 0; 
 652     SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
, 
 655     // Prepare to get an array of "TypeIds", representing each of the children. 
 656     TI_FINDCHILDREN_PARAMS 
*children 
= (TI_FINDCHILDREN_PARAMS 
*) 
 657         malloc(sizeof(TI_FINDCHILDREN_PARAMS
) + 
 658                     (dwChildrenCount 
- 1)*sizeof(ULONG
)); 
 662     children
->Count 
= dwChildrenCount
; 
 665     // Get the array of TypeIds, one for each child type 
 666     if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
, 
 672     // Iterate through all children 
 673     for ( unsigned i 
= 0; i 
< dwChildrenCount
; i
++ ) 
 675         s 
+= FormatField(modBase
, children
->ChildId
[i
], pVariable
, level 
+ 1); 
 680     s 
<< wxString(_T('\t'), level 
+ 1) << _T('}'); 
 685 // return the string containing the symbol of the given symbol 
 686 /* static */ wxString
 
 687 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
) 
 690     SymGetTypeInfo(GetCurrentProcess(), pSym
->ModBase
, pSym
->TypeIndex
, 
 691                    TI_GET_SYMTAG
, &dwTag
); 
 696         case SYMBOL_TAG_FUNCTION
: 
 700         case SYMBOL_TAG_BASECLASS
: 
 701             // show UDT recursively 
 702             s 
= FormatUDT(pSym
->ModBase
, pSym
->TypeIndex
, pVariable
); 
 706             // variable of simple type (but could be array which we don't 
 707             // handle correctly yet...), format it using its type and size 
 708             BasicType bt 
= GetBasicType(pSym
->ModBase
, pSym
->TypeIndex
); 
 710             s 
= FormatSimpleValue(bt
, pSym
->Size
, pVariable
); 
 718 // display contents and type of the given variable 
 719 /* static */ wxString
 
 720 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME 
*sf
) 
 724     if ( pSym
->Tag 
== SYMBOL_TAG_FUNCTION 
) 
 726         // If it's a function, don't do anything. 
 730     if ( pSym
->Flags 
& IMAGEHLP_SYMBOL_INFO_REGISTER 
) 
 732         // Don't try to report register variable 
 738     // Indicate if the variable is a local or parameter 
 739     if ( pSym
->Flags 
& IMAGEHLP_SYMBOL_INFO_PARAMETER 
) 
 740         s 
+= _T("\t[param] "); 
 741     else if ( pSym
->Flags 
& IMAGEHLP_SYMBOL_INFO_LOCAL 
) 
 742         s 
+= _T("\t[local] "); 
 744     // Will point to the variable's data in memory 
 745     DWORD_PTR pVariable 
= 0; 
 747     if ( (pSym
->Flags 
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
) && sf 
) 
 749         pVariable 
= sf
->AddrFrame
.Offset
; 
 750         pVariable 
+= (DWORD_PTR
)pSym
->Address
; 
 752     else // It must be a global variable 
 754         pVariable 
= (DWORD_PTR
)pSym
->Address
; 
 757     s 
<< wxString(pSym
->Name
, wxConvLibc
) 
 759       << FormatAnyValue(pSym
, (PVOID
)pVariable
); 
 765 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME  
*sf
) 
 767     wxString s 
= FormatSymbol(pSymInfo
, sf
); 
 770         Output(_T("%s\r\n"), s
.c_str()); 
 772     //else: not an interesting symbol 
 775 // callback for SymEnumSymbols() 
 778 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO  pSymInfo
, 
 779                                             ULONG         
WXUNUSED(SymbolSize
), 
 782     wxCrashReportImpl 
*self 
= (wxCrashReportImpl 
*)UserContext
; 
 786         self
->OutputSymbol(pSymInfo
, self
->m_sfCurrent
); 
 788     __except ( EXCEPTION_EXECUTE_HANDLER 
) 
 790         self
->Output(_T("Can't process symbol %hs\r\n"), pSymInfo
->Name
); 
 793     // continue with enumeration 
 798 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD 
*pExceptionRecord
, 
 801     // First print information about the type of fault 
 802     const DWORD dwCode 
= pExceptionRecord
->ExceptionCode
; 
 803     Output(_T("Exception code: %s (%#10x)\r\n"), 
 804            GetExceptionString(dwCode
).c_str(), dwCode
); 
 806     // Now print information about where the fault occured 
 807     TCHAR szFaultingModule
[MAX_PATH
]; 
 810     void * const pExceptionAddress 
= pExceptionRecord
->ExceptionAddress
; 
 811     if ( !GetLogicalAddress(pExceptionAddress
, 
 813                             WXSIZEOF(szFaultingModule
), 
 819         wxStrcpy(szFaultingModule
, _T("<< unknown >>")); 
 822     Output(_T("Fault address:  %08x %02x:%08x %s\r\n"), 
 823            pExceptionAddress
, section
, offset
, szFaultingModule
); 
 826     // Show the registers 
 827     Output( _T("\r\nRegisters:\r\n") ); 
 829     Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"), 
 830             pCtx
->Eax
, pCtx
->Ebx
, pCtx
->Ecx
, pCtx
->Edx
, pCtx
->Esi
, pCtx
->Edi
); 
 832     Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x  EBP: %08x\r\n"), 
 833            pCtx
->SegCs
, pCtx
->Eip
, pCtx
->SegSs
, pCtx
->Esp
, pCtx
->Ebp 
); 
 834     Output(_T("DS: %04x  ES: %04x  FS: %04x  GS: %04x\r\n"), 
 835            pCtx
->SegDs
, pCtx
->SegEs
, pCtx
->SegFs
, pCtx
->SegGs
); 
 836     Output(_T("Flags: %08x\r\n"), pCtx
->EFlags 
); 
 839     return ::GetModuleHandle(szFaultingModule
); 
 842 void wxCrashReportImpl::OutputStack(const CONTEXT 
*pCtx
, int flags
) 
 850         // can't show locals under other architectures 
 855     for ( int step 
= 0; step 
< Output_Max
; step
++ ) 
 857         // don't do things we're not asked for 
 858         if ( (step 
== Output_Stack
) && !(flags 
& wxCRASH_REPORT_STACK
) || 
 859                 (step 
== Output_Locals
) && !(flags 
& wxCRASH_REPORT_LOCALS
) ) 
 864         // the context is going to be modified below so make a copy 
 867         Output(_T("\r\n%s\r\n") 
 868                _T(" # Address   Frame     Function            SourceFile\r\n"), 
 869                step 
== Output_Stack 
? _T("Call stack") : _T("Local variables")); 
 871         DWORD dwMachineType 
= 0; 
 877         // Initialize the STACKFRAME structure for the first call.  This is 
 878         // only necessary for Intel CPUs, and isn't mentioned in the 
 880         sf
.AddrPC
.Offset       
= ctx
.Eip
; 
 881         sf
.AddrPC
.Mode         
= AddrModeFlat
; 
 882         sf
.AddrStack
.Offset    
= ctx
.Esp
; 
 883         sf
.AddrStack
.Mode      
= AddrModeFlat
; 
 884         sf
.AddrFrame
.Offset    
= ctx
.Ebp
; 
 885         sf
.AddrFrame
.Mode      
= AddrModeFlat
; 
 887         dwMachineType 
= IMAGE_FILE_MACHINE_I386
; 
 890         const HANDLE hProcess 
= GetCurrentProcess(); 
 891         const HANDLE hThread 
= GetCurrentThread(); 
 893         // first show just the call stack 
 897             // Get the next stack frame 
 898             if ( !StackWalk(dwMachineType
, 
 904                             SymFunctionTableAccess
, 
 911             // Basic sanity check to make sure the frame is OK. 
 912             if ( !sf
.AddrFrame
.Offset 
) 
 915             Output(_T("%2d %08x  %08x  "), 
 916                    frame
++, sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
); 
 918             // Get the name of the function for this stack frame entry 
 919             BYTE symbolBuffer
[ sizeof(SYMBOL_INFO
) + 1024 ]; 
 920             PSYMBOL_INFO pSymbol 
= (PSYMBOL_INFO
)symbolBuffer
; 
 921             pSymbol
->SizeOfStruct 
= sizeof(symbolBuffer
); 
 922             pSymbol
->MaxNameLen 
= 1024; 
 924             // Displacement of the input address, relative to the start of the 
 926             DWORD64 symDisplacement 
= 0; 
 928             if ( SymFromAddr(hProcess
, sf
.AddrPC
.Offset
, 
 929                              &symDisplacement
,pSymbol
) ) 
 931                 Output(_T("%hs()+%#") wxLongLongFmtSpec 
_T("x"), 
 932                        pSymbol
->Name
, symDisplacement
); 
 934             else    // No symbol found.  Print out the logical address instead. 
 936                 TCHAR szModule
[MAX_PATH
]; 
 940                 if ( !GetLogicalAddress((PVOID
)sf
.AddrPC
.Offset
, 
 941                                         szModule
, sizeof(szModule
), 
 944                     szModule
[0] = _T('\0'); 
 949                 Output(_T("%04x:%08x %s"), section
, offset
, szModule
); 
 952             // Get the source line for this stack frame entry 
 953             IMAGEHLP_LINE lineInfo 
= { sizeof(IMAGEHLP_LINE
) }; 
 954             DWORD dwLineDisplacement
; 
 955             if ( SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
, 
 956                                     &dwLineDisplacement
, &lineInfo 
)) 
 958                 Output(_T("  %s line %u"), 
 959                        lineInfo
.FileName
, lineInfo
.LineNumber
); 
 966             // on the second iteration also show the local variables and 
 968             if ( step 
== Output_Locals 
) 
 970                 // Use SymSetContext to get just the locals/params for this 
 972                 IMAGEHLP_STACK_FRAME imagehlpStackFrame
; 
 973                 imagehlpStackFrame
.InstructionOffset 
= sf
.AddrPC
.Offset
; 
 974                 SymSetContext(hProcess
, &imagehlpStackFrame
, 0); 
 976                 // Enumerate the locals/parameters 
 978                 SymEnumSymbols(hProcess
, 0, 0, EnumerateSymbolsCallback
, this); 
 987 void wxCrashReportImpl::OutputGlobals(HANDLE hModule
) 
 990     Output(_T("\r\nGlobal variables:\r\n")); 
 993     SymEnumSymbols(::GetCurrentProcess(), (DWORD64
)hModule
, NULL
, 
 994                    EnumerateSymbolsCallback
, this); 
 998 #endif // wxUSE_MINIDUMP 
1000 bool wxCrashReportImpl::BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
) 
1002     #define LOAD_SYM_FUNCTION(name)                                           \ 
1003         name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name));                  \ 
1006             Output(_T("\r\nFunction ") _T(#name)                              \ 
1007                    _T("() not found.\r\n"));                                  \ 
1012     LOAD_SYM_FUNCTION(MiniDumpWriteDump
); 
1013 #else // !wxUSE_MINIDUMP 
1014     LOAD_SYM_FUNCTION(SymSetOptions
); 
1015     LOAD_SYM_FUNCTION(SymInitialize
); 
1016     LOAD_SYM_FUNCTION(StackWalk
); 
1017     LOAD_SYM_FUNCTION(SymFromAddr
); 
1018     LOAD_SYM_FUNCTION(SymFunctionTableAccess
); 
1019     LOAD_SYM_FUNCTION(SymGetModuleBase
); 
1020     LOAD_SYM_FUNCTION(SymGetLineFromAddr
); 
1021     LOAD_SYM_FUNCTION(SymSetContext
); 
1022     LOAD_SYM_FUNCTION(SymEnumSymbols
); 
1023     LOAD_SYM_FUNCTION(SymGetTypeInfo
); 
1024 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP 
1026     #undef LOAD_SYM_FUNCTION 
1034 wxString 
wxCrashReportImpl::GetExceptionString(DWORD dwCode
) 
1038     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break 
1042         CASE_EXCEPTION(ACCESS_VIOLATION
); 
1043         CASE_EXCEPTION(DATATYPE_MISALIGNMENT
); 
1044         CASE_EXCEPTION(BREAKPOINT
); 
1045         CASE_EXCEPTION(SINGLE_STEP
); 
1046         CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
); 
1047         CASE_EXCEPTION(FLT_DENORMAL_OPERAND
); 
1048         CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
); 
1049         CASE_EXCEPTION(FLT_INEXACT_RESULT
); 
1050         CASE_EXCEPTION(FLT_INVALID_OPERATION
); 
1051         CASE_EXCEPTION(FLT_OVERFLOW
); 
1052         CASE_EXCEPTION(FLT_STACK_CHECK
); 
1053         CASE_EXCEPTION(FLT_UNDERFLOW
); 
1054         CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
); 
1055         CASE_EXCEPTION(INT_OVERFLOW
); 
1056         CASE_EXCEPTION(PRIV_INSTRUCTION
); 
1057         CASE_EXCEPTION(IN_PAGE_ERROR
); 
1058         CASE_EXCEPTION(ILLEGAL_INSTRUCTION
); 
1059         CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
); 
1060         CASE_EXCEPTION(STACK_OVERFLOW
); 
1061         CASE_EXCEPTION(INVALID_DISPOSITION
); 
1062         CASE_EXCEPTION(GUARD_PAGE
); 
1063         CASE_EXCEPTION(INVALID_HANDLE
); 
1066             // unknown exception, ask NTDLL for the name 
1067             if ( !::FormatMessage
 
1069                      FORMAT_MESSAGE_IGNORE_INSERTS 
| 
1070                      FORMAT_MESSAGE_FROM_HMODULE
, 
1071                      ::GetModuleHandle(_T("NTDLL.DLL")), 
1074                      wxStringBuffer(s
, 1024), 
1079                 s 
= _T("UNKNOWN_EXCEPTION"); 
1083     #undef CASE_EXCEPTION 
1088 #endif // !wxUSE_MINIDUMP 
1090 #endif // wxUSE_DBGHELP 
1092 bool wxCrashReportImpl::Generate( 
1100     if ( m_hFile 
== INVALID_HANDLE_VALUE 
) 
1104     if ( !wxGlobalSEInformation 
) 
1108     PEXCEPTION_RECORD pExceptionRecord 
= wxGlobalSEInformation
->ExceptionRecord
; 
1109     PCONTEXT pCtx 
= wxGlobalSEInformation
->ContextRecord
; 
1111     if ( !pExceptionRecord 
|| !pCtx 
) 
1114     HANDLE hModuleCrash 
= OutputBasicContext(pExceptionRecord
, pCtx
); 
1115 #endif // !wxUSE_MINIDUMP 
1117     // show to the user that we're doing something... 
1118     BusyCursor busyCursor
; 
1120     // user-specified crash report flags override those specified by the 
1123     DWORD dwLen 
= ::GetEnvironmentVariable
 
1125                         _T("WX_CRASH_FLAGS"), 
1131     if ( dwLen 
&& dwLen 
< WXSIZEOF(envFlags
) && 
1132             wxSscanf(envFlags
, _T("%d"), &flagsEnv
) == 1 ) 
1137     // for everything else we need dbghelp.dll 
1138     wxDynamicLibrary 
dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM
); 
1139     if ( dllDbgHelp
.IsLoaded() ) 
1141         if ( BindDbgHelpFunctions(dllDbgHelp
) ) 
1144             MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo
; 
1146             minidumpExcInfo
.ThreadId 
= ::GetCurrentThreadId(); 
1147             minidumpExcInfo
.ExceptionPointers 
= wxGlobalSEInformation
; 
1148             minidumpExcInfo
.ClientPointers 
= FALSE
; // in our own address space 
1150             // do generate the dump 
1151             MINIDUMP_TYPE dumpFlags
; 
1152             if ( flags 
& wxCRASH_REPORT_LOCALS 
) 
1154                 // the only way to get local variables is to dump the entire 
1155                 // process memory space -- but this makes for huge (dozens or 
1156                 // even hundreds of Mb) files 
1157                 dumpFlags 
= MiniDumpWithFullMemory
; 
1159             else if ( flags 
& wxCRASH_REPORT_GLOBALS 
) 
1161                 // MiniDumpWriteDump() has the option for dumping just the data 
1162                 // segment which contains all globals -- exactly what we need 
1163                 dumpFlags 
= MiniDumpWithDataSegs
; 
1165             else // minimal dump 
1167                 dumpFlags 
= MiniDumpNormal
; 
1170             if ( !MiniDumpWriteDump
 
1172                     ::GetCurrentProcess(), 
1173                     ::GetCurrentProcessId(), 
1174                     m_hFile
,                    // file to write to 
1175                     dumpFlags
,                  // kind of dump to craete 
1177                     NULL
,                       // no extra user-defined data 
1178                     NULL                        
// no callbacks 
1181                 Output(_T("MiniDumpWriteDump() failed.")); 
1187 #else // !wxUSE_MINIDUMP 
1188             SymSetOptions(SYMOPT_DEFERRED_LOADS 
| SYMOPT_UNDNAME
); 
1190             // Initialize DbgHelp 
1191             if ( SymInitialize(GetCurrentProcess(), NULL
, TRUE 
/* invade */) ) 
1193                 OutputStack(pCtx
, flags
); 
1195                 if ( hModuleCrash 
&& (flags 
& wxCRASH_REPORT_GLOBALS
) ) 
1197                     OutputGlobals(hModuleCrash
); 
1202 #endif // !wxUSE_MINIDUMP 
1206             Output(_T("\r\nPlease update your dbghelp.dll version, ") 
1207                    _T("at least version 5.1 is needed!\r\n") 
1208                    _T("(if you already have a new version, please ") 
1209                    _T("put it in the same directory where the program is.)\r\n")); 
1212     else // failed to load dbghelp.dll 
1214         Output(_T("Please install dbghelp.dll available free of charge ") 
1215                _T("from Microsoft to get more detailed crash information!")); 
1218     Output(_T("\r\nLatest dbghelp.dll is available at ") 
1219            _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n")); 
1221 #else // !wxUSE_DBGHELP 
1222     Output(_T("Support for crash report generation was not included ") 
1223            _T("in this wxWindows version.")); 
1224 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 
1229 // ---------------------------------------------------------------------------- 
1231 // ---------------------------------------------------------------------------- 
1234 void wxCrashReport::SetFileName(const wxChar 
*filename
) 
1236     wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1); 
1237     gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0'); 
1241 const wxChar 
*wxCrashReport::GetFileName() 
1243     return gs_reportFilename
; 
1247 bool wxCrashReport::Generate(int flags
) 
1249     wxCrashReportImpl 
impl(gs_reportFilename
); 
1251     return impl
.Generate(flags
); 
1254 // ---------------------------------------------------------------------------- 
1255 // wxApp::OnFatalException() support 
1256 // ---------------------------------------------------------------------------- 
1258 bool wxHandleFatalExceptions(bool doit
) 
1260     // assume this can only be called from the main thread 
1261     gs_handleExceptions 
= doit
; 
1265         // try to find a place where we can put out report file later 
1268                     WXSIZEOF(gs_reportFilename
), 
1272             wxLogLastError(_T("GetTempPath")); 
1274             // when all else fails... 
1275             wxStrcpy(gs_reportFilename
, _T("c:\\")); 
1278         // use PID and date to make the report file name more unique 
1279         wxString fname 
= wxString::Format
 
1282                             _T("%s_%s_%lu.dmp"), 
1283 #else // !wxUSE_MINIDUMP 
1284                             _T("%s_%s_%lu.rpt"), 
1285 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP 
1286                             wxTheApp 
? wxTheApp
->GetAppName().c_str() 
1288                             wxDateTime::Now().Format(_T("%Y%m%d")).c_str(), 
1289                             ::GetCurrentProcessId() 
1292         wxStrncat(gs_reportFilename
, fname
, 
1293                   WXSIZEOF(gs_reportFilename
) - wxStrlen(gs_reportFilename
) - 1); 
1299 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS 
*pExcPtrs
) 
1301     if ( gs_handleExceptions 
&& wxTheApp 
) 
1303         // store the pointer to exception info 
1304         wxGlobalSEInformation 
= pExcPtrs
; 
1306         // give the user a chance to do something special about this 
1309             wxTheApp
->OnFatalException(); 
1311         __except ( EXCEPTION_EXECUTE_HANDLER 
) 
1313             // nothing to do here, just ignore the exception inside the 
1314             // exception handler 
1318         wxGlobalSEInformation 
= NULL
; 
1320         // this will execute our handler and terminate the process 
1321         return EXCEPTION_EXECUTE_HANDLER
; 
1324     return EXCEPTION_CONTINUE_SEARCH
; 
1327 #else // !wxUSE_ON_FATAL_EXCEPTION 
1329 bool wxHandleFatalExceptions(bool WXUNUSED(doit
)) 
1331     wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function")); 
1336 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION