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"
45 #include "wx/msw/private.h"
47 // ----------------------------------------------------------------------------
48 // types of imagehlp.h functions
49 // ----------------------------------------------------------------------------
51 typedef DWORD (WINAPI
*SymSetOptions_t
)(DWORD
);
52 typedef BOOL (WINAPI
*SymInitialize_t
)(HANDLE
, LPSTR
, BOOL
);
53 typedef BOOL (WINAPI
*StackWalk_t
)(DWORD
, HANDLE
, HANDLE
, LPSTACKFRAME
,
54 LPVOID
, PREAD_PROCESS_MEMORY_ROUTINE
,
55 PFUNCTION_TABLE_ACCESS_ROUTINE
,
56 PGET_MODULE_BASE_ROUTINE
,
57 PTRANSLATE_ADDRESS_ROUTINE
);
58 typedef BOOL (WINAPI
*SymFromAddr_t
)(HANDLE
, DWORD64
, PDWORD64
, PSYMBOL_INFO
);
59 typedef LPVOID (WINAPI
*SymFunctionTableAccess_t
)(HANDLE
, DWORD
);
60 typedef DWORD (WINAPI
*SymGetModuleBase_t
)(HANDLE
, DWORD
);
61 typedef BOOL (WINAPI
*SymGetLineFromAddr_t
)(HANDLE
, DWORD
,
62 PDWORD
, PIMAGEHLP_LINE
);
63 typedef BOOL (WINAPI
*SymSetContext_t
)(HANDLE
, PIMAGEHLP_STACK_FRAME
,
65 typedef BOOL (WINAPI
*SymEnumSymbols_t
)(HANDLE
, ULONG64
, PCSTR
,
66 PSYM_ENUMERATESYMBOLS_CALLBACK
, PVOID
);
67 typedef BOOL (WINAPI
*SymGetTypeInfo_t
)(HANDLE
, DWORD64
, ULONG
,
68 IMAGEHLP_SYMBOL_TYPE_INFO
, PVOID
);
70 // ----------------------------------------------------------------------------
72 // ----------------------------------------------------------------------------
74 // Stolen from CVCONST.H in the DIA 2.0 SDK
88 BASICTYPE_CURRENCY
= 25,
90 BASICTYPE_VARIANT
= 27,
91 BASICTYPE_COMPLEX
= 28,
94 BASICTYPE_HRESULT
= 31
101 SYMBOL_TAG_FUNCTION
= 5,
103 SYMBOL_TAG_PUBLIC
= 10, // appears in .DBGs
106 SYMBOL_TAG_FUNCTION_TYPE
,
107 SYMBOL_TAG_POINTER_TYPE
,
108 SYMBOL_TAG_ARRAY_TYPE
,
109 SYMBOL_TAG_BASE_TYPE
,
114 // ----------------------------------------------------------------------------
116 // ----------------------------------------------------------------------------
118 // the real crash report generator
119 class wxCrashReportImpl
122 wxCrashReportImpl(const wxChar
*filename
);
124 bool Generate(int flags
);
128 if ( m_hFile
!= INVALID_HANDLE_VALUE
)
130 ::CloseHandle(m_hFile
);
135 // formatted output to m_hFile
136 void Output(const wxChar
*format
, ...);
138 // output end of line
139 void OutputEndl() { Output(_T("\r\n")); }
141 // translate exception code to its symbolic name
142 static wxString
GetExceptionString(DWORD dwCode
);
144 // return the type from "type index"
145 static BasicType
GetBasicType(DWORD64 modBase
, DWORD typeIndex
);
147 // return the name for the type index
148 static wxString
GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
);
150 // return the string representation of the variable value
151 static wxString
FormatSimpleValue(BasicType bt
,
155 // return string representation of a struct field (which may itself be a
156 // struct, of course)
157 static wxString
FormatField(DWORD64 modBase
,
162 // show the name and value of the given symbol
163 static wxString
FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
);
165 // show value described by SYMBOL_INFO located at pVariable
166 static wxString
FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
);
168 // show value of possibly complex (user-defined) type
169 static wxString
FormatUDT(DWORD64 modBase
,
174 // outputs information about the given symbol
175 void OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
);
177 // load all the functions we need from dbghelp.dll, return true if all ok
178 bool ResolveSymFunctions(const wxDynamicLibrary
& dllDbgHelp
);
180 // map address to module (and also section:offset), retunr true if ok
181 static bool GetLogicalAddress(PVOID addr
,
187 // callback used with SymEnumSymbols() to process all variables
188 static BOOL CALLBACK
EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
193 // show the general information about exception which should be always
196 // returns the module of the handle where the crash occured
197 HANDLE
OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
, CONTEXT
*pCtx
);
199 // output the call stack and local variables values
200 void OutputStack(const CONTEXT
*pCtx
, int flags
);
202 // output the global variables values
203 void OutputGlobals(HANDLE hModuleCrash
);
206 // the handle of the report file
209 // the current stack frame (may be NULL)
210 STACKFRAME
*m_sfCurrent
;
213 // dynamically loaded dbghelp.dll functions
214 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
216 DECLARE_SYM_FUNCTION(SymSetOptions
);
217 DECLARE_SYM_FUNCTION(SymInitialize
);
218 DECLARE_SYM_FUNCTION(StackWalk
);
219 DECLARE_SYM_FUNCTION(SymFromAddr
);
220 DECLARE_SYM_FUNCTION(SymFunctionTableAccess
);
221 DECLARE_SYM_FUNCTION(SymGetModuleBase
);
222 DECLARE_SYM_FUNCTION(SymGetLineFromAddr
);
223 DECLARE_SYM_FUNCTION(SymSetContext
);
224 DECLARE_SYM_FUNCTION(SymEnumSymbols
);
225 DECLARE_SYM_FUNCTION(SymGetTypeInfo
);
228 // ----------------------------------------------------------------------------
230 // ----------------------------------------------------------------------------
232 // global pointer to exception information, only valid inside OnFatalException
233 extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS
*wxGlobalSEInformation
= NULL
;
236 // flag telling us whether the application wants to handle exceptions at all
237 static bool gs_handleExceptions
= false;
239 // the file name where the report about exception is written
240 static wxChar gs_reportFilename
[MAX_PATH
];
242 // ============================================================================
244 // ============================================================================
246 #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
248 DEFINE_SYM_FUNCTION(SymSetOptions
);
249 DEFINE_SYM_FUNCTION(SymInitialize
);
250 DEFINE_SYM_FUNCTION(StackWalk
);
251 DEFINE_SYM_FUNCTION(SymFromAddr
);
252 DEFINE_SYM_FUNCTION(SymFunctionTableAccess
);
253 DEFINE_SYM_FUNCTION(SymGetModuleBase
);
254 DEFINE_SYM_FUNCTION(SymGetLineFromAddr
);
255 DEFINE_SYM_FUNCTION(SymSetContext
);
256 DEFINE_SYM_FUNCTION(SymEnumSymbols
);
257 DEFINE_SYM_FUNCTION(SymGetTypeInfo
);
259 #undef DEFINE_SYM_FUNCTION
261 // ----------------------------------------------------------------------------
263 // ----------------------------------------------------------------------------
265 wxCrashReportImpl::wxCrashReportImpl(const wxChar
*filename
)
269 m_hFile
= ::CreateFile
274 NULL
, // default security
276 FILE_FLAG_WRITE_THROUGH
,
277 NULL
// no template file
281 void wxCrashReportImpl::Output(const wxChar
*format
, ...)
284 va_start(argptr
, format
);
288 wxString s
= wxString::FormatV(format
, argptr
);
289 ::WriteFile(m_hFile
, s
, s
.length() * sizeof(wxChar
), &cbWritten
, 0);
295 wxCrashReportImpl::GetLogicalAddress(PVOID addr
,
301 MEMORY_BASIC_INFORMATION mbi
;
303 if ( !::VirtualQuery(addr
, &mbi
, sizeof(mbi
)) )
306 DWORD hMod
= (DWORD
)mbi
.AllocationBase
;
308 if ( !::GetModuleFileName((HMODULE
)hMod
, szModule
, len
) )
311 // Point to the DOS header in memory
312 PIMAGE_DOS_HEADER pDosHdr
= (PIMAGE_DOS_HEADER
)hMod
;
314 // From the DOS header, find the NT (PE) header
315 PIMAGE_NT_HEADERS pNtHdr
= (PIMAGE_NT_HEADERS
)(hMod
+ pDosHdr
->e_lfanew
);
317 PIMAGE_SECTION_HEADER pSection
= IMAGE_FIRST_SECTION( pNtHdr
);
319 DWORD rva
= (DWORD
)addr
- hMod
; // RVA is offset from module load address
321 // Iterate through the section table, looking for the one that encompasses
322 // the linear address.
323 const DWORD nSections
= pNtHdr
->FileHeader
.NumberOfSections
;
324 for ( DWORD i
= 0; i
< nSections
; i
++, pSection
++ )
326 DWORD sectionStart
= pSection
->VirtualAddress
;
327 DWORD sectionEnd
= sectionStart
328 + max(pSection
->SizeOfRawData
, pSection
->Misc
.VirtualSize
);
330 // Is the address in this section?
331 if ( (rva
>= sectionStart
) && (rva
<= sectionEnd
) )
333 // Yes, address is in the section. Calculate section and offset,
334 // and store in the "section" & "offset" params, which were
335 // passed by reference.
337 offset
= rva
- sectionStart
;
343 // failed to map to logical address...
347 /* static */ BasicType
348 wxCrashReportImpl::GetBasicType(DWORD64 modBase
, DWORD typeIndex
)
350 const HANDLE hProcess
= GetCurrentProcess();
352 // try the index we have
354 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_BASETYPE
, &bt
) )
359 // if failed, try to get the "real" typeid first
361 if ( SymGetTypeInfo(hProcess
, modBase
, typeIndex
, TI_GET_TYPEID
, &typeId
)
363 (typeId
!= typeIndex
&&
364 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_BASETYPE
, &bt
)) )
369 return BASICTYPE_NOTYPE
;
372 /* static */ wxString
373 wxCrashReportImpl::FormatSimpleValue(BasicType bt
,
380 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
383 s
.Printf(_T("%#04x"), *(PBYTE
)pAddress
);
385 else if ( length
== 2 )
387 s
.Printf(_T("%#06x"), *(PWORD
)pAddress
);
389 else if ( length
== 4 )
391 bool handled
= false;
393 if ( bt
== BASICTYPE_FLOAT
)
395 s
.Printf(_T("%f"), *(PFLOAT
)pAddress
);
399 else if ( bt
== BASICTYPE_CHAR
)
401 static const size_t NUM_CHARS
= 32;
403 const wxChar
* const pc
= *(PSTR
*)pAddress
;
404 if ( !::IsBadStringPtr(pc
, NUM_CHARS
) )
406 s
<< _T('"') << wxString(pc
, NUM_CHARS
) << _T('"');
414 // treat just as an opaque DWORD
415 s
.Printf(_T("%#x"), *(PDWORD
)pAddress
);
418 else if ( length
== 8 )
420 if ( bt
== BASICTYPE_FLOAT
)
422 s
.Printf(_T("%lf"), *(double *)pAddress
);
424 else // opaque 64 bit value
426 s
.Printf(_T("%#" wxLongLongFmtSpec
_T("x")), *(PDWORD
*)pAddress
);
434 wxString
wxCrashReportImpl::GetSymbolName(DWORD64 modBase
, DWORD dwTypeIndex
)
448 s
= wxConvCurrent
->cWC2WX(pwszTypeName
);
450 ::LocalFree(pwszTypeName
);
456 // this is called for the struct members/base classes
458 wxCrashReportImpl::FormatField(DWORD64 modBase
,
465 const HANDLE hProcess
= GetCurrentProcess();
468 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_SYMTAG
, &dwTag
);
473 case SYMBOL_TAG_BASECLASS
:
474 s
= FormatUDT(modBase
, dwTypeIndex
, pVariable
, level
);
477 case SYMBOL_TAG_FUNCTION
:
482 // try to treat all the rest as data even though it's not clear if
483 // it's really a good idea...
485 // Get the offset of the child member, relative to its parent
486 DWORD dwMemberOffset
= 0;
487 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
488 TI_GET_OFFSET
, &dwMemberOffset
);
490 // Get the real "TypeId" of the child. We need this for the
491 // SymGetTypeInfo(TI_GET_LENGTH) call below.
493 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
,
494 TI_GET_TYPEID
, &typeId
) )
496 typeId
= dwTypeIndex
;
499 // Get the size of the child member
501 SymGetTypeInfo(hProcess
, modBase
, typeId
, TI_GET_LENGTH
, &size
);
503 // Calculate the address of the member
504 DWORD_PTR dwFinalOffset
= (DWORD_PTR
)pVariable
+ dwMemberOffset
;
506 BasicType basicType
= GetBasicType(modBase
, dwTypeIndex
);
508 s
= FormatSimpleValue(basicType
, size
, (PVOID
)dwFinalOffset
);
515 // don't show if no value -- what for?
519 return wxString(_T('\t'), level
+ 1) +
520 GetSymbolName(modBase
, dwTypeIndex
) +
521 _T(" = ") + s
+ _T("\r\n");
524 // If it's a user defined type (UDT), recurse through its members until we're
525 // at fundamental types.
527 wxCrashReportImpl::FormatUDT(DWORD64 modBase
,
534 s
= GetSymbolName(modBase
, dwTypeIndex
) + _T(" {\r\n");
536 const HANDLE hProcess
= GetCurrentProcess();
538 // Determine how many children this type has.
539 DWORD dwChildrenCount
= 0;
540 SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_GET_CHILDRENCOUNT
,
543 // Prepare to get an array of "TypeIds", representing each of the children.
544 TI_FINDCHILDREN_PARAMS
*children
= (TI_FINDCHILDREN_PARAMS
*)
545 malloc(sizeof(TI_FINDCHILDREN_PARAMS
) +
546 (dwChildrenCount
- 1)*sizeof(ULONG
));
550 children
->Count
= dwChildrenCount
;
553 // Get the array of TypeIds, one for each child type
554 if ( !SymGetTypeInfo(hProcess
, modBase
, dwTypeIndex
, TI_FINDCHILDREN
,
560 // Iterate through all children
561 for ( unsigned i
= 0; i
< dwChildrenCount
; i
++ )
563 s
+= FormatField(modBase
, children
->ChildId
[i
], pVariable
, level
+ 1);
568 s
<< wxString(_T('\t'), level
+ 1) << _T('}');
573 // return the string containing the symbol of the given symbol
574 /* static */ wxString
575 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym
, void *pVariable
)
578 SymGetTypeInfo(GetCurrentProcess(), pSym
->ModBase
, pSym
->TypeIndex
,
579 TI_GET_SYMTAG
, &dwTag
);
584 case SYMBOL_TAG_FUNCTION
:
588 case SYMBOL_TAG_BASECLASS
:
589 // show UDT recursively
590 s
= FormatUDT(pSym
->ModBase
, pSym
->TypeIndex
, pVariable
);
594 // variable of simple type (but could be array which we don't
595 // handle correctly yet...), format it using its type and size
596 BasicType bt
= GetBasicType(pSym
->ModBase
, pSym
->TypeIndex
);
598 s
= FormatSimpleValue(bt
, pSym
->Size
, pVariable
);
606 // display contents and type of the given variable
607 /* static */ wxString
608 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym
, STACKFRAME
*sf
)
612 if ( pSym
->Tag
== SYMBOL_TAG_FUNCTION
)
614 // If it's a function, don't do anything.
618 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGISTER
)
620 // Don't try to report register variable
626 // Indicate if the variable is a local or parameter
627 if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_PARAMETER
)
628 s
+= _T("\t[param] ");
629 else if ( pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_LOCAL
)
630 s
+= _T("\t[local] ");
632 // Will point to the variable's data in memory
633 DWORD_PTR pVariable
= 0;
635 if ( (pSym
->Flags
& IMAGEHLP_SYMBOL_INFO_REGRELATIVE
) && sf
)
637 pVariable
= sf
->AddrFrame
.Offset
;
638 pVariable
+= (DWORD_PTR
)pSym
->Address
;
640 else // It must be a global variable
642 pVariable
= (DWORD_PTR
)pSym
->Address
;
645 s
<< pSym
->Name
<< _T(" = ") << FormatAnyValue(pSym
, (PVOID
)pVariable
);
651 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo
, STACKFRAME
*sf
)
653 wxString s
= FormatSymbol(pSymInfo
, sf
);
656 Output(_T("%s\r\n"), s
.c_str());
658 //else: not an interesting symbol
661 // callback for SymEnumSymbols()
664 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo
,
665 ULONG
WXUNUSED(SymbolSize
),
668 wxCrashReportImpl
*self
= (wxCrashReportImpl
*)UserContext
;
672 self
->OutputSymbol(pSymInfo
, self
->m_sfCurrent
);
674 __except ( EXCEPTION_EXECUTE_HANDLER
)
676 self
->Output(_T("Can't process symbol %hs\r\n"), pSymInfo
->Name
);
679 // continue with enumeration
684 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD
*pExceptionRecord
,
687 // First print information about the type of fault
688 const DWORD dwCode
= pExceptionRecord
->ExceptionCode
;
689 Output(_T("Exception code: %s (%#10x)\r\n"),
690 GetExceptionString(dwCode
).c_str(), dwCode
);
692 // Now print information about where the fault occured
693 TCHAR szFaultingModule
[MAX_PATH
];
696 void * const pExceptionAddress
= pExceptionRecord
->ExceptionAddress
;
697 if ( !GetLogicalAddress(pExceptionAddress
,
699 WXSIZEOF(szFaultingModule
),
705 wxStrcpy(szFaultingModule
, _T("<< unknown >>"));
708 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
709 pExceptionAddress
, section
, offset
, szFaultingModule
);
712 // Show the registers
713 Output( _T("\r\nRegisters:\r\n") );
715 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
716 pCtx
->Eax
, pCtx
->Ebx
, pCtx
->Ecx
, pCtx
->Edx
, pCtx
->Esi
, pCtx
->Edi
);
718 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
719 pCtx
->SegCs
, pCtx
->Eip
, pCtx
->SegSs
, pCtx
->Esp
, pCtx
->Ebp
);
720 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
721 pCtx
->SegDs
, pCtx
->SegEs
, pCtx
->SegFs
, pCtx
->SegGs
);
722 Output(_T("Flags: %08x\r\n"), pCtx
->EFlags
);
725 return ::GetModuleHandle(szFaultingModule
);
728 void wxCrashReportImpl::OutputStack(const CONTEXT
*pCtx
, int flags
)
736 // can't show locals under other architectures
741 for ( int step
= 0; step
< Output_Max
; step
++ )
743 // don't do things we're not asked for
744 if ( (step
== Output_Stack
) && !(flags
& wxCRASH_REPORT_STACK
) ||
745 (step
== Output_Locals
) && !(flags
& wxCRASH_REPORT_LOCALS
) )
750 // the context is going to be modified below so make a copy
753 Output(_T("\r\n%s\r\n")
754 _T(" # Address Frame Function SourceFile\r\n"),
755 step
== Output_Stack
? _T("Call stack") : _T("Local variables"));
757 DWORD dwMachineType
= 0;
763 // Initialize the STACKFRAME structure for the first call. This is
764 // only necessary for Intel CPUs, and isn't mentioned in the
766 sf
.AddrPC
.Offset
= ctx
.Eip
;
767 sf
.AddrPC
.Mode
= AddrModeFlat
;
768 sf
.AddrStack
.Offset
= ctx
.Esp
;
769 sf
.AddrStack
.Mode
= AddrModeFlat
;
770 sf
.AddrFrame
.Offset
= ctx
.Ebp
;
771 sf
.AddrFrame
.Mode
= AddrModeFlat
;
773 dwMachineType
= IMAGE_FILE_MACHINE_I386
;
776 const HANDLE hProcess
= GetCurrentProcess();
777 const HANDLE hThread
= GetCurrentThread();
779 // first show just the call stack
783 // Get the next stack frame
784 if ( !StackWalk(dwMachineType
,
790 SymFunctionTableAccess
,
797 // Basic sanity check to make sure the frame is OK.
798 if ( !sf
.AddrFrame
.Offset
)
801 Output(_T("%2d %08x %08x "),
802 frame
++, sf
.AddrPC
.Offset
, sf
.AddrFrame
.Offset
);
804 // Get the name of the function for this stack frame entry
805 BYTE symbolBuffer
[ sizeof(SYMBOL_INFO
) + 1024 ];
806 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
)symbolBuffer
;
807 pSymbol
->SizeOfStruct
= sizeof(symbolBuffer
);
808 pSymbol
->MaxNameLen
= 1024;
810 // Displacement of the input address, relative to the start of the
812 DWORD64 symDisplacement
= 0;
814 if ( SymFromAddr(hProcess
, sf
.AddrPC
.Offset
,
815 &symDisplacement
,pSymbol
) )
817 Output(_T("%hs()+%#") wxLongLongFmtSpec
_T("x"),
818 pSymbol
->Name
, symDisplacement
);
820 else // No symbol found. Print out the logical address instead.
822 TCHAR szModule
[MAX_PATH
];
826 if ( !GetLogicalAddress((PVOID
)sf
.AddrPC
.Offset
,
827 szModule
, sizeof(szModule
),
830 szModule
[0] = _T('\0');
835 Output(_T("%04x:%08x %s"), section
, offset
, szModule
);
838 // Get the source line for this stack frame entry
839 IMAGEHLP_LINE lineInfo
= { sizeof(IMAGEHLP_LINE
) };
840 DWORD dwLineDisplacement
;
841 if ( SymGetLineFromAddr(hProcess
, sf
.AddrPC
.Offset
,
842 &dwLineDisplacement
, &lineInfo
))
844 Output(_T(" %s line %u"),
845 lineInfo
.FileName
, lineInfo
.LineNumber
);
852 // on the second iteration also show the local variables and
854 if ( step
== Output_Locals
)
856 // Use SymSetContext to get just the locals/params for this
858 IMAGEHLP_STACK_FRAME imagehlpStackFrame
;
859 imagehlpStackFrame
.InstructionOffset
= sf
.AddrPC
.Offset
;
860 SymSetContext(hProcess
, &imagehlpStackFrame
, 0);
862 // Enumerate the locals/parameters
864 SymEnumSymbols(hProcess
, 0, 0, EnumerateSymbolsCallback
, this);
873 void wxCrashReportImpl::OutputGlobals(HANDLE hModule
)
876 Output(_T("\r\nGlobal variables:\r\n"));
879 SymEnumSymbols(::GetCurrentProcess(), (DWORD64
)hModule
, NULL
,
880 EnumerateSymbolsCallback
, this);
884 bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary
& dllDbgHelp
)
886 #define LOAD_SYM_FUNCTION(name) \
887 name = (name ## _t) dllDbgHelp.GetSymbol(#name); \
890 Output(_T("\r\nFunction ") __XFILE__(#name) \
891 _T("() not found.\r\n")); \
895 LOAD_SYM_FUNCTION(SymSetOptions
);
896 LOAD_SYM_FUNCTION(SymInitialize
);
897 LOAD_SYM_FUNCTION(StackWalk
);
898 LOAD_SYM_FUNCTION(SymFromAddr
);
899 LOAD_SYM_FUNCTION(SymFunctionTableAccess
);
900 LOAD_SYM_FUNCTION(SymGetModuleBase
);
901 LOAD_SYM_FUNCTION(SymGetLineFromAddr
);
902 LOAD_SYM_FUNCTION(SymSetContext
);
903 LOAD_SYM_FUNCTION(SymEnumSymbols
);
904 LOAD_SYM_FUNCTION(SymGetTypeInfo
);
906 #undef LOAD_SYM_FUNCTION
911 bool wxCrashReportImpl::Generate(int flags
)
913 if ( m_hFile
== INVALID_HANDLE_VALUE
)
916 if ( !wxGlobalSEInformation
)
919 PEXCEPTION_RECORD pExceptionRecord
= wxGlobalSEInformation
->ExceptionRecord
;
920 PCONTEXT pCtx
= wxGlobalSEInformation
->ContextRecord
;
922 if ( !pExceptionRecord
|| !pCtx
)
925 HANDLE hModuleCrash
= OutputBasicContext(pExceptionRecord
, pCtx
);
927 // for everything else we need dbghelp.dll
928 wxDynamicLibrary
dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM
);
929 if ( dllDbgHelp
.IsLoaded() )
931 if ( ResolveSymFunctions(dllDbgHelp
) )
933 SymSetOptions(SYMOPT_DEFERRED_LOADS
);
935 // Initialize DbgHelp
936 if ( SymInitialize(GetCurrentProcess(), NULL
, TRUE
/* invade */) )
938 OutputStack(pCtx
, flags
);
940 if ( hModuleCrash
&& (flags
& wxCRASH_REPORT_GLOBALS
) )
942 OutputGlobals(hModuleCrash
);
950 Output(_T("Please update your dbghelp.dll version, "
951 "at least version 5.1 is needed!\r\n"));
956 Output(_T("Please install dbghelp.dll available free of charge ")
957 _T("from Microsoft to get more detailed crash information!"));
960 Output(_T("\r\nLatest dbghelp.dll is available at "
961 "http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
967 wxString
wxCrashReportImpl::GetExceptionString(DWORD dwCode
)
971 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
975 CASE_EXCEPTION(ACCESS_VIOLATION
);
976 CASE_EXCEPTION(DATATYPE_MISALIGNMENT
);
977 CASE_EXCEPTION(BREAKPOINT
);
978 CASE_EXCEPTION(SINGLE_STEP
);
979 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
);
980 CASE_EXCEPTION(FLT_DENORMAL_OPERAND
);
981 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
);
982 CASE_EXCEPTION(FLT_INEXACT_RESULT
);
983 CASE_EXCEPTION(FLT_INVALID_OPERATION
);
984 CASE_EXCEPTION(FLT_OVERFLOW
);
985 CASE_EXCEPTION(FLT_STACK_CHECK
);
986 CASE_EXCEPTION(FLT_UNDERFLOW
);
987 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
);
988 CASE_EXCEPTION(INT_OVERFLOW
);
989 CASE_EXCEPTION(PRIV_INSTRUCTION
);
990 CASE_EXCEPTION(IN_PAGE_ERROR
);
991 CASE_EXCEPTION(ILLEGAL_INSTRUCTION
);
992 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
);
993 CASE_EXCEPTION(STACK_OVERFLOW
);
994 CASE_EXCEPTION(INVALID_DISPOSITION
);
995 CASE_EXCEPTION(GUARD_PAGE
);
996 CASE_EXCEPTION(INVALID_HANDLE
);
999 // unknown exception, ask NTDLL for the name
1000 if ( !::FormatMessage
1002 FORMAT_MESSAGE_IGNORE_INSERTS
|
1003 FORMAT_MESSAGE_FROM_HMODULE
,
1004 ::GetModuleHandle(_T("NTDLL.DLL")),
1007 wxStringBuffer(s
, 1024),
1012 s
= _T("UNKNOWN_EXCEPTION");
1016 #undef CASE_EXCEPTION
1021 // ----------------------------------------------------------------------------
1023 // ----------------------------------------------------------------------------
1026 void wxCrashReport::SetFileName(const wxChar
*filename
)
1028 wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1);
1029 gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0');
1033 const wxChar
*wxCrashReport::GetFileName()
1035 return gs_reportFilename
;
1039 bool wxCrashReport::Generate(int flags
)
1041 wxCrashReportImpl
impl(gs_reportFilename
);
1043 return impl
.Generate(flags
);
1046 // ----------------------------------------------------------------------------
1047 // wxApp::OnFatalException() support
1048 // ----------------------------------------------------------------------------
1050 bool wxHandleFatalExceptions(bool doit
)
1052 // assume this can only be called from the main thread
1053 gs_handleExceptions
= doit
;
1057 // try to find a place where we can put out report file later
1060 WXSIZEOF(gs_reportFilename
),
1064 wxLogLastError(_T("GetTempPath"));
1066 // when all else fails...
1067 wxStrcpy(gs_reportFilename
, _T("c:\\"));
1070 // use PID and date to make the report file name more unique
1071 wxString fname
= wxString::Format
1073 _T("%s_%s_%lu.rpt"),
1074 wxTheApp
? wxTheApp
->GetAppName().c_str()
1076 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1077 ::GetCurrentProcessId()
1080 wxStrncat(gs_reportFilename
, fname
,
1081 WXSIZEOF(gs_reportFilename
) - strlen(gs_reportFilename
) - 1);
1087 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS
*pExcPtrs
)
1089 if ( gs_handleExceptions
&& wxTheApp
)
1091 // store the pointer to exception info
1092 wxGlobalSEInformation
= pExcPtrs
;
1094 // give the user a chance to do something special about this
1095 wxTheApp
->OnFatalException();
1097 wxGlobalSEInformation
= NULL
;
1099 // this will execute our handler and terminate the process
1100 return EXCEPTION_EXECUTE_HANDLER
;
1103 return EXCEPTION_CONTINUE_SEARCH
;
1106 #else // !wxUSE_ON_FATAL_EXCEPTION
1108 bool wxHandleFatalExceptions(bool WXUNUSED(doit
))
1110 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1115 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION