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