1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/crashrpt.cpp
3 // Purpose: code to generate crash dumps (minidumps)
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
31 #include "wx/msw/debughlp.h"
32 #include "wx/msw/crashrpt.h"
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
38 // low level wxBusyCursor replacement: we use Win32 API directly here instead
39 // of going through wxWidgets calls as this could be dangerous
45 HCURSOR hcursorBusy
= ::LoadCursor(NULL
, IDC_WAIT
);
46 m_hcursorOld
= ::SetCursor(hcursorBusy
);
53 ::SetCursor(m_hcursorOld
);
61 // the real crash report generator
62 class wxCrashReportImpl
65 wxCrashReportImpl(const wxChar
*filename
);
67 bool Generate(int flags
, EXCEPTION_POINTERS
*ep
);
71 if ( m_hFile
!= INVALID_HANDLE_VALUE
)
73 ::CloseHandle(m_hFile
);
79 // formatted output to m_hFile
80 void Output(const wxChar
*format
, ...);
83 void OutputEndl() { Output(wxT("\r\n")); }
85 // the handle of the report file
89 // ----------------------------------------------------------------------------
91 // ----------------------------------------------------------------------------
93 // the file name where the report about exception is written
95 // we use fixed buffer to avoid (big) dynamic allocations when the program
97 static wxChar gs_reportFilename
[MAX_PATH
];
99 // this is defined in msw/main.cpp
100 extern EXCEPTION_POINTERS
*wxGlobalSEInformation
;
102 // ============================================================================
104 // ============================================================================
106 // ----------------------------------------------------------------------------
108 // ----------------------------------------------------------------------------
110 wxCrashReportImpl::wxCrashReportImpl(const wxChar
*filename
)
112 m_hFile
= ::CreateFile
117 NULL
, // default security
119 FILE_FLAG_WRITE_THROUGH
,
120 NULL
// no template file
124 void wxCrashReportImpl::Output(const wxChar
*format
, ...)
127 va_start(argptr
, format
);
131 wxString s
= wxString::FormatV(format
, argptr
);
133 wxCharBuffer
buf(s
.mb_str(wxConvUTF8
));
134 ::WriteFile(m_hFile
, buf
.data(), strlen(buf
.data()), &cbWritten
, 0);
139 bool wxCrashReportImpl::Generate(int flags
, EXCEPTION_POINTERS
*ep
)
141 if ( m_hFile
== INVALID_HANDLE_VALUE
)
146 ep
= wxGlobalSEInformation
;
150 Output(wxT("Context for crash report generation not available."));
154 // show to the user that we're doing something...
155 BusyCursor busyCursor
;
157 // user-specified crash report flags override those specified by the
160 DWORD dwLen
= ::GetEnvironmentVariable
162 wxT("WX_CRASH_FLAGS"),
168 if ( dwLen
&& dwLen
< WXSIZEOF(envFlags
) &&
169 wxSscanf(envFlags
, wxT("%d"), &flagsEnv
) == 1 )
174 if ( wxDbgHelpDLL::Init() )
176 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo
;
178 minidumpExcInfo
.ThreadId
= ::GetCurrentThreadId();
179 minidumpExcInfo
.ExceptionPointers
= ep
;
180 minidumpExcInfo
.ClientPointers
= FALSE
; // in our own address space
182 // do generate the dump
183 MINIDUMP_TYPE dumpFlags
;
184 if ( flags
& wxCRASH_REPORT_LOCALS
)
186 // the only way to get local variables is to dump the entire
187 // process memory space -- but this makes for huge (dozens or
188 // even hundreds of Mb) files
189 dumpFlags
= MiniDumpWithFullMemory
;
191 else if ( flags
& wxCRASH_REPORT_GLOBALS
)
193 // MiniDumpWriteDump() has the option for dumping just the data
194 // segment which contains all globals -- exactly what we need
195 dumpFlags
= MiniDumpWithDataSegs
;
199 // the file size is not much bigger than when using MiniDumpNormal
200 // if we use the flags below, but the minidump is much more useful
201 // as it contains the values of many (but not all) local variables
202 dumpFlags
= (MINIDUMP_TYPE
)(MiniDumpScanMemory
204 |MiniDumpWithIndirectlyReferencedMemory
209 if ( !wxDbgHelpDLL::MiniDumpWriteDump
211 ::GetCurrentProcess(),
212 ::GetCurrentProcessId(),
213 m_hFile
, // file to write to
214 dumpFlags
, // kind of dump to craete
216 NULL
, // no extra user-defined data
220 Output(wxT("MiniDumpWriteDump() failed."));
227 else // dbghelp.dll couldn't be loaded
229 Output(wxT("%s"), wxDbgHelpDLL::GetErrorMessage().c_str());
231 #else // !wxUSE_DBGHELP
235 Output(wxT("Support for crash report generation was not included ")
236 wxT("in this wxWidgets version."));
237 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
242 // ----------------------------------------------------------------------------
244 // ----------------------------------------------------------------------------
247 void wxCrashReport::SetFileName(const wxString
& filename
)
249 wxStrlcpy(gs_reportFilename
, filename
.t_str(), WXSIZEOF(gs_reportFilename
));
253 wxString
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
, wxT("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 = wxT(#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(wxT("NTDLL.DLL")),
368 wxStringBuffer(s
, 1024),
373 s
.Printf(wxT("UNKNOWN_EXCEPTION(%d)"), code
);
377 #undef CASE_EXCEPTION
382 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT