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 in this file is heavily based on Matt Pietrek's column from
14 the March 2002 issue of MSDN Magazine.
17 // ============================================================================
19 // ============================================================================
21 // ----------------------------------------------------------------------------
23 // ----------------------------------------------------------------------------
25 // For compilers that support precompilation, includes "wx.h".
26 #include "wx/wxprec.h"
32 #if wxUSE_ON_FATAL_EXCEPTION
37 #include "wx/longlong.h"
38 #include "wx/datetime.h"
39 #include "wx/dynload.h"
41 #include "wx/msw/crashrpt.h"
43 #include "wx/msw/wrapwin.h"
45 #include "wx/msw/private.h"
47 // we need to determine whether we have the declarations for the function in
48 // debughlp.dll version 5.81 (at least) and we check for DBHLPAPI to test this
51 // - VC6 version of imagehlp.h doesn't define it
53 // - testing for compiler version doesn't work as you can install and use
54 // the new SDK headers with VC6
56 // in any case, the user may override by defining wxUSE_DBGHELP himself
59 #define wxUSE_DBGHELP 1
61 #define wxUSE_DBGHELP 0
67 // ----------------------------------------------------------------------------
68 // types of imagehlp.h functions
69 // ----------------------------------------------------------------------------
71 typedef DWORD (WINAPI
*SymSetOptions_t
)(DWORD
);
72 typedef BOOL (WINAPI
*SymInitialize_t
)(HANDLE
, LPSTR
, BOOL
);
73 typedef BOOL (WINAPI
*StackWalk_t
)(DWORD
, HANDLE
, HANDLE
, LPSTACKFRAME
,
74 LPVOID
, PREAD_PROCESS_MEMORY_ROUTINE
,
75 PFUNCTION_TABLE_ACCESS_ROUTINE
,
76 PGET_MODULE_BASE_ROUTINE
,
77 PTRANSLATE_ADDRESS_ROUTINE
);
78 typedef BOOL (WINAPI
*SymFromAddr_t
)(HANDLE
, DWORD64
, PDWORD64
, PSYMBOL_INFO
);
79 typedef LPVOID (WINAPI
*SymFunctionTableAccess_t
)(HANDLE
, DWORD
);
80 typedef DWORD (WINAPI
*SymGetModuleBase_t
)(HANDLE
, DWORD
);
81 typedef BOOL (WINAPI
*SymGetLineFromAddr_t
)(HANDLE
, DWORD
,
82 PDWORD
, PIMAGEHLP_LINE
);
83 typedef BOOL (WINAPI
*SymSetContext_t
)(HANDLE
, PIMAGEHLP_STACK_FRAME
,
85 typedef BOOL (WINAPI
*SymEnumSymbols_t
)(HANDLE
, ULONG64
, PCSTR
,
86 PSYM_ENUMERATESYMBOLS_CALLBACK
, PVOID
);
87 typedef BOOL (WINAPI
*SymGetTypeInfo_t
)(HANDLE
, DWORD64
, ULONG
,
88 IMAGEHLP_SYMBOL_TYPE_INFO
, PVOID
);
90 // ----------------------------------------------------------------------------
92 // ----------------------------------------------------------------------------
94 // Stolen from CVCONST.H in the DIA 2.0 SDK
107 BASICTYPE_ULONG
= 14,
108 BASICTYPE_CURRENCY
= 25,
110 BASICTYPE_VARIANT
= 27,
111 BASICTYPE_COMPLEX
= 28,
114 BASICTYPE_HRESULT
= 31
121 SYMBOL_TAG_FUNCTION
= 5,
123 SYMBOL_TAG_PUBLIC
= 10, // appears in .DBGs
126 SYMBOL_TAG_FUNCTION_TYPE
,
127 SYMBOL_TAG_POINTER_TYPE
,
128 SYMBOL_TAG_ARRAY_TYPE
,
129 SYMBOL_TAG_BASE_TYPE
,
134 #endif // wxUSE_DBGHELP
136 // ----------------------------------------------------------------------------
138 // ----------------------------------------------------------------------------
140 // the real crash report generator
141 class wxCrashReportImpl
144 wxCrashReportImpl(const wxChar
*filename
);
146 bool Generate(int flags
);
150 if ( m_hFile
!= INVALID_HANDLE_VALUE
)
152 ::CloseHandle(m_hFile
);
157 // formatted output to m_hFile
158 void Output(const wxChar
*format
, ...);
160 // output end of line
161 void OutputEndl() { Output(_T("\r\n")); }
164 // translate exception code to its symbolic name
165 static wxString
GetExceptionString(DWORD dwCode
);
167 // return the type from "type index"
168 static BasicType
GetBasicType(DWORD64 modBase
, DWORD typeIndex
);
170 // return the name for the type index
171 static wxString
GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
);
173 // return the string representation of the variable value
174 static wxString
FormatSimpleValue(BasicType bt
,
178 // return string representation of a struct field (which may itself be a
179 // struct, of course)
180 static wxString
FormatField(DWORD64 modBase
,
185 // show the name and value of the given symbol
186 static wxString
FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
);
188 // show value described by SYMBOL_INFO located at pVariable
189 static wxString
FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
);
191 // show value of possibly complex (user-defined) type
192 static wxString
FormatUDT(DWORD64 modBase
,
197 // outputs information about the given symbol
198 void OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
);
200 // load all the functions we need from dbghelp.dll, return true if all ok
201 bool ResolveSymFunctions(const wxDynamicLibrary
& dllDbgHelp
);
203 // map address to module (and also section:offset), retunr true if ok
204 static bool GetLogicalAddress(PVOID addr
,
210 // callback used with SymEnumSymbols() to process all variables
211 static BOOL CALLBACK
EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
216 // show the general information about exception which should be always
219 // returns the module of the handle where the crash occured
220 HANDLE
OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
, CONTEXT
*pCtx
);
222 // output the call stack and local variables values
223 void OutputStack(const CONTEXT
*pCtx
, int flags
);
225 // output the global variables values
226 void OutputGlobals(HANDLE hModuleCrash
);
229 // the current stack frame (may be NULL)
230 STACKFRAME
*m_sfCurrent
;
233 // dynamically loaded dbghelp.dll functions
234 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
236 DECLARE_SYM_FUNCTION(SymSetOptions
);
237 DECLARE_SYM_FUNCTION(SymInitialize
);
238 DECLARE_SYM_FUNCTION(StackWalk
);
239 DECLARE_SYM_FUNCTION(SymFromAddr
);
240 DECLARE_SYM_FUNCTION(SymFunctionTableAccess
);
241 DECLARE_SYM_FUNCTION(SymGetModuleBase
);
242 DECLARE_SYM_FUNCTION(SymGetLineFromAddr
);
243 DECLARE_SYM_FUNCTION(SymSetContext
);
244 DECLARE_SYM_FUNCTION(SymEnumSymbols
);
245 DECLARE_SYM_FUNCTION(SymGetTypeInfo
);
246 #endif // wxUSE_DBGHELP
248 // the handle of the report file
252 // ----------------------------------------------------------------------------
254 // ----------------------------------------------------------------------------
256 // global pointer to exception information, only valid inside OnFatalException
257 extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS
*wxGlobalSEInformation
= NULL
;
260 // flag telling us whether the application wants to handle exceptions at all
261 static bool gs_handleExceptions
= false;
263 // the file name where the report about exception is written
264 static wxChar gs_reportFilename
[MAX_PATH
];
266 // ============================================================================
268 // ============================================================================
272 #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
274 DEFINE_SYM_FUNCTION(SymSetOptions
);
275 DEFINE_SYM_FUNCTION(SymInitialize
);
276 DEFINE_SYM_FUNCTION(StackWalk
);
277 DEFINE_SYM_FUNCTION(SymFromAddr
);
278 DEFINE_SYM_FUNCTION(SymFunctionTableAccess
);
279 DEFINE_SYM_FUNCTION(SymGetModuleBase
);
280 DEFINE_SYM_FUNCTION(SymGetLineFromAddr
);
281 DEFINE_SYM_FUNCTION(SymSetContext
);
282 DEFINE_SYM_FUNCTION(SymEnumSymbols
);
283 DEFINE_SYM_FUNCTION(SymGetTypeInfo
);
285 #undef DEFINE_SYM_FUNCTION
287 #endif // wxUSE_DBGHELP
289 // ----------------------------------------------------------------------------
291 // ----------------------------------------------------------------------------
293 wxCrashReportImpl::wxCrashReportImpl(const wxChar
*filename
)
297 #endif // wxUSE_DBGHELP
299 m_hFile
= ::CreateFile
304 NULL
, // default security
306 FILE_FLAG_WRITE_THROUGH
,
307 NULL
// no template file
311 void wxCrashReportImpl::Output(const wxChar
*format
, ...)
314 va_start(argptr
, format
);
318 wxString s
= wxString::FormatV(format
, argptr
);
319 ::WriteFile(m_hFile
, s
, s
.length() * sizeof(wxChar
), &cbWritten
, 0);
327 wxCrashReportImpl::GetLogicalAddress(PVOID addr
,
333 MEMORY_BASIC_INFORMATION mbi
;
335 if ( !::VirtualQuery(addr
, &mbi
, sizeof(mbi
)) )
338 DWORD hMod
= (DWORD
)mbi
.AllocationBase
;
340 if ( !::GetModuleFileName((HMODULE
)hMod
, szModule
, len
) )
343 // Point to the DOS header in memory
344 PIMAGE_DOS_HEADER pDosHdr
= (PIMAGE_DOS_HEADER
)hMod
;
346 // From the DOS header, find the NT (PE) header
347 PIMAGE_NT_HEADERS pNtHdr
= (PIMAGE_NT_HEADERS
)(hMod
+ pDosHdr
->e_lfanew
);
349 PIMAGE_SECTION_HEADER pSection
= IMAGE_FIRST_SECTION( pNtHdr
);
351 DWORD rva
= (DWORD
)addr
- hMod
; // RVA is offset from module load address
353 // Iterate through the section table, looking for the one that encompasses
354 // the linear address.
355 const DWORD nSections
= pNtHdr
->FileHeader
.NumberOfSections
;
356 for ( DWORD i
= 0; i
< nSections
; i
++, pSection
++ )
358 DWORD sectionStart
= pSection
->VirtualAddress
;
359 DWORD sectionEnd
= sectionStart
360 + max(pSection
->SizeOfRawData
, pSection
->Misc
.VirtualSize
);
362 // Is the address in this section?
363 if ( (rva
>= sectionStart
) && (rva
<= sectionEnd
) )
365 // Yes, address is in the section. Calculate section and offset,
366 // and store in the "section" & "offset" params, which were
367 // passed by reference.
369 offset
= rva
- sectionStart
;
375 // failed to map to logical address...
379 /* static */ BasicType
380 wxCrashReportImpl::GetBasicType(DWORD64 modBase
, DWORD typeIndex
)
382 const HANDLE hProcess
= GetCurrentProcess();
384 // try the index we have
386 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_BASETYPE
, &bt
) )
391 // if failed, try to get the "real" typeid first
393 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_TYPEID
, &typeId
)
395 (typeId
!= typeIndex
&&
396 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_BASETYPE
, &bt
)) )
401 return BASICTYPE_NOTYPE
;
404 /* static */ wxString
405 wxCrashReportImpl::FormatSimpleValue(BasicType bt
,
412 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
415 s
.Printf(_T("%#04x"), *(PBYTE
)pAddress
);
417 else if ( length
== 2 )
419 s
.Printf(_T("%#06x"), *(PWORD
)pAddress
);
421 else if ( length
== 4 )
423 bool handled
= false;
425 if ( bt
== BASICTYPE_FLOAT
)
427 s
.Printf(_T("%f"), *(PFLOAT
)pAddress
);
431 else if ( bt
== BASICTYPE_CHAR
)
433 static const size_t NUM_CHARS
= 32;
435 const wxChar
* const pc
= *(PSTR
*)pAddress
;
436 if ( !::IsBadStringPtr(pc
, NUM_CHARS
) )
438 s
<< _T('"') << wxString(pc
, NUM_CHARS
) << _T('"');
446 // treat just as an opaque DWORD
447 s
.Printf(_T("%#x"), *(PDWORD
)pAddress
);
450 else if ( length
== 8 )
452 if ( bt
== BASICTYPE_FLOAT
)
454 s
.Printf(_T("%lf"), *(double *)pAddress
);
456 else // opaque 64 bit value
458 s
.Printf(_T("%#" wxLongLongFmtSpec
_T("x")), *(PDWORD
*)pAddress
);
466 wxString
wxCrashReportImpl::GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
)
480 s
= wxConvCurrent
->cWC2WX(pwszTypeName
);
482 ::LocalFree(pwszTypeName
);
488 // this is called for the struct members/base classes
490 wxCrashReportImpl::FormatField(DWORD64 modBase
,
497 const HANDLE hProcess
= GetCurrentProcess();
500 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_SYMTAG
, &dwTag
);
505 case SYMBOL_TAG_BASECLASS
:
506 s
= FormatUDT(modBase
, dwTypeIndex
, pVariable
, level
);
509 case SYMBOL_TAG_FUNCTION
:
514 // try to treat all the rest as data even though it's not clear if
515 // it's really a good idea...
517 // Get the offset of the child member, relative to its parent
518 DWORD dwMemberOffset
= 0;
519 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
520 TI_GET_OFFSET
, &dwMemberOffset
);
522 // Get the real "TypeId" of the child. We need this for the
523 // SymGetTypeInfo(TI_GET_LENGTH) call below.
525 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
526 TI_GET_TYPEID
, &typeId
) )
528 typeId
= dwTypeIndex
;
531 // Get the size of the child member
533 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_LENGTH
, &size
);
535 // Calculate the address of the member
536 DWORD_PTR dwFinalOffset
= (DWORD_PTR
)pVariable
+ dwMemberOffset
;
538 BasicType basicType
= GetBasicType(modBase
, dwTypeIndex
);
540 s
= FormatSimpleValue(basicType
, size
, (PVOID
)dwFinalOffset
);
547 // don't show if no value -- what for?
551 return wxString(_T('\t'), level
+ 1) +
552 GetSymbolName(modBase
, dwTypeIndex
) +
553 _T(" = ") + s
+ _T("\r\n");
556 // If it's a user defined type (UDT), recurse through its members until we're
557 // at fundamental types.
559 wxCrashReportImpl::FormatUDT(DWORD64 modBase
,
566 s
= GetSymbolName(modBase
, dwTypeIndex
) + _T(" {\r\n");
568 const HANDLE hProcess
= GetCurrentProcess();
570 // Determine how many children this type has.
571 DWORD dwChildrenCount
= 0;
572 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
,
575 // Prepare to get an array of "TypeIds", representing each of the children.
576 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
577 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
578 (dwChildrenCount
- 1)*sizeof(ULONG
));
582 children
->Count
= dwChildrenCount
;
585 // Get the array of TypeIds, one for each child type
586 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
,
592 // Iterate through all children
593 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
595 s
+= FormatField(modBase
, children
->ChildId
[i
], pVariable
, level
+ 1);
600 s
<< wxString(_T('\t'), level
+ 1) << _T('}');
605 // return the string containing the symbol of the given symbol
606 /* static */ wxString
607 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
)
610 SymGetTypeInfo(GetCurrentProcess(), pSym
->ModBase
, pSym
->TypeIndex
,
611 TI_GET_SYMTAG
, &dwTag
);
616 case SYMBOL_TAG_FUNCTION
:
620 case SYMBOL_TAG_BASECLASS
:
621 // show UDT recursively
622 s
= FormatUDT(pSym
->ModBase
, pSym
->TypeIndex
, pVariable
);
626 // variable of simple type (but could be array which we don't
627 // handle correctly yet...), format it using its type and size
628 BasicType bt
= GetBasicType(pSym
->ModBase
, pSym
->TypeIndex
);
630 s
= FormatSimpleValue(bt
, pSym
->Size
, pVariable
);
638 // display contents and type of the given variable
639 /* static */ wxString
640 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
)
644 if ( pSym
->Tag
== SYMBOL_TAG_FUNCTION
)
646 // If it's a function, don't do anything.
650 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGISTER
)
652 // Don't try to report register variable
658 // Indicate if the variable is a local or parameter
659 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
660 s
+= _T("\t[param] ");
661 else if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_LOCAL
)
662 s
+= _T("\t[local] ");
664 // Will point to the variable's data in memory
665 DWORD_PTR pVariable
= 0;
667 if ( (pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
) && sf
)
669 pVariable
= sf
->AddrFrame
.Offset
;
670 pVariable
+= (DWORD_PTR
)pSym
->Address
;
672 else // It must be a global variable
674 pVariable
= (DWORD_PTR
)pSym
->Address
;
677 s
<< pSym
->Name
<< _T(" = ") << FormatAnyValue(pSym
, (PVOID
)pVariable
);
683 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
)
685 wxString s
= FormatSymbol(pSymInfo
, sf
);
688 Output(_T("%s\r\n"), s
.c_str());
690 //else: not an interesting symbol
693 // callback for SymEnumSymbols()
696 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
697 ULONG
WXUNUSED(SymbolSize
),
700 wxCrashReportImpl
*self
= (wxCrashReportImpl
*)UserContext
;
704 self
->OutputSymbol(pSymInfo
, self
->m_sfCurrent
);
706 __except ( EXCEPTION_EXECUTE_HANDLER
)
708 self
->Output(_T("Can't process symbol %hs\r\n"), pSymInfo
->Name
);
711 // continue with enumeration
716 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
,
719 // First print information about the type of fault
720 const DWORD dwCode
= pExceptionRecord
->ExceptionCode
;
721 Output(_T("Exception code: %s (%#10x)\r\n"),
722 GetExceptionString(dwCode
).c_str(), dwCode
);
724 // Now print information about where the fault occured
725 TCHAR szFaultingModule
[MAX_PATH
];
728 void * const pExceptionAddress
= pExceptionRecord
->ExceptionAddress
;
729 if ( !GetLogicalAddress(pExceptionAddress
,
731 WXSIZEOF(szFaultingModule
),
737 wxStrcpy(szFaultingModule
, _T("<< unknown >>"));
740 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
741 pExceptionAddress
, section
, offset
, szFaultingModule
);
744 // Show the registers
745 Output( _T("\r\nRegisters:\r\n") );
747 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
748 pCtx
->Eax
, pCtx
->Ebx
, pCtx
->Ecx
, pCtx
->Edx
, pCtx
->Esi
, pCtx
->Edi
);
750 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
751 pCtx
->SegCs
, pCtx
->Eip
, pCtx
->SegSs
, pCtx
->Esp
, pCtx
->Ebp
);
752 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
753 pCtx
->SegDs
, pCtx
->SegEs
, pCtx
->SegFs
, pCtx
->SegGs
);
754 Output(_T("Flags: %08x\r\n"), pCtx
->EFlags
);
757 return ::GetModuleHandle(szFaultingModule
);
760 void wxCrashReportImpl::OutputStack(const CONTEXT
*pCtx
, int flags
)
768 // can't show locals under other architectures
773 for ( int step
= 0; step
< Output_Max
; step
++ )
775 // don't do things we're not asked for
776 if ( (step
== Output_Stack
) && !(flags
& wxCRASH_REPORT_STACK
) ||
777 (step
== Output_Locals
) && !(flags
& wxCRASH_REPORT_LOCALS
) )
782 // the context is going to be modified below so make a copy
785 Output(_T("\r\n%s\r\n")
786 _T(" # Address Frame Function SourceFile\r\n"),
787 step
== Output_Stack
? _T("Call stack") : _T("Local variables"));
789 DWORD dwMachineType
= 0;
795 // Initialize the STACKFRAME structure for the first call. This is
796 // only necessary for Intel CPUs, and isn't mentioned in the
798 sf
.AddrPC
.Offset
= ctx
.Eip
;
799 sf
.AddrPC
.Mode
= AddrModeFlat
;
800 sf
.AddrStack
.Offset
= ctx
.Esp
;
801 sf
.AddrStack
.Mode
= AddrModeFlat
;
802 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
803 sf
.AddrFrame
.Mode
= AddrModeFlat
;
805 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
808 const HANDLE hProcess
= GetCurrentProcess();
809 const HANDLE hThread
= GetCurrentThread();
811 // first show just the call stack
815 // Get the next stack frame
816 if ( !StackWalk(dwMachineType
,
822 SymFunctionTableAccess
,
829 // Basic sanity check to make sure the frame is OK.
830 if ( !sf
.AddrFrame
.Offset
)
833 Output(_T("%2d %08x %08x "),
834 frame
++, sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
836 // Get the name of the function for this stack frame entry
837 BYTE symbolBuffer
[ sizeof(SYMBOL_INFO
) + 1024 ];
838 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
839 pSymbol
->SizeOfStruct
= sizeof(symbolBuffer
);
840 pSymbol
->MaxNameLen
= 1024;
842 // Displacement of the input address, relative to the start of the
844 DWORD64 symDisplacement
= 0;
846 if ( SymFromAddr(hProcess
, sf
.AddrPC
.Offset
,
847 &symDisplacement
,pSymbol
) )
849 Output(_T("%hs()+%#") wxLongLongFmtSpec
_T("x"),
850 pSymbol
->Name
, symDisplacement
);
852 else // No symbol found. Print out the logical address instead.
854 TCHAR szModule
[MAX_PATH
];
858 if ( !GetLogicalAddress((PVOID
)sf
.AddrPC
.Offset
,
859 szModule
, sizeof(szModule
),
862 szModule
[0] = _T('\0');
867 Output(_T("%04x:%08x %s"), section
, offset
, szModule
);
870 // Get the source line for this stack frame entry
871 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
872 DWORD dwLineDisplacement
;
873 if ( SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
,
874 &dwLineDisplacement
, &lineInfo
))
876 Output(_T(" %s line %u"),
877 lineInfo
.FileName
, lineInfo
.LineNumber
);
884 // on the second iteration also show the local variables and
886 if ( step
== Output_Locals
)
888 // Use SymSetContext to get just the locals/params for this
890 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
891 imagehlpStackFrame
.InstructionOffset
= sf
.AddrPC
.Offset
;
892 SymSetContext(hProcess
, &imagehlpStackFrame
, 0);
894 // Enumerate the locals/parameters
896 SymEnumSymbols(hProcess
, 0, 0, EnumerateSymbolsCallback
, this);
905 void wxCrashReportImpl::OutputGlobals(HANDLE hModule
)
908 Output(_T("\r\nGlobal variables:\r\n"));
911 SymEnumSymbols(::GetCurrentProcess(), (DWORD64
)hModule
, NULL
,
912 EnumerateSymbolsCallback
, this);
916 bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary
& dllDbgHelp
)
918 #define LOAD_SYM_FUNCTION(name) \
919 name = (name ## _t) dllDbgHelp.GetSymbol(#name); \
922 Output(_T("\r\nFunction ") __XFILE__(#name) \
923 _T("() not found.\r\n")); \
927 LOAD_SYM_FUNCTION(SymSetOptions
);
928 LOAD_SYM_FUNCTION(SymInitialize
);
929 LOAD_SYM_FUNCTION(StackWalk
);
930 LOAD_SYM_FUNCTION(SymFromAddr
);
931 LOAD_SYM_FUNCTION(SymFunctionTableAccess
);
932 LOAD_SYM_FUNCTION(SymGetModuleBase
);
933 LOAD_SYM_FUNCTION(SymGetLineFromAddr
);
934 LOAD_SYM_FUNCTION(SymSetContext
);
935 LOAD_SYM_FUNCTION(SymEnumSymbols
);
936 LOAD_SYM_FUNCTION(SymGetTypeInfo
);
938 #undef LOAD_SYM_FUNCTION
944 wxString
wxCrashReportImpl::GetExceptionString(DWORD dwCode
)
948 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
952 CASE_EXCEPTION(ACCESS_VIOLATION
);
953 CASE_EXCEPTION(DATATYPE_MISALIGNMENT
);
954 CASE_EXCEPTION(BREAKPOINT
);
955 CASE_EXCEPTION(SINGLE_STEP
);
956 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
);
957 CASE_EXCEPTION(FLT_DENORMAL_OPERAND
);
958 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
);
959 CASE_EXCEPTION(FLT_INEXACT_RESULT
);
960 CASE_EXCEPTION(FLT_INVALID_OPERATION
);
961 CASE_EXCEPTION(FLT_OVERFLOW
);
962 CASE_EXCEPTION(FLT_STACK_CHECK
);
963 CASE_EXCEPTION(FLT_UNDERFLOW
);
964 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
);
965 CASE_EXCEPTION(INT_OVERFLOW
);
966 CASE_EXCEPTION(PRIV_INSTRUCTION
);
967 CASE_EXCEPTION(IN_PAGE_ERROR
);
968 CASE_EXCEPTION(ILLEGAL_INSTRUCTION
);
969 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
);
970 CASE_EXCEPTION(STACK_OVERFLOW
);
971 CASE_EXCEPTION(INVALID_DISPOSITION
);
972 CASE_EXCEPTION(GUARD_PAGE
);
973 CASE_EXCEPTION(INVALID_HANDLE
);
976 // unknown exception, ask NTDLL for the name
977 if ( !::FormatMessage
979 FORMAT_MESSAGE_IGNORE_INSERTS
|
980 FORMAT_MESSAGE_FROM_HMODULE
,
981 ::GetModuleHandle(_T("NTDLL.DLL")),
984 wxStringBuffer(s
, 1024),
989 s
= _T("UNKNOWN_EXCEPTION");
993 #undef CASE_EXCEPTION
998 #endif // wxUSE_DBGHELP
1000 bool wxCrashReportImpl::Generate(int flags
)
1002 if ( m_hFile
== INVALID_HANDLE_VALUE
)
1006 if ( !wxGlobalSEInformation
)
1009 PEXCEPTION_RECORD pExceptionRecord
= wxGlobalSEInformation
->ExceptionRecord
;
1010 PCONTEXT pCtx
= wxGlobalSEInformation
->ContextRecord
;
1012 if ( !pExceptionRecord
|| !pCtx
)
1015 HANDLE hModuleCrash
= OutputBasicContext(pExceptionRecord
, pCtx
);
1017 // for everything else we need dbghelp.dll
1018 wxDynamicLibrary
dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM
);
1019 if ( dllDbgHelp
.IsLoaded() )
1021 if ( ResolveSymFunctions(dllDbgHelp
) )
1023 SymSetOptions(SYMOPT_DEFERRED_LOADS
);
1025 // Initialize DbgHelp
1026 if ( SymInitialize(GetCurrentProcess(), NULL
, TRUE
/* invade */) )
1028 OutputStack(pCtx
, flags
);
1030 if ( hModuleCrash
&& (flags
& wxCRASH_REPORT_GLOBALS
) )
1032 OutputGlobals(hModuleCrash
);
1040 Output(_T("Please update your dbghelp.dll version, "
1041 "at least version 5.1 is needed!\r\n"));
1046 Output(_T("Please install dbghelp.dll available free of charge ")
1047 _T("from Microsoft to get more detailed crash information!"));
1050 Output(_T("\r\nLatest dbghelp.dll is available at "
1051 "http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1053 #else // !wxUSE_DBGHELP
1054 Output(_T("Support for crash report generation was not included ")
1055 _T("in this wxWindows version."));
1056 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1061 // ----------------------------------------------------------------------------
1063 // ----------------------------------------------------------------------------
1066 void wxCrashReport::SetFileName(const wxChar
*filename
)
1068 wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1);
1069 gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0');
1073 const wxChar
*wxCrashReport::GetFileName()
1075 return gs_reportFilename
;
1079 bool wxCrashReport::Generate(int flags
)
1081 wxCrashReportImpl
impl(gs_reportFilename
);
1083 return impl
.Generate(flags
);
1086 // ----------------------------------------------------------------------------
1087 // wxApp::OnFatalException() support
1088 // ----------------------------------------------------------------------------
1090 bool wxHandleFatalExceptions(bool doit
)
1092 // assume this can only be called from the main thread
1093 gs_handleExceptions
= doit
;
1097 // try to find a place where we can put out report file later
1100 WXSIZEOF(gs_reportFilename
),
1104 wxLogLastError(_T("GetTempPath"));
1106 // when all else fails...
1107 wxStrcpy(gs_reportFilename
, _T("c:\\"));
1110 // use PID and date to make the report file name more unique
1111 wxString fname
= wxString::Format
1113 _T("%s_%s_%lu.rpt"),
1114 wxTheApp
? wxTheApp
->GetAppName().c_str()
1116 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1117 ::GetCurrentProcessId()
1120 wxStrncat(gs_reportFilename
, fname
,
1121 WXSIZEOF(gs_reportFilename
) - strlen(gs_reportFilename
) - 1);
1127 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS
*pExcPtrs
)
1129 if ( gs_handleExceptions
&& wxTheApp
)
1131 // store the pointer to exception info
1132 wxGlobalSEInformation
= pExcPtrs
;
1134 // give the user a chance to do something special about this
1135 wxTheApp
->OnFatalException();
1137 wxGlobalSEInformation
= NULL
;
1139 // this will execute our handler and terminate the process
1140 return EXCEPTION_EXECUTE_HANDLER
;
1143 return EXCEPTION_CONTINUE_SEARCH
;
1146 #else // !wxUSE_ON_FATAL_EXCEPTION
1148 bool wxHandleFatalExceptions(bool WXUNUSED(doit
))
1150 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1155 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION