]>
Commit | Line | Data |
---|---|---|
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 |
40 | class BusyCursor |
41 | { | |
42 | public: | |
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 | ||
57 | private: | |
58 | HCURSOR m_hcursorOld; | |
59 | }; | |
60 | ||
50bea100 VZ |
61 | // the real crash report generator |
62 | class wxCrashReportImpl | |
63 | { | |
64 | public: | |
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 | ||
77 | private: | |
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 |
97 | static wxChar gs_reportFilename[MAX_PATH]; |
98 | ||
83dee24c VZ |
99 | // this is defined in msw/main.cpp |
100 | extern EXCEPTION_POINTERS *wxGlobalSEInformation; | |
101 | ||
50bea100 VZ |
102 | // ============================================================================ |
103 | // implementation | |
104 | // ============================================================================ | |
105 | ||
50bea100 VZ |
106 | // ---------------------------------------------------------------------------- |
107 | // wxCrashReportImpl | |
108 | // ---------------------------------------------------------------------------- | |
109 | ||
110 | wxCrashReportImpl::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 | ||
124 | void 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 | 139 | bool 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 | 247 | void 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 | 253 | wxString wxCrashReport::GetFileName() |
50bea100 | 254 | { |
83dee24c | 255 | return gs_reportFilename; |
50bea100 VZ |
256 | } |
257 | ||
83dee24c VZ |
258 | /* static */ |
259 | bool 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 */ |
267 | bool 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 | 288 | wxCrashContext::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 | 328 | wxString 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 |