]>
Commit | Line | Data |
---|---|---|
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 |
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 | ||
50bea100 VZ |
62 | // the real crash report generator |
63 | class wxCrashReportImpl | |
64 | { | |
65 | public: | |
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 | ||
78 | private: | |
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 |
98 | static wxChar gs_reportFilename[MAX_PATH]; |
99 | ||
83dee24c VZ |
100 | // this is defined in msw/main.cpp |
101 | extern EXCEPTION_POINTERS *wxGlobalSEInformation; | |
102 | ||
50bea100 VZ |
103 | // ============================================================================ |
104 | // implementation | |
105 | // ============================================================================ | |
106 | ||
50bea100 VZ |
107 | // ---------------------------------------------------------------------------- |
108 | // wxCrashReportImpl | |
109 | // ---------------------------------------------------------------------------- | |
110 | ||
111 | wxCrashReportImpl::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 | ||
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 | ||
83dee24c | 138 | bool 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); | |
1f6c517c | 225 | wxUnusedVar(ep); |
50bea100 | 226 | |
83dee24c VZ |
227 | Output(_T("Support for crash report generation was not included ") |
228 | _T("in this wxWidgets version.")); | |
229 | #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP | |
50bea100 | 230 | |
83dee24c | 231 | return false; |
50bea100 VZ |
232 | } |
233 | ||
83dee24c VZ |
234 | // ---------------------------------------------------------------------------- |
235 | // wxCrashReport | |
236 | // ---------------------------------------------------------------------------- | |
50bea100 | 237 | |
50bea100 | 238 | /* static */ |
83dee24c | 239 | void wxCrashReport::SetFileName(const wxChar *filename) |
50bea100 | 240 | { |
83dee24c VZ |
241 | wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1); |
242 | gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0'); | |
50bea100 VZ |
243 | } |
244 | ||
83dee24c VZ |
245 | /* static */ |
246 | const wxChar *wxCrashReport::GetFileName() | |
50bea100 | 247 | { |
83dee24c | 248 | return gs_reportFilename; |
50bea100 VZ |
249 | } |
250 | ||
83dee24c VZ |
251 | /* static */ |
252 | bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep) | |
50bea100 | 253 | { |
83dee24c | 254 | wxCrashReportImpl impl(gs_reportFilename); |
50bea100 | 255 | |
83dee24c | 256 | return impl.Generate(flags, ep); |
50bea100 VZ |
257 | } |
258 | ||
47e94ded VZ |
259 | /* static */ |
260 | bool wxCrashReport::GenerateNow(int flags) | |
261 | { | |
262 | bool rc = false; | |
263 | ||
264 | __try | |
265 | { | |
266 | RaiseException(0x1976, 0, 0, NULL); | |
267 | } | |
268 | __except( rc = Generate(flags, (EXCEPTION_POINTERS *)GetExceptionInformation()), | |
269 | EXCEPTION_CONTINUE_EXECUTION ) | |
270 | { | |
271 | // never executed because of EXCEPTION_CONTINUE_EXECUTION above | |
272 | } | |
273 | ||
274 | return rc; | |
275 | } | |
276 | ||
83dee24c VZ |
277 | // ---------------------------------------------------------------------------- |
278 | // wxCrashContext | |
279 | // ---------------------------------------------------------------------------- | |
2bbc1b28 | 280 | |
83dee24c | 281 | wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep) |
50bea100 | 282 | { |
83dee24c VZ |
283 | wxZeroMemory(*this); |
284 | ||
285 | if ( !ep ) | |
286 | { | |
287 | wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") ); | |
288 | ep = wxGlobalSEInformation; | |
289 | } | |
290 | ||
291 | // TODO: we could also get the operation (read/write) and address for which | |
292 | // it failed for EXCEPTION_ACCESS_VIOLATION code | |
293 | const EXCEPTION_RECORD& rec = *ep->ExceptionRecord; | |
294 | code = rec.ExceptionCode; | |
295 | addr = rec.ExceptionAddress; | |
296 | ||
297 | #ifdef __INTEL__ | |
298 | const CONTEXT& ctx = *ep->ContextRecord; | |
299 | regs.eax = ctx.Eax; | |
300 | regs.ebx = ctx.Ebx; | |
301 | regs.ecx = ctx.Ecx; | |
302 | regs.edx = ctx.Edx; | |
303 | regs.esi = ctx.Esi; | |
304 | regs.edi = ctx.Edi; | |
305 | ||
306 | regs.ebp = ctx.Ebp; | |
307 | regs.esp = ctx.Esp; | |
308 | regs.eip = ctx.Eip; | |
309 | ||
310 | regs.cs = ctx.SegCs; | |
311 | regs.ds = ctx.SegDs; | |
312 | regs.es = ctx.SegEs; | |
313 | regs.fs = ctx.SegFs; | |
314 | regs.gs = ctx.SegGs; | |
315 | regs.ss = ctx.SegSs; | |
316 | ||
317 | regs.flags = ctx.EFlags; | |
318 | #endif // __INTEL__ | |
50bea100 VZ |
319 | } |
320 | ||
83dee24c | 321 | wxString wxCrashContext::GetExceptionString() const |
50bea100 VZ |
322 | { |
323 | wxString s; | |
324 | ||
325 | #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break | |
326 | ||
83dee24c | 327 | switch ( code ) |
50bea100 VZ |
328 | { |
329 | CASE_EXCEPTION(ACCESS_VIOLATION); | |
330 | CASE_EXCEPTION(DATATYPE_MISALIGNMENT); | |
331 | CASE_EXCEPTION(BREAKPOINT); | |
332 | CASE_EXCEPTION(SINGLE_STEP); | |
333 | CASE_EXCEPTION(ARRAY_BOUNDS_EXCEEDED); | |
334 | CASE_EXCEPTION(FLT_DENORMAL_OPERAND); | |
335 | CASE_EXCEPTION(FLT_DIVIDE_BY_ZERO); | |
336 | CASE_EXCEPTION(FLT_INEXACT_RESULT); | |
337 | CASE_EXCEPTION(FLT_INVALID_OPERATION); | |
338 | CASE_EXCEPTION(FLT_OVERFLOW); | |
339 | CASE_EXCEPTION(FLT_STACK_CHECK); | |
340 | CASE_EXCEPTION(FLT_UNDERFLOW); | |
341 | CASE_EXCEPTION(INT_DIVIDE_BY_ZERO); | |
342 | CASE_EXCEPTION(INT_OVERFLOW); | |
343 | CASE_EXCEPTION(PRIV_INSTRUCTION); | |
344 | CASE_EXCEPTION(IN_PAGE_ERROR); | |
345 | CASE_EXCEPTION(ILLEGAL_INSTRUCTION); | |
346 | CASE_EXCEPTION(NONCONTINUABLE_EXCEPTION); | |
347 | CASE_EXCEPTION(STACK_OVERFLOW); | |
348 | CASE_EXCEPTION(INVALID_DISPOSITION); | |
349 | CASE_EXCEPTION(GUARD_PAGE); | |
350 | CASE_EXCEPTION(INVALID_HANDLE); | |
351 | ||
352 | default: | |
353 | // unknown exception, ask NTDLL for the name | |
354 | if ( !::FormatMessage | |
355 | ( | |
356 | FORMAT_MESSAGE_IGNORE_INSERTS | | |
357 | FORMAT_MESSAGE_FROM_HMODULE, | |
358 | ::GetModuleHandle(_T("NTDLL.DLL")), | |
83dee24c | 359 | code, |
50bea100 VZ |
360 | 0, |
361 | wxStringBuffer(s, 1024), | |
362 | 1024, | |
363 | 0 | |
364 | ) ) | |
365 | { | |
83dee24c | 366 | s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code); |
50bea100 VZ |
367 | } |
368 | } | |
369 | ||
370 | #undef CASE_EXCEPTION | |
371 | ||
372 | return s; | |
373 | } | |
374 | ||
83dee24c | 375 | #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT |
50bea100 | 376 |