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