]> git.saurik.com Git - wxWidgets.git/blob - src/msw/crashrpt.cpp
Add wxSTAY_ON_TOP support [Patch 1206023]
[wxWidgets.git] / src / msw / crashrpt.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/crashrpt.cpp
3 // Purpose: code to generate crash dumps (minidumps)
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>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
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
27 #if wxUSE_CRASHREPORT
28
29 #ifndef WX_PRECOMP
30 #endif //WX_PRECOMP
31
32 #include "wx/msw/debughlp.h"
33 #include "wx/msw/crashrpt.h"
34
35 // ----------------------------------------------------------------------------
36 // classes
37 // ----------------------------------------------------------------------------
38
39 // low level wxBusyCursor replacement: we use Win32 API directly here instead
40 // of going through wxWidgets calls as this could be dangerous
41 class BusyCursor
42 {
43 public:
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
58 private:
59 HCURSOR m_hcursorOld;
60 };
61
62 // the real crash report generator
63 class wxCrashReportImpl
64 {
65 public:
66 wxCrashReportImpl(const wxChar *filename);
67
68 bool Generate(int flags, EXCEPTION_POINTERS *ep);
69
70 ~wxCrashReportImpl()
71 {
72 if ( m_hFile != INVALID_HANDLE_VALUE )
73 {
74 ::CloseHandle(m_hFile);
75 }
76 }
77
78 private:
79
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
86 // the handle of the report file
87 HANDLE m_hFile;
88 };
89
90 // ----------------------------------------------------------------------------
91 // globals
92 // ----------------------------------------------------------------------------
93
94 // the file name where the report about exception is written
95 //
96 // we use fixed buffer to avoid (big) dynamic allocations when the program
97 // crashes
98 static wxChar gs_reportFilename[MAX_PATH];
99
100 // this is defined in msw/main.cpp
101 extern EXCEPTION_POINTERS *wxGlobalSEInformation;
102
103 // ============================================================================
104 // implementation
105 // ============================================================================
106
107 // ----------------------------------------------------------------------------
108 // wxCrashReportImpl
109 // ----------------------------------------------------------------------------
110
111 wxCrashReportImpl::wxCrashReportImpl(const wxChar *filename)
112 {
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
125 void 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
138 bool wxCrashReportImpl::Generate(int flags, EXCEPTION_POINTERS *ep)
139 {
140 if ( m_hFile == INVALID_HANDLE_VALUE )
141 return false;
142
143 #if wxUSE_DBGHELP
144 if ( !ep )
145 ep = wxGlobalSEInformation;
146
147 if ( !ep )
148 {
149 Output(_T("Context for crash report generation not available."));
150 return false;
151 }
152
153 // show to the user that we're doing something...
154 BusyCursor busyCursor;
155
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 );
165
166 int flagsEnv;
167 if ( dwLen && dwLen < WXSIZEOF(envFlags) &&
168 wxSscanf(envFlags, _T("%d"), &flagsEnv) == 1 )
169 {
170 flags = flagsEnv;
171 }
172
173 if ( wxDbgHelpDLL::Init() )
174 {
175 MINIDUMP_EXCEPTION_INFORMATION minidumpExcInfo;
176
177 minidumpExcInfo.ThreadId = ::GetCurrentThreadId();
178 minidumpExcInfo.ExceptionPointers = ep;
179 minidumpExcInfo.ClientPointers = FALSE; // in our own address space
180
181 // do generate the dump
182 MINIDUMP_TYPE dumpFlags;
183 if ( flags & wxCRASH_REPORT_LOCALS )
184 {
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;
189 }
190 else if ( flags & wxCRASH_REPORT_GLOBALS )
191 {
192 // MiniDumpWriteDump() has the option for dumping just the data
193 // segment which contains all globals -- exactly what we need
194 dumpFlags = MiniDumpWithDataSegs;
195 }
196 else // minimal dump
197 {
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);
203 }
204
205 if ( !wxDbgHelpDLL::MiniDumpWriteDump
206 (
207 ::GetCurrentProcess(),
208 ::GetCurrentProcessId(),
209 m_hFile, // file to write to
210 dumpFlags, // kind of dump to craete
211 &minidumpExcInfo,
212 NULL, // no extra user-defined data
213 NULL // no callbacks
214 ) )
215 {
216 Output(_T("MiniDumpWriteDump() failed."));
217
218 return false;
219 }
220
221 return true;
222 }
223 else // dbghelp.dll couldn't be loaded
224 {
225 Output(wxDbgHelpDLL::GetErrorMessage());
226 }
227 #else // !wxUSE_DBGHELP
228 wxUnusedVar(flags);
229 wxUnusedVar(ep);
230
231 Output(_T("Support for crash report generation was not included ")
232 _T("in this wxWidgets version."));
233 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
234
235 return false;
236 }
237
238 // ----------------------------------------------------------------------------
239 // wxCrashReport
240 // ----------------------------------------------------------------------------
241
242 /* static */
243 void wxCrashReport::SetFileName(const wxChar *filename)
244 {
245 wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
246 gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
247 }
248
249 /* static */
250 const wxChar *wxCrashReport::GetFileName()
251 {
252 return gs_reportFilename;
253 }
254
255 /* static */
256 bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
257 {
258 wxCrashReportImpl impl(gs_reportFilename);
259
260 return impl.Generate(flags, ep);
261 }
262
263 /* static */
264 bool wxCrashReport::GenerateNow(int flags)
265 {
266 bool rc = false;
267
268 __try
269 {
270 RaiseException(0x1976, 0, 0, NULL);
271 }
272 __except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()),
273 EXCEPTION_CONTINUE_EXECUTION )
274 {
275 // never executed because of EXCEPTION_CONTINUE_EXECUTION above
276 }
277
278 return rc;
279 }
280
281 // ----------------------------------------------------------------------------
282 // wxCrashContext
283 // ----------------------------------------------------------------------------
284
285 wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
286 {
287 wxZeroMemory(*this);
288
289 if ( !ep )
290 {
291 wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") );
292 ep = wxGlobalSEInformation;
293 }
294
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;
300
301 #ifdef __INTEL__
302 const CONTEXT& ctx = *ep->ContextRecord;
303 regs.eax = ctx.Eax;
304 regs.ebx = ctx.Ebx;
305 regs.ecx = ctx.Ecx;
306 regs.edx = ctx.Edx;
307 regs.esi = ctx.Esi;
308 regs.edi = ctx.Edi;
309
310 regs.ebp = ctx.Ebp;
311 regs.esp = ctx.Esp;
312 regs.eip = ctx.Eip;
313
314 regs.cs = ctx.SegCs;
315 regs.ds = ctx.SegDs;
316 regs.es = ctx.SegEs;
317 regs.fs = ctx.SegFs;
318 regs.gs = ctx.SegGs;
319 regs.ss = ctx.SegSs;
320
321 regs.flags = ctx.EFlags;
322 #endif // __INTEL__
323 }
324
325 wxString wxCrashContext::GetExceptionString() const
326 {
327 wxString s;
328
329 #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
330
331 switch ( code )
332 {
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);
355
356 default:
357 // unknown exception, ask NTDLL for the name
358 if ( !::FormatMessage
359 (
360 FORMAT_MESSAGE_IGNORE_INSERTS |
361 FORMAT_MESSAGE_FROM_HMODULE,
362 ::GetModuleHandle(_T("NTDLL.DLL")),
363 code,
364 0,
365 wxStringBuffer(s, 1024),
366 1024,
367 0
368 ) )
369 {
370 s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code);
371 }
372 }
373
374 #undef CASE_EXCEPTION
375
376 return s;
377 }
378
379 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
380