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
|
202 MiniDumpWithIndirectlyReferencedMemory
);
205 if ( !wxDbgHelpDLL::MiniDumpWriteDump
207 ::GetCurrentProcess(),
208 ::GetCurrentProcessId(),
209 m_hFile
, // file to write to
210 dumpFlags
, // kind of dump to craete
212 NULL
, // no extra user-defined data
216 Output(_T("MiniDumpWriteDump() failed."));
223 else // dbghelp.dll couldn't be loaded
225 Output(wxDbgHelpDLL::GetErrorMessage());
227 #else // !wxUSE_DBGHELP
231 Output(_T("Support for crash report generation was not included ")
232 _T("in this wxWidgets version."));
233 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
238 // ----------------------------------------------------------------------------
240 // ----------------------------------------------------------------------------
243 void wxCrashReport::SetFileName(const wxChar
*filename
)
245 wxStrncpy(gs_reportFilename
, filename
, WXSIZEOF(gs_reportFilename
) - 1);
246 gs_reportFilename
[WXSIZEOF(gs_reportFilename
) - 1] = _T('\0');
250 const wxChar
*wxCrashReport::GetFileName()
252 return gs_reportFilename
;
256 bool wxCrashReport::Generate(int flags
, EXCEPTION_POINTERS
*ep
)
258 wxCrashReportImpl
impl(gs_reportFilename
);
260 return impl
.Generate(flags
, ep
);
264 bool wxCrashReport::GenerateNow(int flags
)
270 RaiseException(0x1976, 0, 0, NULL
);
272 __except( rc
= Generate(flags
, (EXCEPTION_POINTERS
*)GetExceptionInformation()),
273 EXCEPTION_CONTINUE_EXECUTION
)
275 // never executed because of EXCEPTION_CONTINUE_EXECUTION above
281 // ----------------------------------------------------------------------------
283 // ----------------------------------------------------------------------------
285 wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS
*ep
)
291 wxCHECK_RET( wxGlobalSEInformation
, _T("no exception info available") );
292 ep
= wxGlobalSEInformation
;
295 // TODO: we could also get the operation (read/write) and address for which
296 // it failed for EXCEPTION_ACCESS_VIOLATION code
297 const EXCEPTION_RECORD
& rec
= *ep
->ExceptionRecord
;
298 code
= rec
.ExceptionCode
;
299 addr
= rec
.ExceptionAddress
;
302 const CONTEXT
& ctx
= *ep
->ContextRecord
;
321 regs
.flags
= ctx
.EFlags
;
325 wxString
wxCrashContext::GetExceptionString() const
329 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
333 CASE_EXCEPTION(ACCESS_VIOLATION
);
334 CASE_EXCEPTION(DATATYPE_MISALIGNMENT
);
335 CASE_EXCEPTION(BREAKPOINT
);
336 CASE_EXCEPTION(SINGLE_STEP
);
337 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED
);
338 CASE_EXCEPTION(FLT_DENORMAL_OPERAND
);
339 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO
);
340 CASE_EXCEPTION(FLT_INEXACT_RESULT
);
341 CASE_EXCEPTION(FLT_INVALID_OPERATION
);
342 CASE_EXCEPTION(FLT_OVERFLOW
);
343 CASE_EXCEPTION(FLT_STACK_CHECK
);
344 CASE_EXCEPTION(FLT_UNDERFLOW
);
345 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO
);
346 CASE_EXCEPTION(INT_OVERFLOW
);
347 CASE_EXCEPTION(PRIV_INSTRUCTION
);
348 CASE_EXCEPTION(IN_PAGE_ERROR
);
349 CASE_EXCEPTION(ILLEGAL_INSTRUCTION
);
350 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION
);
351 CASE_EXCEPTION(STACK_OVERFLOW
);
352 CASE_EXCEPTION(INVALID_DISPOSITION
);
353 CASE_EXCEPTION(GUARD_PAGE
);
354 CASE_EXCEPTION(INVALID_HANDLE
);
357 // unknown exception, ask NTDLL for the name
358 if ( !::FormatMessage
360 FORMAT_MESSAGE_IGNORE_INSERTS
|
361 FORMAT_MESSAGE_FROM_HMODULE
,
362 ::GetModuleHandle(_T("NTDLL.DLL")),
365 wxStringBuffer(s
, 1024),
370 s
.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code
);
374 #undef CASE_EXCEPTION
379 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT