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