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