]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
compilation fix
[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
497 const HANDLE hProcess = GetCurrentProcess();
498
499 DWORD dwTag = 0;
500 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag);
501
502 switch ( dwTag )
503 {
504 case SYMBOL_TAG_UDT:
505 case SYMBOL_TAG_BASECLASS:
506 s = FormatUDT(modBase, dwTypeIndex, pVariable, level);
507 break;
508
509 case SYMBOL_TAG_FUNCTION:
510 // don't show
511 break;
512
513 default:
514 // try to treat all the rest as data even though it's not clear if
515 // it's really a good idea...
516
517 // Get the offset of the child member, relative to its parent
518 DWORD dwMemberOffset = 0;
519 SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
520 TI_GET_OFFSET, &dwMemberOffset);
521
522 // Get the real "TypeId" of the child. We need this for the
523 // SymGetTypeInfo(TI_GET_LENGTH) call below.
524 DWORD typeId;
525 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex,
526 TI_GET_TYPEID, &typeId) )
527 {
528 typeId = dwTypeIndex;
529 }
530
531 // Get the size of the child member
532 ULONG64 size;
533 SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size);
534
535 // Calculate the address of the member
536 DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset;
537
538 BasicType basicType = GetBasicType(modBase, dwTypeIndex);
539
540 s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset);
541 break;
542
543 }
544
545 if ( s.empty() )
546 {
547 // don't show if no value -- what for?
548 return s;
549 }
550
551 return wxString(_T('\t'), level + 1) +
552 GetSymbolName(modBase, dwTypeIndex) +
553 _T(" = ") + s + _T("\r\n");
554}
555
556// If it's a user defined type (UDT), recurse through its members until we're
557// at fundamental types.
558wxString
559wxCrashReportImpl::FormatUDT(DWORD64 modBase,
560 DWORD dwTypeIndex,
561 void *pVariable,
562 unsigned level)
563{
564 wxString s;
565 s.reserve(512);
566 s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n");
567
568 const HANDLE hProcess = GetCurrentProcess();
569
570 // Determine how many children this type has.
571 DWORD dwChildrenCount = 0;
572 SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
573 &dwChildrenCount);
574
575 // Prepare to get an array of "TypeIds", representing each of the children.
576 TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *)
577 malloc(sizeof(TI_FINDCHILDREN_PARAMS) +
578 (dwChildrenCount - 1)*sizeof(ULONG));
579 if ( !children )
580 return s;
581
582 children->Count = dwChildrenCount;
583 children->Start = 0;
584
585 // Get the array of TypeIds, one for each child type
586 if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
587 children) )
588 {
589 return s;
590 }
591
592 // Iterate through all children
593 for ( unsigned i = 0; i < dwChildrenCount; i++ )
594 {
595 s += FormatField(modBase, children->ChildId[i], pVariable, level + 1);
596 }
597
598 free(children);
599
600 s << wxString(_T('\t'), level + 1) << _T('}');
601
602 return s;
603}
604
605// return the string containing the symbol of the given symbol
606/* static */ wxString
607wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable)
608{
609 DWORD dwTag = 0;
610 SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
611 TI_GET_SYMTAG, &dwTag);
612
613 wxString s;
614 switch ( dwTag )
615 {
616 case SYMBOL_TAG_FUNCTION:
617 break;
618
619 case SYMBOL_TAG_UDT:
620 case SYMBOL_TAG_BASECLASS:
621 // show UDT recursively
622 s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable);
623 break;
624
625 default:
626 // variable of simple type (but could be array which we don't
627 // handle correctly yet...), format it using its type and size
628 BasicType bt = GetBasicType(pSym->ModBase, pSym->TypeIndex);
629
630 s = FormatSimpleValue(bt, pSym->Size, pVariable);
631 break;
632
633 }
634
635 return s;
636}
637
638// display contents and type of the given variable
639/* static */ wxString
640wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf)
641{
642 wxString s;
643
644 if ( pSym->Tag == SYMBOL_TAG_FUNCTION )
645 {
646 // If it's a function, don't do anything.
647 return s;
648 }
649
650 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER )
651 {
652 // Don't try to report register variable
653 return s;
654 }
655
656 s.reserve(512);
657
658 // Indicate if the variable is a local or parameter
659 if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER )
660 s += _T("\t[param] ");
661 else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL )
662 s += _T("\t[local] ");
663
664 // Will point to the variable's data in memory
665 DWORD_PTR pVariable = 0;
666
667 if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf )
668 {
669 pVariable = sf->AddrFrame.Offset;
670 pVariable += (DWORD_PTR)pSym->Address;
671 }
672 else // It must be a global variable
673 {
674 pVariable = (DWORD_PTR)pSym->Address;
675 }
676
87baeeaf
VZ
677 s << wxString(pSym->Name, wxConvLibc)
678 << _T(" = ")
679 << FormatAnyValue(pSym, (PVOID)pVariable);
50bea100
VZ
680
681 return s;
682}
683
684void
685wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf)
686{
687 wxString s = FormatSymbol(pSymInfo, sf);
688 if ( !s.empty() )
689 {
690 Output(_T("%s\r\n"), s.c_str());
691 }
692 //else: not an interesting symbol
693}
694
695// callback for SymEnumSymbols()
696/* static */
697BOOL CALLBACK
698wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,
699 ULONG WXUNUSED(SymbolSize),
700 PVOID UserContext)
701{
702 wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext;
703
704 __try
705 {
706 self->OutputSymbol(pSymInfo, self->m_sfCurrent);
707 }
708 __except ( EXCEPTION_EXECUTE_HANDLER )
709 {
710 self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name);
711 }
712
713 // continue with enumeration
714 return true;
715}
716
717HANDLE
718wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord,
719 CONTEXT *pCtx)
720{
721 // First print information about the type of fault
722 const DWORD dwCode = pExceptionRecord->ExceptionCode;
723 Output(_T("Exception code: %s (%#10x)\r\n"),
724 GetExceptionString(dwCode).c_str(), dwCode);
725
726 // Now print information about where the fault occured
727 TCHAR szFaultingModule[MAX_PATH];
728 DWORD section,
729 offset;
730 void * const pExceptionAddress = pExceptionRecord->ExceptionAddress;
731 if ( !GetLogicalAddress(pExceptionAddress,
732 szFaultingModule,
733 WXSIZEOF(szFaultingModule),
734 section, offset) )
735 {
736 section =
737 offset = 0;
738
739 wxStrcpy(szFaultingModule, _T("<< unknown >>"));
740 }
741
742 Output(_T("Fault address: %08x %02x:%08x %s\r\n"),
743 pExceptionAddress, section, offset, szFaultingModule);
744
745#ifdef _M_IX86
746 // Show the registers
747 Output( _T("\r\nRegisters:\r\n") );
748
749 Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"),
750 pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi);
751
752 Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"),
753 pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
754 Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"),
755 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
756 Output(_T("Flags: %08x\r\n"), pCtx->EFlags );
757#endif // _M_IX86
758
759 return ::GetModuleHandle(szFaultingModule);
760}
761
762void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags)
763{
764 enum
765 {
766 Output_Stack,
767 Output_Locals,
768 Output_Max
769#ifndef _M_IX86
770 // can't show locals under other architectures
771 = Output_Locals
772#endif
773 };
774
775 for ( int step = 0; step < Output_Max; step++ )
776 {
777 // don't do things we're not asked for
778 if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) ||
779 (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) )
780 {
781 continue;
782 }
783
784 // the context is going to be modified below so make a copy
785 CONTEXT ctx = *pCtx;
786
787 Output(_T("\r\n%s\r\n")
788 _T(" # Address Frame Function SourceFile\r\n"),
789 step == Output_Stack ? _T("Call stack") : _T("Local variables"));
790
791 DWORD dwMachineType = 0;
792
793 STACKFRAME sf;
794 wxZeroMemory(sf);
795
796#ifdef _M_IX86
797 // Initialize the STACKFRAME structure for the first call. This is
798 // only necessary for Intel CPUs, and isn't mentioned in the
799 // documentation.
800 sf.AddrPC.Offset = ctx.Eip;
801 sf.AddrPC.Mode = AddrModeFlat;
802 sf.AddrStack.Offset = ctx.Esp;
803 sf.AddrStack.Mode = AddrModeFlat;
804 sf.AddrFrame.Offset = ctx.Ebp;
805 sf.AddrFrame.Mode = AddrModeFlat;
806
807 dwMachineType = IMAGE_FILE_MACHINE_I386;
808#endif // _M_IX86
809
810 const HANDLE hProcess = GetCurrentProcess();
811 const HANDLE hThread = GetCurrentThread();
812
813 // first show just the call stack
814 int frame = 0;
815 for ( ;; )
816 {
817 // Get the next stack frame
818 if ( !StackWalk(dwMachineType,
819 hProcess,
820 hThread,
821 &sf,
822 &ctx,
823 0,
824 SymFunctionTableAccess,
825 SymGetModuleBase,
826 0) )
827 {
828 break;
829 }
830
831 // Basic sanity check to make sure the frame is OK.
832 if ( !sf.AddrFrame.Offset )
833 break;
834
835 Output(_T("%2d %08x %08x "),
836 frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset);
837
838 // Get the name of the function for this stack frame entry
839 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ];
840 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
841 pSymbol->SizeOfStruct = sizeof(symbolBuffer);
842 pSymbol->MaxNameLen = 1024;
843
844 // Displacement of the input address, relative to the start of the
845 // symbol
846 DWORD64 symDisplacement = 0;
847
848 if ( SymFromAddr(hProcess, sf.AddrPC.Offset,
849 &symDisplacement,pSymbol) )
850 {
851 Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"),
852 pSymbol->Name, symDisplacement);
853 }
854 else // No symbol found. Print out the logical address instead.
855 {
856 TCHAR szModule[MAX_PATH];
857 DWORD section,
858 offset;
859
860 if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset,
861 szModule, sizeof(szModule),
862 section, offset) )
863 {
864 szModule[0] = _T('\0');
865 section =
866 offset = 0;
867 }
868
869 Output(_T("%04x:%08x %s"), section, offset, szModule);
870 }
871
872 // Get the source line for this stack frame entry
873 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
874 DWORD dwLineDisplacement;
875 if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset,
876 &dwLineDisplacement, &lineInfo ))
877 {
878 Output(_T(" %s line %u"),
879 lineInfo.FileName, lineInfo.LineNumber);
880 }
881
882 OutputEndl();
883
884
885#ifdef _M_IX86
886 // on the second iteration also show the local variables and
887 // parameters
888 if ( step == Output_Locals )
889 {
890 // Use SymSetContext to get just the locals/params for this
891 // frame
892 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
893 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
894 SymSetContext(hProcess, &imagehlpStackFrame, 0);
895
896 // Enumerate the locals/parameters
897 m_sfCurrent = &sf;
898 SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this);
899
900 OutputEndl();
901 }
902#endif // _M_IX86
903 }
904 }
905}
906
907void wxCrashReportImpl::OutputGlobals(HANDLE hModule)
908{
909#ifdef _M_IX86
910 Output(_T("\r\nGlobal variables:\r\n"));
911
912 m_sfCurrent = NULL;
913 SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL,
914 EnumerateSymbolsCallback, this);
915#endif // _M_IX86
916}
917
918bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp)
919{
920 #define LOAD_SYM_FUNCTION(name) \
87baeeaf 921 name = (name ## _t) dllDbgHelp.GetSymbol(_T(#name)); \
50bea100
VZ
922 if ( !name ) \
923 { \
87baeeaf 924 Output(_T("\r\nFunction ") _T(#name) \
50bea100
VZ
925 _T("() not found.\r\n")); \
926 return false; \
927 }
928
929 LOAD_SYM_FUNCTION(SymSetOptions);
930 LOAD_SYM_FUNCTION(SymInitialize);
931 LOAD_SYM_FUNCTION(StackWalk);
932 LOAD_SYM_FUNCTION(SymFromAddr);
933 LOAD_SYM_FUNCTION(SymFunctionTableAccess);
934 LOAD_SYM_FUNCTION(SymGetModuleBase);
935 LOAD_SYM_FUNCTION(SymGetLineFromAddr);
936 LOAD_SYM_FUNCTION(SymSetContext);
937 LOAD_SYM_FUNCTION(SymEnumSymbols);
938 LOAD_SYM_FUNCTION(SymGetTypeInfo);
939
940 #undef LOAD_SYM_FUNCTION
941
942 return true;
943}
944
50bea100
VZ
945/* static */
946wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode)
947{
948 wxString s;
949
950 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
951
952 switch ( dwCode )
953 {
954 CASE_EXCEPTION(ACCESS_VIOLATION);
955 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
956 CASE_EXCEPTION(BREAKPOINT);
957 CASE_EXCEPTION(SINGLE_STEP);
958 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
959 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
960 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
961 CASE_EXCEPTION(FLT_INEXACT_RESULT);
962 CASE_EXCEPTION(FLT_INVALID_OPERATION);
963 CASE_EXCEPTION(FLT_OVERFLOW);
964 CASE_EXCEPTION(FLT_STACK_CHECK);
965 CASE_EXCEPTION(FLT_UNDERFLOW);
966 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
967 CASE_EXCEPTION(INT_OVERFLOW);
968 CASE_EXCEPTION(PRIV_INSTRUCTION);
969 CASE_EXCEPTION(IN_PAGE_ERROR);
970 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
971 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
972 CASE_EXCEPTION(STACK_OVERFLOW);
973 CASE_EXCEPTION(INVALID_DISPOSITION);
974 CASE_EXCEPTION(GUARD_PAGE);
975 CASE_EXCEPTION(INVALID_HANDLE);
976
977 default:
978 // unknown exception, ask NTDLL for the name
979 if ( !::FormatMessage
980 (
981 FORMAT_MESSAGE_IGNORE_INSERTS |
982 FORMAT_MESSAGE_FROM_HMODULE,
983 ::GetModuleHandle(_T("NTDLL.DLL")),
984 dwCode,
985 0,
986 wxStringBuffer(s, 1024),
987 1024,
988 0
989 ) )
990 {
991 s = _T("UNKNOWN_EXCEPTION");
992 }
993 }
994
995 #undef CASE_EXCEPTION
996
997 return s;
998}
999
0b7824d7
VZ
1000#endif // wxUSE_DBGHELP
1001
c6151f2a
JS
1002// Remove warning
1003#if wxUSE_DBGHELP
1004#define _WXUNUSED(x) x
1005#else
1006#define _WXUNUSED WXUNUSED
1007#endif
1008
1009bool wxCrashReportImpl::Generate(int _WXUNUSED(flags))
0b7824d7
VZ
1010{
1011 if ( m_hFile == INVALID_HANDLE_VALUE )
1012 return false;
1013
1014#if wxUSE_DBGHELP
1015 if ( !wxGlobalSEInformation )
1016 return false;
1017
1018 PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord;
1019 PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord;
1020
1021 if ( !pExceptionRecord || !pCtx )
1022 return false;
1023
1024 HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx);
1025
1026 // for everything else we need dbghelp.dll
1027 wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM);
1028 if ( dllDbgHelp.IsLoaded() )
1029 {
1030 if ( ResolveSymFunctions(dllDbgHelp) )
1031 {
96f32e18 1032 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);
0b7824d7
VZ
1033
1034 // Initialize DbgHelp
1035 if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) )
1036 {
1037 OutputStack(pCtx, flags);
1038
1039 if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) )
1040 {
1041 OutputGlobals(hModuleCrash);
1042 }
1043
1044 return true;
1045 }
1046 }
1047 else
1048 {
87baeeaf
VZ
1049 Output(_T("Please update your dbghelp.dll version, ")
1050 _T("at least version 5.1 is needed!\r\n"));
0b7824d7
VZ
1051 }
1052 }
1053 else
1054 {
1055 Output(_T("Please install dbghelp.dll available free of charge ")
1056 _T("from Microsoft to get more detailed crash information!"));
1057 }
1058
87baeeaf
VZ
1059 Output(_T("\r\nLatest dbghelp.dll is available at ")
1060 _T("http://www.microsoft.com/whdc/ddk/debugging/\r\n"));
0b7824d7
VZ
1061
1062#else // !wxUSE_DBGHELP
1063 Output(_T("Support for crash report generation was not included ")
1064 _T("in this wxWindows version."));
1065#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
1066
1067 return true;
1068}
1069
50bea100
VZ
1070// ----------------------------------------------------------------------------
1071// wxCrashReport
1072// ----------------------------------------------------------------------------
1073
1074/* static */
1075void wxCrashReport::SetFileName(const wxChar *filename)
1076{
1077 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
1078 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
1079}
1080
1081/* static */
1082const wxChar *wxCrashReport::GetFileName()
1083{
1084 return gs_reportFilename;
1085}
1086
1087/* static */
1088bool wxCrashReport::Generate(int flags)
1089{
1090 wxCrashReportImpl impl(gs_reportFilename);
1091
1092 return impl.Generate(flags);
1093}
1094
1095// ----------------------------------------------------------------------------
1096// wxApp::OnFatalException() support
1097// ----------------------------------------------------------------------------
1098
1099bool wxHandleFatalExceptions(bool doit)
1100{
1101 // assume this can only be called from the main thread
1102 gs_handleExceptions = doit;
1103
1104 if ( doit )
1105 {
1106 // try to find a place where we can put out report file later
1107 if ( !::GetTempPath
1108 (
1109 WXSIZEOF(gs_reportFilename),
1110 gs_reportFilename
1111 ) )
1112 {
1113 wxLogLastError(_T("GetTempPath"));
1114
1115 // when all else fails...
1116 wxStrcpy(gs_reportFilename, _T("c:\\"));
1117 }
1118
1119 // use PID and date to make the report file name more unique
1120 wxString fname = wxString::Format
1121 (
1122 _T("%s_%s_%lu.rpt"),
1123 wxTheApp ? wxTheApp->GetAppName().c_str()
1124 : _T("wxwindows"),
1125 wxDateTime::Now().Format(_T("%Y%m%d")).c_str(),
1126 ::GetCurrentProcessId()
1127 );
1128
1129 wxStrncat(gs_reportFilename, fname,
d4cb34b0 1130 WXSIZEOF(gs_reportFilename) - wxStrlen(gs_reportFilename) - 1);
50bea100
VZ
1131 }
1132
1133 return true;
1134}
1135
1136extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs)
1137{
1138 if ( gs_handleExceptions && wxTheApp )
1139 {
1140 // store the pointer to exception info
1141 wxGlobalSEInformation = pExcPtrs;
1142
1143 // give the user a chance to do something special about this
3f1d3756
VZ
1144 __try
1145 {
1146 wxTheApp->OnFatalException();
1147 }
1148 __except ( EXCEPTION_EXECUTE_HANDLER )
1149 {
1150 // nothing to do here, just ignore the exception inside the
1151 // exception handler
1152 ;
1153 }
50bea100
VZ
1154
1155 wxGlobalSEInformation = NULL;
1156
1157 // this will execute our handler and terminate the process
1158 return EXCEPTION_EXECUTE_HANDLER;
1159 }
1160
1161 return EXCEPTION_CONTINUE_SEARCH;
1162}
1163
1164#else // !wxUSE_ON_FATAL_EXCEPTION
1165
1166bool wxHandleFatalExceptions(bool WXUNUSED(doit))
1167{
1168 wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function"));
1169
1170 return false;
1171}
1172
1173#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION
1174