]> git.saurik.com Git - wxWidgets.git/blob - src/msw/crashrpt.cpp
compilation fix
[wxWidgets.git] / src / msw / crashrpt.cpp
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
43 #include "wx/msw/wrapwin.h"
44 #include <imagehlp.h>
45 #include "wx/msw/private.h"
46
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
49 //
50 // reasons:
51 // - VC6 version of imagehlp.h doesn't define it
52 // - VC7 one does
53 // - testing for compiler version doesn't work as you can install and use
54 // the new SDK headers with VC6
55 //
56 // in any case, the user may override by defining wxUSE_DBGHELP himself
57 #ifndef wxUSE_DBGHELP
58 #ifdef DBHLPAPI
59 #define wxUSE_DBGHELP 1
60 #else
61 #define wxUSE_DBGHELP 0
62 #endif
63 #endif
64
65 #if wxUSE_DBGHELP
66
67 // ----------------------------------------------------------------------------
68 // types of imagehlp.h functions
69 // ----------------------------------------------------------------------------
70
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,
84 PIMAGEHLP_CONTEXT);
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);
89
90 // ----------------------------------------------------------------------------
91 // constant
92 // ----------------------------------------------------------------------------
93
94 // Stolen from CVCONST.H in the DIA 2.0 SDK
95 enum BasicType
96 {
97 BASICTYPE_NOTYPE = 0,
98 BASICTYPE_VOID = 1,
99 BASICTYPE_CHAR = 2,
100 BASICTYPE_WCHAR = 3,
101 BASICTYPE_INT = 6,
102 BASICTYPE_UINT = 7,
103 BASICTYPE_FLOAT = 8,
104 BASICTYPE_BCD = 9,
105 BASICTYPE_BOOL = 10,
106 BASICTYPE_LONG = 13,
107 BASICTYPE_ULONG = 14,
108 BASICTYPE_CURRENCY = 25,
109 BASICTYPE_DATE = 26,
110 BASICTYPE_VARIANT = 27,
111 BASICTYPE_COMPLEX = 28,
112 BASICTYPE_BIT = 29,
113 BASICTYPE_BSTR = 30,
114 BASICTYPE_HRESULT = 31
115 };
116
117 // Same as above
118 enum SymbolTag
119 {
120 SYMBOL_TAG_NULL,
121 SYMBOL_TAG_FUNCTION = 5,
122 SYMBOL_TAG_DATA = 7,
123 SYMBOL_TAG_PUBLIC = 10, // appears in .DBGs
124 SYMBOL_TAG_UDT,
125 SYMBOL_TAG_ENUM,
126 SYMBOL_TAG_FUNCTION_TYPE,
127 SYMBOL_TAG_POINTER_TYPE,
128 SYMBOL_TAG_ARRAY_TYPE,
129 SYMBOL_TAG_BASE_TYPE,
130 SYMBOL_TAG_TYPEDEF,
131 SYMBOL_TAG_BASECLASS
132 };
133
134 #endif // wxUSE_DBGHELP
135
136 // ----------------------------------------------------------------------------
137 // classes
138 // ----------------------------------------------------------------------------
139
140 // the real crash report generator
141 class wxCrashReportImpl
142 {
143 public:
144 wxCrashReportImpl(const wxChar *filename);
145
146 bool Generate(int flags);
147
148 ~wxCrashReportImpl()
149 {
150 if ( m_hFile != INVALID_HANDLE_VALUE )
151 {
152 ::CloseHandle(m_hFile);
153 }
154 }
155
156 private:
157 // formatted output to m_hFile
158 void Output(const wxChar *format, ...);
159
160 // output end of line
161 void OutputEndl() { Output(_T("\r\n")); }
162
163 #if wxUSE_DBGHELP
164 // translate exception code to its symbolic name
165 static wxString GetExceptionString(DWORD dwCode);
166
167 // return the type from "type index"
168 static BasicType GetBasicType(DWORD64 modBase, DWORD typeIndex);
169
170 // return the name for the type index
171 static wxString GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex);
172
173 // return the string representation of the variable value
174 static wxString FormatSimpleValue(BasicType bt,
175 DWORD64 length,
176 PVOID pAddress);
177
178 // return string representation of a struct field (which may itself be a
179 // struct, of course)
180 static wxString FormatField(DWORD64 modBase,
181 DWORD dwTypeIndex,
182 void *pVariable,
183 unsigned level);
184
185 // show the name and value of the given symbol
186 static wxString FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf);
187
188 // show value described by SYMBOL_INFO located at pVariable
189 static wxString FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable);
190
191 // show value of possibly complex (user-defined) type
192 static wxString FormatUDT(DWORD64 modBase,
193 DWORD dwTypeIndex,
194 void *pVariable,
195 unsigned level = 0);
196
197 // outputs information about the given symbol
198 void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf);
199
200 // load all the functions we need from dbghelp.dll, return true if all ok
201 bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp);
202
203 // map address to module (and also section:offset), retunr true if ok
204 static bool GetLogicalAddress(PVOID addr,
205 PTSTR szModule,
206 DWORD len,
207 DWORD& section,
208 DWORD& offset);
209
210 // callback used with SymEnumSymbols() to process all variables
211 static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
212 ULONG SymbolSize,
213 PVOID UserContext);
214
215
216 // show the general information about exception which should be always
217 // available
218 //
219 // returns the module of the handle where the crash occured
220 HANDLE OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx);
221
222 // output the call stack and local variables values
223 void OutputStack(const CONTEXT *pCtx, int flags);
224
225 // output the global variables values
226 void OutputGlobals(HANDLE hModuleCrash);
227
228
229 // the current stack frame (may be NULL)
230 STACKFRAME *m_sfCurrent;
231
232
233 // dynamically loaded dbghelp.dll functions
234 #define DECLARE_SYM_FUNCTION(func) static func ## _t func
235
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
247
248 // the handle of the report file
249 HANDLE m_hFile;
250 };
251
252 // ----------------------------------------------------------------------------
253 // globals
254 // ----------------------------------------------------------------------------
255
256 // global pointer to exception information, only valid inside OnFatalException
257 extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL;
258
259
260 // flag telling us whether the application wants to handle exceptions at all
261 static bool gs_handleExceptions = false;
262
263 // the file name where the report about exception is written
264 static wxChar gs_reportFilename[MAX_PATH];
265
266 // ============================================================================
267 // implementation
268 // ============================================================================
269
270 #if wxUSE_DBGHELP
271
272 #define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0
273
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);
284
285 #undef DEFINE_SYM_FUNCTION
286
287 #endif // wxUSE_DBGHELP
288
289 // ----------------------------------------------------------------------------
290 // wxCrashReportImpl
291 // ----------------------------------------------------------------------------
292
293 wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
294 {
295 #if wxUSE_DBGHELP
296 m_sfCurrent = NULL;
297 #endif // wxUSE_DBGHELP
298
299 m_hFile = ::CreateFile
300 (
301 filename,
302 GENERIC_WRITE,
303 0, // no sharing
304 NULL, // default security
305 CREATE_ALWAYS,
306 FILE_FLAG_WRITE_THROUGH,
307 NULL // no template file
308 );
309 }
310
311 void wxCrashReportImpl::Output(const wxChar *format, ...)
312 {
313 va_list argptr;
314 va_start(argptr, format);
315
316 DWORD cbWritten;
317
318 wxString s = wxString::FormatV(format, argptr);
319 ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0);
320
321 va_end(argptr);
322 }
323
324 #if wxUSE_DBGHELP
325
326 bool
327 wxCrashReportImpl::GetLogicalAddress(PVOID addr,
328 PTSTR szModule,
329 DWORD len,
330 DWORD& section,
331 DWORD& offset)
332 {
333 MEMORY_BASIC_INFORMATION mbi;
334
335 if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) )
336 return false;
337
338 DWORD hMod = (DWORD)mbi.AllocationBase;
339
340 if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) )
341 return false;
342
343 // Point to the DOS header in memory
344 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
345
346 // From the DOS header, find the NT (PE) header
347 PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
348
349 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
350
351 DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
352
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++ )
357 {
358 DWORD sectionStart = pSection->VirtualAddress;
359 DWORD sectionEnd = sectionStart
360 + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
361
362 // Is the address in this section?
363 if ( (rva >= sectionStart) && (rva <= sectionEnd) )
364 {
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.
368 section = i + 1;
369 offset = rva - sectionStart;
370
371 return true;
372 }
373 }
374
375 // failed to map to logical address...
376 return false;
377 }
378
379 /* static */ BasicType
380 wxCrashReportImpl::GetBasicType(DWORD64 modBase, DWORD typeIndex)
381 {
382 const HANDLE hProcess = GetCurrentProcess();
383
384 // try the index we have
385 BasicType bt;
386 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &bt) )
387 {
388 return bt;
389 }
390
391 // if failed, try to get the "real" typeid first
392 DWORD typeId;
393 if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId)
394 &&
395 (typeId != typeIndex &&
396 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_BASETYPE, &bt)) )
397 {
398 return bt;
399 }
400
401 return BASICTYPE_NOTYPE;
402 }
403
404 /* static */ wxString
405 wxCrashReportImpl::FormatSimpleValue(BasicType bt,
406 DWORD64 length,
407 PVOID pAddress)
408 {
409 wxString s;
410 s.reserve(256);
411
412 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
413 if ( length == 1 )
414 {
415 s.Printf(_T("%#04x"), *(PBYTE)pAddress);
416 }
417 else if ( length == 2 )
418 {
419 s.Printf(_T("%#06x"), *(PWORD)pAddress);
420 }
421 else if ( length == 4 )
422 {
423 bool handled = false;
424
425 if ( bt == BASICTYPE_FLOAT )
426 {
427 s.Printf(_T("%f"), *(PFLOAT)pAddress);
428
429 handled = true;
430 }
431 else if ( bt == BASICTYPE_CHAR )
432 {
433 static const size_t NUM_CHARS = 32;
434
435 const char * const pc = *(PSTR *)pAddress;
436 if ( !::IsBadStringPtrA(pc, NUM_CHARS) )
437 {
438 s << _T('"') << wxString(pc, wxConvLibc, NUM_CHARS) << _T('"');
439
440 handled = true;
441 }
442 }
443
444 if ( !handled )
445 {
446 // treat just as an opaque DWORD
447 s.Printf(_T("%#x"), *(PDWORD)pAddress);
448 }
449 }
450 else if ( length == 8 )
451 {
452 if ( bt == BASICTYPE_FLOAT )
453 {
454 s.Printf(_T("%lf"), *(double *)pAddress);
455 }
456 else // opaque 64 bit value
457 {
458 s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress);
459 }
460 }
461
462 return s;
463 }
464
465 /* static */
466 wxString wxCrashReportImpl::GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex)
467 {
468 wxString s;
469
470 WCHAR *pwszTypeName;
471 if ( SymGetTypeInfo
472 (
473 GetCurrentProcess(),
474 modBase,
475 dwTypeIndex,
476 TI_GET_SYMNAME,
477 &pwszTypeName
478 ) )
479 {
480 s = wxConvCurrent->cWC2WX(pwszTypeName);
481
482 ::LocalFree(pwszTypeName);
483 }
484
485 return s;
486 }
487
488 // this is called for the struct members/base classes
489 wxString
490 wxCrashReportImpl::FormatField(DWORD64 modBase,
491 DWORD dwTypeIndex,
492 void *pVariable,
493 unsigned level)
494 {
495 wxString s;
496
497 const HANDLE hProcess = GetCurrentProcess();
498
499 DWORD dwTag = 0;
500 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag);
501
502 switch ( dwTag )
503 {
504 case SYMBOL_TAG_UDT:
505 case SYMBOL_TAG_BASECLASS:
506 s = FormatUDT(modBase, dwTypeIndex, pVariable, level);
507 break;
508
509 case SYMBOL_TAG_FUNCTION:
510 // don't show
511 break;
512
513 default:
514 // try to treat all the rest as data even though it's not clear if
515 // it's really a good idea...
516
517 // Get the offset of the child member, relative to its parent
518 DWORD dwMemberOffset = 0;
519 SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
520 TI_GET_OFFSET, &dwMemberOffset);
521
522 // Get the real "TypeId" of the child. We need this for the
523 // SymGetTypeInfo(TI_GET_LENGTH) call below.
524 DWORD typeId;
525 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
526 TI_GET_TYPEID, &typeId) )
527 {
528 typeId = dwTypeIndex;
529 }
530
531 // Get the size of the child member
532 ULONG64 size;
533 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size);
534
535 // Calculate the address of the member
536 DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset;
537
538 BasicType basicType = GetBasicType(modBase, dwTypeIndex);
539
540 s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset);
541 break;
542
543 }
544
545 if ( s.empty() )
546 {
547 // don't show if no value -- what for?
548 return s;
549 }
550
551 return wxString(_T('\t'), level + 1) +
552 GetSymbolName(modBase, dwTypeIndex) +
553 _T(" = ") + s + _T("\r\n");
554 }
555
556 // If it's a user defined type (UDT), recurse through its members until we're
557 // at fundamental types.
558 wxString
559 wxCrashReportImpl::FormatUDT(DWORD64 modBase,
560 DWORD dwTypeIndex,
561 void *pVariable,
562 unsigned level)
563 {
564 wxString s;
565 s.reserve(512);
566 s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n");
567
568 const HANDLE hProcess = GetCurrentProcess();
569
570 // Determine how many children this type has.
571 DWORD dwChildrenCount = 0;
572 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
573 &dwChildrenCount);
574
575 // Prepare to get an array of "TypeIds", representing each of the children.
576 TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
577 malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
578 (dwChildrenCount - 1)*sizeof(ULONG));
579 if ( !children )
580 return s;
581
582 children->Count = dwChildrenCount;
583 children->Start = 0;
584
585 // Get the array of TypeIds, one for each child type
586 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
587 children) )
588 {
589 return s;
590 }
591
592 // Iterate through all children
593 for ( unsigned i = 0; i < dwChildrenCount; i++ )
594 {
595 s += FormatField(modBase, children->ChildId[i], pVariable, level + 1);
596 }
597
598 free(children);
599
600 s << wxString(_T('\t'), level + 1) << _T('}');
601
602 return s;
603 }
604
605 // return the string containing the symbol of the given symbol
606 /* static */ wxString
607 wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable)
608 {
609 DWORD dwTag = 0;
610 SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
611 TI_GET_SYMTAG, &dwTag);
612
613 wxString s;
614 switch ( dwTag )
615 {
616 case SYMBOL_TAG_FUNCTION:
617 break;
618
619 case SYMBOL_TAG_UDT:
620 case SYMBOL_TAG_BASECLASS:
621 // show UDT recursively
622 s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable);
623 break;
624
625 default:
626 // variable of simple type (but could be array which we don't
627 // handle correctly yet...), format it using its type and size
628 BasicType bt = GetBasicType(pSym->ModBase, pSym->TypeIndex);
629
630 s = FormatSimpleValue(bt, pSym->Size, pVariable);
631 break;
632
633 }
634
635 return s;
636 }
637
638 // display contents and type of the given variable
639 /* static */ wxString
640 wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf)
641 {
642 wxString s;
643
644 if ( pSym->Tag == SYMBOL_TAG_FUNCTION )
645 {
646 // If it's a function, don't do anything.
647 return s;
648 }
649
650 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
651 {
652 // Don't try to report register variable
653 return s;
654 }
655
656 s.reserve(512);
657
658 // Indicate if the variable is a local or parameter
659 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
660 s += _T("\t[param] ");
661 else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
662 s += _T("\t[local] ");
663
664 // Will point to the variable's data in memory
665 DWORD_PTR pVariable = 0;
666
667 if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf )
668 {
669 pVariable = sf->AddrFrame.Offset;
670 pVariable += (DWORD_PTR)pSym->Address;
671 }
672 else // It must be a global variable
673 {
674 pVariable = (DWORD_PTR)pSym->Address;
675 }
676
677 s << wxString(pSym->Name, wxConvLibc)
678 << _T(" = ")
679 << FormatAnyValue(pSym, (PVOID)pVariable);
680
681 return s;
682 }
683
684 void
685 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf)
686 {
687 wxString s = FormatSymbol(pSymInfo, sf);
688 if ( !s.empty() )
689 {
690 Output(_T("%s\r\n"), s.c_str());
691 }
692 //else: not an interesting symbol
693 }
694
695 // callback for SymEnumSymbols()
696 /* static */
697 BOOL CALLBACK
698 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
699 ULONG WXUNUSED(SymbolSize),
700 PVOID UserContext)
701 {
702 wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
703
704 __try
705 {
706 self->OutputSymbol(pSymInfo, self->m_sfCurrent);
707 }
708 __except ( EXCEPTION_EXECUTE_HANDLER )
709 {
710 self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
711 }
712
713 // continue with enumeration
714 return true;
715 }
716
717 HANDLE
718 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
719 CONTEXT *pCtx)
720 {
721 // First print information about the type of fault
722 const DWORD dwCode = pExceptionRecord->ExceptionCode;
723 Output(_T("Exception code: %s (%#10x)\r\n"),
724 GetExceptionString(dwCode).c_str(), dwCode);
725
726 // Now print information about where the fault occured
727 TCHAR szFaultingModule[MAX_PATH];
728 DWORD section,
729 offset;
730 void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
731 if ( !GetLogicalAddress(pExceptionAddress,
732 szFaultingModule,
733 WXSIZEOF(szFaultingModule),
734 section, offset) )
735 {
736 section =
737 offset = 0;
738
739 wxStrcpy(szFaultingModule, _T("<< unknown >>"));
740 }
741
742 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
743 pExceptionAddress, section, offset, szFaultingModule);
744
745 #ifdef _M_IX86
746 // Show the registers
747 Output( _T("\r\nRegisters:\r\n") );
748
749 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
750 pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi);
751
752 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
753 pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
754 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
755 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
756 Output(_T("Flags: %08x\r\n"), pCtx->EFlags );
757 #endif // _M_IX86
758
759 return ::GetModuleHandle(szFaultingModule);
760 }
761
762 void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
763 {
764 enum
765 {
766 Output_Stack,
767 Output_Locals,
768 Output_Max
769 #ifndef _M_IX86
770 // can't show locals under other architectures
771 = Output_Locals
772 #endif
773 };
774
775 for ( int step = 0; step < Output_Max; step++ )
776 {
777 // don't do things we're not asked for
778 if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) ||
779 (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) )
780 {
781 continue;
782 }
783
784 // the context is going to be modified below so make a copy
785 CONTEXT ctx = *pCtx;
786
787 Output(_T("\r\n%s\r\n")
788 _T(" # Address Frame Function SourceFile\r\n"),
789 step == Output_Stack ? _T("Call stack") : _T("Local variables"));
790
791 DWORD dwMachineType = 0;
792
793 STACKFRAME sf;
794 wxZeroMemory(sf);
795
796 #ifdef _M_IX86
797 // Initialize the STACKFRAME structure for the first call. This is
798 // only necessary for Intel CPUs, and isn't mentioned in the
799 // documentation.
800 sf.AddrPC.Offset = ctx.Eip;
801 sf.AddrPC.Mode = AddrModeFlat;
802 sf.AddrStack.Offset = ctx.Esp;
803 sf.AddrStack.Mode = AddrModeFlat;
804 sf.AddrFrame.Offset = ctx.Ebp;
805 sf.AddrFrame.Mode = AddrModeFlat;
806
807 dwMachineType = IMAGE_FILE_MACHINE_I386;
808 #endif // _M_IX86
809
810 const HANDLE hProcess = GetCurrentProcess();
811 const HANDLE hThread = GetCurrentThread();
812
813 // first show just the call stack
814 int frame = 0;
815 for ( ;; )
816 {
817 // Get the next stack frame
818 if ( !StackWalk(dwMachineType,
819 hProcess,
820 hThread,
821 &sf,
822 &ctx,
823 0,
824 SymFunctionTableAccess,
825 SymGetModuleBase,
826 0) )
827 {
828 break;
829 }
830
831 // Basic sanity check to make sure the frame is OK.
832 if ( !sf.AddrFrame.Offset )
833 break;
834
835 Output(_T("%2d %08x %08x "),
836 frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
837
838 // Get the name of the function for this stack frame entry
839 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
840 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
841 pSymbol->SizeOfStruct = sizeof(symbolBuffer);
842 pSymbol->MaxNameLen = 1024;
843
844 // Displacement of the input address, relative to the start of the
845 // symbol
846 DWORD64 symDisplacement = 0;
847
848 if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
849 &symDisplacement,pSymbol) )
850 {
851 Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
852 pSymbol->Name, symDisplacement);
853 }
854 else // No symbol found. Print out the logical address instead.
855 {
856 TCHAR szModule[MAX_PATH];
857 DWORD section,
858 offset;
859
860 if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
861 szModule, sizeof(szModule),
862 section, offset) )
863 {
864 szModule[0] = _T('\0');
865 section =
866 offset = 0;
867 }
868
869 Output(_T("%04x:%08x %s"), section, offset, szModule);
870 }
871
872 // Get the source line for this stack frame entry
873 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
874 DWORD dwLineDisplacement;
875 if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset,
876 &dwLineDisplacement, &lineInfo ))
877 {
878 Output(_T(" %s line %u"),
879 lineInfo.FileName, lineInfo.LineNumber);
880 }
881
882 OutputEndl();
883
884
885 #ifdef _M_IX86
886 // on the second iteration also show the local variables and
887 // parameters
888 if ( step == Output_Locals )
889 {
890 // Use SymSetContext to get just the locals/params for this
891 // frame
892 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
893 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
894 SymSetContext(hProcess, &imagehlpStackFrame, 0);
895
896 // Enumerate the locals/parameters
897 m_sfCurrent = &sf;
898 SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
899
900 OutputEndl();
901 }
902 #endif // _M_IX86
903 }
904 }
905 }
906
907 void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
908 {
909 #ifdef _M_IX86
910 Output(_T("\r\nGlobal variables:\r\n"));
911
912 m_sfCurrent = NULL;
913 SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
914 EnumerateSymbolsCallback, this);
915 #endif // _M_IX86
916 }
917
918 bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp)
919 {
920 #define LOAD_SYM_FUNCTION(name) \
921 name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \
922 if ( !name ) \
923 { \
924 Output(_T("\r\nFunction ") _T(#name) \
925 _T("() not found.\r\n")); \
926 return false; \
927 }
928
929 LOAD_SYM_FUNCTION(SymSetOptions);
930 LOAD_SYM_FUNCTION(SymInitialize);
931 LOAD_SYM_FUNCTION(StackWalk);
932 LOAD_SYM_FUNCTION(SymFromAddr);
933 LOAD_SYM_FUNCTION(SymFunctionTableAccess);
934 LOAD_SYM_FUNCTION(SymGetModuleBase);
935 LOAD_SYM_FUNCTION(SymGetLineFromAddr);
936 LOAD_SYM_FUNCTION(SymSetContext);
937 LOAD_SYM_FUNCTION(SymEnumSymbols);
938 LOAD_SYM_FUNCTION(SymGetTypeInfo);
939
940 #undef LOAD_SYM_FUNCTION
941
942 return true;
943 }
944
945 /* static */
946 wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
947 {
948 wxString s;
949
950 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
951
952 switch ( dwCode )
953 {
954 CASE_EXCEPTION(ACCESS_VIOLATION);
955 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
956 CASE_EXCEPTION(BREAKPOINT);
957 CASE_EXCEPTION(SINGLE_STEP);
958 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
959 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
960 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
961 CASE_EXCEPTION(FLT_INEXACT_RESULT);
962 CASE_EXCEPTION(FLT_INVALID_OPERATION);
963 CASE_EXCEPTION(FLT_OVERFLOW);
964 CASE_EXCEPTION(FLT_STACK_CHECK);
965 CASE_EXCEPTION(FLT_UNDERFLOW);
966 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
967 CASE_EXCEPTION(INT_OVERFLOW);
968 CASE_EXCEPTION(PRIV_INSTRUCTION);
969 CASE_EXCEPTION(IN_PAGE_ERROR);
970 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
971 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
972 CASE_EXCEPTION(STACK_OVERFLOW);
973 CASE_EXCEPTION(INVALID_DISPOSITION);
974 CASE_EXCEPTION(GUARD_PAGE);
975 CASE_EXCEPTION(INVALID_HANDLE);
976
977 default:
978 // unknown exception, ask NTDLL for the name
979 if ( !::FormatMessage
980 (
981 FORMAT_MESSAGE_IGNORE_INSERTS |
982 FORMAT_MESSAGE_FROM_HMODULE,
983 ::GetModuleHandle(_T("NTDLL.DLL")),
984 dwCode,
985 0,
986 wxStringBuffer(s, 1024),
987 1024,
988 0
989 ) )
990 {
991 s = _T("UNKNOWN_EXCEPTION");
992 }
993 }
994
995 #undef CASE_EXCEPTION
996
997 return s;
998 }
999
1000 #endif // wxUSE_DBGHELP
1001
1002 // Remove warning
1003 #if wxUSE_DBGHELP
1004 #define _WXUNUSED(x) x
1005 #else
1006 #define _WXUNUSED WXUNUSED
1007 #endif
1008
1009 bool wxCrashReportImpl::Generate(int _WXUNUSED(flags))
1010 {
1011 if ( m_hFile == INVALID_HANDLE_VALUE )
1012 return false;
1013
1014 #if wxUSE_DBGHELP
1015 if ( !wxGlobalSEInformation )
1016 return false;
1017
1018 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
1019 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
1020
1021 if ( !pExceptionRecord || !pCtx )
1022 return false;
1023
1024 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
1025
1026 // for everything else we need dbghelp.dll
1027 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
1028 if ( dllDbgHelp.IsLoaded() )
1029 {
1030 if ( ResolveSymFunctions(dllDbgHelp) )
1031 {
1032 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
1033
1034 // Initialize DbgHelp
1035 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
1036 {
1037 OutputStack(pCtx, flags);
1038
1039 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
1040 {
1041 OutputGlobals(hModuleCrash);
1042 }
1043
1044 return true;
1045 }
1046 }
1047 else
1048 {
1049 Output(_T("Please update your dbghelp.dll version, ")
1050 _T("at least version 5.1 is needed!\r\n"));
1051 }
1052 }
1053 else
1054 {
1055 Output(_T("Please install dbghelp.dll available free of charge ")
1056 _T("from Microsoft to get more detailed crash information!"));
1057 }
1058
1059 Output(_T("\r\nLatest dbghelp.dll is available at ")
1060 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1061
1062 #else // !wxUSE_DBGHELP
1063 Output(_T("Support for crash report generation was not included ")
1064 _T("in this wxWindows version."));
1065 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1066
1067 return true;
1068 }
1069
1070 // ----------------------------------------------------------------------------
1071 // wxCrashReport
1072 // ----------------------------------------------------------------------------
1073
1074 /* static */
1075 void wxCrashReport::SetFileName(const wxChar *filename)
1076 {
1077 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1078 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1079 }
1080
1081 /* static */
1082 const wxChar *wxCrashReport::GetFileName()
1083 {
1084 return gs_reportFilename;
1085 }
1086
1087 /* static */
1088 bool wxCrashReport::Generate(int flags)
1089 {
1090 wxCrashReportImpl impl(gs_reportFilename);
1091
1092 return impl.Generate(flags);
1093 }
1094
1095 // ----------------------------------------------------------------------------
1096 // wxApp::OnFatalException() support
1097 // ----------------------------------------------------------------------------
1098
1099 bool wxHandleFatalExceptions(bool doit)
1100 {
1101 // assume this can only be called from the main thread
1102 gs_handleExceptions = doit;
1103
1104 if ( doit )
1105 {
1106 // try to find a place where we can put out report file later
1107 if ( !::GetTempPath
1108 (
1109 WXSIZEOF(gs_reportFilename),
1110 gs_reportFilename
1111 ) )
1112 {
1113 wxLogLastError(_T("GetTempPath"));
1114
1115 // when all else fails...
1116 wxStrcpy(gs_reportFilename, _T("c:\\"));
1117 }
1118
1119 // use PID and date to make the report file name more unique
1120 wxString fname = wxString::Format
1121 (
1122 _T("%s_%s_%lu.rpt"),
1123 wxTheApp ? wxTheApp->GetAppName().c_str()
1124 : _T("wxwindows"),
1125 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1126 ::GetCurrentProcessId()
1127 );
1128
1129 wxStrncat(gs_reportFilename, fname,
1130 WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
1131 }
1132
1133 return true;
1134 }
1135
1136 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1137 {
1138 if ( gs_handleExceptions && wxTheApp )
1139 {
1140 // store the pointer to exception info
1141 wxGlobalSEInformation = pExcPtrs;
1142
1143 // give the user a chance to do something special about this
1144 __try
1145 {
1146 wxTheApp->OnFatalException();
1147 }
1148 __except ( EXCEPTION_EXECUTE_HANDLER )
1149 {
1150 // nothing to do here, just ignore the exception inside the
1151 // exception handler
1152 ;
1153 }
1154
1155 wxGlobalSEInformation = NULL;
1156
1157 // this will execute our handler and terminate the process
1158 return EXCEPTION_EXECUTE_HANDLER;
1159 }
1160
1161 return EXCEPTION_CONTINUE_SEARCH;
1162 }
1163
1164 #else // !wxUSE_ON_FATAL_EXCEPTION
1165
1166 bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1167 {
1168 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1169
1170 return false;
1171 }
1172
1173 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1174