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             // the file size is not much bigger than when using MiniDumpNormal 
 199             // if we use the flags below, but the minidump is much more useful 
 200             // as it contains the values of many (but not all) local variables 
 201             dumpFlags 
= (MINIDUMP_TYPE
)(MiniDumpScanMemory
 
 203                                         |MiniDumpWithIndirectlyReferencedMemory
 
 208         if ( !wxDbgHelpDLL::MiniDumpWriteDump
 
 210                 ::GetCurrentProcess(), 
 211                 ::GetCurrentProcessId(), 
 212                 m_hFile
,                    // file to write to 
 213                 dumpFlags
,                  // kind of dump to craete 
 215                 NULL
,                       // no extra user-defined data 
 219             Output(_T("MiniDumpWriteDump() failed.")); 
 226     else // dbghelp.dll couldn't be loaded 
 228         Output(wxDbgHelpDLL::GetErrorMessage()); 
 230 #else // !wxUSE_DBGHELP 
 234     Output(_T("Support for crash report generation was not included ") 
 235            _T("in this wxWidgets version.")); 
 236 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP 
 241 // ---------------------------------------------------------------------------- 
 243 // ---------------------------------------------------------------------------- 
 246 void wxCrashReport::SetFileName(const wxChar 
*filename
) 
 248     wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1); 
 249     gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0'); 
 253 const wxChar 
*wxCrashReport::GetFileName() 
 255     return gs_reportFilename
; 
 259 bool wxCrashReport::Generate(int flags
, EXCEPTION_POINTERS 
*ep
) 
 261     wxCrashReportImpl 
impl(gs_reportFilename
); 
 263     return impl
.Generate(flags
, ep
); 
 267 bool wxCrashReport::GenerateNow(int flags
) 
 273         RaiseException(0x1976, 0, 0, NULL
); 
 275     __except( rc 
= Generate(flags
, (EXCEPTION_POINTERS 
*)GetExceptionInformation()), 
 276               EXCEPTION_CONTINUE_EXECUTION 
) 
 278         // never executed because of EXCEPTION_CONTINUE_EXECUTION above 
 284 // ---------------------------------------------------------------------------- 
 286 // ---------------------------------------------------------------------------- 
 288 wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS 
*ep
) 
 294         wxCHECK_RET( wxGlobalSEInformation
, _T("no exception info available") ); 
 295         ep 
= wxGlobalSEInformation
; 
 298     // TODO: we could also get the operation (read/write) and address for which 
 299     //       it failed for EXCEPTION_ACCESS_VIOLATION code 
 300     const EXCEPTION_RECORD
& rec 
= *ep
->ExceptionRecord
; 
 301     code 
= rec
.ExceptionCode
; 
 302     addr 
= rec
.ExceptionAddress
; 
 305     const CONTEXT
& ctx 
= *ep
->ContextRecord
; 
 324     regs
.flags 
= ctx
.EFlags
; 
 328 wxString 
wxCrashContext::GetExceptionString() const 
 332     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break 
 336         CASE_EXCEPTION(ACCESS_VIOLATION
); 
 337         CASE_EXCEPTION(DATATYPE_MISALIGNMENT
); 
 338         CASE_EXCEPTION(BREAKPOINT
); 
 339         CASE_EXCEPTION(SINGLE_STEP
); 
 340         CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
); 
 341         CASE_EXCEPTION(FLT_DENORMAL_OPERAND
); 
 342         CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
); 
 343         CASE_EXCEPTION(FLT_INEXACT_RESULT
); 
 344         CASE_EXCEPTION(FLT_INVALID_OPERATION
); 
 345         CASE_EXCEPTION(FLT_OVERFLOW
); 
 346         CASE_EXCEPTION(FLT_STACK_CHECK
); 
 347         CASE_EXCEPTION(FLT_UNDERFLOW
); 
 348         CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
); 
 349         CASE_EXCEPTION(INT_OVERFLOW
); 
 350         CASE_EXCEPTION(PRIV_INSTRUCTION
); 
 351         CASE_EXCEPTION(IN_PAGE_ERROR
); 
 352         CASE_EXCEPTION(ILLEGAL_INSTRUCTION
); 
 353         CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
); 
 354         CASE_EXCEPTION(STACK_OVERFLOW
); 
 355         CASE_EXCEPTION(INVALID_DISPOSITION
); 
 356         CASE_EXCEPTION(GUARD_PAGE
); 
 357         CASE_EXCEPTION(INVALID_HANDLE
); 
 360             // unknown exception, ask NTDLL for the name 
 361             if ( !::FormatMessage
 
 363                      FORMAT_MESSAGE_IGNORE_INSERTS 
| 
 364                      FORMAT_MESSAGE_FROM_HMODULE
, 
 365                      ::GetModuleHandle(_T("NTDLL.DLL")), 
 368                      wxStringBuffer(s
, 1024), 
 373                 s
.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code
); 
 377     #undef CASE_EXCEPTION 
 382 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT