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: crashrpt.cpp 34532 2005-06-02 20:58:18Z JS $
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:
BusyCursor()44     BusyCursor()
45     {
46         HCURSOR hcursorBusy = ::LoadCursor(NULL, IDC_WAIT);
47         m_hcursorOld = ::SetCursor(hcursorBusy);
48     }
49 
~BusyCursor()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 
~wxCrashReportImpl()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
OutputEndl()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 
wxCrashReportImpl(const wxChar * filename)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 
Output(const wxChar * format,...)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 
Generate(int flags,EXCEPTION_POINTERS * ep)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 #if _MSC_VER > 1300
203                                         |MiniDumpWithIndirectlyReferencedMemory
204 #endif
205                                         );
206         }
207 
208         if ( !wxDbgHelpDLL::MiniDumpWriteDump
209               (
210                 ::GetCurrentProcess(),
211                 ::GetCurrentProcessId(),
212                 m_hFile,                    // file to write to
213                 dumpFlags,                  // kind of dump to craete
214                 &minidumpExcInfo,
215                 NULL,                       // no extra user-defined data
216                 NULL                        // no callbacks
217               ) )
218         {
219             Output(_T("MiniDumpWriteDump() failed."));
220 
221             return false;
222         }
223 
224         return true;
225     }
226     else // dbghelp.dll couldn't be loaded
227     {
228         Output(wxDbgHelpDLL::GetErrorMessage());
229     }
230 #else // !wxUSE_DBGHELP
231     wxUnusedVar(flags);
232     wxUnusedVar(ep);
233 
234     Output(_T("Support for crash report generation was not included ")
235            _T("in this wxWidgets version."));
236 #endif // wxUSE_DBGHELP/!wxUSE_DBGHELP
237 
238     return false;
239 }
240 
241 // ----------------------------------------------------------------------------
242 // wxCrashReport
243 // ----------------------------------------------------------------------------
244 
245 /* static */
SetFileName(const wxChar * filename)246 void wxCrashReport::SetFileName(const wxChar *filename)
247 {
248     wxStrncpy(gs_reportFilename, filename, WXSIZEOF(gs_reportFilename) - 1);
249     gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
250 }
251 
252 /* static */
GetFileName()253 const wxChar *wxCrashReport::GetFileName()
254 {
255     return gs_reportFilename;
256 }
257 
258 /* static */
Generate(int flags,EXCEPTION_POINTERS * ep)259 bool wxCrashReport::Generate(int flags, EXCEPTION_POINTERS *ep)
260 {
261     wxCrashReportImpl impl(gs_reportFilename);
262 
263     return impl.Generate(flags, ep);
264 }
265 
266 /* static */
GenerateNow(int flags)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 
284 // ----------------------------------------------------------------------------
285 // wxCrashContext
286 // ----------------------------------------------------------------------------
287 
wxCrashContext(_EXCEPTION_POINTERS * ep)288 wxCrashContext::wxCrashContext(_EXCEPTION_POINTERS *ep)
289 {
290     wxZeroMemory(*this);
291 
292     if ( !ep )
293     {
294         wxCHECK_RET( wxGlobalSEInformation, _T("no exception info available") );
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__
326 }
327 
GetExceptionString() const328 wxString wxCrashContext::GetExceptionString() const
329 {
330     wxString s;
331 
332     #define CASE_EXCEPTION( x ) case EXCEPTION_##x: s = _T(#x); break
333 
334     switch ( code )
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,
365                      ::GetModuleHandle(_T("NTDLL.DLL")),
366                      code,
367                      0,
368                      wxStringBuffer(s, 1024),
369                      1024,
370                      0
371                     ) )
372             {
373                 s.Printf(_T("UNKNOWN_EXCEPTION(%d)"), code);
374             }
375     }
376 
377     #undef CASE_EXCEPTION
378 
379     return s;
380 }
381 
382 #endif // wxUSE_CRASHREPORT/!wxUSE_CRASHREPORT
383 
384