git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53816 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
386 lines
11 KiB
C++
386 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: msw/crashrpt.cpp
|
|
// Purpose: code to generate crash dumps (minidumps)
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 13.07.03
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_CRASHREPORT
|
|
|
|
#ifndef WX_PRECOMP
|
|
#endif //WX_PRECOMP
|
|
|
|
#include "wx/msw/debughlp.h"
|
|
#include "wx/msw/crashrpt.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// classes
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// low level wxBusyCursor replacement: we use Win32 API directly here instead
|
|
// of going through wxWidgets calls as this could be dangerous
|
|
class BusyCursor
|
|
{
|
|
public:
|
|
BusyCursor()
|
|
{
|
|
HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
|
|
m_hcursorOld = ::SetCursor(hcursorBusy);
|
|
}
|
|
|
|
~BusyCursor()
|
|
{
|
|
if ( m_hcursorOld )
|
|
{
|
|
::SetCursor(m_hcursorOld);
|
|
}
|
|
}
|
|
|
|
private:
|
|
HCURSOR m_hcursorOld;
|
|
};
|
|
|
|
// the real crash report generator
|
|
class wxCrashReportImpl
|
|
{
|
|
public:
|
|
wxCrashReportImpl(const wxChar *filename);
|
|
|
|
bool Generate(int flags, EXCEPTION_POINTERS *ep);
|
|
|
|
~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")); }
|
|
|
|
// the handle of the report file
|
|
HANDLE m_hFile;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// globals
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// the file name where the report about exception is written
|
|
//
|
|
// we use fixed buffer to avoid (big) dynamic allocations when the program
|
|
// crashes
|
|
static wxChar gs_reportFilename[MAX_PATH];
|
|
|
|
// this is defined in msw/main.cpp
|
|
extern EXCEPTION_POINTERS *wxGlobalSEInformation;
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxCrashReportImpl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxCrashReportImpl::wxCrashReportImpl(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 wxCrashReportImpl::Output(const wxChar *format, ...)
|
|
{
|
|
va_list argptr;
|
|
va_start(argptr, format);
|
|
|
|
DWORD cbWritten;
|
|
|
|
wxString s = wxString::FormatV(format, argptr);
|
|
|
|
wxCharBuffer buf(s.mb_str(wxConvUTF8));
|
|
::WriteFile(m_hFile, buf.data(), strlen(buf.data()), &cbWritten, 0);
|
|
|
|
va_end(argptr);
|
|
}
|
|
|
|
bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
|
|
{
|
|
if ( m_hFile == INVALID_HANDLE_VALUE )
|
|
return false;
|
|
|
|
#if wxUSE_DBGHELP
|
|
if ( !ep )
|
|
ep = wxGlobalSEInformation;
|
|
|
|
if ( !ep )
|
|
{
|
|
Output(_T("Context for crash report generation not available."));
|
|
return false;
|
|
}
|
|
|
|
// show to the user that we're doing something...
|
|
BusyCursor busyCursor;
|
|
|
|
// user-specified crash report flags override those specified by the
|
|
// programmer
|
|
TCHAR envFlags[64];
|
|
DWORD dwLen = ::GetEnvironmentVariable
|
|
(
|
|
_T("WX_CRASH_FLAGS"),
|
|
envFlags,
|
|
WXSIZEOF(envFlags)
|
|
);
|
|
|
|
int flagsEnv;
|
|
if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
|
|
wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
|
|
{
|
|
flags = flagsEnv;
|
|
}
|
|
|
|
if ( wxDbgHelpDLL::Init() )
|
|
{
|
|
MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
|
|
|
|
minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
|
|
minidumpExcInfo.ExceptionPointers = ep;
|
|
minidumpExcInfo.ClientPointers = FALSE; // in our own address space
|
|
|
|
// do generate the dump
|
|
MINIDUMP_TYPE dumpFlags;
|
|
if ( flags & wxCRASH_REPORT_LOCALS )
|
|
{
|
|
// the only way to get local variables is to dump the entire
|
|
// process memory space -- but this makes for huge (dozens or
|
|
// even hundreds of Mb) files
|
|
dumpFlags = MiniDumpWithFullMemory;
|
|
}
|
|
else if ( flags & wxCRASH_REPORT_GLOBALS )
|
|
{
|
|
// MiniDumpWriteDump() has the option for dumping just the data
|
|
// segment which contains all globals -- exactly what we need
|
|
dumpFlags = MiniDumpWithDataSegs;
|
|
}
|
|
else // minimal dump
|
|
{
|
|
// the file size is not much bigger than when using MiniDumpNormal
|
|
// if we use the flags below, but the minidump is much more useful
|
|
// as it contains the values of many (but not all) local variables
|
|
dumpFlags = (MINIDUMP_TYPE)(MiniDumpScanMemory
|
|
#if _MSC_VER > 1300
|
|
|MiniDumpWithIndirectlyReferencedMemory
|
|
#endif
|
|
);
|
|
}
|
|
|
|
if ( !wxDbgHelpDLL::MiniDumpWriteDump
|
|
(
|
|
::GetCurrentProcess(),
|
|
::GetCurrentProcessId(),
|
|
m_hFile, // file to write to
|
|
dumpFlags, // kind of dump to craete
|
|
&minidumpExcInfo,
|
|
NULL, // no extra user-defined data
|
|
NULL // no callbacks
|
|
) )
|
|
{
|
|
Output(_T("MiniDumpWriteDump() failed."));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else // dbghelp.dll couldn't be loaded
|
|
{
|
|
Output(_T("%s"), wxDbgHelpDLL::GetErrorMessage().c_str());
|
|
}
|
|
#else // !wxUSE_DBGHELP
|
|
wxUnusedVar(flags);
|
|
wxUnusedVar(ep);
|
|
|
|
Output(_T("Support for crash report generation was not included ")
|
|
_T("in this wxWidgets version."));
|
|
#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxCrashReport
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/* static */
|
|
void wxCrashReport::SetFileName(const wxString& filename)
|
|
{
|
|
wxStrncpy(gs_reportFilename, filename.c_str(), WXSIZEOF(gs_reportFilename) - 1);
|
|
gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
|
|
}
|
|
|
|
/* static */
|
|
wxString wxCrashReport::GetFileName()
|
|
{
|
|
return gs_reportFilename;
|
|
}
|
|
|
|
/* static */
|
|
bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
|
|
{
|
|
wxCrashReportImpl impl(gs_reportFilename);
|
|
|
|
return impl.Generate(flags, ep);
|
|
}
|
|
|
|
/* static */
|
|
bool wxCrashReport::GenerateNow(int flags)
|
|
{
|
|
bool rc = false;
|
|
|
|
__try
|
|
{
|
|
RaiseException(0x1976, 0, 0, NULL);
|
|
}
|
|
__except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()),
|
|
EXCEPTION_CONTINUE_EXECUTION )
|
|
{
|
|
// never executed because of EXCEPTION_CONTINUE_EXECUTION above
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxCrashContext
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
|
|
{
|
|
wxZeroMemory(*this);
|
|
|
|
if ( !ep )
|
|
{
|
|
wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") );
|
|
ep = wxGlobalSEInformation;
|
|
}
|
|
|
|
// TODO: we could also get the operation (read/write) and address for which
|
|
// it failed for EXCEPTION_ACCESS_VIOLATION code
|
|
const EXCEPTION_RECORD& rec = *ep->ExceptionRecord;
|
|
code = rec.ExceptionCode;
|
|
addr = rec.ExceptionAddress;
|
|
|
|
#ifdef __INTEL__
|
|
const CONTEXT& ctx = *ep->ContextRecord;
|
|
regs.eax = ctx.Eax;
|
|
regs.ebx = ctx.Ebx;
|
|
regs.ecx = ctx.Ecx;
|
|
regs.edx = ctx.Edx;
|
|
regs.esi = ctx.Esi;
|
|
regs.edi = ctx.Edi;
|
|
|
|
regs.ebp = ctx.Ebp;
|
|
regs.esp = ctx.Esp;
|
|
regs.eip = ctx.Eip;
|
|
|
|
regs.cs = ctx.SegCs;
|
|
regs.ds = ctx.SegDs;
|
|
regs.es = ctx.SegEs;
|
|
regs.fs = ctx.SegFs;
|
|
regs.gs = ctx.SegGs;
|
|
regs.ss = ctx.SegSs;
|
|
|
|
regs.flags = ctx.EFlags;
|
|
#endif // __INTEL__
|
|
}
|
|
|
|
wxString wxCrashContext::GetExceptionString() const
|
|
{
|
|
wxString s;
|
|
|
|
#define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
|
|
|
|
switch ( code )
|
|
{
|
|
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")),
|
|
code,
|
|
0,
|
|
wxStringBuffer(s, 1024),
|
|
1024,
|
|
0
|
|
) )
|
|
{
|
|
s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code);
|
|
}
|
|
}
|
|
|
|
#undef CASE_EXCEPTION
|
|
|
|
return s;
|
|
}
|
|
|
|
#endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
|
|
|