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