Fixed a warning....
[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(int WXUNUSED(flags))
1093 {
1094 if ( m_hFile == INVALID_HANDLE_VALUE )
1095 return false;
1096
1097 #if wxUSE_DBGHELP
1098 if ( !wxGlobalSEInformation )
1099 return false;
1100
1101 #if !wxUSE_MINIDUMP
1102 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
1103 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
1104
1105 if ( !pExceptionRecord || !pCtx )
1106 return false;
1107
1108 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
1109 #endif // !wxUSE_MINIDUMP
1110
1111 // show to the user that we're doing something...
1112 BusyCursor busyCursor;
1113
1114 // user-specified crash report flags override those specified by the
1115 // programmer
1116 TCHAR envFlags[64];
1117 DWORD dwLen = ::GetEnvironmentVariable
1118 (
1119 _T("WX_CRASH_FLAGS"),
1120 envFlags,
1121 WXSIZEOF(envFlags)
1122 );
1123
1124 int flagsEnv;
1125 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
1126 wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
1127 {
1128 flags = flagsEnv;
1129 }
1130
1131 // for everything else we need dbghelp.dll
1132 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
1133 if ( dllDbgHelp.IsLoaded() )
1134 {
1135 if ( BindDbgHelpFunctions(dllDbgHelp) )
1136 {
1137 #if wxUSE_MINIDUMP
1138 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
1139
1140 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
1141 minidumpExcInfo.ExceptionPointers = wxGlobalSEInformation;
1142 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
1143
1144 // do generate the dump
1145 MINIDUMP_TYPE dumpFlags;
1146 if ( flags & wxCRASH_REPORT_LOCALS )
1147 {
1148 // the only way to get local variables is to dump the entire
1149 // process memory space -- but this makes for huge (dozens or
1150 // even hundreds of Mb) files
1151 dumpFlags = MiniDumpWithFullMemory;
1152 }
1153 else if ( flags & wxCRASH_REPORT_GLOBALS )
1154 {
1155 // MiniDumpWriteDump() has the option for dumping just the data
1156 // segment which contains all globals -- exactly what we need
1157 dumpFlags = MiniDumpWithDataSegs;
1158 }
1159 else // minimal dump
1160 {
1161 dumpFlags = MiniDumpNormal;
1162 }
1163
1164 if ( !MiniDumpWriteDump
1165 (
1166 ::GetCurrentProcess(),
1167 ::GetCurrentProcessId(),
1168 m_hFile, // file to write to
1169 dumpFlags, // kind of dump to craete
1170 &minidumpExcInfo,
1171 NULL, // no extra user-defined data
1172 NULL // no callbacks
1173 ) )
1174 {
1175 Output(_T("MiniDumpWriteDump() failed."));
1176
1177 return false;
1178 }
1179
1180 return true;
1181 #else // !wxUSE_MINIDUMP
1182 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
1183
1184 // Initialize DbgHelp
1185 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
1186 {
1187 OutputStack(pCtx, flags);
1188
1189 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
1190 {
1191 OutputGlobals(hModuleCrash);
1192 }
1193
1194 return true;
1195 }
1196 #endif // !wxUSE_MINIDUMP
1197 }
1198 else
1199 {
1200 Output(_T("\r\nPlease update your dbghelp.dll version, ")
1201 _T("at least version 5.1 is needed!\r\n")
1202 _T("(if you already have a new version, please ")
1203 _T("put it in the same directory where the program is.)\r\n"));
1204 }
1205 }
1206 else // failed to load dbghelp.dll
1207 {
1208 Output(_T("Please install dbghelp.dll available free of charge ")
1209 _T("from Microsoft to get more detailed crash information!"));
1210 }
1211
1212 Output(_T("\r\nLatest dbghelp.dll is available at ")
1213 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
1214
1215 #else // !wxUSE_DBGHELP
1216 Output(_T("Support for crash report generation was not included ")
1217 _T("in this wxWindows version."));
1218 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1219
1220 return false;
1221 }
1222
1223 // ----------------------------------------------------------------------------
1224 // wxCrashReport
1225 // ----------------------------------------------------------------------------
1226
1227 /* static */
1228 void wxCrashReport::SetFileName(const wxChar *filename)
1229 {
1230 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1231 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1232 }
1233
1234 /* static */
1235 const wxChar *wxCrashReport::GetFileName()
1236 {
1237 return gs_reportFilename;
1238 }
1239
1240 /* static */
1241 bool wxCrashReport::Generate(int flags)
1242 {
1243 wxCrashReportImpl impl(gs_reportFilename);
1244
1245 return impl.Generate(flags);
1246 }
1247
1248 // ----------------------------------------------------------------------------
1249 // wxApp::OnFatalException() support
1250 // ----------------------------------------------------------------------------
1251
1252 bool wxHandleFatalExceptions(bool doit)
1253 {
1254 // assume this can only be called from the main thread
1255 gs_handleExceptions = doit;
1256
1257 if ( doit )
1258 {
1259 // try to find a place where we can put out report file later
1260 if ( !::GetTempPath
1261 (
1262 WXSIZEOF(gs_reportFilename),
1263 gs_reportFilename
1264 ) )
1265 {
1266 wxLogLastError(_T("GetTempPath"));
1267
1268 // when all else fails...
1269 wxStrcpy(gs_reportFilename, _T("c:\\"));
1270 }
1271
1272 // use PID and date to make the report file name more unique
1273 wxString fname = wxString::Format
1274 (
1275 #if wxUSE_MINIDUMP
1276 _T("%s_%s_%lu.dmp"),
1277 #else // !wxUSE_MINIDUMP
1278 _T("%s_%s_%lu.rpt"),
1279 #endif // wxUSE_MINIDUMP/!wxUSE_MINIDUMP
1280 wxTheApp ? wxTheApp->GetAppName().c_str()
1281 : _T("wxwindows"),
1282 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1283 ::GetCurrentProcessId()
1284 );
1285
1286 wxStrncat(gs_reportFilename, fname,
1287 WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
1288 }
1289
1290 return true;
1291 }
1292
1293 extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1294 {
1295 if ( gs_handleExceptions && wxTheApp )
1296 {
1297 // store the pointer to exception info
1298 wxGlobalSEInformation = pExcPtrs;
1299
1300 // give the user a chance to do something special about this
1301 __try
1302 {
1303 wxTheApp->OnFatalException();
1304 }
1305 __except ( EXCEPTION_EXECUTE_HANDLER )
1306 {
1307 // nothing to do here, just ignore the exception inside the
1308 // exception handler
1309 ;
1310 }
1311
1312 wxGlobalSEInformation = NULL;
1313
1314 // this will execute our handler and terminate the process
1315 return EXCEPTION_EXECUTE_HANDLER;
1316 }
1317
1318 return EXCEPTION_CONTINUE_SEARCH;
1319 }
1320
1321 #else // !wxUSE_ON_FATAL_EXCEPTION
1322
1323 bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1324 {
1325 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1326
1327 return false;
1328 }
1329
1330 #endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1331