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