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