]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
Always link with expat in monolithic build.
[wxWidgets.git] / src / msw / crashrpt.cpp
CommitLineData
50bea100 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/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
9a83f860 84 void OutputEndl() { Output(wxT("\r\n")); }
50bea100 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);
11aac4ba
VS
133
134 wxCharBuffer buf(s.mb_str(wxConvUTF8));
135 ::WriteFile(m_hFile, buf.data(), strlen(buf.data()), &cbWritten, 0);
50bea100
VZ
136
137 va_end(argptr);
138}
139
83dee24c 140bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 141{
83dee24c 142 if ( m_hFile == INVALID_HANDLE_VALUE )
50bea100
VZ
143 return false;
144
83dee24c
VZ
145#if wxUSE_DBGHELP
146 if ( !ep )
147 ep = wxGlobalSEInformation;
50bea100 148
83dee24c 149 if ( !ep )
50bea100 150 {
9a83f860 151 Output(wxT("Context for crash report generation not available."));
83dee24c 152 return false;
50bea100
VZ
153 }
154
83dee24c
VZ
155 // show to the user that we're doing something...
156 BusyCursor busyCursor;
50bea100 157
83dee24c
VZ
158 // user-specified crash report flags override those specified by the
159 // programmer
160 TCHAR envFlags[64];
161 DWORD dwLen = ::GetEnvironmentVariable
162 (
9a83f860 163 wxT("WX_CRASH_FLAGS"),
83dee24c
VZ
164 envFlags,
165 WXSIZEOF(envFlags)
166 );
50bea100 167
83dee24c
VZ
168 int flagsEnv;
169 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
9a83f860 170 wxSscanf(envFlags, wxT("%d"), &flagsEnv) == 1 )
50bea100 171 {
83dee24c 172 flags = flagsEnv;
50bea100
VZ
173 }
174
83dee24c 175 if ( wxDbgHelpDLL::Init() )
50bea100 176 {
83dee24c 177 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
50bea100 178
83dee24c
VZ
179 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
180 minidumpExcInfo.ExceptionPointers = ep;
181 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
50bea100 182
83dee24c
VZ
183 // do generate the dump
184 MINIDUMP_TYPE dumpFlags;
185 if ( flags & wxCRASH_REPORT_LOCALS )
50bea100 186 {
83dee24c
VZ
187 // the only way to get local variables is to dump the entire
188 // process memory space -- but this makes for huge (dozens or
189 // even hundreds of Mb) files
190 dumpFlags = MiniDumpWithFullMemory;
50bea100 191 }
83dee24c 192 else if ( flags & wxCRASH_REPORT_GLOBALS )
50bea100 193 {
83dee24c
VZ
194 // MiniDumpWriteDump() has the option for dumping just the data
195 // segment which contains all globals -- exactly what we need
196 dumpFlags = MiniDumpWithDataSegs;
50bea100 197 }
83dee24c 198 else // minimal dump
50bea100 199 {
8b71723c
VZ
200 // the file size is not much bigger than when using MiniDumpNormal
201 // if we use the flags below, but the minidump is much more useful
202 // as it contains the values of many (but not all) local variables
80ff6e5d
JS
203 dumpFlags = (MINIDUMP_TYPE)(MiniDumpScanMemory
204#if _MSC_VER > 1300
205 |MiniDumpWithIndirectlyReferencedMemory
f4322df6 206#endif
80ff6e5d 207 );
50bea100 208 }
50bea100 209
83dee24c
VZ
210 if ( !wxDbgHelpDLL::MiniDumpWriteDump
211 (
212 ::GetCurrentProcess(),
213 ::GetCurrentProcessId(),
214 m_hFile, // file to write to
215 dumpFlags, // kind of dump to craete
216 &minidumpExcInfo,
217 NULL, // no extra user-defined data
218 NULL // no callbacks
219 ) )
220 {
9a83f860 221 Output(wxT("MiniDumpWriteDump() failed."));
50bea100 222
83dee24c
VZ
223 return false;
224 }
50bea100 225
83dee24c 226 return true;
50bea100 227 }
83dee24c 228 else // dbghelp.dll couldn't be loaded
50bea100 229 {
9a83f860 230 Output(wxT("%s"), wxDbgHelpDLL::GetErrorMessage().c_str());
50bea100 231 }
83dee24c
VZ
232#else // !wxUSE_DBGHELP
233 wxUnusedVar(flags);
1f6c517c 234 wxUnusedVar(ep);
50bea100 235
9a83f860
VZ
236 Output(wxT("Support for crash report generation was not included ")
237 wxT("in this wxWidgets version."));
83dee24c 238#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
50bea100 239
83dee24c 240 return false;
50bea100
VZ
241}
242
83dee24c
VZ
243// ----------------------------------------------------------------------------
244// wxCrashReport
245// ----------------------------------------------------------------------------
50bea100 246
50bea100 247/* static */
fe267c2f 248void wxCrashReport::SetFileName(const wxString& filename)
50bea100 249{
e408bf52 250 wxStrlcpy(gs_reportFilename, filename.wx_str(), WXSIZEOF(gs_reportFilename));
50bea100
VZ
251}
252
83dee24c 253/* static */
fe267c2f 254wxString wxCrashReport::GetFileName()
50bea100 255{
83dee24c 256 return gs_reportFilename;
50bea100
VZ
257}
258
83dee24c
VZ
259/* static */
260bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 261{
83dee24c 262 wxCrashReportImpl impl(gs_reportFilename);
50bea100 263
83dee24c 264 return impl.Generate(flags, ep);
50bea100
VZ
265}
266
47e94ded
VZ
267/* static */
268bool wxCrashReport::GenerateNow(int flags)
269{
270 bool rc = false;
271
272 __try
273 {
274 RaiseException(0x1976, 0, 0, NULL);
275 }
276 __except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()),
277 EXCEPTION_CONTINUE_EXECUTION )
278 {
279 // never executed because of EXCEPTION_CONTINUE_EXECUTION above
280 }
281
282 return rc;
283}
284
83dee24c
VZ
285// ----------------------------------------------------------------------------
286// wxCrashContext
287// ----------------------------------------------------------------------------
2bbc1b28 288
83dee24c 289wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
50bea100 290{
83dee24c
VZ
291 wxZeroMemory(*this);
292
293 if ( !ep )
294 {
9a83f860 295 wxCHECK_RET( wxGlobalSEInformation, wxT("no exception info available") );
83dee24c
VZ
296 ep = wxGlobalSEInformation;
297 }
298
299 // TODO: we could also get the operation (read/write) and address for which
300 // it failed for EXCEPTION_ACCESS_VIOLATION code
301 const EXCEPTION_RECORD& rec = *ep->ExceptionRecord;
302 code = rec.ExceptionCode;
303 addr = rec.ExceptionAddress;
304
305#ifdef __INTEL__
306 const CONTEXT& ctx = *ep->ContextRecord;
307 regs.eax = ctx.Eax;
308 regs.ebx = ctx.Ebx;
309 regs.ecx = ctx.Ecx;
310 regs.edx = ctx.Edx;
311 regs.esi = ctx.Esi;
312 regs.edi = ctx.Edi;
313
314 regs.ebp = ctx.Ebp;
315 regs.esp = ctx.Esp;
316 regs.eip = ctx.Eip;
317
318 regs.cs = ctx.SegCs;
319 regs.ds = ctx.SegDs;
320 regs.es = ctx.SegEs;
321 regs.fs = ctx.SegFs;
322 regs.gs = ctx.SegGs;
323 regs.ss = ctx.SegSs;
324
325 regs.flags = ctx.EFlags;
326#endif // __INTEL__
50bea100
VZ
327}
328
83dee24c 329wxString wxCrashContext::GetExceptionString() const
50bea100
VZ
330{
331 wxString s;
332
9a83f860 333 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = wxT(#x); break
50bea100 334
83dee24c 335 switch ( code )
50bea100
VZ
336 {
337 CASE_EXCEPTION(ACCESS_VIOLATION);
338 CASE_EXCEPTION(DATATYPE_MISALIGNMENT);
339 CASE_EXCEPTION(BREAKPOINT);
340 CASE_EXCEPTION(SINGLE_STEP);
341 CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED);
342 CASE_EXCEPTION(FLT_DENORMAL_OPERAND);
343 CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO);
344 CASE_EXCEPTION(FLT_INEXACT_RESULT);
345 CASE_EXCEPTION(FLT_INVALID_OPERATION);
346 CASE_EXCEPTION(FLT_OVERFLOW);
347 CASE_EXCEPTION(FLT_STACK_CHECK);
348 CASE_EXCEPTION(FLT_UNDERFLOW);
349 CASE_EXCEPTION(INT_DIVIDE_BY_ZERO);
350 CASE_EXCEPTION(INT_OVERFLOW);
351 CASE_EXCEPTION(PRIV_INSTRUCTION);
352 CASE_EXCEPTION(IN_PAGE_ERROR);
353 CASE_EXCEPTION(ILLEGAL_INSTRUCTION);
354 CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION);
355 CASE_EXCEPTION(STACK_OVERFLOW);
356 CASE_EXCEPTION(INVALID_DISPOSITION);
357 CASE_EXCEPTION(GUARD_PAGE);
358 CASE_EXCEPTION(INVALID_HANDLE);
359
360 default:
361 // unknown exception, ask NTDLL for the name
362 if ( !::FormatMessage
363 (
364 FORMAT_MESSAGE_IGNORE_INSERTS |
365 FORMAT_MESSAGE_FROM_HMODULE,
9a83f860 366 ::GetModuleHandle(wxT("NTDLL.DLL")),
83dee24c 367 code,
50bea100
VZ
368 0,
369 wxStringBuffer(s, 1024),
370 1024,
371 0
372 ) )
373 {
9a83f860 374 s.Printf(wxT("UNKNOWN_EXCEPTION(%d)"), code);
50bea100
VZ
375 }
376 }
377
378 #undef CASE_EXCEPTION
379
380 return s;
381}
382
83dee24c 383#endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
50bea100 384