From 50bea100324dcbeebf400eb65869fd4eae476171 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 13 Jul 2003 21:18:04 +0000 Subject: [PATCH] wxSEHReport renamed to wxCrashReport enhanced and debugged; seems to work git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@21936 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/{seh.h => crashrpt.h} | 32 +- src/msw/crashrpt.cpp | 1116 ++++++++++++++++++++++++++ src/msw/seh.cpp | 585 -------------- 3 files changed, 1144 insertions(+), 589 deletions(-) rename include/wx/msw/{seh.h => crashrpt.h} (51%) create mode 100644 src/msw/crashrpt.cpp delete mode 100644 src/msw/seh.cpp diff --git a/include/wx/msw/seh.h b/include/wx/msw/crashrpt.h similarity index 51% rename from include/wx/msw/seh.h rename to include/wx/msw/crashrpt.h index ae7c2f9d26..97a5e4359a 100644 --- a/include/wx/msw/seh.h +++ b/include/wx/msw/crashrpt.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Name: wx/msw/seh.h +// Name: wx/msw/crashrpt.h // Purpose: helpers for the structured exception handling (SEH) under Win32 // Author: Vadim Zeitlin // Modified by: @@ -17,10 +17,32 @@ #if wxUSE_ON_FATAL_EXCEPTION // ---------------------------------------------------------------------------- -// wxSEHReport: this class is used as a namespace for the SEH-related functions +// report generation flags // ---------------------------------------------------------------------------- -struct WXDLLIMPEXP_BASE wxSEHReport +enum +{ + // we always report where the crash occured + wxCRASH_REPORT_LOCATION = 0, + + // if this flag is given, the call stack is dumped + wxCRASH_REPORT_STACK = 1, + + // if this flag is given, the values of the local variables are dumped + wxCRASH_REPORT_LOCALS = 2, + + // if this flag is given, the values of all global variables are dumped + // + // WARNING: this may take a very long time and generate megabytes of output + // in a big program, this is why it is off by default + wxCRASH_REPORT_GLOBALS = 4 +}; + +// ---------------------------------------------------------------------------- +// wxCrashReport: this class is used to create crash reports +// ---------------------------------------------------------------------------- + +struct WXDLLIMPEXP_BASE wxCrashReport { // set the name of the file to which the report is written, it is // constructed from the .exe name by default @@ -31,7 +53,9 @@ struct WXDLLIMPEXP_BASE wxSEHReport // write the exception report to the file, return true if it could be done // or false otherwise - static bool Generate(); + static bool Generate(int flags = wxCRASH_REPORT_LOCATION | + wxCRASH_REPORT_STACK | + wxCRASH_REPORT_LOCALS); }; #endif // wxUSE_ON_FATAL_EXCEPTION diff --git a/src/msw/crashrpt.cpp b/src/msw/crashrpt.cpp new file mode 100644 index 0000000000..3b5da991bb --- /dev/null +++ b/src/msw/crashrpt.cpp @@ -0,0 +1,1116 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: msw/crashrpt.cpp +// Purpose: helpers for structured exception handling (SEH) +// Author: Vadim Zeitlin +// Modified by: +// Created: 13.07.03 +// RCS-ID: $Id$ +// Copyright: (c) 2003 Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +/* + The code in this file is heavily based on Matt Pietrek's column from + the March 2002 issue of MSDN Magazine. + */ + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_ON_FATAL_EXCEPTION + +#ifndef WX_PRECOMP +#endif //WX_PRECOMP + +#include "wx/longlong.h" +#include "wx/datetime.h" +#include "wx/dynload.h" + +#include "wx/msw/crashrpt.h" + +#include +#include +#include "wx/msw/private.h" + +// ---------------------------------------------------------------------------- +// types of imagehlp.h functions +// ---------------------------------------------------------------------------- + +typedef DWORD (WINAPI *SymSetOptions_t)(DWORD); +typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL); +typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, + LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, + PFUNCTION_TABLE_ACCESS_ROUTINE, + PGET_MODULE_BASE_ROUTINE, + PTRANSLATE_ADDRESS_ROUTINE); +typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); +typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD); +typedef DWORD (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD); +typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD, + PDWORD, PIMAGEHLP_LINE); +typedef BOOL (WINAPI *SymSetContext_t)(HANDLE, PIMAGEHLP_STACK_FRAME, + PIMAGEHLP_CONTEXT); +typedef BOOL (WINAPI *SymEnumSymbols_t)(HANDLE, ULONG64, PCSTR, + PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID); +typedef BOOL (WINAPI *SymGetTypeInfo_t)(HANDLE, DWORD64, ULONG, + IMAGEHLP_SYMBOL_TYPE_INFO, PVOID); + +// ---------------------------------------------------------------------------- +// constant +// ---------------------------------------------------------------------------- + +// Stolen from CVCONST.H in the DIA 2.0 SDK +enum BasicType +{ + BASICTYPE_NOTYPE = 0, + BASICTYPE_VOID = 1, + BASICTYPE_CHAR = 2, + BASICTYPE_WCHAR = 3, + BASICTYPE_INT = 6, + BASICTYPE_UINT = 7, + BASICTYPE_FLOAT = 8, + BASICTYPE_BCD = 9, + BASICTYPE_BOOL = 10, + BASICTYPE_LONG = 13, + BASICTYPE_ULONG = 14, + BASICTYPE_CURRENCY = 25, + BASICTYPE_DATE = 26, + BASICTYPE_VARIANT = 27, + BASICTYPE_COMPLEX = 28, + BASICTYPE_BIT = 29, + BASICTYPE_BSTR = 30, + BASICTYPE_HRESULT = 31 +}; + +// Same as above +enum SymbolTag +{ + SYMBOL_TAG_NULL, + SYMBOL_TAG_FUNCTION = 5, + SYMBOL_TAG_DATA = 7, + SYMBOL_TAG_PUBLIC = 10, // appears in .DBGs + SYMBOL_TAG_UDT, + SYMBOL_TAG_ENUM, + SYMBOL_TAG_FUNCTION_TYPE, + SYMBOL_TAG_POINTER_TYPE, + SYMBOL_TAG_ARRAY_TYPE, + SYMBOL_TAG_BASE_TYPE, + SYMBOL_TAG_TYPEDEF, + SYMBOL_TAG_BASECLASS +}; + +// ---------------------------------------------------------------------------- +// classes +// ---------------------------------------------------------------------------- + +// the real crash report generator +class wxCrashReportImpl +{ +public: + wxCrashReportImpl(const wxChar *filename); + + bool Generate(int flags); + + ~wxCrashReportImpl() + { + if ( m_hFile != INVALID_HANDLE_VALUE ) + { + ::CloseHandle(m_hFile); + } + } + +private: + // formatted output to m_hFile + void Output(const wxChar *format, ...); + + // output end of line + void OutputEndl() { Output(_T("\r\n")); } + + // translate exception code to its symbolic name + static wxString GetExceptionString(DWORD dwCode); + + // return the type from "type index" + static BasicType GetBasicType(DWORD64 modBase, DWORD typeIndex); + + // return the name for the type index + static wxString GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex); + + // return the string representation of the variable value + static wxString FormatSimpleValue(BasicType bt, + DWORD64 length, + PVOID pAddress); + + // return string representation of a struct field (which may itself be a + // struct, of course) + static wxString FormatField(DWORD64 modBase, + DWORD dwTypeIndex, + void *pVariable, + unsigned level); + + // show the name and value of the given symbol + static wxString FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf); + + // show value described by SYMBOL_INFO located at pVariable + static wxString FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable); + + // show value of possibly complex (user-defined) type + static wxString FormatUDT(DWORD64 modBase, + DWORD dwTypeIndex, + void *pVariable, + unsigned level = 0); + + // outputs information about the given symbol + void OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf); + + // load all the functions we need from dbghelp.dll, return true if all ok + bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp); + + // map address to module (and also section:offset), retunr true if ok + static bool GetLogicalAddress(PVOID addr, + PTSTR szModule, + DWORD len, + DWORD& section, + DWORD& offset); + + // callback used with SymEnumSymbols() to process all variables + static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo, + ULONG SymbolSize, + PVOID UserContext); + + + // show the general information about exception which should be always + // available + // + // returns the module of the handle where the crash occured + HANDLE OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx); + + // output the call stack and local variables values + void OutputStack(const CONTEXT *pCtx, int flags); + + // output the global variables values + void OutputGlobals(HANDLE hModuleCrash); + + + // the handle of the report file + HANDLE m_hFile; + + // the current stack frame (may be NULL) + STACKFRAME *m_sfCurrent; + + + // dynamically loaded dbghelp.dll functions + #define DECLARE_SYM_FUNCTION(func) static func ## _t func + + DECLARE_SYM_FUNCTION(SymSetOptions); + DECLARE_SYM_FUNCTION(SymInitialize); + DECLARE_SYM_FUNCTION(StackWalk); + DECLARE_SYM_FUNCTION(SymFromAddr); + DECLARE_SYM_FUNCTION(SymFunctionTableAccess); + DECLARE_SYM_FUNCTION(SymGetModuleBase); + DECLARE_SYM_FUNCTION(SymGetLineFromAddr); + DECLARE_SYM_FUNCTION(SymSetContext); + DECLARE_SYM_FUNCTION(SymEnumSymbols); + DECLARE_SYM_FUNCTION(SymGetTypeInfo); +}; + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +// global pointer to exception information, only valid inside OnFatalException +extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL; + + +// flag telling us whether the application wants to handle exceptions at all +static bool gs_handleExceptions = false; + +// the file name where the report about exception is written +static wxChar gs_reportFilename[MAX_PATH]; + +// ============================================================================ +// implementation +// ============================================================================ + +#define DEFINE_SYM_FUNCTION(func) func ## _t wxCrashReportImpl::func = 0 + +DEFINE_SYM_FUNCTION(SymSetOptions); +DEFINE_SYM_FUNCTION(SymInitialize); +DEFINE_SYM_FUNCTION(StackWalk); +DEFINE_SYM_FUNCTION(SymFromAddr); +DEFINE_SYM_FUNCTION(SymFunctionTableAccess); +DEFINE_SYM_FUNCTION(SymGetModuleBase); +DEFINE_SYM_FUNCTION(SymGetLineFromAddr); +DEFINE_SYM_FUNCTION(SymSetContext); +DEFINE_SYM_FUNCTION(SymEnumSymbols); +DEFINE_SYM_FUNCTION(SymGetTypeInfo); + +#undef DEFINE_SYM_FUNCTION + +// ---------------------------------------------------------------------------- +// wxCrashReportImpl +// ---------------------------------------------------------------------------- + +wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename) +{ + m_sfCurrent = NULL; + + m_hFile = ::CreateFile + ( + filename, + GENERIC_WRITE, + 0, // no sharing + NULL, // default security + CREATE_ALWAYS, + FILE_FLAG_WRITE_THROUGH, + NULL // no template file + ); +} + +void wxCrashReportImpl::Output(const wxChar *format, ...) +{ + va_list argptr; + va_start(argptr, format); + + DWORD cbWritten; + + wxString s = wxString::FormatV(format, argptr); + ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0); + + va_end(argptr); +} + +bool +wxCrashReportImpl::GetLogicalAddress(PVOID addr, + PTSTR szModule, + DWORD len, + DWORD& section, + DWORD& offset) +{ + MEMORY_BASIC_INFORMATION mbi; + + if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) ) + return false; + + DWORD hMod = (DWORD)mbi.AllocationBase; + + if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) ) + return false; + + // Point to the DOS header in memory + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; + + // From the DOS header, find the NT (PE) header + PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew); + + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); + + DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address + + // Iterate through the section table, looking for the one that encompasses + // the linear address. + const DWORD nSections = pNtHdr->FileHeader.NumberOfSections; + for ( DWORD i = 0; i < nSections; i++, pSection++ ) + { + DWORD sectionStart = pSection->VirtualAddress; + DWORD sectionEnd = sectionStart + + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize); + + // Is the address in this section? + if ( (rva >= sectionStart) && (rva <= sectionEnd) ) + { + // Yes, address is in the section. Calculate section and offset, + // and store in the "section" & "offset" params, which were + // passed by reference. + section = i + 1; + offset = rva - sectionStart; + + return true; + } + } + + // failed to map to logical address... + return false; +} + +/* static */ BasicType +wxCrashReportImpl::GetBasicType(DWORD64 modBase, DWORD typeIndex) +{ + const HANDLE hProcess = GetCurrentProcess(); + + // try the index we have + BasicType bt; + if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_BASETYPE, &bt) ) + { + return bt; + } + + // if failed, try to get the "real" typeid first + DWORD typeId; + if ( SymGetTypeInfo(hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId) + && + (typeId != typeIndex && + SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_BASETYPE, &bt)) ) + { + return bt; + } + + return BASICTYPE_NOTYPE; +} + +/* static */ wxString +wxCrashReportImpl::FormatSimpleValue(BasicType bt, + DWORD64 length, + PVOID pAddress) +{ + wxString s; + s.reserve(256); + + // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) + if ( length == 1 ) + { + s.Printf(_T("%#04x"), *(PBYTE)pAddress); + } + else if ( length == 2 ) + { + s.Printf(_T("%#06x"), *(PWORD)pAddress); + } + else if ( length == 4 ) + { + bool handled = false; + + if ( bt == BASICTYPE_FLOAT ) + { + s.Printf(_T("%f"), *(PFLOAT)pAddress); + + handled = true; + } + else if ( bt == BASICTYPE_CHAR ) + { + static const size_t NUM_CHARS = 32; + + const wxChar * const pc = *(PSTR *)pAddress; + if ( !::IsBadStringPtr(pc, NUM_CHARS) ) + { + s << _T('"') << wxString(pc, NUM_CHARS) << _T('"'); + + handled = true; + } + } + + if ( !handled ) + { + // treat just as an opaque DWORD + s.Printf(_T("%#x"), *(PDWORD)pAddress); + } + } + else if ( length == 8 ) + { + if ( bt == BASICTYPE_FLOAT ) + { + s.Printf(_T("%lf"), *(double *)pAddress); + } + else // opaque 64 bit value + { + s.Printf(_T("%#" wxLongLongFmtSpec _T("x")), *(PDWORD *)pAddress); + } + } + + return s; +} + +/* static */ +wxString wxCrashReportImpl::GetSymbolName(DWORD64 modBase, DWORD dwTypeIndex) +{ + wxString s; + + WCHAR *pwszTypeName; + if ( SymGetTypeInfo + ( + GetCurrentProcess(), + modBase, + dwTypeIndex, + TI_GET_SYMNAME, + &pwszTypeName + ) ) + { + s = wxConvCurrent->cWC2WX(pwszTypeName); + + ::LocalFree(pwszTypeName); + } + + return s; +} + +// this is called for the struct members/base classes +wxString +wxCrashReportImpl::FormatField(DWORD64 modBase, + DWORD dwTypeIndex, + void *pVariable, + unsigned level) +{ + wxString s; + + const HANDLE hProcess = GetCurrentProcess(); + + DWORD dwTag = 0; + SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &dwTag); + + switch ( dwTag ) + { + case SYMBOL_TAG_UDT: + case SYMBOL_TAG_BASECLASS: + s = FormatUDT(modBase, dwTypeIndex, pVariable, level); + break; + + case SYMBOL_TAG_FUNCTION: + // don't show + break; + + default: + // try to treat all the rest as data even though it's not clear if + // it's really a good idea... + + // Get the offset of the child member, relative to its parent + DWORD dwMemberOffset = 0; + SymGetTypeInfo(hProcess, modBase, dwTypeIndex, + TI_GET_OFFSET, &dwMemberOffset); + + // Get the real "TypeId" of the child. We need this for the + // SymGetTypeInfo(TI_GET_LENGTH) call below. + DWORD typeId; + if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, + TI_GET_TYPEID, &typeId) ) + { + typeId = dwTypeIndex; + } + + // Get the size of the child member + ULONG64 size; + SymGetTypeInfo(hProcess, modBase, typeId, TI_GET_LENGTH, &size); + + // Calculate the address of the member + DWORD_PTR dwFinalOffset = (DWORD_PTR)pVariable + dwMemberOffset; + + BasicType basicType = GetBasicType(modBase, dwTypeIndex); + + s = FormatSimpleValue(basicType, size, (PVOID)dwFinalOffset); + break; + + } + + if ( s.empty() ) + { + // don't show if no value -- what for? + return s; + } + + return wxString(_T('\t'), level + 1) + + GetSymbolName(modBase, dwTypeIndex) + + _T(" = ") + s + _T("\r\n"); +} + +// If it's a user defined type (UDT), recurse through its members until we're +// at fundamental types. +wxString +wxCrashReportImpl::FormatUDT(DWORD64 modBase, + DWORD dwTypeIndex, + void *pVariable, + unsigned level) +{ + wxString s; + s.reserve(512); + s = GetSymbolName(modBase, dwTypeIndex) + _T(" {\r\n"); + + const HANDLE hProcess = GetCurrentProcess(); + + // Determine how many children this type has. + DWORD dwChildrenCount = 0; + SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, + &dwChildrenCount); + + // Prepare to get an array of "TypeIds", representing each of the children. + TI_FINDCHILDREN_PARAMS *children = (TI_FINDCHILDREN_PARAMS *) + malloc(sizeof(TI_FINDCHILDREN_PARAMS) + + (dwChildrenCount - 1)*sizeof(ULONG)); + if ( !children ) + return s; + + children->Count = dwChildrenCount; + children->Start = 0; + + // Get the array of TypeIds, one for each child type + if ( !SymGetTypeInfo(hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, + children) ) + { + return s; + } + + // Iterate through all children + for ( unsigned i = 0; i < dwChildrenCount; i++ ) + { + s += FormatField(modBase, children->ChildId[i], pVariable, level + 1); + } + + free(children); + + s << wxString(_T('\t'), level + 1) << _T('}'); + + return s; +} + +// return the string containing the symbol of the given symbol +/* static */ wxString +wxCrashReportImpl::FormatAnyValue(PSYMBOL_INFO pSym, void *pVariable) +{ + DWORD dwTag = 0; + SymGetTypeInfo(GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex, + TI_GET_SYMTAG, &dwTag); + + wxString s; + switch ( dwTag ) + { + case SYMBOL_TAG_FUNCTION: + break; + + case SYMBOL_TAG_UDT: + case SYMBOL_TAG_BASECLASS: + // show UDT recursively + s = FormatUDT(pSym->ModBase, pSym->TypeIndex, pVariable); + break; + + default: + // variable of simple type (but could be array which we don't + // handle correctly yet...), format it using its type and size + BasicType bt = GetBasicType(pSym->ModBase, pSym->TypeIndex); + + s = FormatSimpleValue(bt, pSym->Size, pVariable); + break; + + } + + return s; +} + +// display contents and type of the given variable +/* static */ wxString +wxCrashReportImpl::FormatSymbol(PSYMBOL_INFO pSym, STACKFRAME *sf) +{ + wxString s; + + if ( pSym->Tag == SYMBOL_TAG_FUNCTION ) + { + // If it's a function, don't do anything. + return s; + } + + if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER ) + { + // Don't try to report register variable + return s; + } + + s.reserve(512); + + // Indicate if the variable is a local or parameter + if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) + s += _T("\t[param] "); + else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL ) + s += _T("\t[local] "); + + // Will point to the variable's data in memory + DWORD_PTR pVariable = 0; + + if ( (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) && sf ) + { + pVariable = sf->AddrFrame.Offset; + pVariable += (DWORD_PTR)pSym->Address; + } + else // It must be a global variable + { + pVariable = (DWORD_PTR)pSym->Address; + } + + s << pSym->Name << _T(" = ") << FormatAnyValue(pSym, (PVOID)pVariable); + + return s; +} + +void +wxCrashReportImpl::OutputSymbol(PSYMBOL_INFO pSymInfo, STACKFRAME *sf) +{ + wxString s = FormatSymbol(pSymInfo, sf); + if ( !s.empty() ) + { + Output(_T("%s\r\n"), s.c_str()); + } + //else: not an interesting symbol +} + +// callback for SymEnumSymbols() +/* static */ +BOOL CALLBACK +wxCrashReportImpl::EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo, + ULONG WXUNUSED(SymbolSize), + PVOID UserContext) +{ + wxCrashReportImpl *self = (wxCrashReportImpl *)UserContext; + + __try + { + self->OutputSymbol(pSymInfo, self->m_sfCurrent); + } + __except ( EXCEPTION_EXECUTE_HANDLER ) + { + self->Output(_T("Can't process symbol %hs\r\n"), pSymInfo->Name); + } + + // continue with enumeration + return true; +} + +HANDLE +wxCrashReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, + CONTEXT *pCtx) +{ + // First print information about the type of fault + const DWORD dwCode = pExceptionRecord->ExceptionCode; + Output(_T("Exception code: %s (%#10x)\r\n"), + GetExceptionString(dwCode).c_str(), dwCode); + + // Now print information about where the fault occured + TCHAR szFaultingModule[MAX_PATH]; + DWORD section, + offset; + void * const pExceptionAddress = pExceptionRecord->ExceptionAddress; + if ( !GetLogicalAddress(pExceptionAddress, + szFaultingModule, + WXSIZEOF(szFaultingModule), + section, offset) ) + { + section = + offset = 0; + + wxStrcpy(szFaultingModule, _T("<< unknown >>")); + } + + Output(_T("Fault address: %08x %02x:%08x %s\r\n"), + pExceptionAddress, section, offset, szFaultingModule); + +#ifdef _M_IX86 + // Show the registers + Output( _T("\r\nRegisters:\r\n") ); + + Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"), + pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi); + + Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"), + pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); + Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); + Output(_T("Flags: %08x\r\n"), pCtx->EFlags ); +#endif // _M_IX86 + + return ::GetModuleHandle(szFaultingModule); +} + +void wxCrashReportImpl::OutputStack(const CONTEXT *pCtx, int flags) +{ + enum + { + Output_Stack, + Output_Locals, + Output_Max +#ifndef _M_IX86 + // can't show locals under other architectures + = Output_Locals +#endif + }; + + for ( int step = 0; step < Output_Max; step++ ) + { + // don't do things we're not asked for + if ( (step == Output_Stack) && !(flags & wxCRASH_REPORT_STACK) || + (step == Output_Locals) && !(flags & wxCRASH_REPORT_LOCALS) ) + { + continue; + } + + // the context is going to be modified below so make a copy + CONTEXT ctx = *pCtx; + + Output(_T("\r\n%s\r\n") + _T(" # Address Frame Function SourceFile\r\n"), + step == Output_Stack ? _T("Call stack") : _T("Local variables")); + + DWORD dwMachineType = 0; + + STACKFRAME sf; + wxZeroMemory(sf); + +#ifdef _M_IX86 + // Initialize the STACKFRAME structure for the first call. This is + // only necessary for Intel CPUs, and isn't mentioned in the + // documentation. + sf.AddrPC.Offset = ctx.Eip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = ctx.Esp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = ctx.Ebp; + sf.AddrFrame.Mode = AddrModeFlat; + + dwMachineType = IMAGE_FILE_MACHINE_I386; +#endif // _M_IX86 + + const HANDLE hProcess = GetCurrentProcess(); + const HANDLE hThread = GetCurrentThread(); + + // first show just the call stack + int frame = 0; + for ( ;; ) + { + // Get the next stack frame + if ( !StackWalk(dwMachineType, + hProcess, + hThread, + &sf, + &ctx, + 0, + SymFunctionTableAccess, + SymGetModuleBase, + 0) ) + { + break; + } + + // Basic sanity check to make sure the frame is OK. + if ( !sf.AddrFrame.Offset ) + break; + + Output(_T("%2d %08x %08x "), + frame++, sf.AddrPC.Offset, sf.AddrFrame.Offset); + + // Get the name of the function for this stack frame entry + BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; + pSymbol->SizeOfStruct = sizeof(symbolBuffer); + pSymbol->MaxNameLen = 1024; + + // Displacement of the input address, relative to the start of the + // symbol + DWORD64 symDisplacement = 0; + + if ( SymFromAddr(hProcess, sf.AddrPC.Offset, + &symDisplacement,pSymbol) ) + { + Output(_T("%hs()+%#") wxLongLongFmtSpec _T("x"), + pSymbol->Name, symDisplacement); + } + else // No symbol found. Print out the logical address instead. + { + TCHAR szModule[MAX_PATH]; + DWORD section, + offset; + + if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset, + szModule, sizeof(szModule), + section, offset) ) + { + szModule[0] = _T('\0'); + section = + offset = 0; + } + + Output(_T("%04x:%08x %s"), section, offset, szModule); + } + + // Get the source line for this stack frame entry + IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; + DWORD dwLineDisplacement; + if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset, + &dwLineDisplacement, &lineInfo )) + { + Output(_T(" %s line %u"), + lineInfo.FileName, lineInfo.LineNumber); + } + + OutputEndl(); + + +#ifdef _M_IX86 + // on the second iteration also show the local variables and + // parameters + if ( step == Output_Locals ) + { + // Use SymSetContext to get just the locals/params for this + // frame + IMAGEHLP_STACK_FRAME imagehlpStackFrame; + imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; + SymSetContext(hProcess, &imagehlpStackFrame, 0); + + // Enumerate the locals/parameters + m_sfCurrent = &sf; + SymEnumSymbols(hProcess, 0, 0, EnumerateSymbolsCallback, this); + + OutputEndl(); + } +#endif // _M_IX86 + } + } +} + +void wxCrashReportImpl::OutputGlobals(HANDLE hModule) +{ +#ifdef _M_IX86 + Output(_T("\r\nGlobal variables:\r\n")); + + m_sfCurrent = NULL; + SymEnumSymbols(::GetCurrentProcess(), (DWORD64)hModule, NULL, + EnumerateSymbolsCallback, this); +#endif // _M_IX86 +} + +bool wxCrashReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp) +{ + #define LOAD_SYM_FUNCTION(name) \ + name = (name ## _t) dllDbgHelp.GetSymbol(#name); \ + if ( !name ) \ + { \ + Output(_T("\r\nFunction ") __XFILE__(#name) \ + _T("() not found.\r\n")); \ + return false; \ + } + + LOAD_SYM_FUNCTION(SymSetOptions); + LOAD_SYM_FUNCTION(SymInitialize); + LOAD_SYM_FUNCTION(StackWalk); + LOAD_SYM_FUNCTION(SymFromAddr); + LOAD_SYM_FUNCTION(SymFunctionTableAccess); + LOAD_SYM_FUNCTION(SymGetModuleBase); + LOAD_SYM_FUNCTION(SymGetLineFromAddr); + LOAD_SYM_FUNCTION(SymSetContext); + LOAD_SYM_FUNCTION(SymEnumSymbols); + LOAD_SYM_FUNCTION(SymGetTypeInfo); + + #undef LOAD_SYM_FUNCTION + + return true; +} + +bool wxCrashReportImpl::Generate(int flags) +{ + if ( m_hFile == INVALID_HANDLE_VALUE ) + return false; + + if ( !wxGlobalSEInformation ) + return false; + + PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord; + PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord; + + if ( !pExceptionRecord || !pCtx ) + return false; + + HANDLE hModuleCrash = OutputBasicContext(pExceptionRecord, pCtx); + + // for everything else we need dbghelp.dll + wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM); + if ( dllDbgHelp.IsLoaded() ) + { + if ( ResolveSymFunctions(dllDbgHelp) ) + { + SymSetOptions(SYMOPT_DEFERRED_LOADS); + + // Initialize DbgHelp + if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) ) + { + OutputStack(pCtx, flags); + + if ( hModuleCrash && (flags & wxCRASH_REPORT_GLOBALS) ) + { + OutputGlobals(hModuleCrash); + } + + return true; + } + } + else + { + Output(_T("Please update your dbghelp.dll version, " + "at least version 5.1 is needed!\r\n")); + } + } + else + { + Output(_T("Please install dbghelp.dll available free of charge ") + _T("from Microsoft to get more detailed crash information!")); + } + + Output(_T("\r\nLatest dbghelp.dll is available at " + "http://www.microsoft.com/whdc/ddk/debugging/\r\n")); + + return true; +} + +/* static */ +wxString wxCrashReportImpl::GetExceptionString(DWORD dwCode) +{ + wxString s; + + #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break + + switch ( dwCode ) + { + CASE_EXCEPTION(ACCESS_VIOLATION); + CASE_EXCEPTION(DATATYPE_MISALIGNMENT); + CASE_EXCEPTION(BREAKPOINT); + CASE_EXCEPTION(SINGLE_STEP); + CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED); + CASE_EXCEPTION(FLT_DENORMAL_OPERAND); + CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO); + CASE_EXCEPTION(FLT_INEXACT_RESULT); + CASE_EXCEPTION(FLT_INVALID_OPERATION); + CASE_EXCEPTION(FLT_OVERFLOW); + CASE_EXCEPTION(FLT_STACK_CHECK); + CASE_EXCEPTION(FLT_UNDERFLOW); + CASE_EXCEPTION(INT_DIVIDE_BY_ZERO); + CASE_EXCEPTION(INT_OVERFLOW); + CASE_EXCEPTION(PRIV_INSTRUCTION); + CASE_EXCEPTION(IN_PAGE_ERROR); + CASE_EXCEPTION(ILLEGAL_INSTRUCTION); + CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION); + CASE_EXCEPTION(STACK_OVERFLOW); + CASE_EXCEPTION(INVALID_DISPOSITION); + CASE_EXCEPTION(GUARD_PAGE); + CASE_EXCEPTION(INVALID_HANDLE); + + default: + // unknown exception, ask NTDLL for the name + if ( !::FormatMessage + ( + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_HMODULE, + ::GetModuleHandle(_T("NTDLL.DLL")), + dwCode, + 0, + wxStringBuffer(s, 1024), + 1024, + 0 + ) ) + { + s = _T("UNKNOWN_EXCEPTION"); + } + } + + #undef CASE_EXCEPTION + + return s; +} + +// ---------------------------------------------------------------------------- +// wxCrashReport +// ---------------------------------------------------------------------------- + +/* static */ +void wxCrashReport::SetFileName(const wxChar *filename) +{ + wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1); + gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0'); +} + +/* static */ +const wxChar *wxCrashReport::GetFileName() +{ + return gs_reportFilename; +} + +/* static */ +bool wxCrashReport::Generate(int flags) +{ + wxCrashReportImpl impl(gs_reportFilename); + + return impl.Generate(flags); +} + +// ---------------------------------------------------------------------------- +// wxApp::OnFatalException() support +// ---------------------------------------------------------------------------- + +bool wxHandleFatalExceptions(bool doit) +{ + // assume this can only be called from the main thread + gs_handleExceptions = doit; + + if ( doit ) + { + // try to find a place where we can put out report file later + if ( !::GetTempPath + ( + WXSIZEOF(gs_reportFilename), + gs_reportFilename + ) ) + { + wxLogLastError(_T("GetTempPath")); + + // when all else fails... + wxStrcpy(gs_reportFilename, _T("c:\\")); + } + + // use PID and date to make the report file name more unique + wxString fname = wxString::Format + ( + _T("%s_%s_%lu.rpt"), + wxTheApp ? wxTheApp->GetAppName().c_str() + : _T("wxwindows"), + wxDateTime::Now().Format(_T("%Y%m%d")).c_str(), + ::GetCurrentProcessId() + ); + + wxStrncat(gs_reportFilename, fname, + WXSIZEOF(gs_reportFilename) - strlen(gs_reportFilename) - 1); + } + + return true; +} + +extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs) +{ + if ( gs_handleExceptions && wxTheApp ) + { + // store the pointer to exception info + wxGlobalSEInformation = pExcPtrs; + + // give the user a chance to do something special about this + wxTheApp->OnFatalException(); + + wxGlobalSEInformation = NULL; + + // this will execute our handler and terminate the process + return EXCEPTION_EXECUTE_HANDLER; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +#else // !wxUSE_ON_FATAL_EXCEPTION + +bool wxHandleFatalExceptions(bool WXUNUSED(doit)) +{ + wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function")); + + return false; +} + +#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION + diff --git a/src/msw/seh.cpp b/src/msw/seh.cpp deleted file mode 100644 index 1e5e336659..0000000000 --- a/src/msw/seh.cpp +++ /dev/null @@ -1,585 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: msw/seh.cpp -// Purpose: helpers for structured exception handling (SEH) -// Author: Vadim Zeitlin -// Modified by: -// Created: 13.07.03 -// RCS-ID: $Id$ -// Copyright: (c) 2003 Vadim Zeitlin -// Licence: wxWindows licence -///////////////////////////////////////////////////////////////////////////// - -/* - The code in this file is heavily based on Matt Pietrek's column from - the 2002 issue of MSDN Magazine. - */ - -// ============================================================================ -// declarations -// ============================================================================ - -// ---------------------------------------------------------------------------- -// headers -// ---------------------------------------------------------------------------- - -// For compilers that support precompilation, includes "wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ - #pragma hdrstop -#endif - -#if wxUSE_ON_FATAL_EXCEPTION - -#ifndef WX_PRECOMP -#endif //WX_PRECOMP - -#include "wx/datetime.h" -#include "wx/dynload.h" - -#include "wx/msw/seh.h" - -#include -#include -#include "wx/msw/private.h" - -// ---------------------------------------------------------------------------- -// types of imagehlp.h functions -// ---------------------------------------------------------------------------- - -typedef DWORD (WINAPI *SymSetOptions_t)(DWORD); -typedef BOOL (WINAPI *SymInitialize_t)(HANDLE, LPSTR, BOOL); -typedef BOOL (WINAPI *StackWalk_t)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, - LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, - PFUNCTION_TABLE_ACCESS_ROUTINE, - PGET_MODULE_BASE_ROUTINE, - PTRANSLATE_ADDRESS_ROUTINE); -typedef BOOL (WINAPI *SymFromAddr_t)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); -typedef LPVOID (WINAPI *SymFunctionTableAccess_t)(HANDLE, DWORD); -typedef DWORD (WINAPI *SymGetModuleBase_t)(HANDLE, DWORD); -typedef BOOL (WINAPI *SymGetLineFromAddr_t)(HANDLE, DWORD, - PDWORD, PIMAGEHLP_LINE); - -// ---------------------------------------------------------------------------- -// classes -// ---------------------------------------------------------------------------- - -// the real crash report generator -class wxSEHReportImpl -{ -public: - wxSEHReportImpl(const wxChar *filename); - - bool Generate(); - - ~wxSEHReportImpl() - { - if ( m_hFile != INVALID_HANDLE_VALUE ) - { - ::CloseHandle(m_hFile); - } - } - -private: - // formatted output to m_hFile - void Output(const wxChar *format, ...); - - // translate exception code to its symbolic name - static wxString GetExceptionString(DWORD dwCode); - - // load all the functions we need from dbghelp.dll, return true if all ok - bool ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp); - - // map address to module (and also section:offset), retunr true if ok - static bool GetLogicalAddress(PVOID addr, - PTSTR szModule, - DWORD len, - DWORD& section, - DWORD& offset); - - - // show the general information about exception which should be always - // available - bool OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pCtx); - - // output the call stack (pCtx may be modified, make copy before call!) - void OutputStack(CONTEXT *pCtx); - - - // the handle of the report file - HANDLE m_hFile; - - // dynamically loaded dbghelp.dll functions - #define DECLARE_SYM_FUNCTION(func) func ## _t func - - DECLARE_SYM_FUNCTION(SymSetOptions); - DECLARE_SYM_FUNCTION(SymInitialize); - DECLARE_SYM_FUNCTION(StackWalk); - DECLARE_SYM_FUNCTION(SymFromAddr); - DECLARE_SYM_FUNCTION(SymFunctionTableAccess); - DECLARE_SYM_FUNCTION(SymGetModuleBase); - DECLARE_SYM_FUNCTION(SymGetLineFromAddr); -}; - -// ---------------------------------------------------------------------------- -// globals -// ---------------------------------------------------------------------------- - -// global pointer to exception information, only valid inside OnFatalException -extern WXDLLIMPEXP_BASE EXCEPTION_POINTERS *wxGlobalSEInformation = NULL; - - -// flag telling us whether the application wants to handle exceptions at all -static bool gs_handleExceptions = false; - -// the file name where the report about exception is written -static wxChar gs_reportFilename[MAX_PATH]; - -// ============================================================================ -// implementation -// ============================================================================ - -// ---------------------------------------------------------------------------- -// wxSEHReport -// ---------------------------------------------------------------------------- - -wxSEHReportImpl::wxSEHReportImpl(const wxChar *filename) -{ - m_hFile = ::CreateFile - ( - filename, - GENERIC_WRITE, - 0, // no sharing - NULL, // default security - CREATE_ALWAYS, - FILE_FLAG_WRITE_THROUGH, - NULL // no template file - ); -} - -void wxSEHReportImpl::Output(const wxChar *format, ...) -{ - va_list argptr; - va_start(argptr, format); - - DWORD cbWritten; - - wxString s = wxString::FormatV(format, argptr); - ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0); - - va_end(argptr); -} - -bool -wxSEHReportImpl::GetLogicalAddress(PVOID addr, - PTSTR szModule, - DWORD len, - DWORD& section, - DWORD& offset) -{ - MEMORY_BASIC_INFORMATION mbi; - - if ( !::VirtualQuery(addr, &mbi, sizeof(mbi)) ) - return false; - - DWORD hMod = (DWORD)mbi.AllocationBase; - - if ( !::GetModuleFileName((HMODULE)hMod, szModule, len) ) - return false; - - // Point to the DOS header in memory - PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; - - // From the DOS header, find the NT (PE) header - PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew); - - PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); - - DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address - - // Iterate through the section table, looking for the one that encompasses - // the linear address. - const DWORD nSections = pNtHdr->FileHeader.NumberOfSections; - for ( DWORD i = 0; i < nSections; i++, pSection++ ) - { - DWORD sectionStart = pSection->VirtualAddress; - DWORD sectionEnd = sectionStart - + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize); - - // Is the address in this section? - if ( (rva >= sectionStart) && (rva <= sectionEnd) ) - { - // Yes, address is in the section. Calculate section and offset, - // and store in the "section" & "offset" params, which were - // passed by reference. - section = i + 1; - offset = rva - sectionStart; - - return true; - } - } - - // failed to map to logical address... - return false; -} - -bool -wxSEHReportImpl::OutputBasicContext(EXCEPTION_RECORD *pExceptionRecord, - CONTEXT *pCtx) -{ - // First print information about the type of fault - const DWORD dwCode = pExceptionRecord->ExceptionCode; - Output(_T("Exception code: %s (%#08x)\r\n"), - GetExceptionString(dwCode).c_str(), dwCode); - - // Now print information about where the fault occured - TCHAR szFaultingModule[MAX_PATH]; - DWORD section, - offset; - void * const pExceptionAddress = pExceptionRecord->ExceptionAddress; - if ( !GetLogicalAddress(pExceptionAddress, - szFaultingModule, - WXSIZEOF(szFaultingModule), - section, offset) ) - { - section = - offset = 0; - - wxStrcpy(szFaultingModule, _T("<< unknown >>")); - } - - Output(_T("Fault address: %08x %02x:%08x %s\r\n"), - pExceptionAddress, section, offset, szFaultingModule); - - // Show the registers -#ifdef _M_IX86 - Output( _T("\r\nRegisters:\r\n") ); - - Output(_T("EAX: %08x EBX: %08x ECX: %08x EDX: %08x ESI: %08x EDI: %08x\r\n"), - pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi); - - Output(_T("CS:EIP: %04x:%08x SS:ESP: %04x:%08x EBP: %08x\r\n"), - pCtx->SegCs, pCtx->Eip, pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); - Output(_T("DS: %04x ES: %04x FS: %04x GS: %04x\r\n"), - pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); - Output(_T("Flags: %08x\r\n"), pCtx->EFlags ); -#endif // _M_IX86 - - return true; -} - -void wxSEHReportImpl::OutputStack(CONTEXT *pCtx) -{ - Output(_T("\r\nCall stack:\r\n")); - - Output(_T("Address Frame Function SourceFile\r\n")); - - DWORD dwMachineType = 0; - - STACKFRAME sf; - wxZeroMemory(sf); - -#ifdef _M_IX86 - // Initialize the STACKFRAME structure for the first call. This is only - // necessary for Intel CPUs, and isn't mentioned in the documentation. - sf.AddrPC.Offset = pCtx->Eip; - sf.AddrPC.Mode = AddrModeFlat; - sf.AddrStack.Offset = pCtx->Esp; - sf.AddrStack.Mode = AddrModeFlat; - sf.AddrFrame.Offset = pCtx->Ebp; - sf.AddrFrame.Mode = AddrModeFlat; - - dwMachineType = IMAGE_FILE_MACHINE_I386; -#endif // _M_IX86 - - const HANDLE hProcess = GetCurrentProcess(); - const HANDLE hThread = GetCurrentThread(); - - for ( ;; ) - { - // Get the next stack frame - if ( !StackWalk(dwMachineType, - hProcess, - hThread, - &sf, - pCtx, - 0, - SymFunctionTableAccess, - SymGetModuleBase, - 0) ) - { - break; - } - - // Basic sanity check to make sure the frame is OK. - if ( !sf.AddrFrame.Offset ) - break; - - Output(_T("%08x %08x "), sf.AddrPC.Offset, sf.AddrFrame.Offset); - - // Get the name of the function for this stack frame entry - BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ]; - PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; - pSymbol->SizeOfStruct = sizeof(symbolBuffer); - pSymbol->MaxNameLen = 1024; - DWORD64 symDisplacement = 0; // Displacement of the input address, - // relative to the start of the symbol - - if ( SymFromAddr(hProcess, sf.AddrPC.Offset, - &symDisplacement,pSymbol) ) - { - Output(_T("%hs() + %#08I64x"), pSymbol->Name, symDisplacement); - } - else // No symbol found. Print out the logical address instead. - { - TCHAR szModule[MAX_PATH]; - DWORD section, - offset; - - if ( !GetLogicalAddress((PVOID)sf.AddrPC.Offset, - szModule, sizeof(szModule), - section, offset) ) - { - szModule[0] = _T('\0'); - section = - offset = 0; - } - - Output(_T("%04x:%08x %s"), section, offset, szModule); - } - - // Get the source line for this stack frame entry - IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; - DWORD dwLineDisplacement; - if ( SymGetLineFromAddr(hProcess, sf.AddrPC.Offset, - &dwLineDisplacement, &lineInfo )) - { - Output(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber); - } - - Output(_T("\r\n")); - } -} - -bool wxSEHReportImpl::ResolveSymFunctions(const wxDynamicLibrary& dllDbgHelp) -{ - #define LOAD_SYM_FUNCTION(name) \ - name = (name ## _t) dllDbgHelp.GetSymbol(#name); \ - if ( !name ) \ - { \ - Output(_T("\r\nFunction ") __XFILE__(#name) \ - _T("() not found.\r\n")); \ - return false; \ - } - - LOAD_SYM_FUNCTION(SymSetOptions); - LOAD_SYM_FUNCTION(SymInitialize); - LOAD_SYM_FUNCTION(StackWalk); - LOAD_SYM_FUNCTION(SymFromAddr); - LOAD_SYM_FUNCTION(SymFunctionTableAccess); - LOAD_SYM_FUNCTION(SymGetModuleBase); - LOAD_SYM_FUNCTION(SymGetLineFromAddr); - - #undef LOAD_SYM_FUNCTION - - return true; -} - -bool wxSEHReportImpl::Generate() -{ - if ( m_hFile == INVALID_HANDLE_VALUE ) - return false; - - PEXCEPTION_RECORD pExceptionRecord = wxGlobalSEInformation->ExceptionRecord; - PCONTEXT pCtx = wxGlobalSEInformation->ContextRecord; - - if ( !OutputBasicContext(pExceptionRecord, pCtx) ) - return false; - - // for everything else we need dbghelp.dll - wxDynamicLibrary dllDbgHelp(_T("dbghelp.dll"), wxDL_VERBATIM); - if ( dllDbgHelp.IsLoaded() ) - { - if ( ResolveSymFunctions(dllDbgHelp) ) - { - SymSetOptions(SYMOPT_DEFERRED_LOADS); - - // Initialize DbgHelp - if ( SymInitialize(GetCurrentProcess(), NULL, TRUE /* invade */) ) - { - CONTEXT ctxCopy = *pCtx; - - OutputStack(&ctxCopy); - - return true; - } - } - else - { - Output(_T("Please update your dbghelp.dll version, " - "at least version 6.0 is needed!\r\n")); - } - } - else - { - Output(_T("Please install dbghelp.dll available free of charge ") - _T("from Microsoft to get more detailed crash information!")); - } - - Output(_T("\r\nLatest dbghelp.dll is available at " - "http://www.microsoft.com/whdc/ddk/debugging/\r\n")); - - return true; -} - -/* static */ -wxString wxSEHReportImpl::GetExceptionString(DWORD dwCode) -{ - wxString s; - - #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break - - switch ( dwCode ) - { - CASE_EXCEPTION(ACCESS_VIOLATION); - CASE_EXCEPTION(DATATYPE_MISALIGNMENT); - CASE_EXCEPTION(BREAKPOINT); - CASE_EXCEPTION(SINGLE_STEP); - CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED); - CASE_EXCEPTION(FLT_DENORMAL_OPERAND); - CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO); - CASE_EXCEPTION(FLT_INEXACT_RESULT); - CASE_EXCEPTION(FLT_INVALID_OPERATION); - CASE_EXCEPTION(FLT_OVERFLOW); - CASE_EXCEPTION(FLT_STACK_CHECK); - CASE_EXCEPTION(FLT_UNDERFLOW); - CASE_EXCEPTION(INT_DIVIDE_BY_ZERO); - CASE_EXCEPTION(INT_OVERFLOW); - CASE_EXCEPTION(PRIV_INSTRUCTION); - CASE_EXCEPTION(IN_PAGE_ERROR); - CASE_EXCEPTION(ILLEGAL_INSTRUCTION); - CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION); - CASE_EXCEPTION(STACK_OVERFLOW); - CASE_EXCEPTION(INVALID_DISPOSITION); - CASE_EXCEPTION(GUARD_PAGE); - CASE_EXCEPTION(INVALID_HANDLE); - - default: - // unknown exception, ask NTDLL for the name - if ( !::FormatMessage - ( - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_HMODULE, - ::GetModuleHandle(_T("NTDLL.DLL")), - dwCode, - 0, - wxStringBuffer(s, 1024), - 1024, - 0 - ) ) - { - s = _T("UNKNOWN_EXCEPTION"); - } - } - - #undef CASE_EXCEPTION - - return s; -} - -// ---------------------------------------------------------------------------- -// wxSEHReport -// ---------------------------------------------------------------------------- - -/* static */ -void wxSEHReport::SetFileName(const wxChar *filename) -{ - wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1); - gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0'); -} - -/* static */ -const wxChar *wxSEHReport::GetFileName() -{ - return gs_reportFilename; -} - -/* static */ -bool wxSEHReport::Generate() -{ - wxSEHReportImpl impl(gs_reportFilename); - - return impl.Generate(); -} - -// ---------------------------------------------------------------------------- -// wxApp::OnFatalException() support -// ---------------------------------------------------------------------------- - -bool wxHandleFatalExceptions(bool doit) -{ - // assume this can only be called from the main thread - gs_handleExceptions = doit; - - if ( doit ) - { - // try to find a place where we can put out report file later - if ( !::GetTempPath - ( - WXSIZEOF(gs_reportFilename), - gs_reportFilename - ) ) - { - wxLogLastError(_T("GetTempPath")); - - // when all else fails... - wxStrcpy(gs_reportFilename, _T("c:\\")); - } - - // use PID and date to make the report file name more unique - wxString fname = wxString::Format - ( - _T("%s_%s_%lu.rpt"), - wxTheApp ? wxTheApp->GetAppName().c_str() - : _T("wxwindows"), - wxDateTime::Now().Format(_T("%Y%m%d")).c_str(), - ::GetCurrentProcessId() - ); - - wxStrncat(gs_reportFilename, fname, - WXSIZEOF(gs_reportFilename) - strlen(gs_reportFilename) - 1); - } - - return true; -} - -extern unsigned long wxGlobalSEHandler(EXCEPTION_POINTERS *pExcPtrs) -{ - if ( gs_handleExceptions && wxTheApp ) - { - // store the pointer to exception info - wxGlobalSEInformation = pExcPtrs; - - // give the user a chance to do something special about this - wxTheApp->OnFatalException(); - - wxGlobalSEInformation = NULL; - - // this will execute our handler and terminate the process - return EXCEPTION_EXECUTE_HANDLER; - } - - return EXCEPTION_CONTINUE_SEARCH; -} - -#else // !wxUSE_ON_FATAL_EXCEPTION - -bool wxHandleFatalExceptions(bool WXUNUSED(doit)) -{ - wxFAIL_MSG(_T("set wxUSE_ON_FATAL_EXCEPTION to 1 to use this function")); - - return false; -} - -#endif // wxUSE_ON_FATAL_EXCEPTION/!wxUSE_ON_FATAL_EXCEPTION - -- 2.45.2