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 char * const pc
= *(PSTR
*)pAddress
;
436 if ( !::IsBadStringPtrA(pc
, NUM_CHARS
) )
438 s
<< _T('"') << wxString(pc
, wxConvLibc
, 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 // avoid infinite recursion
503 const HANDLE hProcess
= GetCurrentProcess();
506 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_SYMTAG
, &dwTag
);
511 case SYMBOL_TAG_BASECLASS
:
512 s
= FormatUDT(modBase
, dwTypeIndex
, pVariable
, level
);
515 case SYMBOL_TAG_FUNCTION
:
520 // try to treat all the rest as data even though it's not clear if
521 // it's really a good idea...
523 // Get the offset of the child member, relative to its parent
524 DWORD dwMemberOffset
= 0;
525 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
526 TI_GET_OFFSET
, &dwMemberOffset
);
528 // Get the real "TypeId" of the child. We need this for the
529 // SymGetTypeInfo(TI_GET_LENGTH) call below.
531 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
532 TI_GET_TYPEID
, &typeId
) )
534 typeId
= dwTypeIndex
;
537 // Get the size of the child member
539 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_LENGTH
, &size
);
541 // Calculate the address of the member
542 DWORD_PTR dwFinalOffset
= (DWORD_PTR
)pVariable
+ dwMemberOffset
;
544 BasicType basicType
= GetBasicType(modBase
, dwTypeIndex
);
546 s
= FormatSimpleValue(basicType
, size
, (PVOID
)dwFinalOffset
);
553 // don't show if no value -- what for?
557 return wxString(_T('\t'), level
+ 1) +
558 GetSymbolName(modBase
, dwTypeIndex
) +
559 _T(" = ") + s
+ _T("\r\n");
562 // If it's a user defined type (UDT), recurse through its members until we're
563 // at fundamental types.
565 wxCrashReportImpl::FormatUDT(DWORD64 modBase
,
572 s
= GetSymbolName(modBase
, dwTypeIndex
) + _T(" {\r\n");
574 const HANDLE hProcess
= GetCurrentProcess();
576 // Determine how many children this type has.
577 DWORD dwChildrenCount
= 0;
578 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
,
581 // Prepare to get an array of "TypeIds", representing each of the children.
582 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
583 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
584 (dwChildrenCount
- 1)*sizeof(ULONG
));
588 children
->Count
= dwChildrenCount
;
591 // Get the array of TypeIds, one for each child type
592 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
,
598 // Iterate through all children
599 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
601 s
+= FormatField(modBase
, children
->ChildId
[i
], pVariable
, level
+ 1);
606 s
<< wxString(_T('\t'), level
+ 1) << _T('}');
611 // return the string containing the symbol of the given symbol
612 /* static */ wxString
613 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
)
616 SymGetTypeInfo(GetCurrentProcess(), pSym
->ModBase
, pSym
->TypeIndex
,
617 TI_GET_SYMTAG
, &dwTag
);
622 case SYMBOL_TAG_FUNCTION
:
626 case SYMBOL_TAG_BASECLASS
:
627 // show UDT recursively
628 s
= FormatUDT(pSym
->ModBase
, pSym
->TypeIndex
, pVariable
);
632 // variable of simple type (but could be array which we don't
633 // handle correctly yet...), format it using its type and size
634 BasicType bt
= GetBasicType(pSym
->ModBase
, pSym
->TypeIndex
);
636 s
= FormatSimpleValue(bt
, pSym
->Size
, pVariable
);
644 // display contents and type of the given variable
645 /* static */ wxString
646 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
)
650 if ( pSym
->Tag
== SYMBOL_TAG_FUNCTION
)
652 // If it's a function, don't do anything.
656 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGISTER
)
658 // Don't try to report register variable
664 // Indicate if the variable is a local or parameter
665 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
666 s
+= _T("\t[param] ");
667 else if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_LOCAL
)
668 s
+= _T("\t[local] ");
670 // Will point to the variable's data in memory
671 DWORD_PTR pVariable
= 0;
673 if ( (pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
) && sf
)
675 pVariable
= sf
->AddrFrame
.Offset
;
676 pVariable
+= (DWORD_PTR
)pSym
->Address
;
678 else // It must be a global variable
680 pVariable
= (DWORD_PTR
)pSym
->Address
;
683 s
<< wxString(pSym
->Name
, wxConvLibc
)
685 << FormatAnyValue(pSym
, (PVOID
)pVariable
);
691 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
)
693 wxString s
= FormatSymbol(pSymInfo
, sf
);
696 Output(_T("%s\r\n"), s
.c_str());
698 //else: not an interesting symbol
701 // callback for SymEnumSymbols()
704 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
705 ULONG
WXUNUSED(SymbolSize
),
708 wxCrashReportImpl
*self
= (wxCrashReportImpl
*)UserContext
;
712 self
->OutputSymbol(pSymInfo
, self
->m_sfCurrent
);
714 __except ( EXCEPTION_EXECUTE_HANDLER
)
716 self
->Output(_T("Can't process symbol %hs\r\n"), pSymInfo
->Name
);
719 // continue with enumeration
724 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
,
727 // First print information about the type of fault
728 const DWORD dwCode
= pExceptionRecord
->ExceptionCode
;
729 Output(_T("Exception code: %s (%#10x)\r\n"),
730 GetExceptionString(dwCode
).c_str(), dwCode
);
732 // Now print information about where the fault occured
733 TCHAR szFaultingModule
[MAX_PATH
];
736 void * const pExceptionAddress
= pExceptionRecord
->ExceptionAddress
;
737 if ( !GetLogicalAddress(pExceptionAddress
,
739 WXSIZEOF(szFaultingModule
),
745 wxStrcpy(szFaultingModule
, _T("<< unknown >>"));
748 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
749 pExceptionAddress
, section
, offset
, szFaultingModule
);
752 // Show the registers
753 Output( _T("\r\nRegisters:\r\n") );
755 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
756 pCtx
->Eax
, pCtx
->Ebx
, pCtx
->Ecx
, pCtx
->Edx
, pCtx
->Esi
, pCtx
->Edi
);
758 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
759 pCtx
->SegCs
, pCtx
->Eip
, pCtx
->SegSs
, pCtx
->Esp
, pCtx
->Ebp
);
760 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
761 pCtx
->SegDs
, pCtx
->SegEs
, pCtx
->SegFs
, pCtx
->SegGs
);
762 Output(_T("Flags: %08x\r\n"), pCtx
->EFlags
);
765 return ::GetModuleHandle(szFaultingModule
);
768 void wxCrashReportImpl::OutputStack(const CONTEXT
*pCtx
, int flags
)
776 // can't show locals under other architectures
781 for ( int step
= 0; step
< Output_Max
; step
++ )
783 // don't do things we're not asked for
784 if ( (step
== Output_Stack
) && !(flags
& wxCRASH_REPORT_STACK
) ||
785 (step
== Output_Locals
) && !(flags
& wxCRASH_REPORT_LOCALS
) )
790 // the context is going to be modified below so make a copy
793 Output(_T("\r\n%s\r\n")
794 _T(" # Address Frame Function SourceFile\r\n"),
795 step
== Output_Stack
? _T("Call stack") : _T("Local variables"));
797 DWORD dwMachineType
= 0;
803 // Initialize the STACKFRAME structure for the first call. This is
804 // only necessary for Intel CPUs, and isn't mentioned in the
806 sf
.AddrPC
.Offset
= ctx
.Eip
;
807 sf
.AddrPC
.Mode
= AddrModeFlat
;
808 sf
.AddrStack
.Offset
= ctx
.Esp
;
809 sf
.AddrStack
.Mode
= AddrModeFlat
;
810 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
811 sf
.AddrFrame
.Mode
= AddrModeFlat
;
813 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
816 const HANDLE hProcess
= GetCurrentProcess();
817 const HANDLE hThread
= GetCurrentThread();
819 // first show just the call stack
823 // Get the next stack frame
824 if ( !StackWalk(dwMachineType
,
830 SymFunctionTableAccess
,
837 // Basic sanity check to make sure the frame is OK.
838 if ( !sf
.AddrFrame
.Offset
)
841 Output(_T("%2d %08x %08x "),
842 frame
++, sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
844 // Get the name of the function for this stack frame entry
845 BYTE symbolBuffer
[ sizeof(SYMBOL_INFO
) + 1024 ];
846 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
847 pSymbol
->SizeOfStruct
= sizeof(symbolBuffer
);
848 pSymbol
->MaxNameLen
= 1024;
850 // Displacement of the input address, relative to the start of the
852 DWORD64 symDisplacement
= 0;
854 if ( SymFromAddr(hProcess
, sf
.AddrPC
.Offset
,
855 &symDisplacement
,pSymbol
) )
857 Output(_T("%hs()+%#") wxLongLongFmtSpec
_T("x"),
858 pSymbol
->Name
, symDisplacement
);
860 else // No symbol found. Print out the logical address instead.
862 TCHAR szModule
[MAX_PATH
];
866 if ( !GetLogicalAddress((PVOID
)sf
.AddrPC
.Offset
,
867 szModule
, sizeof(szModule
),
870 szModule
[0] = _T('\0');
875 Output(_T("%04x:%08x %s"), section
, offset
, szModule
);
878 // Get the source line for this stack frame entry
879 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
880 DWORD dwLineDisplacement
;
881 if ( SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
,
882 &dwLineDisplacement
, &lineInfo
))
884 Output(_T(" %s line %u"),
885 lineInfo
.FileName
, lineInfo
.LineNumber
);
892 // on the second iteration also show the local variables and
894 if ( step
== Output_Locals
)
896 // Use SymSetContext to get just the locals/params for this
898 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
899 imagehlpStackFrame
.InstructionOffset
= sf
.AddrPC
.Offset
;
900 SymSetContext(hProcess
, &imagehlpStackFrame
, 0);
902 // Enumerate the locals/parameters
904 SymEnumSymbols(hProcess
, 0, 0, EnumerateSymbolsCallback
, this);
913 void wxCrashReportImpl::OutputGlobals(HANDLE hModule
)
916 Output(_T("\r\nGlobal variables:\r\n"));
919 SymEnumSymbols(::GetCurrentProcess(), (DWORD64
)hModule
, NULL
,
920 EnumerateSymbolsCallback
, this);
924 bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary
& dllDbgHelp
)
926 #define LOAD_SYM_FUNCTION(name) \
927 name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \
930 Output(_T("\r\nFunction ") _T(#name) \
931 _T("() not found.\r\n")); \
935 LOAD_SYM_FUNCTION(SymSetOptions
);
936 LOAD_SYM_FUNCTION(SymInitialize
);
937 LOAD_SYM_FUNCTION(StackWalk
);
938 LOAD_SYM_FUNCTION(SymFromAddr
);
939 LOAD_SYM_FUNCTION(SymFunctionTableAccess
);
940 LOAD_SYM_FUNCTION(SymGetModuleBase
);
941 LOAD_SYM_FUNCTION(SymGetLineFromAddr
);
942 LOAD_SYM_FUNCTION(SymSetContext
);
943 LOAD_SYM_FUNCTION(SymEnumSymbols
);
944 LOAD_SYM_FUNCTION(SymGetTypeInfo
);
946 #undef LOAD_SYM_FUNCTION
952 wxString
wxCrashReportImpl::GetExceptionString(DWORD dwCode
)
956 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
960 CASE_EXCEPTION(ACCESS_VIOLATION
);
961 CASE_EXCEPTION(DATATYPE_MISALIGNMENT
);
962 CASE_EXCEPTION(BREAKPOINT
);
963 CASE_EXCEPTION(SINGLE_STEP
);
964 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
);
965 CASE_EXCEPTION(FLT_DENORMAL_OPERAND
);
966 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
);
967 CASE_EXCEPTION(FLT_INEXACT_RESULT
);
968 CASE_EXCEPTION(FLT_INVALID_OPERATION
);
969 CASE_EXCEPTION(FLT_OVERFLOW
);
970 CASE_EXCEPTION(FLT_STACK_CHECK
);
971 CASE_EXCEPTION(FLT_UNDERFLOW
);
972 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
);
973 CASE_EXCEPTION(INT_OVERFLOW
);
974 CASE_EXCEPTION(PRIV_INSTRUCTION
);
975 CASE_EXCEPTION(IN_PAGE_ERROR
);
976 CASE_EXCEPTION(ILLEGAL_INSTRUCTION
);
977 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
);
978 CASE_EXCEPTION(STACK_OVERFLOW
);
979 CASE_EXCEPTION(INVALID_DISPOSITION
);
980 CASE_EXCEPTION(GUARD_PAGE
);
981 CASE_EXCEPTION(INVALID_HANDLE
);
984 // unknown exception, ask NTDLL for the name
985 if ( !::FormatMessage
987 FORMAT_MESSAGE_IGNORE_INSERTS
|
988 FORMAT_MESSAGE_FROM_HMODULE
,
989 ::GetModuleHandle(_T("NTDLL.DLL")),
992 wxStringBuffer(s
, 1024),
997 s
= _T("UNKNOWN_EXCEPTION");
1001 #undef CASE_EXCEPTION
1006 #endif // wxUSE_DBGHELP
1010 #define _WXUNUSED(x) x
1012 #define _WXUNUSED WXUNUSED
1015 bool wxCrashReportImpl::Generate(int _WXUNUSED(flags
))
1017 if ( m_hFile
== INVALID_HANDLE_VALUE
)
1021 if ( !wxGlobalSEInformation
)
1024 PEXCEPTION_RECORD pExceptionRecord
= wxGlobalSEInformation
->ExceptionRecord
;
1025 PCONTEXT pCtx
= wxGlobalSEInformation
->ContextRecord
;
1027 if ( !pExceptionRecord
|| !pCtx
)
1030 HANDLE hModuleCrash
= OutputBasicContext(pExceptionRecord
, pCtx
);
1032 // for everything else we need dbghelp.dll
1033 wxDynamicLibrary
dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM
);
1034 if ( dllDbgHelp
.IsLoaded() )
1036 if ( ResolveSymFunctions(dllDbgHelp
) )
1038 SymSetOptions(SYMOPT_DEFERRED_LOADS
| SYMOPT_UNDNAME
);
1040 // Initialize DbgHelp
1041 if ( SymInitialize(GetCurrentProcess(), NULL
, TRUE
/* invade */) )
1043 OutputStack(pCtx
, flags
);
1045 if ( hModuleCrash
&& (flags
& wxCRASH_REPORT_GLOBALS
) )
1047 OutputGlobals(hModuleCrash
);
1055 Output(_T("Please update your dbghelp.dll version, ")
1056 _T("at least version 5.1 is needed!\r\n"));
1061 Output(_T("Please install dbghelp.dll available free of charge ")
1062 _T("from Microsoft to get more detailed crash information!"));
1065 Output(_T("\r\nLatest dbghelp.dll is available at ")
1066 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1068 #else // !wxUSE_DBGHELP
1069 Output(_T("Support for crash report generation was not included ")
1070 _T("in this wxWindows version."));
1071 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1076 // ----------------------------------------------------------------------------
1078 // ----------------------------------------------------------------------------
1081 void wxCrashReport::SetFileName(const wxChar
*filename
)
1083 wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1);
1084 gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0');
1088 const wxChar
*wxCrashReport::GetFileName()
1090 return gs_reportFilename
;
1094 bool wxCrashReport::Generate(int flags
)
1096 wxCrashReportImpl
impl(gs_reportFilename
);
1098 return impl
.Generate(flags
);
1101 // ----------------------------------------------------------------------------
1102 // wxApp::OnFatalException() support
1103 // ----------------------------------------------------------------------------
1105 bool wxHandleFatalExceptions(bool doit
)
1107 // assume this can only be called from the main thread
1108 gs_handleExceptions
= doit
;
1112 // try to find a place where we can put out report file later
1115 WXSIZEOF(gs_reportFilename
),
1119 wxLogLastError(_T("GetTempPath"));
1121 // when all else fails...
1122 wxStrcpy(gs_reportFilename
, _T("c:\\"));
1125 // use PID and date to make the report file name more unique
1126 wxString fname
= wxString::Format
1128 _T("%s_%s_%lu.rpt"),
1129 wxTheApp
? wxTheApp
->GetAppName().c_str()
1131 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1132 ::GetCurrentProcessId()
1135 wxStrncat(gs_reportFilename
, fname
,
1136 WXSIZEOF(gs_reportFilename
) - wxStrlen(gs_reportFilename
) - 1);
1142 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS
*pExcPtrs
)
1144 if ( gs_handleExceptions
&& wxTheApp
)
1146 // store the pointer to exception info
1147 wxGlobalSEInformation
= pExcPtrs
;
1149 // give the user a chance to do something special about this
1152 wxTheApp
->OnFatalException();
1154 __except ( EXCEPTION_EXECUTE_HANDLER
)
1156 // nothing to do here, just ignore the exception inside the
1157 // exception handler
1161 wxGlobalSEInformation
= NULL
;
1163 // this will execute our handler and terminate the process
1164 return EXCEPTION_EXECUTE_HANDLER
;
1167 return EXCEPTION_CONTINUE_SEARCH
;
1170 #else // !wxUSE_ON_FATAL_EXCEPTION
1172 bool wxHandleFatalExceptions(bool WXUNUSED(doit
))
1174 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1179 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION