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