]> git.saurik.com Git - wxWidgets.git/blame - src/msw/crashrpt.cpp
Add virtual ~wxAnyScrollHelperBase() to fix compiler warning.
[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
50bea100 7// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
65571936 8// Licence: wxWindows licence
50bea100
VZ
9/////////////////////////////////////////////////////////////////////////////
10
50bea100
VZ
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
83dee24c 26#if wxUSE_CRASHREPORT
50bea100
VZ
27
28#ifndef WX_PRECOMP
29#endif //WX_PRECOMP
30
83dee24c 31#include "wx/msw/debughlp.h"
50bea100
VZ
32#include "wx/msw/crashrpt.h"
33
50bea100
VZ
34// ----------------------------------------------------------------------------
35// classes
36// ----------------------------------------------------------------------------
37
8d008965 38// low level wxBusyCursor replacement: we use Win32 API directly here instead
77ffb593 39// of going through wxWidgets calls as this could be dangerous
8d008965
VZ
40class BusyCursor
41{
42public:
43 BusyCursor()
44 {
45 HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
46 m_hcursorOld = ::SetCursor(hcursorBusy);
47 }
48
49 ~BusyCursor()
50 {
51 if ( m_hcursorOld )
52 {
53 ::SetCursor(m_hcursorOld);
54 }
55 }
56
57private:
58 HCURSOR m_hcursorOld;
59};
60
50bea100
VZ
61// the real crash report generator
62class wxCrashReportImpl
63{
64public:
65 wxCrashReportImpl(const wxChar *filename);
66
83dee24c 67 bool Generate(int flags, EXCEPTION_POINTERS *ep);
50bea100
VZ
68
69 ~wxCrashReportImpl()
70 {
71 if ( m_hFile != INVALID_HANDLE_VALUE )
72 {
73 ::CloseHandle(m_hFile);
74 }
75 }
76
77private:
2bbc1b28 78
50bea100
VZ
79 // formatted output to m_hFile
80 void Output(const wxChar *format, ...);
81
82 // output end of line
9a83f860 83 void OutputEndl() { Output(wxT("\r\n")); }
50bea100 84
f83aa777
VZ
85 // the handle of the report file
86 HANDLE m_hFile;
50bea100
VZ
87};
88
89// ----------------------------------------------------------------------------
90// globals
91// ----------------------------------------------------------------------------
92
50bea100 93// the file name where the report about exception is written
83dee24c
VZ
94//
95// we use fixed buffer to avoid (big) dynamic allocations when the program
96// crashes
50bea100
VZ
97static wxChar gs_reportFilename[MAX_PATH];
98
83dee24c
VZ
99// this is defined in msw/main.cpp
100extern EXCEPTION_POINTERS *wxGlobalSEInformation;
101
50bea100
VZ
102// ============================================================================
103// implementation
104// ============================================================================
105
50bea100
VZ
106// ----------------------------------------------------------------------------
107// wxCrashReportImpl
108// ----------------------------------------------------------------------------
109
110wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
111{
50bea100
VZ
112 m_hFile = ::CreateFile
113 (
114 filename,
115 GENERIC_WRITE,
116 0, // no sharing
117 NULL, // default security
118 CREATE_ALWAYS,
119 FILE_FLAG_WRITE_THROUGH,
120 NULL // no template file
121 );
122}
123
124void wxCrashReportImpl::Output(const wxChar *format, ...)
125{
126 va_list argptr;
127 va_start(argptr, format);
128
129 DWORD cbWritten;
130
131 wxString s = wxString::FormatV(format, argptr);
11aac4ba
VS
132
133 wxCharBuffer buf(s.mb_str(wxConvUTF8));
134 ::WriteFile(m_hFile, buf.data(), strlen(buf.data()), &cbWritten, 0);
50bea100
VZ
135
136 va_end(argptr);
137}
138
83dee24c 139bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 140{
83dee24c 141 if ( m_hFile == INVALID_HANDLE_VALUE )
50bea100
VZ
142 return false;
143
83dee24c
VZ
144#if wxUSE_DBGHELP
145 if ( !ep )
146 ep = wxGlobalSEInformation;
50bea100 147
83dee24c 148 if ( !ep )
50bea100 149 {
9a83f860 150 Output(wxT("Context for crash report generation not available."));
83dee24c 151 return false;
50bea100
VZ
152 }
153
83dee24c
VZ
154 // show to the user that we're doing something...
155 BusyCursor busyCursor;
50bea100 156
83dee24c
VZ
157 // user-specified crash report flags override those specified by the
158 // programmer
159 TCHAR envFlags[64];
160 DWORD dwLen = ::GetEnvironmentVariable
161 (
9a83f860 162 wxT("WX_CRASH_FLAGS"),
83dee24c
VZ
163 envFlags,
164 WXSIZEOF(envFlags)
165 );
50bea100 166
83dee24c
VZ
167 int flagsEnv;
168 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
9a83f860 169 wxSscanf(envFlags, wxT("%d"), &flagsEnv) == 1 )
50bea100 170 {
83dee24c 171 flags = flagsEnv;
50bea100
VZ
172 }
173
83dee24c 174 if ( wxDbgHelpDLL::Init() )
50bea100 175 {
83dee24c 176 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
50bea100 177
83dee24c
VZ
178 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
179 minidumpExcInfo.ExceptionPointers = ep;
180 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
50bea100 181
83dee24c
VZ
182 // do generate the dump
183 MINIDUMP_TYPE dumpFlags;
184 if ( flags & wxCRASH_REPORT_LOCALS )
50bea100 185 {
83dee24c
VZ
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;
50bea100 190 }
83dee24c 191 else if ( flags & wxCRASH_REPORT_GLOBALS )
50bea100 192 {
83dee24c
VZ
193 // MiniDumpWriteDump() has the option for dumping just the data
194 // segment which contains all globals -- exactly what we need
195 dumpFlags = MiniDumpWithDataSegs;
50bea100 196 }
83dee24c 197 else // minimal dump
50bea100 198 {
8b71723c
VZ
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
80ff6e5d
JS
202 dumpFlags = (MINIDUMP_TYPE)(MiniDumpScanMemory
203#if _MSC_VER > 1300
204 |MiniDumpWithIndirectlyReferencedMemory
f4322df6 205#endif
80ff6e5d 206 );
50bea100 207 }
50bea100 208
83dee24c
VZ
209 if ( !wxDbgHelpDLL::MiniDumpWriteDump
210 (
211 ::GetCurrentProcess(),
212 ::GetCurrentProcessId(),
213 m_hFile, // file to write to
214 dumpFlags, // kind of dump to craete
215 &minidumpExcInfo,
216 NULL, // no extra user-defined data
217 NULL // no callbacks
218 ) )
219 {
9a83f860 220 Output(wxT("MiniDumpWriteDump() failed."));
50bea100 221
83dee24c
VZ
222 return false;
223 }
50bea100 224
83dee24c 225 return true;
50bea100 226 }
83dee24c 227 else // dbghelp.dll couldn't be loaded
50bea100 228 {
9a83f860 229 Output(wxT("%s"), wxDbgHelpDLL::GetErrorMessage().c_str());
50bea100 230 }
83dee24c
VZ
231#else // !wxUSE_DBGHELP
232 wxUnusedVar(flags);
1f6c517c 233 wxUnusedVar(ep);
50bea100 234
9a83f860
VZ
235 Output(wxT("Support for crash report generation was not included ")
236 wxT("in this wxWidgets version."));
83dee24c 237#endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
50bea100 238
83dee24c 239 return false;
50bea100
VZ
240}
241
83dee24c
VZ
242// ----------------------------------------------------------------------------
243// wxCrashReport
244// ----------------------------------------------------------------------------
50bea100 245
50bea100 246/* static */
fe267c2f 247void wxCrashReport::SetFileName(const wxString& filename)
50bea100 248{
b2b6da49 249 wxStrlcpy(gs_reportFilename, filename.t_str(), WXSIZEOF(gs_reportFilename));
50bea100
VZ
250}
251
83dee24c 252/* static */
fe267c2f 253wxString wxCrashReport::GetFileName()
50bea100 254{
83dee24c 255 return gs_reportFilename;
50bea100
VZ
256}
257
83dee24c
VZ
258/* static */
259bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
50bea100 260{
83dee24c 261 wxCrashReportImpl impl(gs_reportFilename);
50bea100 262
83dee24c 263 return impl.Generate(flags, ep);
50bea100
VZ
264}
265
47e94ded
VZ
266/* static */
267bool wxCrashReport::GenerateNow(int flags)
268{
269 bool rc = false;
270
271 __try
272 {
273 RaiseException(0x1976, 0, 0, NULL);
274 }
275 __except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()),
276 EXCEPTION_CONTINUE_EXECUTION )
277 {
278 // never executed because of EXCEPTION_CONTINUE_EXECUTION above
279 }
280
281 return rc;
282}
283
83dee24c
VZ
284// ----------------------------------------------------------------------------
285// wxCrashContext
286// ----------------------------------------------------------------------------
2bbc1b28 287
83dee24c 288wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
50bea100 289{
83dee24c
VZ
290 wxZeroMemory(*this);
291
292 if ( !ep )
293 {
9a83f860 294 wxCHECK_RET( wxGlobalSEInformation, wxT("no exception info available") );
83dee24c
VZ
295 ep = wxGlobalSEInformation;
296 }
297
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;
303
304#ifdef __INTEL__
305 const CONTEXT& ctx = *ep->ContextRecord;
306 regs.eax = ctx.Eax;
307 regs.ebx = ctx.Ebx;
308 regs.ecx = ctx.Ecx;
309 regs.edx = ctx.Edx;
310 regs.esi = ctx.Esi;
311 regs.edi = ctx.Edi;
312
313 regs.ebp = ctx.Ebp;
314 regs.esp = ctx.Esp;
315 regs.eip = ctx.Eip;
316
317 regs.cs = ctx.SegCs;
318 regs.ds = ctx.SegDs;
319 regs.es = ctx.SegEs;
320 regs.fs = ctx.SegFs;
321 regs.gs = ctx.SegGs;
322 regs.ss = ctx.SegSs;
323
324 regs.flags = ctx.EFlags;
325#endif // __INTEL__
50bea100
VZ
326}
327
83dee24c 328wxString wxCrashContext::GetExceptionString() const
50bea100
VZ
329{
330 wxString s;
331
9a83f860 332 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = wxT(#x); break
50bea100 333
83dee24c 334 switch ( code )
50bea100
VZ
335 {
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);
358
359 default:
360 // unknown exception, ask NTDLL for the name
361 if ( !::FormatMessage
362 (
363 FORMAT_MESSAGE_IGNORE_INSERTS |
364 FORMAT_MESSAGE_FROM_HMODULE,
9a83f860 365 ::GetModuleHandle(wxT("NTDLL.DLL")),
83dee24c 366 code,
50bea100
VZ
367 0,
368 wxStringBuffer(s, 1024),
369 1024,
370 0
371 ) )
372 {
9a83f860 373 s.Printf(wxT("UNKNOWN_EXCEPTION(%d)"), code);
50bea100
VZ
374 }
375 }
376
377 #undef CASE_EXCEPTION
378
379 return s;
380}
381
83dee24c 382#endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
50bea100 383