]> git.saurik.com Git - wxWidgets.git/blob - src/msw/crashrpt.cpp
Load Smartphone-specific dummy menu
[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 wxChar * const pc = *(PSTR *)pAddress;
436 if ( !::IsBadStringPtr(pc, NUM_CHARS) )
437 {
438 s << _T('"') << wxString(pc, 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 << pSym->Name << _T(" = ") << FormatAnyValue(pSym, (PVOID)pVariable);
678
679 return s;
680 }
681
682 void
683 wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf)
684 {
685 wxString s = FormatSymbol(pSymInfo, sf);
686 if ( !s.empty() )
687 {
688 Output(_T("%s\r\n"), s.c_str());
689 }
690 //else: not an interesting symbol
691 }
692
693 // callback for SymEnumSymbols()
694 /* static */
695 BOOL CALLBACK
696 wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
697 ULONG WXUNUSED(SymbolSize),
698 PVOID UserContext)
699 {
700 wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
701
702 __try
703 {
704 self->OutputSymbol(pSymInfo, self->m_sfCurrent);
705 }
706 __except ( EXCEPTION_EXECUTE_HANDLER )
707 {
708 self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
709 }
710
711 // continue with enumeration
712 return true;
713 }
714
715 HANDLE
716 wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
717 CONTEXT *pCtx)
718 {
719 // First print information about the type of fault
720 const DWORD dwCode = pExceptionRecord->ExceptionCode;
721 Output(_T("Exception code: %s (%#10x)\r\n"),
722 GetExceptionString(dwCode).c_str(), dwCode);
723
724 // Now print information about where the fault occured
725 TCHAR szFaultingModule[MAX_PATH];
726 DWORD section,
727 offset;
728 void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
729 if ( !GetLogicalAddress(pExceptionAddress,
730 szFaultingModule,
731 WXSIZEOF(szFaultingModule),
732 section, offset) )
733 {
734 section =
735 offset = 0;
736
737 wxStrcpy(szFaultingModule, _T("<< unknown >>"));
738 }
739
740 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
741 pExceptionAddress, section, offset, szFaultingModule);
742
743 #ifdef _M_IX86
744 // Show the registers
745 Output( _T("\r\nRegisters:\r\n") );
746
747 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
748 pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi);
749
750 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
751 pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
752 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
753 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
754 Output(_T("Flags: %08x\r\n"), pCtx->EFlags );
755 #endif // _M_IX86
756
757 return ::GetModuleHandle(szFaultingModule);
758 }
759
760 void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
761 {
762 enum
763 {
764 Output_Stack,
765 Output_Locals,
766 Output_Max
767 #ifndef _M_IX86
768 // can't show locals under other architectures
769 = Output_Locals
770 #endif
771 };
772
773 for ( int step = 0; step < Output_Max; step++ )
774 {
775 // don't do things we're not asked for
776 if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) ||
777 (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) )
778 {
779 continue;
780 }
781
782 // the context is going to be modified below so make a copy
783 CONTEXT ctx = *pCtx;
784
785 Output(_T("\r\n%s\r\n")
786 _T(" # Address Frame Function SourceFile\r\n"),
787 step == Output_Stack ? _T("Call stack") : _T("Local variables"));
788
789 DWORD dwMachineType = 0;
790
791 STACKFRAME sf;
792 wxZeroMemory(sf);
793
794 #ifdef _M_IX86
795 // Initialize the STACKFRAME structure for the first call. This is
796 // only necessary for Intel CPUs, and isn't mentioned in the
797 // documentation.
798 sf.AddrPC.Offset = ctx.Eip;
799 sf.AddrPC.Mode = AddrModeFlat;
800 sf.AddrStack.Offset = ctx.Esp;
801 sf.AddrStack.Mode = AddrModeFlat;
802 sf.AddrFrame.Offset = ctx.Ebp;
803 sf.AddrFrame.Mode = AddrModeFlat;
804
805 dwMachineType = IMAGE_FILE_MACHINE_I386;
806 #endif // _M_IX86
807
808 const HANDLE hProcess = GetCurrentProcess();
809 const HANDLE hThread = GetCurrentThread();
810
811 // first show just the call stack
812 int frame = 0;
813 for ( ;; )
814 {
815 // Get the next stack frame
816 if ( !StackWalk(dwMachineType,
817 hProcess,
818 hThread,
819 &sf,
820 &ctx,
821 0,
822 SymFunctionTableAccess,
823 SymGetModuleBase,
824 0) )
825 {
826 break;
827 }
828
829 // Basic sanity check to make sure the frame is OK.
830 if ( !sf.AddrFrame.Offset )
831 break;
832
833 Output(_T("%2d %08x %08x "),
834 frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
835
836 // Get the name of the function for this stack frame entry
837 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
838 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
839 pSymbol->SizeOfStruct = sizeof(symbolBuffer);
840 pSymbol->MaxNameLen = 1024;
841
842 // Displacement of the input address, relative to the start of the
843 // symbol
844 DWORD64 symDisplacement = 0;
845
846 if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
847 &symDisplacement,pSymbol) )
848 {
849 Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
850 pSymbol->Name, symDisplacement);
851 }
852 else // No symbol found. Print out the logical address instead.
853 {
854 TCHAR szModule[MAX_PATH];
855 DWORD section,
856 offset;
857
858 if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
859 szModule, sizeof(szModule),
860 section, offset) )
861 {
862 szModule[0] = _T('\0');
863 section =
864 offset = 0;
865 }
866
867 Output(_T("%04x:%08x %s"), section, offset, szModule);
868 }
869
870 // Get the source line for this stack frame entry
871 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
872 DWORD dwLineDisplacement;
873 if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset,
874 &dwLineDisplacement, &lineInfo ))
875 {
876 Output(_T(" %s line %u"),
877 lineInfo.FileName, lineInfo.LineNumber);
878 }
879
880 OutputEndl();
881
882
883 #ifdef _M_IX86
884 // on the second iteration also show the local variables and
885 // parameters
886 if ( step == Output_Locals )
887 {
888 // Use SymSetContext to get just the locals/params for this
889 // frame
890 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
891 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
892 SymSetContext(hProcess, &imagehlpStackFrame, 0);
893
894 // Enumerate the locals/parameters
895 m_sfCurrent = &sf;
896 SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
897
898 OutputEndl();
899 }
900 #endif // _M_IX86
901 }
902 }
903 }
904
905 void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
906 {
907 #ifdef _M_IX86
908 Output(_T("\r\nGlobal variables:\r\n"));
909
910 m_sfCurrent = NULL;
911 SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
912 EnumerateSymbolsCallback, this);
913 #endif // _M_IX86
914 }
915
916 bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp)
917 {
918 #define LOAD_SYM_FUNCTION(name) \
919 name = (name ## _t) dllDbgHelp.GetSymbol(#name); \
920 if ( !name ) \
921 { \
922 Output(_T("\r\nFunction ") __XFILE__(#name) \
923 _T("() not found.\r\n")); \
924 return false; \
925 }
926
927 LOAD_SYM_FUNCTION(SymSetOptions);
928 LOAD_SYM_FUNCTION(SymInitialize);
929 LOAD_SYM_FUNCTION(StackWalk);
930 LOAD_SYM_FUNCTION(SymFromAddr);
931 LOAD_SYM_FUNCTION(SymFunctionTableAccess);
932 LOAD_SYM_FUNCTION(SymGetModuleBase);
933 LOAD_SYM_FUNCTION(SymGetLineFromAddr);
934 LOAD_SYM_FUNCTION(SymSetContext);
935 LOAD_SYM_FUNCTION(SymEnumSymbols);
936 LOAD_SYM_FUNCTION(SymGetTypeInfo);
937
938 #undef LOAD_SYM_FUNCTION
939
940 return true;
941 }
942
943 /* static */
944 wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
945 {
946 wxString s;
947
948 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
949
950 switch ( dwCode )
951 {
952 CASE_EXCEPTION(ACCESS_VIOLATION);
953 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
954 CASE_EXCEPTION(BREAKPOINT);
955 CASE_EXCEPTION(SINGLE_STEP);
956 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
957 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
958 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
959 CASE_EXCEPTION(FLT_INEXACT_RESULT);
960 CASE_EXCEPTION(FLT_INVALID_OPERATION);
961 CASE_EXCEPTION(FLT_OVERFLOW);
962 CASE_EXCEPTION(FLT_STACK_CHECK);
963 CASE_EXCEPTION(FLT_UNDERFLOW);
964 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
965 CASE_EXCEPTION(INT_OVERFLOW);
966 CASE_EXCEPTION(PRIV_INSTRUCTION);
967 CASE_EXCEPTION(IN_PAGE_ERROR);
968 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
969 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
970 CASE_EXCEPTION(STACK_OVERFLOW);
971 CASE_EXCEPTION(INVALID_DISPOSITION);
972 CASE_EXCEPTION(GUARD_PAGE);
973 CASE_EXCEPTION(INVALID_HANDLE);
974
975 default:
976 // unknown exception, ask NTDLL for the name
977 if ( !::FormatMessage
978 (
979 FORMAT_MESSAGE_IGNORE_INSERTS |
980 FORMAT_MESSAGE_FROM_HMODULE,
981 ::GetModuleHandle(_T("NTDLL.DLL")),
982 dwCode,
983 0,
984 wxStringBuffer(s, 1024),
985 1024,
986 0
987 ) )
988 {
989 s = _T("UNKNOWN_EXCEPTION");
990 }
991 }
992
993 #undef CASE_EXCEPTION
994
995 return s;
996 }
997
998 #endif // wxUSE_DBGHELP
999
1000 bool wxCrashReportImpl::Generate(int flags)
1001 {
1002 if ( m_hFile == INVALID_HANDLE_VALUE )
1003 return false;
1004
1005 #if wxUSE_DBGHELP
1006 if ( !wxGlobalSEInformation )
1007 return false;
1008
1009 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
1010 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
1011
1012 if ( !pExceptionRecord || !pCtx )
1013 return false;
1014
1015 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
1016
1017 // for everything else we need dbghelp.dll
1018 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
1019 if ( dllDbgHelp.IsLoaded() )
1020 {
1021 if ( ResolveSymFunctions(dllDbgHelp) )
1022 {
1023 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
1024
1025 // Initialize DbgHelp
1026 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
1027 {
1028 OutputStack(pCtx, flags);
1029
1030 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
1031 {
1032 OutputGlobals(hModuleCrash);
1033 }
1034
1035 return true;
1036 }
1037 }
1038 else
1039 {
1040 Output(_T("Please update your dbghelp.dll version, "
1041 "at least version 5.1 is needed!\r\n"));
1042 }
1043 }
1044 else
1045 {
1046 Output(_T("Please install dbghelp.dll available free of charge ")
1047 _T("from Microsoft to get more detailed crash information!"));
1048 }
1049
1050 Output(_T("\r\nLatest dbghelp.dll is available at "
1051 "http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1052
1053 #else // !wxUSE_DBGHELP
1054 Output(_T("Support for crash report generation was not included ")
1055 _T("in this wxWindows version."));
1056 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1057
1058 return true;
1059 }
1060
1061 // ----------------------------------------------------------------------------
1062 // wxCrashReport
1063 // ----------------------------------------------------------------------------
1064
1065 /* static */
1066 void wxCrashReport::SetFileName(const wxChar *filename)
1067 {
1068 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1069 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1070 }
1071
1072 /* static */
1073 const wxChar *wxCrashReport::GetFileName()
1074 {
1075 return gs_reportFilename;
1076 }
1077
1078 /* static */
1079 bool wxCrashReport::Generate(int flags)
1080 {
1081 wxCrashReportImpl impl(gs_reportFilename);
1082
1083 return impl.Generate(flags);
1084 }
1085
1086 // ----------------------------------------------------------------------------
1087 // wxApp::OnFatalException() support
1088 // ----------------------------------------------------------------------------
1089
1090 bool wxHandleFatalExceptions(bool doit)
1091 {
1092 // assume this can only be called from the main thread
1093 gs_handleExceptions = doit;
1094
1095 if ( doit )
1096 {
1097 // try to find a place where we can put out report file later
1098 if ( !::GetTempPath
1099 (
1100 WXSIZEOF(gs_reportFilename),
1101 gs_reportFilename
1102 ) )
1103 {
1104 wxLogLastError(_T("GetTempPath"));
1105
1106 // when all else fails...
1107 wxStrcpy(gs_reportFilename, _T("c:\\"));
1108 }
1109
1110 // use PID and date to make the report file name more unique
1111 wxString fname = wxString::Format
1112 (
1113 _T("%s_%s_%lu.rpt"),
1114 wxTheApp ? wxTheApp->GetAppName().c_str()
1115 : _T("wxwindows"),
1116 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1117 ::GetCurrentProcessId()
1118 );
1119
1120 wxStrncat(gs_reportFilename, fname,
1121 WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
1122 }
1123
1124 return true;
1125 }
1126
1127 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1128 {
1129 if ( gs_handleExceptions && wxTheApp )
1130 {
1131 // store the pointer to exception info
1132 wxGlobalSEInformation = pExcPtrs;
1133
1134 // give the user a chance to do something special about this
1135 __try
1136 {
1137 wxTheApp->OnFatalException();
1138 }
1139 __except ( EXCEPTION_EXECUTE_HANDLER )
1140 {
1141 // nothing to do here, just ignore the exception inside the
1142 // exception handler
1143 ;
1144 }
1145
1146 wxGlobalSEInformation = NULL;
1147
1148 // this will execute our handler and terminate the process
1149 return EXCEPTION_EXECUTE_HANDLER;
1150 }
1151
1152 return EXCEPTION_CONTINUE_SEARCH;
1153 }
1154
1155 #else // !wxUSE_ON_FATAL_EXCEPTION
1156
1157 bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1158 {
1159 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1160
1161 return false;
1162 }
1163
1164 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1165