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 // the real crash report generator
176 class wxCrashReportImpl
179 wxCrashReportImpl(const wxChar
*filename
);
181 bool Generate(int flags
);
185 if ( m_hFile
!= INVALID_HANDLE_VALUE
)
187 ::CloseHandle(m_hFile
);
193 // formatted output to m_hFile
194 void Output(const wxChar
*format
, ...);
196 // output end of line
197 void OutputEndl() { Output(_T("\r\n")); }
202 // translate exception code to its symbolic name
203 static wxString
GetExceptionString(DWORD dwCode
);
205 // return the type from "type index"
206 static BasicType
GetBasicType(DWORD64 modBase
, DWORD typeIndex
);
208 // return the name for the type index
209 static wxString
GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
);
211 // return the string representation of the variable value
212 static wxString
FormatSimpleValue(BasicType bt
,
216 // return string representation of a struct field (which may itself be a
217 // struct, of course)
218 static wxString
FormatField(DWORD64 modBase
,
223 // show the name and value of the given symbol
224 static wxString
FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
);
226 // show value described by SYMBOL_INFO located at pVariable
227 static wxString
FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
);
229 // show value of possibly complex (user-defined) type
230 static wxString
FormatUDT(DWORD64 modBase
,
235 // outputs information about the given symbol
236 void OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
);
238 // map address to module (and also section:offset), retunr true if ok
239 static bool GetLogicalAddress(PVOID addr
,
245 // callback used with SymEnumSymbols() to process all variables
246 static BOOL CALLBACK
EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
251 // show the general information about exception which should be always
254 // returns the module of the handle where the crash occured
255 HANDLE
OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
, CONTEXT
*pCtx
);
257 // output the call stack and local variables values
258 void OutputStack(const CONTEXT
*pCtx
, int flags
);
260 // output the global variables values
261 void OutputGlobals(HANDLE hModuleCrash
);
264 // the current stack frame (may be NULL)
265 STACKFRAME
*m_sfCurrent
;
266 #endif // !wxUSE_MINIDUMP
268 // load all the functions we need from dbghelp.dll, return true if all ok
269 bool BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
);
272 // dynamically loaded dbghelp.dll functions
273 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
276 DECLARE_SYM_FUNCTION(MiniDumpWriteDump
);
277 #else // !wxUSE_MINIDUMP
278 DECLARE_SYM_FUNCTION(SymSetOptions
);
279 DECLARE_SYM_FUNCTION(SymInitialize
);
280 DECLARE_SYM_FUNCTION(StackWalk
);
281 DECLARE_SYM_FUNCTION(SymFromAddr
);
282 DECLARE_SYM_FUNCTION(SymFunctionTableAccess
);
283 DECLARE_SYM_FUNCTION(SymGetModuleBase
);
284 DECLARE_SYM_FUNCTION(SymGetLineFromAddr
);
285 DECLARE_SYM_FUNCTION(SymSetContext
);
286 DECLARE_SYM_FUNCTION(SymEnumSymbols
);
287 DECLARE_SYM_FUNCTION(SymGetTypeInfo
);
288 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
290 #undef DECLARE_SYM_FUNCTION
291 #endif // wxUSE_DBGHELP
293 // the handle of the report file
297 // ----------------------------------------------------------------------------
299 // ----------------------------------------------------------------------------
301 // global pointer to exception information, only valid inside OnFatalException
302 extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS
*wxGlobalSEInformation
= NULL
;
305 // flag telling us whether the application wants to handle exceptions at all
306 static bool gs_handleExceptions
= false;
308 // the file name where the report about exception is written
309 static wxChar gs_reportFilename
[MAX_PATH
];
311 // ============================================================================
313 // ============================================================================
317 #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
320 DEFINE_SYM_FUNCTION(MiniDumpWriteDump
);
321 #else // !wxUSE_MINIDUMP
322 DEFINE_SYM_FUNCTION(SymSetOptions
);
323 DEFINE_SYM_FUNCTION(SymInitialize
);
324 DEFINE_SYM_FUNCTION(StackWalk
);
325 DEFINE_SYM_FUNCTION(SymFromAddr
);
326 DEFINE_SYM_FUNCTION(SymFunctionTableAccess
);
327 DEFINE_SYM_FUNCTION(SymGetModuleBase
);
328 DEFINE_SYM_FUNCTION(SymGetLineFromAddr
);
329 DEFINE_SYM_FUNCTION(SymSetContext
);
330 DEFINE_SYM_FUNCTION(SymEnumSymbols
);
331 DEFINE_SYM_FUNCTION(SymGetTypeInfo
);
332 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
334 #undef DEFINE_SYM_FUNCTION
336 #endif // wxUSE_DBGHELP
338 // ----------------------------------------------------------------------------
340 // ----------------------------------------------------------------------------
342 wxCrashReportImpl::wxCrashReportImpl(const wxChar
*filename
)
344 #if wxUSE_DBGHELP && !wxUSE_MINIDUMP
346 #endif // wxUSE_DBGHELP
348 m_hFile
= ::CreateFile
353 NULL
, // default security
355 FILE_FLAG_WRITE_THROUGH
,
356 NULL
// no template file
360 void wxCrashReportImpl::Output(const wxChar
*format
, ...)
363 va_start(argptr
, format
);
367 wxString s
= wxString::FormatV(format
, argptr
);
368 ::WriteFile(m_hFile
, s
, s
.length() * sizeof(wxChar
), &cbWritten
, 0);
378 wxCrashReportImpl::GetLogicalAddress(PVOID addr
,
384 MEMORY_BASIC_INFORMATION mbi
;
386 if ( !::VirtualQuery(addr
, &mbi
, sizeof(mbi
)) )
389 DWORD hMod
= (DWORD
)mbi
.AllocationBase
;
391 if ( !::GetModuleFileName((HMODULE
)hMod
, szModule
, len
) )
394 // Point to the DOS header in memory
395 PIMAGE_DOS_HEADER pDosHdr
= (PIMAGE_DOS_HEADER
)hMod
;
397 // From the DOS header, find the NT (PE) header
398 PIMAGE_NT_HEADERS pNtHdr
= (PIMAGE_NT_HEADERS
)(hMod
+ pDosHdr
->e_lfanew
);
400 PIMAGE_SECTION_HEADER pSection
= IMAGE_FIRST_SECTION( pNtHdr
);
402 DWORD rva
= (DWORD
)addr
- hMod
; // RVA is offset from module load address
404 // Iterate through the section table, looking for the one that encompasses
405 // the linear address.
406 const DWORD nSections
= pNtHdr
->FileHeader
.NumberOfSections
;
407 for ( DWORD i
= 0; i
< nSections
; i
++, pSection
++ )
409 DWORD sectionStart
= pSection
->VirtualAddress
;
410 DWORD sectionEnd
= sectionStart
411 + max(pSection
->SizeOfRawData
, pSection
->Misc
.VirtualSize
);
413 // Is the address in this section?
414 if ( (rva
>= sectionStart
) && (rva
<= sectionEnd
) )
416 // Yes, address is in the section. Calculate section and offset,
417 // and store in the "section" & "offset" params, which were
418 // passed by reference.
420 offset
= rva
- sectionStart
;
426 // failed to map to logical address...
430 /* static */ BasicType
431 wxCrashReportImpl::GetBasicType(DWORD64 modBase
, DWORD typeIndex
)
433 const HANDLE hProcess
= GetCurrentProcess();
435 // try the index we have
437 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_BASETYPE
, &bt
) )
442 // if failed, try to get the "real" typeid first
444 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_TYPEID
, &typeId
)
446 (typeId
!= typeIndex
&&
447 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_BASETYPE
, &bt
)) )
452 return BASICTYPE_NOTYPE
;
455 /* static */ wxString
456 wxCrashReportImpl::FormatSimpleValue(BasicType bt
,
463 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
466 s
.Printf(_T("%#04x"), *(PBYTE
)pAddress
);
468 else if ( length
== 2 )
470 s
.Printf(_T("%#06x"), *(PWORD
)pAddress
);
472 else if ( length
== 4 )
474 bool handled
= false;
476 if ( bt
== BASICTYPE_FLOAT
)
478 s
.Printf(_T("%f"), *(PFLOAT
)pAddress
);
482 else if ( bt
== BASICTYPE_CHAR
)
484 static const size_t NUM_CHARS
= 32;
486 const char * const pc
= *(PSTR
*)pAddress
;
487 if ( !::IsBadStringPtrA(pc
, NUM_CHARS
) )
489 s
<< _T('"') << wxString(pc
, wxConvLibc
, NUM_CHARS
) << _T('"');
497 // treat just as an opaque DWORD
498 s
.Printf(_T("%#x"), *(PDWORD
)pAddress
);
501 else if ( length
== 8 )
503 if ( bt
== BASICTYPE_FLOAT
)
505 s
.Printf(_T("%lf"), *(double *)pAddress
);
507 else // opaque 64 bit value
509 s
.Printf(_T("%#" wxLongLongFmtSpec
_T("x")), *(PDWORD
*)pAddress
);
517 wxString
wxCrashReportImpl::GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
)
531 s
= wxConvCurrent
->cWC2WX(pwszTypeName
);
533 ::LocalFree(pwszTypeName
);
539 // this is called for the struct members/base classes
541 wxCrashReportImpl::FormatField(DWORD64 modBase
,
548 // avoid infinite recursion
554 const HANDLE hProcess
= GetCurrentProcess();
557 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_SYMTAG
, &dwTag
);
562 case SYMBOL_TAG_BASECLASS
:
563 s
= FormatUDT(modBase
, dwTypeIndex
, pVariable
, level
);
566 case SYMBOL_TAG_FUNCTION
:
571 // try to treat all the rest as data even though it's not clear if
572 // it's really a good idea...
574 // Get the offset of the child member, relative to its parent
575 DWORD dwMemberOffset
= 0;
576 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
577 TI_GET_OFFSET
, &dwMemberOffset
);
579 // Get the real "TypeId" of the child. We need this for the
580 // SymGetTypeInfo(TI_GET_LENGTH) call below.
582 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
583 TI_GET_TYPEID
, &typeId
) )
585 typeId
= dwTypeIndex
;
588 // Get the size of the child member
590 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_LENGTH
, &size
);
592 // Calculate the address of the member
593 DWORD_PTR dwFinalOffset
= (DWORD_PTR
)pVariable
+ dwMemberOffset
;
595 BasicType basicType
= GetBasicType(modBase
, dwTypeIndex
);
597 s
= FormatSimpleValue(basicType
, size
, (PVOID
)dwFinalOffset
);
604 // don't show if no value -- what for?
608 return wxString(_T('\t'), level
+ 1) +
609 GetSymbolName(modBase
, dwTypeIndex
) +
610 _T(" = ") + s
+ _T("\r\n");
613 // If it's a user defined type (UDT), recurse through its members until we're
614 // at fundamental types.
616 wxCrashReportImpl::FormatUDT(DWORD64 modBase
,
623 s
= GetSymbolName(modBase
, dwTypeIndex
) + _T(" {\r\n");
625 const HANDLE hProcess
= GetCurrentProcess();
627 // Determine how many children this type has.
628 DWORD dwChildrenCount
= 0;
629 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
,
632 // Prepare to get an array of "TypeIds", representing each of the children.
633 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
634 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
635 (dwChildrenCount
- 1)*sizeof(ULONG
));
639 children
->Count
= dwChildrenCount
;
642 // Get the array of TypeIds, one for each child type
643 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
,
649 // Iterate through all children
650 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
652 s
+= FormatField(modBase
, children
->ChildId
[i
], pVariable
, level
+ 1);
657 s
<< wxString(_T('\t'), level
+ 1) << _T('}');
662 // return the string containing the symbol of the given symbol
663 /* static */ wxString
664 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
)
667 SymGetTypeInfo(GetCurrentProcess(), pSym
->ModBase
, pSym
->TypeIndex
,
668 TI_GET_SYMTAG
, &dwTag
);
673 case SYMBOL_TAG_FUNCTION
:
677 case SYMBOL_TAG_BASECLASS
:
678 // show UDT recursively
679 s
= FormatUDT(pSym
->ModBase
, pSym
->TypeIndex
, pVariable
);
683 // variable of simple type (but could be array which we don't
684 // handle correctly yet...), format it using its type and size
685 BasicType bt
= GetBasicType(pSym
->ModBase
, pSym
->TypeIndex
);
687 s
= FormatSimpleValue(bt
, pSym
->Size
, pVariable
);
695 // display contents and type of the given variable
696 /* static */ wxString
697 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
)
701 if ( pSym
->Tag
== SYMBOL_TAG_FUNCTION
)
703 // If it's a function, don't do anything.
707 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGISTER
)
709 // Don't try to report register variable
715 // Indicate if the variable is a local or parameter
716 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
717 s
+= _T("\t[param] ");
718 else if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_LOCAL
)
719 s
+= _T("\t[local] ");
721 // Will point to the variable's data in memory
722 DWORD_PTR pVariable
= 0;
724 if ( (pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
) && sf
)
726 pVariable
= sf
->AddrFrame
.Offset
;
727 pVariable
+= (DWORD_PTR
)pSym
->Address
;
729 else // It must be a global variable
731 pVariable
= (DWORD_PTR
)pSym
->Address
;
734 s
<< wxString(pSym
->Name
, wxConvLibc
)
736 << FormatAnyValue(pSym
, (PVOID
)pVariable
);
742 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
)
744 wxString s
= FormatSymbol(pSymInfo
, sf
);
747 Output(_T("%s\r\n"), s
.c_str());
749 //else: not an interesting symbol
752 // callback for SymEnumSymbols()
755 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
756 ULONG
WXUNUSED(SymbolSize
),
759 wxCrashReportImpl
*self
= (wxCrashReportImpl
*)UserContext
;
763 self
->OutputSymbol(pSymInfo
, self
->m_sfCurrent
);
765 __except ( EXCEPTION_EXECUTE_HANDLER
)
767 self
->Output(_T("Can't process symbol %hs\r\n"), pSymInfo
->Name
);
770 // continue with enumeration
775 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
,
778 // First print information about the type of fault
779 const DWORD dwCode
= pExceptionRecord
->ExceptionCode
;
780 Output(_T("Exception code: %s (%#10x)\r\n"),
781 GetExceptionString(dwCode
).c_str(), dwCode
);
783 // Now print information about where the fault occured
784 TCHAR szFaultingModule
[MAX_PATH
];
787 void * const pExceptionAddress
= pExceptionRecord
->ExceptionAddress
;
788 if ( !GetLogicalAddress(pExceptionAddress
,
790 WXSIZEOF(szFaultingModule
),
796 wxStrcpy(szFaultingModule
, _T("<< unknown >>"));
799 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
800 pExceptionAddress
, section
, offset
, szFaultingModule
);
803 // Show the registers
804 Output( _T("\r\nRegisters:\r\n") );
806 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
807 pCtx
->Eax
, pCtx
->Ebx
, pCtx
->Ecx
, pCtx
->Edx
, pCtx
->Esi
, pCtx
->Edi
);
809 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
810 pCtx
->SegCs
, pCtx
->Eip
, pCtx
->SegSs
, pCtx
->Esp
, pCtx
->Ebp
);
811 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
812 pCtx
->SegDs
, pCtx
->SegEs
, pCtx
->SegFs
, pCtx
->SegGs
);
813 Output(_T("Flags: %08x\r\n"), pCtx
->EFlags
);
816 return ::GetModuleHandle(szFaultingModule
);
819 void wxCrashReportImpl::OutputStack(const CONTEXT
*pCtx
, int flags
)
827 // can't show locals under other architectures
832 for ( int step
= 0; step
< Output_Max
; step
++ )
834 // don't do things we're not asked for
835 if ( (step
== Output_Stack
) && !(flags
& wxCRASH_REPORT_STACK
) ||
836 (step
== Output_Locals
) && !(flags
& wxCRASH_REPORT_LOCALS
) )
841 // the context is going to be modified below so make a copy
844 Output(_T("\r\n%s\r\n")
845 _T(" # Address Frame Function SourceFile\r\n"),
846 step
== Output_Stack
? _T("Call stack") : _T("Local variables"));
848 DWORD dwMachineType
= 0;
854 // Initialize the STACKFRAME structure for the first call. This is
855 // only necessary for Intel CPUs, and isn't mentioned in the
857 sf
.AddrPC
.Offset
= ctx
.Eip
;
858 sf
.AddrPC
.Mode
= AddrModeFlat
;
859 sf
.AddrStack
.Offset
= ctx
.Esp
;
860 sf
.AddrStack
.Mode
= AddrModeFlat
;
861 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
862 sf
.AddrFrame
.Mode
= AddrModeFlat
;
864 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
867 const HANDLE hProcess
= GetCurrentProcess();
868 const HANDLE hThread
= GetCurrentThread();
870 // first show just the call stack
874 // Get the next stack frame
875 if ( !StackWalk(dwMachineType
,
881 SymFunctionTableAccess
,
888 // Basic sanity check to make sure the frame is OK.
889 if ( !sf
.AddrFrame
.Offset
)
892 Output(_T("%2d %08x %08x "),
893 frame
++, sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
895 // Get the name of the function for this stack frame entry
896 BYTE symbolBuffer
[ sizeof(SYMBOL_INFO
) + 1024 ];
897 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
898 pSymbol
->SizeOfStruct
= sizeof(symbolBuffer
);
899 pSymbol
->MaxNameLen
= 1024;
901 // Displacement of the input address, relative to the start of the
903 DWORD64 symDisplacement
= 0;
905 if ( SymFromAddr(hProcess
, sf
.AddrPC
.Offset
,
906 &symDisplacement
,pSymbol
) )
908 Output(_T("%hs()+%#") wxLongLongFmtSpec
_T("x"),
909 pSymbol
->Name
, symDisplacement
);
911 else // No symbol found. Print out the logical address instead.
913 TCHAR szModule
[MAX_PATH
];
917 if ( !GetLogicalAddress((PVOID
)sf
.AddrPC
.Offset
,
918 szModule
, sizeof(szModule
),
921 szModule
[0] = _T('\0');
926 Output(_T("%04x:%08x %s"), section
, offset
, szModule
);
929 // Get the source line for this stack frame entry
930 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
931 DWORD dwLineDisplacement
;
932 if ( SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
,
933 &dwLineDisplacement
, &lineInfo
))
935 Output(_T(" %s line %u"),
936 lineInfo
.FileName
, lineInfo
.LineNumber
);
943 // on the second iteration also show the local variables and
945 if ( step
== Output_Locals
)
947 // Use SymSetContext to get just the locals/params for this
949 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
950 imagehlpStackFrame
.InstructionOffset
= sf
.AddrPC
.Offset
;
951 SymSetContext(hProcess
, &imagehlpStackFrame
, 0);
953 // Enumerate the locals/parameters
955 SymEnumSymbols(hProcess
, 0, 0, EnumerateSymbolsCallback
, this);
964 void wxCrashReportImpl::OutputGlobals(HANDLE hModule
)
967 Output(_T("\r\nGlobal variables:\r\n"));
970 SymEnumSymbols(::GetCurrentProcess(), (DWORD64
)hModule
, NULL
,
971 EnumerateSymbolsCallback
, this);
975 #endif // wxUSE_MINIDUMP
977 bool wxCrashReportImpl::BindDbgHelpFunctions(const wxDynamicLibrary
& dllDbgHelp
)
979 #define LOAD_SYM_FUNCTION(name) \
980 name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \
983 Output(_T("\r\nFunction ") _T(#name) \
984 _T("() not found.\r\n")); \
989 LOAD_SYM_FUNCTION(MiniDumpWriteDump
);
990 #else // !wxUSE_MINIDUMP
991 LOAD_SYM_FUNCTION(SymSetOptions
);
992 LOAD_SYM_FUNCTION(SymInitialize
);
993 LOAD_SYM_FUNCTION(StackWalk
);
994 LOAD_SYM_FUNCTION(SymFromAddr
);
995 LOAD_SYM_FUNCTION(SymFunctionTableAccess
);
996 LOAD_SYM_FUNCTION(SymGetModuleBase
);
997 LOAD_SYM_FUNCTION(SymGetLineFromAddr
);
998 LOAD_SYM_FUNCTION(SymSetContext
);
999 LOAD_SYM_FUNCTION(SymEnumSymbols
);
1000 LOAD_SYM_FUNCTION(SymGetTypeInfo
);
1001 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
1003 #undef LOAD_SYM_FUNCTION
1011 wxString
wxCrashReportImpl::GetExceptionString(DWORD dwCode
)
1015 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
1019 CASE_EXCEPTION(ACCESS_VIOLATION
);
1020 CASE_EXCEPTION(DATATYPE_MISALIGNMENT
);
1021 CASE_EXCEPTION(BREAKPOINT
);
1022 CASE_EXCEPTION(SINGLE_STEP
);
1023 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
);
1024 CASE_EXCEPTION(FLT_DENORMAL_OPERAND
);
1025 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
);
1026 CASE_EXCEPTION(FLT_INEXACT_RESULT
);
1027 CASE_EXCEPTION(FLT_INVALID_OPERATION
);
1028 CASE_EXCEPTION(FLT_OVERFLOW
);
1029 CASE_EXCEPTION(FLT_STACK_CHECK
);
1030 CASE_EXCEPTION(FLT_UNDERFLOW
);
1031 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
);
1032 CASE_EXCEPTION(INT_OVERFLOW
);
1033 CASE_EXCEPTION(PRIV_INSTRUCTION
);
1034 CASE_EXCEPTION(IN_PAGE_ERROR
);
1035 CASE_EXCEPTION(ILLEGAL_INSTRUCTION
);
1036 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
);
1037 CASE_EXCEPTION(STACK_OVERFLOW
);
1038 CASE_EXCEPTION(INVALID_DISPOSITION
);
1039 CASE_EXCEPTION(GUARD_PAGE
);
1040 CASE_EXCEPTION(INVALID_HANDLE
);
1043 // unknown exception, ask NTDLL for the name
1044 if ( !::FormatMessage
1046 FORMAT_MESSAGE_IGNORE_INSERTS
|
1047 FORMAT_MESSAGE_FROM_HMODULE
,
1048 ::GetModuleHandle(_T("NTDLL.DLL")),
1051 wxStringBuffer(s
, 1024),
1056 s
= _T("UNKNOWN_EXCEPTION");
1060 #undef CASE_EXCEPTION
1065 #endif // !wxUSE_MINIDUMP
1067 #endif // wxUSE_DBGHELP
1070 #if wxUSE_DBGHELP && !wxUSE_MINIDUMP
1071 #define WXUNUSED_LOCAL(x) x
1073 #define WXUNUSED_LOCAL WXUNUSED
1076 bool wxCrashReportImpl::Generate(int WXUNUSED_LOCAL(flags
))
1078 if ( m_hFile
== INVALID_HANDLE_VALUE
)
1082 if ( !wxGlobalSEInformation
)
1086 PEXCEPTION_RECORD pExceptionRecord
= wxGlobalSEInformation
->ExceptionRecord
;
1087 PCONTEXT pCtx
= wxGlobalSEInformation
->ContextRecord
;
1089 if ( !pExceptionRecord
|| !pCtx
)
1092 HANDLE hModuleCrash
= OutputBasicContext(pExceptionRecord
, pCtx
);
1093 #endif // !wxUSE_MINIDUMP
1096 // for everything else we need dbghelp.dll
1097 wxDynamicLibrary
dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM
);
1098 if ( dllDbgHelp
.IsLoaded() )
1100 if ( BindDbgHelpFunctions(dllDbgHelp
) )
1103 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo
;
1105 minidumpExcInfo
.ThreadId
= ::GetCurrentThreadId();
1106 minidumpExcInfo
.ExceptionPointers
= wxGlobalSEInformation
;
1107 minidumpExcInfo
.ClientPointers
= FALSE
; // in our own address space
1109 // do generate the dump
1110 if ( !MiniDumpWriteDump
1112 ::GetCurrentProcess(),
1113 ::GetCurrentProcessId(),
1114 m_hFile
, // file to write to
1115 MiniDumpNormal
, // just the minimum
1117 NULL
, // no extra user-defined data
1118 NULL
// no callbacks
1121 Output(_T("MiniDumpWriteDump() failed."));
1127 #else // !wxUSE_MINIDUMP
1128 SymSetOptions(SYMOPT_DEFERRED_LOADS
| SYMOPT_UNDNAME
);
1130 // Initialize DbgHelp
1131 if ( SymInitialize(GetCurrentProcess(), NULL
, TRUE
/* invade */) )
1133 OutputStack(pCtx
, flags
);
1135 if ( hModuleCrash
&& (flags
& wxCRASH_REPORT_GLOBALS
) )
1137 OutputGlobals(hModuleCrash
);
1142 #endif // !wxUSE_MINIDUMP
1146 Output(_T("\r\nPlease update your dbghelp.dll version, ")
1147 _T("at least version 5.1 is needed!\r\n")
1148 _T("(if you already have a new version, please ")
1149 _T("put it in the same directory where the program is.)\r\n"));
1152 else // failed to load dbghelp.dll
1154 Output(_T("Please install dbghelp.dll available free of charge ")
1155 _T("from Microsoft to get more detailed crash information!"));
1158 Output(_T("\r\nLatest dbghelp.dll is available at ")
1159 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1161 #else // !wxUSE_DBGHELP
1162 Output(_T("Support for crash report generation was not included ")
1163 _T("in this wxWindows version."));
1164 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1169 // ----------------------------------------------------------------------------
1171 // ----------------------------------------------------------------------------
1174 void wxCrashReport::SetFileName(const wxChar
*filename
)
1176 wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1);
1177 gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0');
1181 const wxChar
*wxCrashReport::GetFileName()
1183 return gs_reportFilename
;
1187 bool wxCrashReport::Generate(int flags
)
1189 wxCrashReportImpl
impl(gs_reportFilename
);
1191 return impl
.Generate(flags
);
1194 // ----------------------------------------------------------------------------
1195 // wxApp::OnFatalException() support
1196 // ----------------------------------------------------------------------------
1198 bool wxHandleFatalExceptions(bool doit
)
1200 // assume this can only be called from the main thread
1201 gs_handleExceptions
= doit
;
1205 // try to find a place where we can put out report file later
1208 WXSIZEOF(gs_reportFilename
),
1212 wxLogLastError(_T("GetTempPath"));
1214 // when all else fails...
1215 wxStrcpy(gs_reportFilename
, _T("c:\\"));
1218 // use PID and date to make the report file name more unique
1219 wxString fname
= wxString::Format
1222 _T("%s_%s_%lu.dmp"),
1223 #else // !wxUSE_MINIDUMP
1224 _T("%s_%s_%lu.rpt"),
1225 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
1226 wxTheApp
? wxTheApp
->GetAppName().c_str()
1228 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1229 ::GetCurrentProcessId()
1232 wxStrncat(gs_reportFilename
, fname
,
1233 WXSIZEOF(gs_reportFilename
) - wxStrlen(gs_reportFilename
) - 1);
1239 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS
*pExcPtrs
)
1241 if ( gs_handleExceptions
&& wxTheApp
)
1243 // store the pointer to exception info
1244 wxGlobalSEInformation
= pExcPtrs
;
1246 // give the user a chance to do something special about this
1249 wxTheApp
->OnFatalException();
1251 __except ( EXCEPTION_EXECUTE_HANDLER
)
1253 // nothing to do here, just ignore the exception inside the
1254 // exception handler
1258 wxGlobalSEInformation
= NULL
;
1260 // this will execute our handler and terminate the process
1261 return EXCEPTION_EXECUTE_HANDLER
;
1264 return EXCEPTION_CONTINUE_SEARCH
;
1267 #else // !wxUSE_ON_FATAL_EXCEPTION
1269 bool wxHandleFatalExceptions(bool WXUNUSED(doit
))
1271 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1276 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION