// Created: 2005-01-20
// RCS-ID: $Id$
// Copyright: (c) 2005 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
-// License: wxWindows licence
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
#include "wx/app.h"
#include "wx/log.h"
+#include "wx/frame.h"
+#include "wx/icon.h"
+#include "wx/menu.h"
+#include "wx/msgdlg.h"
+#include "wx/button.h"
+#include "wx/dcclient.h"
+
#include "wx/datetime.h"
#include "wx/ffile.h"
#include "wx/filename.h"
-#include "wx/dynlib.h"
#include "wx/debugrpt.h"
-#include "wx/msgdlg.h"
-
#if !wxUSE_DEBUGREPORT
#error "This sample can't be built without wxUSE_DEBUGREPORT"
#endif // wxUSE_DEBUGREPORT
+#if !wxUSE_ON_FATAL_EXCEPTION
+ #error "This sample can't be built without wxUSE_ON_FATAL_EXCEPTION"
+#endif // wxUSE_ON_FATAL_EXCEPTION
+
+#if !defined(__WXMSW__) && !defined(__WXPM__)
+ #include "../sample.xpm"
+#endif
+
// ----------------------------------------------------------------------------
// custom debug reporting class
// ----------------------------------------------------------------------------
-// this is your custom debug reporter, you will probably want to parse the XML
-// document in OnServerReply() instead of just dumping it as I do
+// this is your custom debug reporter: it will use curl program (which should
+// be available) to upload the crash report to the given URL (which should be
+// set up by you)
class MyDebugReport : public wxDebugReportUpload
{
public:
MyDebugReport() : wxDebugReportUpload
(
- _T("http://your.url.here/"),
- _T("report:file"),
- _T("action")
+ wxT("http://your.url.here/"),
+ wxT("report:file"),
+ wxT("action")
)
{
}
protected:
+ // this is called with the contents of the server response: you will
+ // probably want to parse the XML document in OnServerReply() instead of
+ // just dumping it as I do
virtual bool OnServerReply(const wxArrayString& reply)
{
if ( reply.IsEmpty() )
{
- wxLogError(_T("Didn't receive the expected server reply."));
+ wxLogError(wxT("Didn't receive the expected server reply."));
return false;
}
- wxString s(_T("Server replied:\n"));
+ wxString s(wxT("Server replied:\n"));
const size_t count = reply.GetCount();
for ( size_t n = 0; n < count; n++ )
{
- s << _T('\t') << reply[n] << _T('\n');
+ s << wxT('\t') << reply[n] << wxT('\n');
}
- wxLogMessage(_T("%s"), s.c_str());
+ wxLogMessage(wxT("%s"), s.c_str());
return true;
}
};
+// another possibility would be to build email library from contrib and use
+// this class, after uncommenting it:
+#if 0
+
+#include "wx/net/email.h"
+
+class MyDebugReport : public wxDebugReportCompress
+{
+public:
+ virtual bool DoProcess()
+ {
+ if ( !wxDebugReportCompress::DoProcess() )
+ return false;
+ wxMailMessage msg(GetReportName() + wxT(" crash report"),
+ wxT("vadim@wxwindows.org"),
+ wxEmptyString, // mail body
+ wxEmptyString, // from address
+ GetCompressedFileName(),
+ wxT("crashreport.zip"));
+
+ return wxEmail::Send(msg);
+ }
+};
+
+#endif // 0
+
// ----------------------------------------------------------------------------
// helper functions
// ----------------------------------------------------------------------------
// just some functions to get a slightly deeper stack trace
-static void bar(const wxChar *p)
+static void bar(const char *p)
{
char *pc = 0;
*pc = *p;
void baz(const wxString& s)
{
- printf("baz: %s\n", s.c_str());
+ printf("baz: %s\n", (const char*)s.c_str());
}
void foo(int n)
{
if ( n % 2 )
- baz(wxT("odd"));
+ baz("odd");
else
- bar(wxT("even"));
+ bar("even");
}
+// ----------------------------------------------------------------------------
+// main window
+// ----------------------------------------------------------------------------
+
+enum
+{
+ DebugRpt_Quit = wxID_EXIT,
+ DebugRpt_Crash = 100,
+ DebugRpt_Current,
+ DebugRpt_Paint,
+ DebugRpt_Upload,
+ DebugRpt_About = wxID_ABOUT
+};
+
+class MyFrame : public wxFrame
+{
+public:
+ MyFrame();
+
+private:
+ void OnQuit(wxCommandEvent& event);
+ void OnReportForCrash(wxCommandEvent& event);
+ void OnReportForCurrent(wxCommandEvent& event);
+ void OnReportPaint(wxCommandEvent& event);
+ void OnReportUpload(wxCommandEvent& event);
+ void OnAbout(wxCommandEvent& event);
+
+ void OnPaint(wxPaintEvent& event);
+
+
+ // number of lines drawn in OnPaint()
+ int m_numLines;
+
+ wxDECLARE_NO_COPY_CLASS(MyFrame);
+ DECLARE_EVENT_TABLE()
+};
+
// ----------------------------------------------------------------------------
// application class
// ----------------------------------------------------------------------------
class MyApp : public wxApp
{
public:
- virtual bool OnInit()
- {
- wxHandleFatalExceptions();
+ // call wxHandleFatalExceptions here
+ MyApp();
- if ( !wxApp::OnInit() )
- return false;
+ // create our main window here
+ virtual bool OnInit();
- return true;
- }
+ // called when a crash occurs in this application
+ virtual void OnFatalException();
- virtual int OnRun()
- {
- // a real program would be presumably be a bit harder to crash than
- // just pressing "yes" in a dialog... but this is just an example
- switch ( wxMessageBox
- (
- _T("Generate report for crash (or just current context)?"),
- _T("wxDebugReport Test"),
- wxYES_NO | wxCANCEL
- ) )
- {
- case wxYES:
- // this call is going to crash
- foo(32);
- foo(17);
- break;
-
- case wxNO:
- // example of manually generated report, this could be also
- // used in wxApp::OnAssert()
- GenerateReport(wxDebugReport::Context_Curent);
- break;
-
- case wxCANCEL:
- break;
- }
+ // this is where we really generate the debug report
+ void GenerateReport(wxDebugReport::Context ctx);
- return 0;
- }
+ // if this function is called, we'll use MyDebugReport which would try to
+ // upload the (next) generated debug report to its URL, otherwise we just
+ // generate the debug report and leave it in a local file
+ void UploadReport(bool doIt) { m_uploadReport = doIt; }
+
+private:
+ bool m_uploadReport;
+
+ wxDECLARE_NO_COPY_CLASS(MyApp);
+};
+
+IMPLEMENT_APP(MyApp)
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// MyFrame
+// ----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(MyFrame, wxFrame)
+ EVT_MENU(DebugRpt_Quit, MyFrame::OnQuit)
+ EVT_MENU(DebugRpt_Crash, MyFrame::OnReportForCrash)
+ EVT_MENU(DebugRpt_Current, MyFrame::OnReportForCurrent)
+ EVT_MENU(DebugRpt_Paint, MyFrame::OnReportPaint)
+ EVT_MENU(DebugRpt_Upload, MyFrame::OnReportUpload)
+ EVT_MENU(DebugRpt_About, MyFrame::OnAbout)
+
+ EVT_PAINT(MyFrame::OnPaint)
+END_EVENT_TABLE()
+
+MyFrame::MyFrame()
+ : wxFrame(NULL, wxID_ANY, wxT("wxWidgets Debug Report Sample"),
+ wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE|wxDEFAULT_FRAME_STYLE)
+{
+ m_numLines = 10;
+
+ SetIcon(wxICON(sample));
+
+ wxMenu *menuFile = new wxMenu;
+ menuFile->Append(DebugRpt_Quit, wxT("E&xit\tAlt-X"));
- virtual void OnFatalException()
+ wxMenu *menuReport = new wxMenu;
+ menuReport->Append(DebugRpt_Crash, wxT("Report for &crash\tCtrl-C"),
+ wxT("Provoke a crash inside the program and create report for it"));
+ menuReport->Append(DebugRpt_Current, wxT("Report for c&urrent context\tCtrl-U"),
+ wxT("Create report for the current program context"));
+ menuReport->Append(DebugRpt_Paint, wxT("Report for &paint handler\tCtrl-P"),
+ wxT("Provoke a repeatable crash in wxEVT_PAINT handler"));
+ menuReport->AppendSeparator();
+ menuReport->AppendCheckItem(DebugRpt_Upload, wxT("Up&load debug report"),
+ wxT("You need to configure a web server accepting debug report uploads to use this function"));
+
+ wxMenu *menuHelp = new wxMenu;
+ menuHelp->Append(DebugRpt_About, wxT("&About...\tF1"));
+
+ wxMenuBar *mbar = new wxMenuBar();
+ mbar->Append(menuFile, wxT("&File"));
+ mbar->Append(menuReport, wxT("&Report"));
+ mbar->Append(menuHelp, wxT("&Help"));
+ SetMenuBar(mbar);
+
+ CreateStatusBar();
+
+ Show();
+}
+
+void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+{
+ Close(true);
+}
+
+void MyFrame::OnReportForCrash(wxCommandEvent& WXUNUSED(event))
+{
+ // this call is going to crash
+ foo(32);
+ foo(17);
+}
+
+void MyFrame::OnReportForCurrent(wxCommandEvent& WXUNUSED(event))
+{
+ // example of manually generated report, this could be also
+ // used in wxApp::OnAssert()
+ wxGetApp().GenerateReport(wxDebugReport::Context_Current);
+}
+
+void MyFrame::OnReportPaint(wxCommandEvent& WXUNUSED(event))
+{
+ // this will result in a crash in OnPaint()
+ m_numLines = 0;
+
+ // ensure it's called immediately
+ Refresh();
+}
+
+void MyFrame::OnReportUpload(wxCommandEvent& event)
+{
+ wxGetApp().UploadReport(event.IsChecked());
+}
+
+void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+{
+ wxMessageBox
+ (
+ wxT("wxDebugReport sample\n(c) 2005 Vadim Zeitlin <vadim@wxwindows.org>"),
+ wxT("wxWidgets Debug Report Sample"),
+ wxOK | wxICON_INFORMATION,
+ this
+ );
+}
+
+void MyFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
+{
+ wxPaintDC dc(this);
+ const wxSize size = GetClientSize();
+ for ( wxCoord x = 0; x < size.x; x += size.x/m_numLines )
+ dc.DrawLine(x, 0, x, size.y);
+}
+
+// ----------------------------------------------------------------------------
+// MyApp
+// ----------------------------------------------------------------------------
+
+MyApp::MyApp()
+{
+ // user needs to explicitly enable this
+ m_uploadReport = false;
+
+ // call this to tell the library to call our OnFatalException()
+ wxHandleFatalExceptions();
+}
+
+bool MyApp::OnInit()
+{
+ if ( !wxApp::OnInit() )
+ return false;
+
+ new MyFrame;
+
+ return true;
+}
+
+void MyApp::OnFatalException()
+{
+ GenerateReport(wxDebugReport::Context_Exception);
+}
+
+void MyApp::GenerateReport(wxDebugReport::Context ctx)
+{
+ wxDebugReportCompress *report = m_uploadReport ? new MyDebugReport
+ : new wxDebugReportCompress;
+
+ // add all standard files: currently this means just a minidump and an
+ // XML file with system info and stack trace
+ report->AddAll(ctx);
+
+ // you can also call report->AddFile(...) with your own log files, files
+ // created using wxRegKey::Export() and so on, here we just add a test
+ // file containing the date of the crash
+ wxFileName fn(report->GetDirectory(), wxT("timestamp.my"));
+ wxFFile file(fn.GetFullPath(), wxT("w"));
+ if ( file.IsOpened() )
{
- GenerateReport(wxDebugReport::Context_Exception);
+ wxDateTime dt = wxDateTime::Now();
+ file.Write(dt.FormatISODate() + wxT(' ') + dt.FormatISOTime());
+ file.Close();
}
- void GenerateReport(wxDebugReport::Context ctx)
- {
- MyDebugReport report;
-
- // add all standard files: currently this means just a minidump and an
- // XML file with system info and stack trace
- report.AddAll(ctx);
-
- // you can also call report.AddFile(...) with your own log files, files
- // created using wxRegKey::Export() and so on, here we just add a test
- // file containing the date of the crash
- wxFileName fn(report.GetDirectory(), _T("timestamp.my"));
- wxFFile file(fn.GetFullPath(), _T("w"));
- if ( file.IsOpened() )
- {
- wxDateTime dt = wxDateTime::Now();
- file.Write(dt.FormatISODate() + _T(' ') + dt.FormatISOTime());
- file.Close();
- }
+ report->AddFile(fn.GetFullName(), wxT("timestamp of this report"));
- report.AddFile(fn.GetFullName(), _T("timestamp of this report"));
+ // can also add an existing file directly, it will be copied
+ // automatically
+#ifdef __WXMSW__
+ report->AddFile(wxT("c:\\autoexec.bat"), wxT("DOS startup file"));
+#else
+ report->AddFile(wxT("/etc/motd"), wxT("Message of the day"));
+#endif
- // calling Show() is not mandatory, but is more polite
- if ( wxDebugReportPreviewStd().Show(report) )
+ // calling Show() is not mandatory, but is more polite
+ if ( wxDebugReportPreviewStd().Show(*report) )
+ {
+ if ( report->Process() )
{
- if ( report.Process() )
+ if ( m_uploadReport )
+ {
+ wxLogMessage(wxT("Report successfully uploaded."));
+ }
+ else
{
- // report successfully uploaded
+ wxLogMessage(wxT("Report generated in \"%s\"."),
+ report->GetCompressedFileName().c_str());
+ report->Reset();
}
}
- //else: user cancelled the report
}
-};
+ //else: user cancelled the report
-IMPLEMENT_APP(MyApp)
+ delete report;
+}