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