]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
disable an apparently harmless VC++ warning about /EHa being required with _set_se_tr...
[wxWidgets.git] / src / msw / crashrpt.cpp
CommitLineData
50bea100
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: msw/crashrpt.cpp
83dee24c 3// Purpose: code to generate crash dumps (minidumps)
50bea100
VZ
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 13.07.03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
65571936 9// Licence: wxWindows licence
50bea100
VZ
10/////////////////////////////////////////////////////////////////////////////
11
50bea100
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
83dee24c 27#if wxUSE_CRASHREPORT
50bea100
VZ
28
29#ifndef WX_PRECOMP
30#endif //WX_PRECOMP
31
83dee24c 32#include "wx/msw/debughlp.h"
50bea100
VZ
33#include "wx/msw/crashrpt.h"
34
50bea100
VZ
35// ----------------------------------------------------------------------------
36// classes
37// ----------------------------------------------------------------------------
38
8d008965 39// low level wxBusyCursor replacement: we use Win32 API directly here instead
77ffb593 40// of going through wxWidgets calls as this could be dangerous
8d008965
VZ
41class BusyCursor
42{
43public:
44 BusyCursor()
45 {
46 HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
47 m_hcursorOld = ::SetCursor(hcursorBusy);
48 }
49
50 ~BusyCursor()
51 {
52 if ( m_hcursorOld )
53 {
54 ::SetCursor(m_hcursorOld);
55 }
56 }
57
58private:
59 HCURSOR m_hcursorOld;
60};
61
50bea100
VZ
62// the real crash report generator
63class wxCrashReportImpl
64{
65public:
66 wxCrashReportImpl(const wxChar *filename);
67
83dee24c 68 bool Generate(int flags, EXCEPTION_POINTERS *ep);
50bea100
VZ
69
70 ~wxCrashReportImpl()
71 {
72 if ( m_hFile != INVALID_HANDLE_VALUE )
73 {
74 ::CloseHandle(m_hFile);
75 }
76 }
77
78private:
2bbc1b28 79
50bea100
VZ
80 // formatted output to m_hFile
81 void Output(const wxChar *format, ...);
82
83 // output end of line
84 void OutputEndl() { Output(_T("\r\n")); }
85
f83aa777
VZ
86 // the handle of the report file
87 HANDLE m_hFile;
50bea100
VZ
88};
89
90// ----------------------------------------------------------------------------
91// globals
92// ----------------------------------------------------------------------------
93
50bea100 94// the file name where the report about exception is written
83dee24c
VZ
95//
96// we use fixed buffer to avoid (big) dynamic allocations when the program
97// crashes
50bea100
VZ
98static wxChar gs_reportFilename[MAX_PATH];
99
83dee24c
VZ
100// this is defined in msw/main.cpp
101extern EXCEPTION_POINTERS *wxGlobalSEInformation;
102
50bea100
VZ
103// ============================================================================
104// implementation
105// ============================================================================
106
50bea100
VZ
107// ----------------------------------------------------------------------------
108// wxCrashReportImpl
109// ----------------------------------------------------------------------------
110
111wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
112{
50bea100
VZ
113 m_hFile = ::CreateFile
114 (
115 filename,
116 GENERIC_WRITE,
117 0, // no sharing
118 NULL, // default security
119 CREATE_ALWAYS,
120 FILE_FLAG_WRITE_THROUGH,
121 NULL // no template file
122 );
123}
124
125void wxCrashReportImpl::Output(const wxChar *format, ...)
126{
127 va_list argptr;
128 va_start(argptr, format);
129
130 DWORD cbWritten;
131
132 wxString s = wxString::FormatV(format, argptr);
133 ::WriteFile(m_hFile, s, s.length() * sizeof(wxChar), &cbWritten, 0);
134
135 va_end(argptr);
136}
137
83dee24c 138bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 139{
83dee24c 140 if ( m_hFile == INVALID_HANDLE_VALUE )
50bea100
VZ
141 return false;
142
83dee24c
VZ
143#if wxUSE_DBGHELP
144 if ( !ep )
145 ep = wxGlobalSEInformation;
50bea100 146
83dee24c 147 if ( !ep )
50bea100 148 {
83dee24c
VZ
149 Output(_T("Context for crash report generation not available."));
150 return false;
50bea100
VZ
151 }
152
83dee24c
VZ
153 // show to the user that we're doing something...
154 BusyCursor busyCursor;
50bea100 155
83dee24c
VZ
156 // user-specified crash report flags override those specified by the
157 // programmer
158 TCHAR envFlags[64];
159 DWORD dwLen = ::GetEnvironmentVariable
160 (
161 _T("WX_CRASH_FLAGS"),
162 envFlags,
163 WXSIZEOF(envFlags)
164 );
50bea100 165
83dee24c
VZ
166 int flagsEnv;
167 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
168 wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
50bea100 169 {
83dee24c 170 flags = flagsEnv;
50bea100
VZ
171 }
172
83dee24c 173 if ( wxDbgHelpDLL::Init() )
50bea100 174 {
83dee24c 175 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
50bea100 176
83dee24c
VZ
177 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
178 minidumpExcInfo.ExceptionPointers = ep;
179 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
50bea100 180
83dee24c
VZ
181 // do generate the dump
182 MINIDUMP_TYPE dumpFlags;
183 if ( flags & wxCRASH_REPORT_LOCALS )
50bea100 184 {
83dee24c
VZ
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;
50bea100 189 }
83dee24c 190 else if ( flags & wxCRASH_REPORT_GLOBALS )
50bea100 191 {
83dee24c
VZ
192 // MiniDumpWriteDump() has the option for dumping just the data
193 // segment which contains all globals -- exactly what we need
194 dumpFlags = MiniDumpWithDataSegs;
50bea100 195 }
83dee24c 196 else // minimal dump
50bea100 197 {
83dee24c 198 dumpFlags = MiniDumpNormal;
50bea100 199 }
50bea100 200
83dee24c
VZ
201 if ( !wxDbgHelpDLL::MiniDumpWriteDump
202 (
203 ::GetCurrentProcess(),
204 ::GetCurrentProcessId(),
205 m_hFile, // file to write to
206 dumpFlags, // kind of dump to craete
207 &minidumpExcInfo,
208 NULL, // no extra user-defined data
209 NULL // no callbacks
210 ) )
211 {
212 Output(_T("MiniDumpWriteDump() failed."));
50bea100 213
83dee24c
VZ
214 return false;
215 }
50bea100 216
83dee24c 217 return true;
50bea100 218 }
83dee24c 219 else // dbghelp.dll couldn't be loaded
50bea100 220 {
83dee24c 221 Output(wxDbgHelpDLL::GetErrorMessage());
50bea100 222 }
83dee24c
VZ
223#else // !wxUSE_DBGHELP
224 wxUnusedVar(flags);
50bea100 225
83dee24c
VZ
226 Output(_T("Support for crash report generation was not included ")
227 _T("in this wxWidgets version."));
228#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
50bea100 229
83dee24c 230 return false;
50bea100
VZ
231}
232
83dee24c
VZ
233// ----------------------------------------------------------------------------
234// wxCrashReport
235// ----------------------------------------------------------------------------
50bea100 236
50bea100 237/* static */
83dee24c 238void wxCrashReport::SetFileName(const wxChar *filename)
50bea100 239{
83dee24c
VZ
240 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
241 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
50bea100
VZ
242}
243
83dee24c
VZ
244/* static */
245const wxChar *wxCrashReport::GetFileName()
50bea100 246{
83dee24c 247 return gs_reportFilename;
50bea100
VZ
248}
249
83dee24c
VZ
250/* static */
251bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 252{
83dee24c 253 wxCrashReportImpl impl(gs_reportFilename);
50bea100 254
83dee24c 255 return impl.Generate(flags, ep);
50bea100
VZ
256}
257
83dee24c
VZ
258// ----------------------------------------------------------------------------
259// wxCrashContext
260// ----------------------------------------------------------------------------
2bbc1b28 261
83dee24c 262wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
50bea100 263{
83dee24c
VZ
264 wxZeroMemory(*this);
265
266 if ( !ep )
267 {
268 wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") );
269 ep = wxGlobalSEInformation;
270 }
271
272 // TODO: we could also get the operation (read/write) and address for which
273 // it failed for EXCEPTION_ACCESS_VIOLATION code
274 const EXCEPTION_RECORD& rec = *ep->ExceptionRecord;
275 code = rec.ExceptionCode;
276 addr = rec.ExceptionAddress;
277
278#ifdef __INTEL__
279 const CONTEXT& ctx = *ep->ContextRecord;
280 regs.eax = ctx.Eax;
281 regs.ebx = ctx.Ebx;
282 regs.ecx = ctx.Ecx;
283 regs.edx = ctx.Edx;
284 regs.esi = ctx.Esi;
285 regs.edi = ctx.Edi;
286
287 regs.ebp = ctx.Ebp;
288 regs.esp = ctx.Esp;
289 regs.eip = ctx.Eip;
290
291 regs.cs = ctx.SegCs;
292 regs.ds = ctx.SegDs;
293 regs.es = ctx.SegEs;
294 regs.fs = ctx.SegFs;
295 regs.gs = ctx.SegGs;
296 regs.ss = ctx.SegSs;
297
298 regs.flags = ctx.EFlags;
299#endif // __INTEL__
50bea100
VZ
300}
301
83dee24c 302wxString wxCrashContext::GetExceptionString() const
50bea100
VZ
303{
304 wxString s;
305
306 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
307
83dee24c 308 switch ( code )
50bea100
VZ
309 {
310 CASE_EXCEPTION(ACCESS_VIOLATION);
311 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
312 CASE_EXCEPTION(BREAKPOINT);
313 CASE_EXCEPTION(SINGLE_STEP);
314 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
315 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
316 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
317 CASE_EXCEPTION(FLT_INEXACT_RESULT);
318 CASE_EXCEPTION(FLT_INVALID_OPERATION);
319 CASE_EXCEPTION(FLT_OVERFLOW);
320 CASE_EXCEPTION(FLT_STACK_CHECK);
321 CASE_EXCEPTION(FLT_UNDERFLOW);
322 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
323 CASE_EXCEPTION(INT_OVERFLOW);
324 CASE_EXCEPTION(PRIV_INSTRUCTION);
325 CASE_EXCEPTION(IN_PAGE_ERROR);
326 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
327 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
328 CASE_EXCEPTION(STACK_OVERFLOW);
329 CASE_EXCEPTION(INVALID_DISPOSITION);
330 CASE_EXCEPTION(GUARD_PAGE);
331 CASE_EXCEPTION(INVALID_HANDLE);
332
333 default:
334 // unknown exception, ask NTDLL for the name
335 if ( !::FormatMessage
336 (
337 FORMAT_MESSAGE_IGNORE_INSERTS |
338 FORMAT_MESSAGE_FROM_HMODULE,
339 ::GetModuleHandle(_T("NTDLL.DLL")),
83dee24c 340 code,
50bea100
VZ
341 0,
342 wxStringBuffer(s, 1024),
343 1024,
344 0
345 ) )
346 {
83dee24c 347 s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code);
50bea100
VZ
348 }
349 }
350
351 #undef CASE_EXCEPTION
352
353 return s;
354}
355
83dee24c 356#endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
50bea100 357