1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/crashrpt.cpp 
   3 // Purpose:     code to generate crash dumps (minidumps) 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  32 #include "wx/msw/debughlp.h" 
  33 #include "wx/msw/crashrpt.h" 
  35 // ---------------------------------------------------------------------------- 
  37 // ---------------------------------------------------------------------------- 
  39 // low level wxBusyCursor replacement: we use Win32 API directly here instead 
  40 // of going through wxWidgets calls as this could be dangerous 
  46         HCURSOR hcursorBusy 
= ::LoadCursor(NULL
, IDC_WAIT
); 
  47         m_hcursorOld 
= ::SetCursor(hcursorBusy
); 
  54             ::SetCursor(m_hcursorOld
); 
  62 // the real crash report generator 
  63 class wxCrashReportImpl
 
  66     wxCrashReportImpl(const wxChar 
*filename
); 
  68     bool Generate(int flags
, EXCEPTION_POINTERS 
*ep
); 
  72         if ( m_hFile 
!= INVALID_HANDLE_VALUE 
) 
  74             ::CloseHandle(m_hFile
); 
  80     // formatted output to m_hFile 
  81     void Output(const wxChar 
*format
, ...); 
  84     void OutputEndl() { Output(_T("\r\n")); } 
  86     // the handle of the report file 
  90 // ---------------------------------------------------------------------------- 
  92 // ---------------------------------------------------------------------------- 
  94 // the file name where the report about exception is written 
  96 // we use fixed buffer to avoid (big) dynamic allocations when the program 
  98 static wxChar gs_reportFilename
[MAX_PATH
]; 
 100 // this is defined in msw/main.cpp 
 101 extern EXCEPTION_POINTERS 
*wxGlobalSEInformation
; 
 103 // ============================================================================ 
 105 // ============================================================================ 
 107 // ---------------------------------------------------------------------------- 
 109 // ---------------------------------------------------------------------------- 
 111 wxCrashReportImpl::wxCrashReportImpl(const wxChar 
*filename
) 
 113     m_hFile 
= ::CreateFile
 
 118                     NULL
,                       // default security 
 120                     FILE_FLAG_WRITE_THROUGH
, 
 121                     NULL                        
// no template file 
 125 void wxCrashReportImpl::Output(const wxChar 
*format
, ...) 
 128     va_start(argptr
, format
); 
 132     wxString s 
= wxString::FormatV(format
, argptr
); 
 133     ::WriteFile(m_hFile
, s
, s
.length() * sizeof(wxChar
), &cbWritten
, 0); 
 138 bool wxCrashReportImpl::Generate(int flags
, EXCEPTION_POINTERS 
*ep
) 
 140     if ( m_hFile 
== INVALID_HANDLE_VALUE 
) 
 145         ep 
= wxGlobalSEInformation
; 
 149         Output(_T("Context for crash report generation not available.")); 
 153     // show to the user that we're doing something... 
 154     BusyCursor busyCursor
; 
 156     // user-specified crash report flags override those specified by the 
 159     DWORD dwLen 
= ::GetEnvironmentVariable
 
 161                         _T("WX_CRASH_FLAGS"), 
 167     if ( dwLen 
&& dwLen 
< WXSIZEOF(envFlags
) && 
 168             wxSscanf(envFlags
, _T("%d"), &flagsEnv
) == 1 ) 
 173     if ( wxDbgHelpDLL::Init() ) 
 175         MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo
; 
 177         minidumpExcInfo
.ThreadId 
= ::GetCurrentThreadId(); 
 178         minidumpExcInfo
.ExceptionPointers 
= ep
; 
 179         minidumpExcInfo
.ClientPointers 
= FALSE
; // in our own address space 
 181         // do generate the dump 
 182         MINIDUMP_TYPE dumpFlags
; 
 183         if ( flags 
& wxCRASH_REPORT_LOCALS 
) 
 185             // the only way to get local variables is to dump the entire 
 186             // process memory space -- but this makes for huge (dozens or 
 187             // even hundreds of Mb) files 
 188             dumpFlags 
= MiniDumpWithFullMemory
; 
 190         else if ( flags 
& wxCRASH_REPORT_GLOBALS 
) 
 192             // MiniDumpWriteDump() has the option for dumping just the data 
 193             // segment which contains all globals -- exactly what we need 
 194             dumpFlags 
= MiniDumpWithDataSegs
; 
 198             dumpFlags 
= MiniDumpNormal
; 
 201         if ( !wxDbgHelpDLL::MiniDumpWriteDump
 
 203                 ::GetCurrentProcess(), 
 204                 ::GetCurrentProcessId(), 
 205                 m_hFile
,                    // file to write to 
 206                 dumpFlags
,                  // kind of dump to craete 
 208                 NULL
,                       // no extra user-defined data 
 212             Output(_T("MiniDumpWriteDump() failed.")); 
 219     else // dbghelp.dll couldn't be loaded 
 221         Output(wxDbgHelpDLL::GetErrorMessage()); 
 223 #else // !wxUSE_DBGHELP 
 226     Output(_T("Support for crash report generation was not included ") 
 227            _T("in this wxWidgets version.")); 
 228 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 
 233 // ---------------------------------------------------------------------------- 
 235 // ---------------------------------------------------------------------------- 
 238 void wxCrashReport::SetFileName(const wxChar 
*filename
) 
 240     wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1); 
 241     gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0'); 
 245 const wxChar 
*wxCrashReport::GetFileName() 
 247     return gs_reportFilename
; 
 251 bool wxCrashReport::Generate(int flags
, EXCEPTION_POINTERS 
*ep
) 
 253     wxCrashReportImpl 
impl(gs_reportFilename
); 
 255     return impl
.Generate(flags
, ep
); 
 259 bool wxCrashReport::GenerateNow(int flags
) 
 265         RaiseException(0x1976, 0, 0, NULL
); 
 267     __except( rc 
= Generate(flags
, (EXCEPTION_POINTERS 
*)GetExceptionInformation()), 
 268               EXCEPTION_CONTINUE_EXECUTION 
) 
 270         // never executed because of EXCEPTION_CONTINUE_EXECUTION above 
 276 // ---------------------------------------------------------------------------- 
 278 // ---------------------------------------------------------------------------- 
 280 wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS 
*ep
) 
 286         wxCHECK_RET( wxGlobalSEInformation
, _T("no exception info available") ); 
 287         ep 
= wxGlobalSEInformation
; 
 290     // TODO: we could also get the operation (read/write) and address for which 
 291     //       it failed for EXCEPTION_ACCESS_VIOLATION code 
 292     const EXCEPTION_RECORD
& rec 
= *ep
->ExceptionRecord
; 
 293     code 
= rec
.ExceptionCode
; 
 294     addr 
= rec
.ExceptionAddress
; 
 297     const CONTEXT
& ctx 
= *ep
->ContextRecord
; 
 316     regs
.flags 
= ctx
.EFlags
; 
 320 wxString 
wxCrashContext::GetExceptionString() const 
 324     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break 
 328         CASE_EXCEPTION(ACCESS_VIOLATION
); 
 329         CASE_EXCEPTION(DATATYPE_MISALIGNMENT
); 
 330         CASE_EXCEPTION(BREAKPOINT
); 
 331         CASE_EXCEPTION(SINGLE_STEP
); 
 332         CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
); 
 333         CASE_EXCEPTION(FLT_DENORMAL_OPERAND
); 
 334         CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
); 
 335         CASE_EXCEPTION(FLT_INEXACT_RESULT
); 
 336         CASE_EXCEPTION(FLT_INVALID_OPERATION
); 
 337         CASE_EXCEPTION(FLT_OVERFLOW
); 
 338         CASE_EXCEPTION(FLT_STACK_CHECK
); 
 339         CASE_EXCEPTION(FLT_UNDERFLOW
); 
 340         CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
); 
 341         CASE_EXCEPTION(INT_OVERFLOW
); 
 342         CASE_EXCEPTION(PRIV_INSTRUCTION
); 
 343         CASE_EXCEPTION(IN_PAGE_ERROR
); 
 344         CASE_EXCEPTION(ILLEGAL_INSTRUCTION
); 
 345         CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
); 
 346         CASE_EXCEPTION(STACK_OVERFLOW
); 
 347         CASE_EXCEPTION(INVALID_DISPOSITION
); 
 348         CASE_EXCEPTION(GUARD_PAGE
); 
 349         CASE_EXCEPTION(INVALID_HANDLE
); 
 352             // unknown exception, ask NTDLL for the name 
 353             if ( !::FormatMessage
 
 355                      FORMAT_MESSAGE_IGNORE_INSERTS 
| 
 356                      FORMAT_MESSAGE_FROM_HMODULE
, 
 357                      ::GetModuleHandle(_T("NTDLL.DLL")), 
 360                      wxStringBuffer(s
, 1024), 
 365                 s
.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code
); 
 369     #undef CASE_EXCEPTION 
 374 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT