]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
Compile with EVC 3.0 2002
[wxWidgets.git] / src / msw / crashrpt.cpp
CommitLineData
50bea100
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: msw/crashrpt.cpp
3// Purpose: helpers for structured exception handling (SEH)
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 13.07.03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12/*
13 The code in this file is heavily based on Matt Pietrek's column from
14 the March 2002 issue of MSDN Magazine.
15 */
16
17// ============================================================================
18// declarations
19// ============================================================================
20
21// ----------------------------------------------------------------------------
22// headers
23// ----------------------------------------------------------------------------
24
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
27
28#ifdef __BORLANDC__
29 #pragma hdrstop
30#endif
31
32#if wxUSE_ON_FATAL_EXCEPTION
33
34#ifndef WX_PRECOMP
35#endif //WX_PRECOMP
36
37#include "wx/longlong.h"
38#include "wx/datetime.h"
39#include "wx/dynload.h"
40
41#include "wx/msw/crashrpt.h"
42
9ed0d735 43#include "wx/msw/wrapwin.h"
50bea100
VZ
44#include <imagehlp.h>
45#include "wx/msw/private.h"
46
47// ----------------------------------------------------------------------------
48// types of imagehlp.h functions
49// ----------------------------------------------------------------------------
50
51typedef DWORD (WINAPI *SymSetOptions_t)(DWORD);
52typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL);
53typedef 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);
58typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
59typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD);
60typedef DWORD (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD);
61typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD,
62 PDWORD, PIMAGEHLP_LINE);
63typedef BOOL (WINAPI *SymSetContext_t)(HANDLE, PIMAGEHLP_STACK_FRAME,
64 PIMAGEHLP_CONTEXT);
65typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR,
66 PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
67typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG,
68 IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
69
70// ----------------------------------------------------------------------------
71// constant
72// ----------------------------------------------------------------------------
73
74// Stolen from CVCONST.H in the DIA 2.0 SDK
75enum BasicType
76{
77 BASICTYPE_NOTYPE = 0,
78 BASICTYPE_VOID = 1,
79 BASICTYPE_CHAR = 2,
80 BASICTYPE_WCHAR = 3,
81 BASICTYPE_INT = 6,
82 BASICTYPE_UINT = 7,
83 BASICTYPE_FLOAT = 8,
84 BASICTYPE_BCD = 9,
85 BASICTYPE_BOOL = 10,
86 BASICTYPE_LONG = 13,
87 BASICTYPE_ULONG = 14,
88 BASICTYPE_CURRENCY = 25,
89 BASICTYPE_DATE = 26,
90 BASICTYPE_VARIANT = 27,
91 BASICTYPE_COMPLEX = 28,
92 BASICTYPE_BIT = 29,
93 BASICTYPE_BSTR = 30,
94 BASICTYPE_HRESULT = 31
95};
96
97// Same as above
98enum SymbolTag
99{
100 SYMBOL_TAG_NULL,
101 SYMBOL_TAG_FUNCTION = 5,
102 SYMBOL_TAG_DATA = 7,
103 SYMBOL_TAG_PUBLIC = 10, // appears in .DBGs
104 SYMBOL_TAG_UDT,
105 SYMBOL_TAG_ENUM,
106 SYMBOL_TAG_FUNCTION_TYPE,
107 SYMBOL_TAG_POINTER_TYPE,
108 SYMBOL_TAG_ARRAY_TYPE,
109 SYMBOL_TAG_BASE_TYPE,
110 SYMBOL_TAG_TYPEDEF,
111 SYMBOL_TAG_BASECLASS
112};
113
114// ----------------------------------------------------------------------------
115// classes
116// ----------------------------------------------------------------------------
117
118// the real crash report generator
119class wxCrashReportImpl
120{
121public:
122 wxCrashReportImpl(const wxChar *filename);
123
124 bool Generate(int flags);
125
126 ~wxCrashReportImpl()
127 {
128 if ( m_hFile != INVALID_HANDLE_VALUE )
129 {
130 ::CloseHandle(m_hFile);
131 }
132 }
133
134private:
135 // formatted output to m_hFile
136 void Output(const wxChar *format, ...);
137
138 // output end of line
139 void OutputEndl() { Output(_T("\r\n")); }
140
141 // translate exception code to its symbolic name
142 static wxString GetExceptionString(DWORD dwCode);
143
144 // return the type from "type index"
145 static BasicType GetBasicType(DWORD64 modBase, DWORD typeIndex);
146
147 // return the name for the type index
148 static wxString GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex);
149
150 // return the string representation of the variable value
151 static wxString FormatSimpleValue(BasicType bt,
152 DWORD64 length,
153 PVOID pAddress);
154
155 // return string representation of a struct field (which may itself be a
156 // struct, of course)
157 static wxString FormatField(DWORD64 modBase,
158 DWORD dwTypeIndex,
159 void *pVariable,
160 unsigned level);
161
162 // show the name and value of the given symbol
163 static wxString FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf);
164
165 // show value described by SYMBOL_INFO located at pVariable
166 static wxString FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable);
167
168 // show value of possibly complex (user-defined) type
169 static wxString FormatUDT(DWORD64 modBase,
170 DWORD dwTypeIndex,
171 void *pVariable,
172 unsigned level = 0);
173
174 // outputs information about the given symbol
175 void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf);
176
177 // load all the functions we need from dbghelp.dll, return true if all ok
178 bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp);
179
180 // map address to module (and also section:offset), retunr true if ok
181 static bool GetLogicalAddress(PVOID addr,
182 PTSTR szModule,
183 DWORD len,
184 DWORD& section,
185 DWORD& offset);
186
187 // callback used with SymEnumSymbols() to process all variables
188 static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
189 ULONG SymbolSize,
190 PVOID UserContext);
191
192
193 // show the general information about exception which should be always
194 // available
195 //
196 // returns the module of the handle where the crash occured
197 HANDLE OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx);
198
199 // output the call stack and local variables values
200 void OutputStack(const CONTEXT *pCtx, int flags);
201
202 // output the global variables values
203 void OutputGlobals(HANDLE hModuleCrash);
204
205
206 // the handle of the report file
207 HANDLE m_hFile;
208
209 // the current stack frame (may be NULL)
210 STACKFRAME *m_sfCurrent;
211
212
213 // dynamically loaded dbghelp.dll functions
214 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
215
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);
226};
227
228// ----------------------------------------------------------------------------
229// globals
230// ----------------------------------------------------------------------------
231
232// global pointer to exception information, only valid inside OnFatalException
233extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL;
234
235
236// flag telling us whether the application wants to handle exceptions at all
237static bool gs_handleExceptions = false;
238
239// the file name where the report about exception is written
240static wxChar gs_reportFilename[MAX_PATH];
241
242// ============================================================================
243// implementation
244// ============================================================================
245
246#define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
247
248DEFINE_SYM_FUNCTION(SymSetOptions);
249DEFINE_SYM_FUNCTION(SymInitialize);
250DEFINE_SYM_FUNCTION(StackWalk);
251DEFINE_SYM_FUNCTION(SymFromAddr);
252DEFINE_SYM_FUNCTION(SymFunctionTableAccess);
253DEFINE_SYM_FUNCTION(SymGetModuleBase);
254DEFINE_SYM_FUNCTION(SymGetLineFromAddr);
255DEFINE_SYM_FUNCTION(SymSetContext);
256DEFINE_SYM_FUNCTION(SymEnumSymbols);
257DEFINE_SYM_FUNCTION(SymGetTypeInfo);
258
259#undef DEFINE_SYM_FUNCTION
260
261// ----------------------------------------------------------------------------
262// wxCrashReportImpl
263// ----------------------------------------------------------------------------
264
265wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
266{
267 m_sfCurrent = NULL;
268
269 m_hFile = ::CreateFile
270 (
271 filename,
272 GENERIC_WRITE,
273 0, // no sharing
274 NULL, // default security
275 CREATE_ALWAYS,
276 FILE_FLAG_WRITE_THROUGH,
277 NULL // no template file
278 );
279}
280
281void wxCrashReportImpl::Output(const wxChar *format, ...)
282{
283 va_list argptr;
284 va_start(argptr, format);
285
286 DWORD cbWritten;
287
288 wxString s = wxString::FormatV(format, argptr);
289 ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0);
290
291 va_end(argptr);
292}
293
294bool
295wxCrashReportImpl::GetLogicalAddress(PVOID addr,
296 PTSTR szModule,
297 DWORD len,
298 DWORD& section,
299 DWORD& offset)
300{
301 MEMORY_BASIC_INFORMATION mbi;
302
303 if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) )
304 return false;
305
306 DWORD hMod = (DWORD)mbi.AllocationBase;
307
308 if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) )
309 return false;
310
311 // Point to the DOS header in memory
312 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
313
314 // From the DOS header, find the NT (PE) header
315 PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
316
317 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
318
319 DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
320
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++ )
325 {
326 DWORD sectionStart = pSection->VirtualAddress;
327 DWORD sectionEnd = sectionStart
328 + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
329
330 // Is the address in this section?
331 if ( (rva >= sectionStart) && (rva <= sectionEnd) )
332 {
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.
336 section = i + 1;
337 offset = rva - sectionStart;
338
339 return true;
340 }
341 }
342
343 // failed to map to logical address...
344 return false;
345}
346
347/* static */ BasicType
348wxCrashReportImpl::GetBasicType(DWORD64 modBase, DWORD typeIndex)
349{
350 const HANDLE hProcess = GetCurrentProcess();
351
352 // try the index we have
353 BasicType bt;
354 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &bt) )
355 {
356 return bt;
357 }
358
359 // if failed, try to get the "real" typeid first
360 DWORD typeId;
361 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId)
362 &&
363 (typeId != typeIndex &&
364 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_BASETYPE, &bt)) )
365 {
366 return bt;
367 }
368
369 return BASICTYPE_NOTYPE;
370}
371
372/* static */ wxString
373wxCrashReportImpl::FormatSimpleValue(BasicType bt,
374 DWORD64 length,
375 PVOID pAddress)
376{
377 wxString s;
378 s.reserve(256);
379
380 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
381 if ( length == 1 )
382 {
383 s.Printf(_T("%#04x"), *(PBYTE)pAddress);
384 }
385 else if ( length == 2 )
386 {
387 s.Printf(_T("%#06x"), *(PWORD)pAddress);
388 }
389 else if ( length == 4 )
390 {
391 bool handled = false;
392
393 if ( bt == BASICTYPE_FLOAT )
394 {
395 s.Printf(_T("%f"), *(PFLOAT)pAddress);
396
397 handled = true;
398 }
399 else if ( bt == BASICTYPE_CHAR )
400 {
401 static const size_t NUM_CHARS = 32;
402
403 const wxChar * const pc = *(PSTR *)pAddress;
404 if ( !::IsBadStringPtr(pc, NUM_CHARS) )
405 {
406 s << _T('"') << wxString(pc, NUM_CHARS) << _T('"');
407
408 handled = true;
409 }
410 }
411
412 if ( !handled )
413 {
414 // treat just as an opaque DWORD
415 s.Printf(_T("%#x"), *(PDWORD)pAddress);
416 }
417 }
418 else if ( length == 8 )
419 {
420 if ( bt == BASICTYPE_FLOAT )
421 {
422 s.Printf(_T("%lf"), *(double *)pAddress);
423 }
424 else // opaque 64 bit value
425 {
426 s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress);
427 }
428 }
429
430 return s;
431}
432
433/* static */
434wxString wxCrashReportImpl::GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex)
435{
436 wxString s;
437
438 WCHAR *pwszTypeName;
439 if ( SymGetTypeInfo
440 (
441 GetCurrentProcess(),
442 modBase,
443 dwTypeIndex,
444 TI_GET_SYMNAME,
445 &pwszTypeName
446 ) )
447 {
448 s = wxConvCurrent->cWC2WX(pwszTypeName);
449
450 ::LocalFree(pwszTypeName);
451 }
452
453 return s;
454}
455
456// this is called for the struct members/base classes
457wxString
458wxCrashReportImpl::FormatField(DWORD64 modBase,
459 DWORD dwTypeIndex,
460 void *pVariable,
461 unsigned level)
462{
463 wxString s;
464
465 const HANDLE hProcess = GetCurrentProcess();
466
467 DWORD dwTag = 0;
468 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag);
469
470 switch ( dwTag )
471 {
472 case SYMBOL_TAG_UDT:
473 case SYMBOL_TAG_BASECLASS:
474 s = FormatUDT(modBase, dwTypeIndex, pVariable, level);
475 break;
476
477 case SYMBOL_TAG_FUNCTION:
478 // don't show
479 break;
480
481 default:
482 // try to treat all the rest as data even though it's not clear if
483 // it's really a good idea...
484
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);
489
490 // Get the real "TypeId" of the child. We need this for the
491 // SymGetTypeInfo(TI_GET_LENGTH) call below.
492 DWORD typeId;
493 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
494 TI_GET_TYPEID, &typeId) )
495 {
496 typeId = dwTypeIndex;
497 }
498
499 // Get the size of the child member
500 ULONG64 size;
501 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size);
502
503 // Calculate the address of the member
504 DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset;
505
506 BasicType basicType = GetBasicType(modBase, dwTypeIndex);
507
508 s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset);
509 break;
510
511 }
512
513 if ( s.empty() )
514 {
515 // don't show if no value -- what for?
516 return s;
517 }
518
519 return wxString(_T('\t'), level + 1) +
520 GetSymbolName(modBase, dwTypeIndex) +
521 _T(" = ") + s + _T("\r\n");
522}
523
524// If it's a user defined type (UDT), recurse through its members until we're
525// at fundamental types.
526wxString
527wxCrashReportImpl::FormatUDT(DWORD64 modBase,
528 DWORD dwTypeIndex,
529 void *pVariable,
530 unsigned level)
531{
532 wxString s;
533 s.reserve(512);
534 s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n");
535
536 const HANDLE hProcess = GetCurrentProcess();
537
538 // Determine how many children this type has.
539 DWORD dwChildrenCount = 0;
540 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
541 &dwChildrenCount);
542
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));
547 if ( !children )
548 return s;
549
550 children->Count = dwChildrenCount;
551 children->Start = 0;
552
553 // Get the array of TypeIds, one for each child type
554 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
555 children) )
556 {
557 return s;
558 }
559
560 // Iterate through all children
561 for ( unsigned i = 0; i < dwChildrenCount; i++ )
562 {
563 s += FormatField(modBase, children->ChildId[i], pVariable, level + 1);
564 }
565
566 free(children);
567
568 s << wxString(_T('\t'), level + 1) << _T('}');
569
570 return s;
571}
572
573// return the string containing the symbol of the given symbol
574/* static */ wxString
575wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable)
576{
577 DWORD dwTag = 0;
578 SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
579 TI_GET_SYMTAG, &dwTag);
580
581 wxString s;
582 switch ( dwTag )
583 {
584 case SYMBOL_TAG_FUNCTION:
585 break;
586
587 case SYMBOL_TAG_UDT:
588 case SYMBOL_TAG_BASECLASS:
589 // show UDT recursively
590 s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable);
591 break;
592
593 default:
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);
597
598 s = FormatSimpleValue(bt, pSym->Size, pVariable);
599 break;
600
601 }
602
603 return s;
604}
605
606// display contents and type of the given variable
607/* static */ wxString
608wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf)
609{
610 wxString s;
611
612 if ( pSym->Tag == SYMBOL_TAG_FUNCTION )
613 {
614 // If it's a function, don't do anything.
615 return s;
616 }
617
618 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
619 {
620 // Don't try to report register variable
621 return s;
622 }
623
624 s.reserve(512);
625
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] ");
631
632 // Will point to the variable's data in memory
633 DWORD_PTR pVariable = 0;
634
635 if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf )
636 {
637 pVariable = sf->AddrFrame.Offset;
638 pVariable += (DWORD_PTR)pSym->Address;
639 }
640 else // It must be a global variable
641 {
642 pVariable = (DWORD_PTR)pSym->Address;
643 }
644
645 s << pSym->Name << _T(" = ") << FormatAnyValue(pSym, (PVOID)pVariable);
646
647 return s;
648}
649
650void
651wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf)
652{
653 wxString s = FormatSymbol(pSymInfo, sf);
654 if ( !s.empty() )
655 {
656 Output(_T("%s\r\n"), s.c_str());
657 }
658 //else: not an interesting symbol
659}
660
661// callback for SymEnumSymbols()
662/* static */
663BOOL CALLBACK
664wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
665 ULONG WXUNUSED(SymbolSize),
666 PVOID UserContext)
667{
668 wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
669
670 __try
671 {
672 self->OutputSymbol(pSymInfo, self->m_sfCurrent);
673 }
674 __except ( EXCEPTION_EXECUTE_HANDLER )
675 {
676 self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
677 }
678
679 // continue with enumeration
680 return true;
681}
682
683HANDLE
684wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
685 CONTEXT *pCtx)
686{
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);
691
692 // Now print information about where the fault occured
693 TCHAR szFaultingModule[MAX_PATH];
694 DWORD section,
695 offset;
696 void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
697 if ( !GetLogicalAddress(pExceptionAddress,
698 szFaultingModule,
699 WXSIZEOF(szFaultingModule),
700 section, offset) )
701 {
702 section =
703 offset = 0;
704
705 wxStrcpy(szFaultingModule, _T("<< unknown >>"));
706 }
707
708 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
709 pExceptionAddress, section, offset, szFaultingModule);
710
711#ifdef _M_IX86
712 // Show the registers
713 Output( _T("\r\nRegisters:\r\n") );
714
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);
717
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 );
723#endif // _M_IX86
724
725 return ::GetModuleHandle(szFaultingModule);
726}
727
728void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
729{
730 enum
731 {
732 Output_Stack,
733 Output_Locals,
734 Output_Max
735#ifndef _M_IX86
736 // can't show locals under other architectures
737 = Output_Locals
738#endif
739 };
740
741 for ( int step = 0; step < Output_Max; step++ )
742 {
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) )
746 {
747 continue;
748 }
749
750 // the context is going to be modified below so make a copy
751 CONTEXT ctx = *pCtx;
752
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"));
756
757 DWORD dwMachineType = 0;
758
759 STACKFRAME sf;
760 wxZeroMemory(sf);
761
762#ifdef _M_IX86
763 // Initialize the STACKFRAME structure for the first call. This is
764 // only necessary for Intel CPUs, and isn't mentioned in the
765 // documentation.
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;
772
773 dwMachineType = IMAGE_FILE_MACHINE_I386;
774#endif // _M_IX86
775
776 const HANDLE hProcess = GetCurrentProcess();
777 const HANDLE hThread = GetCurrentThread();
778
779 // first show just the call stack
780 int frame = 0;
781 for ( ;; )
782 {
783 // Get the next stack frame
784 if ( !StackWalk(dwMachineType,
785 hProcess,
786 hThread,
787 &sf,
788 &ctx,
789 0,
790 SymFunctionTableAccess,
791 SymGetModuleBase,
792 0) )
793 {
794 break;
795 }
796
797 // Basic sanity check to make sure the frame is OK.
798 if ( !sf.AddrFrame.Offset )
799 break;
800
801 Output(_T("%2d %08x %08x "),
802 frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
803
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;
809
810 // Displacement of the input address, relative to the start of the
811 // symbol
812 DWORD64 symDisplacement = 0;
813
814 if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
815 &symDisplacement,pSymbol) )
816 {
817 Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
818 pSymbol->Name, symDisplacement);
819 }
820 else // No symbol found. Print out the logical address instead.
821 {
822 TCHAR szModule[MAX_PATH];
823 DWORD section,
824 offset;
825
826 if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
827 szModule, sizeof(szModule),
828 section, offset) )
829 {
830 szModule[0] = _T('\0');
831 section =
832 offset = 0;
833 }
834
835 Output(_T("%04x:%08x %s"), section, offset, szModule);
836 }
837
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 ))
843 {
844 Output(_T(" %s line %u"),
845 lineInfo.FileName, lineInfo.LineNumber);
846 }
847
848 OutputEndl();
849
850
851#ifdef _M_IX86
852 // on the second iteration also show the local variables and
853 // parameters
854 if ( step == Output_Locals )
855 {
856 // Use SymSetContext to get just the locals/params for this
857 // frame
858 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
859 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
860 SymSetContext(hProcess, &imagehlpStackFrame, 0);
861
862 // Enumerate the locals/parameters
863 m_sfCurrent = &sf;
864 SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
865
866 OutputEndl();
867 }
868#endif // _M_IX86
869 }
870 }
871}
872
873void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
874{
875#ifdef _M_IX86
876 Output(_T("\r\nGlobal variables:\r\n"));
877
878 m_sfCurrent = NULL;
879 SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
880 EnumerateSymbolsCallback, this);
881#endif // _M_IX86
882}
883
884bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp)
885{
886 #define LOAD_SYM_FUNCTION(name) \
887 name = (name ## _t) dllDbgHelp.GetSymbol(#name); \
888 if ( !name ) \
889 { \
890 Output(_T("\r\nFunction ") __XFILE__(#name) \
891 _T("() not found.\r\n")); \
892 return false; \
893 }
894
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);
905
906 #undef LOAD_SYM_FUNCTION
907
908 return true;
909}
910
911bool wxCrashReportImpl::Generate(int flags)
912{
913 if ( m_hFile == INVALID_HANDLE_VALUE )
914 return false;
915
916 if ( !wxGlobalSEInformation )
917 return false;
918
919 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
920 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
921
922 if ( !pExceptionRecord || !pCtx )
923 return false;
924
925 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
926
927 // for everything else we need dbghelp.dll
928 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
929 if ( dllDbgHelp.IsLoaded() )
930 {
931 if ( ResolveSymFunctions(dllDbgHelp) )
932 {
933 SymSetOptions(SYMOPT_DEFERRED_LOADS);
934
935 // Initialize DbgHelp
936 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
937 {
938 OutputStack(pCtx, flags);
939
940 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
941 {
942 OutputGlobals(hModuleCrash);
943 }
944
945 return true;
946 }
947 }
948 else
949 {
950 Output(_T("Please update your dbghelp.dll version, "
951 "at least version 5.1 is needed!\r\n"));
952 }
953 }
954 else
955 {
956 Output(_T("Please install dbghelp.dll available free of charge ")
957 _T("from Microsoft to get more detailed crash information!"));
958 }
959
960 Output(_T("\r\nLatest dbghelp.dll is available at "
961 "http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
962
963 return true;
964}
965
966/* static */
967wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
968{
969 wxString s;
970
971 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
972
973 switch ( dwCode )
974 {
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);
997
998 default:
999 // unknown exception, ask NTDLL for the name
1000 if ( !::FormatMessage
1001 (
1002 FORMAT_MESSAGE_IGNORE_INSERTS |
1003 FORMAT_MESSAGE_FROM_HMODULE,
1004 ::GetModuleHandle(_T("NTDLL.DLL")),
1005 dwCode,
1006 0,
1007 wxStringBuffer(s, 1024),
1008 1024,
1009 0
1010 ) )
1011 {
1012 s = _T("UNKNOWN_EXCEPTION");
1013 }
1014 }
1015
1016 #undef CASE_EXCEPTION
1017
1018 return s;
1019}
1020
1021// ----------------------------------------------------------------------------
1022// wxCrashReport
1023// ----------------------------------------------------------------------------
1024
1025/* static */
1026void wxCrashReport::SetFileName(const wxChar *filename)
1027{
1028 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1029 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1030}
1031
1032/* static */
1033const wxChar *wxCrashReport::GetFileName()
1034{
1035 return gs_reportFilename;
1036}
1037
1038/* static */
1039bool wxCrashReport::Generate(int flags)
1040{
1041 wxCrashReportImpl impl(gs_reportFilename);
1042
1043 return impl.Generate(flags);
1044}
1045
1046// ----------------------------------------------------------------------------
1047// wxApp::OnFatalException() support
1048// ----------------------------------------------------------------------------
1049
1050bool wxHandleFatalExceptions(bool doit)
1051{
1052 // assume this can only be called from the main thread
1053 gs_handleExceptions = doit;
1054
1055 if ( doit )
1056 {
1057 // try to find a place where we can put out report file later
1058 if ( !::GetTempPath
1059 (
1060 WXSIZEOF(gs_reportFilename),
1061 gs_reportFilename
1062 ) )
1063 {
1064 wxLogLastError(_T("GetTempPath"));
1065
1066 // when all else fails...
1067 wxStrcpy(gs_reportFilename, _T("c:\\"));
1068 }
1069
1070 // use PID and date to make the report file name more unique
1071 wxString fname = wxString::Format
1072 (
1073 _T("%s_%s_%lu.rpt"),
1074 wxTheApp ? wxTheApp->GetAppName().c_str()
1075 : _T("wxwindows"),
1076 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1077 ::GetCurrentProcessId()
1078 );
1079
1080 wxStrncat(gs_reportFilename, fname,
1081 WXSIZEOF(gs_reportFilename) - strlen(gs_reportFilename) - 1);
1082 }
1083
1084 return true;
1085}
1086
1087extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1088{
1089 if ( gs_handleExceptions && wxTheApp )
1090 {
1091 // store the pointer to exception info
1092 wxGlobalSEInformation = pExcPtrs;
1093
1094 // give the user a chance to do something special about this
1095 wxTheApp->OnFatalException();
1096
1097 wxGlobalSEInformation = NULL;
1098
1099 // this will execute our handler and terminate the process
1100 return EXCEPTION_EXECUTE_HANDLER;
1101 }
1102
1103 return EXCEPTION_CONTINUE_SEARCH;
1104}
1105
1106#else // !wxUSE_ON_FATAL_EXCEPTION
1107
1108bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1109{
1110 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1111
1112 return false;
1113}
1114
1115#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1116