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
This commit is contained in:
Vadim Zeitlin 2003-07-13 21:18:04 +00:00
parent 335991afa5
commit 50bea10032
3 changed files with 1144 additions and 589 deletions

View File

@ -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

1116
src/msw/crashrpt.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 <vadim@wxwindows.org>
// 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 <windows.h>
#include <imagehlp.h>
#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