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: debugrpt.cpp 35373 2005-08-30 17:30:43Z ABX $ 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) 49class MyDebugReport : public wxDebugReportUpload 50{ 51public: 52 MyDebugReport() : wxDebugReportUpload 53 ( 54 _T("http://your.url.here/"), 55 _T("report:file"), 56 _T("action") 57 ) 58 { 59 } 60 61protected: 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 93class MyDebugReport : public wxDebugReportCompress 94{ 95public: 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 118static void bar(const wxChar *p) 119{ 120 char *pc = 0; 121 *pc = *p; 122 123 printf("bar: %s\n", p); 124} 125 126void baz(const wxString& s) 127{ 128 printf("baz: %s\n", s.c_str()); 129} 130 131void 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 143enum 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 153class MyFrame : public wxFrame 154{ 155public: 156 MyFrame(); 157 158private: 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 184class MyApp : public wxApp 185{ 186public: 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 204private: 205 bool m_uploadReport; 206 207 DECLARE_NO_COPY_CLASS(MyApp) 208}; 209 210IMPLEMENT_APP(MyApp) 211 212// ============================================================================ 213// implementation 214// ============================================================================ 215 216// ---------------------------------------------------------------------------- 217// MyFrame 218// ---------------------------------------------------------------------------- 219 220BEGIN_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) 229END_EVENT_TABLE() 230 231MyFrame::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 266void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) 267{ 268 Close(true); 269} 270 271void MyFrame::OnReportForCrash(wxCommandEvent& WXUNUSED(event)) 272{ 273 // this call is going to crash 274 foo(32); 275 foo(17); 276} 277 278void 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 285void 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 294void MyFrame::OnReportUpload(wxCommandEvent& event) 295{ 296 wxGetApp().UploadReport(event.IsChecked()); 297} 298 299void 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 310void 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 322MyApp::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 331bool MyApp::OnInit() 332{ 333 if ( !wxApp::OnInit() ) 334 return false; 335 336 new MyFrame; 337 338 return true; 339} 340 341void MyApp::OnFatalException() 342{ 343 GenerateReport(wxDebugReport::Context_Exception); 344} 345 346void 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 399