added a test of a crash inside wxEVT_PAINT handler (this is a problem as it happens...
[wxWidgets.git] / samples / debugrpt / debugrpt.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: debugrpt.cpp
3 // Purpose: minimal sample showing wxDebugReport and related classes
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 2005-01-20
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ----------------------------------------------------------------------------
13 // headers
14 // ----------------------------------------------------------------------------
15
16 #include "wx/app.h"
17 #include "wx/frame.h"
18 #include "wx/menu.h"
19 #include "wx/msgdlg.h"
20 #include "wx/button.h"
21 #include "wx/dcclient.h"
22
23 #include "wx/datetime.h"
24 #include "wx/ffile.h"
25 #include "wx/filename.h"
26 #include "wx/debugrpt.h"
27
28 #if !wxUSE_DEBUGREPORT
29 #error "This sample can't be built without wxUSE_DEBUGREPORT"
30 #endif // wxUSE_DEBUGREPORT
31
32 #if !wxUSE_ON_FATAL_EXCEPTION
33 #error "This sample can't be built without wxUSE_ON_FATAL_EXCEPTION"
34 #endif // wxUSE_ON_FATAL_EXCEPTION
35
36 #if !defined(__WXMSW__) && !defined(__WXPM__)
37 #include "../sample.xpm"
38 #endif
39
40 // ----------------------------------------------------------------------------
41 // custom debug reporting class
42 // ----------------------------------------------------------------------------
43
44 // this is your custom debug reporter: it will use curl program (which should
45 // be available) to upload the crash report to the given URL (which should be
46 // set up by you)
47 class MyDebugReport : public wxDebugReportUpload
48 {
49 public:
50 MyDebugReport() : wxDebugReportUpload
51 (
52 _T("http://your.url.here/"),
53 _T("report:file"),
54 _T("action")
55 )
56 {
57 }
58
59 protected:
60 // this is called with the contents of the server response: you will
61 // probably want to parse the XML document in OnServerReply() instead of
62 // just dumping it as I do
63 virtual bool OnServerReply(const wxArrayString& reply)
64 {
65 if ( reply.IsEmpty() )
66 {
67 wxLogError(_T("Didn't receive the expected server reply."));
68 return false;
69 }
70
71 wxString s(_T("Server replied:\n"));
72
73 const size_t count = reply.GetCount();
74 for ( size_t n = 0; n < count; n++ )
75 {
76 s << _T('\t') << reply[n] << _T('\n');
77 }
78
79 wxLogMessage(_T("%s"), s.c_str());
80
81 return true;
82 }
83 };
84
85 // another possibility would be to build email library from contrib and use
86 // this class, after uncommenting it:
87 #if 0
88
89 #include "wx/net/email.h"
90
91 class MyDebugReport : public wxDebugReportCompress
92 {
93 public:
94 virtual bool DoProcess()
95 {
96 if ( !wxDebugReportCompress::DoProcess() )
97 return false;
98 wxMailMessage msg(GetReportName() + _T(" crash report"),
99 _T("vadim@wxwindows.org"),
100 wxEmptyString, // mail body
101 wxEmptyString, // from address
102 GetCompressedFileName(),
103 _T("crashreport.zip"));
104
105 return wxEmail::Send(msg);
106 }
107 };
108
109 #endif // 0
110
111 // ----------------------------------------------------------------------------
112 // helper functions
113 // ----------------------------------------------------------------------------
114
115 // just some functions to get a slightly deeper stack trace
116 static void bar(const wxChar *p)
117 {
118 char *pc = 0;
119 *pc = *p;
120
121 printf("bar: %s\n", p);
122 }
123
124 void baz(const wxString& s)
125 {
126 printf("baz: %s\n", s.c_str());
127 }
128
129 void foo(int n)
130 {
131 if ( n % 2 )
132 baz(wxT("odd"));
133 else
134 bar(wxT("even"));
135 }
136
137 // ----------------------------------------------------------------------------
138 // main window
139 // ----------------------------------------------------------------------------
140
141 enum
142 {
143 DebugRpt_Quit = wxID_EXIT,
144 DebugRpt_Crash = 100,
145 DebugRpt_Current,
146 DebugRpt_Paint,
147 DebugRpt_Upload,
148 DebugRpt_About = wxID_ABOUT
149 };
150
151 class MyFrame : public wxFrame
152 {
153 public:
154 MyFrame();
155
156 private:
157 void OnQuit(wxCommandEvent& event);
158 void OnReportForCrash(wxCommandEvent& event);
159 void OnReportForCurrent(wxCommandEvent& event);
160 void OnReportPaint(wxCommandEvent& event);
161 void OnReportUpload(wxCommandEvent& event);
162 void OnAbout(wxCommandEvent& event);
163
164 void OnPaint(wxPaintEvent& event);
165
166
167 // number of lines drawn in OnPaint()
168 int m_numLines;
169
170 DECLARE_NO_COPY_CLASS(MyFrame)
171 DECLARE_EVENT_TABLE()
172 };
173
174 // ----------------------------------------------------------------------------
175 // application class
176 // ----------------------------------------------------------------------------
177
178 // this is a usual application class modified to work with debug reporter
179 //
180 // basically just 2 things are necessary: call wxHandleFatalExceptions() as
181 // early as possible and override OnFatalException() to create the report there
182 class MyApp : public wxApp
183 {
184 public:
185 // call wxHandleFatalExceptions here
186 MyApp();
187
188 // create our main window here
189 virtual bool OnInit();
190
191 // called when a crash occurs in this application
192 virtual void OnFatalException();
193
194 // this is where we really generate the debug report
195 void GenerateReport(wxDebugReport::Context ctx);
196
197 // if this function is called, we'll use MyDebugReport which would try to
198 // upload the (next) generated debug report to its URL, otherwise we just
199 // generate the debug report and leave it in a local file
200 void UploadReport(bool doIt) { m_uploadReport = doIt; }
201
202 private:
203 bool m_uploadReport;
204
205 DECLARE_NO_COPY_CLASS(MyApp)
206 };
207
208 IMPLEMENT_APP(MyApp)
209
210 // ============================================================================
211 // implementation
212 // ============================================================================
213
214 // ----------------------------------------------------------------------------
215 // MyFrame
216 // ----------------------------------------------------------------------------
217
218 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
219 EVT_MENU(DebugRpt_Quit, MyFrame::OnQuit)
220 EVT_MENU(DebugRpt_Crash, MyFrame::OnReportForCrash)
221 EVT_MENU(DebugRpt_Current, MyFrame::OnReportForCurrent)
222 EVT_MENU(DebugRpt_Paint, MyFrame::OnReportPaint)
223 EVT_MENU(DebugRpt_Upload, MyFrame::OnReportUpload)
224 EVT_MENU(DebugRpt_About, MyFrame::OnAbout)
225
226 EVT_PAINT(MyFrame::OnPaint)
227 END_EVENT_TABLE()
228
229 MyFrame::MyFrame()
230 : wxFrame(NULL, wxID_ANY, _T("wxWidgets Debug Report Sample"))
231 {
232 m_numLines = 10;
233
234 SetIcon(wxICON(sample));
235
236 wxMenu *menuFile = new wxMenu;
237 menuFile->Append(DebugRpt_Quit, _T("E&xit\tAlt-X"));
238
239 wxMenu *menuReport = new wxMenu;
240 menuReport->Append(DebugRpt_Crash, _T("Report for &crash\tCtrl-C"),
241 _T("Provoke a crash inside the program and create report for it"));
242 menuReport->Append(DebugRpt_Current, _T("Report for c&urrent context\tCtrl-U"),
243 _T("Create report for the current program context"));
244 menuReport->Append(DebugRpt_Paint, _T("Report for &paint handler\tCtrl-P"),
245 _T("Provoke a repeatable crash in wxEVT_PAINT handler"));
246 menuReport->AppendSeparator();
247 menuReport->AppendCheckItem(DebugRpt_Upload, _T("Up&load debug report"),
248 _T("You need to configure a web server accepting debug report uploads to use this function"));
249
250 wxMenu *menuHelp = new wxMenu;
251 menuHelp->Append(DebugRpt_About, _T("&About...\tF1"));
252
253 wxMenuBar *mbar = new wxMenuBar();
254 mbar->Append(menuFile, _T("&File"));
255 mbar->Append(menuReport, _T("&Report"));
256 mbar->Append(menuHelp, _T("&Help"));
257 SetMenuBar(mbar);
258
259 CreateStatusBar();
260
261 Show();
262 }
263
264 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
265 {
266 Close(true);
267 }
268
269 void MyFrame::OnReportForCrash(wxCommandEvent& WXUNUSED(event))
270 {
271 // this call is going to crash
272 foo(32);
273 foo(17);
274 }
275
276 void MyFrame::OnReportForCurrent(wxCommandEvent& WXUNUSED(event))
277 {
278 // example of manually generated report, this could be also
279 // used in wxApp::OnAssert()
280 wxGetApp().GenerateReport(wxDebugReport::Context_Current);
281 }
282
283 void MyFrame::OnReportPaint(wxCommandEvent& WXUNUSED(event))
284 {
285 // this will result in a crash in OnPaint()
286 m_numLines = 0;
287
288 // ensure it's called immediately
289 Refresh();
290 }
291
292 void MyFrame::OnReportUpload(wxCommandEvent& event)
293 {
294 wxGetApp().UploadReport(event.IsChecked());
295 }
296
297 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
298 {
299 wxMessageBox
300 (
301 _T("wxDebugReport sample\n(c) 2005 Vadim Zeitlin <vadim@wxwindows.org>"),
302 _T("wxWidgets Debug Report Sample"),
303 wxOK | wxICON_INFORMATION,
304 this
305 );
306 }
307
308 void MyFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
309 {
310 wxPaintDC dc(this);
311 const wxSize size = GetClientSize();
312 for ( wxCoord x = 0; x < size.x; x += size.x/m_numLines )
313 dc.DrawLine(x, 0, x, size.y);
314 }
315
316 // ----------------------------------------------------------------------------
317 // MyApp
318 // ----------------------------------------------------------------------------
319
320 MyApp::MyApp()
321 {
322 // user needs to explicitely enable this
323 m_uploadReport = false;
324
325 // call this to tell the library to call our OnFatalException()
326 wxHandleFatalExceptions();
327 }
328
329 bool MyApp::OnInit()
330 {
331 if ( !wxApp::OnInit() )
332 return false;
333
334 new MyFrame;
335
336 return true;
337 }
338
339 void MyApp::OnFatalException()
340 {
341 GenerateReport(wxDebugReport::Context_Exception);
342 }
343
344 void MyApp::GenerateReport(wxDebugReport::Context ctx)
345 {
346 wxDebugReportCompress *report = m_uploadReport ? new MyDebugReport
347 : new wxDebugReportCompress;
348
349 // add all standard files: currently this means just a minidump and an
350 // XML file with system info and stack trace
351 report->AddAll(ctx);
352
353 // you can also call report->AddFile(...) with your own log files, files
354 // created using wxRegKey::Export() and so on, here we just add a test
355 // file containing the date of the crash
356 wxFileName fn(report->GetDirectory(), _T("timestamp.my"));
357 wxFFile file(fn.GetFullPath(), _T("w"));
358 if ( file.IsOpened() )
359 {
360 wxDateTime dt = wxDateTime::Now();
361 file.Write(dt.FormatISODate() + _T(' ') + dt.FormatISOTime());
362 file.Close();
363 }
364
365 report->AddFile(fn.GetFullName(), _T("timestamp of this report"));
366
367 // can also add an existing file directly, it will be copied
368 // automatically
369 #ifdef __WXMSW__
370 report->AddFile(_T("c:\\autoexec.bat"), _T("DOS startup file"));
371 #else
372 report->AddFile(_T("/etc/motd"), _T("Message of the day"));
373 #endif
374
375 // calling Show() is not mandatory, but is more polite
376 if ( wxDebugReportPreviewStd().Show(*report) )
377 {
378 if ( report->Process() )
379 {
380 if ( m_uploadReport )
381 {
382 wxLogMessage(_T("Report successfully uploaded."));
383 }
384 else
385 {
386 wxLogMessage(_T("Report generated in \"%s\"."),
387 report->GetCompressedFileName().c_str());
388 report->Reset();
389 }
390 }
391 }
392 //else: user cancelled the report
393
394 delete report;
395 }
396