b70ed2d8c8
Also use wxString instead of wxChar* strings. Closes https://github.com/wxWidgets/wxWidgets/pull/950
480 lines
14 KiB
C++
480 lines
14 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: debugrpt.cpp
|
|
// Purpose: minimal sample showing wxDebugReport and related classes
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 2005-01-20
|
|
// Copyright: (c) 2005 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/app.h"
|
|
#include "wx/log.h"
|
|
#include "wx/frame.h"
|
|
#include "wx/icon.h"
|
|
#include "wx/menu.h"
|
|
#include "wx/choicdlg.h"
|
|
#include "wx/msgdlg.h"
|
|
#include "wx/button.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/timer.h"
|
|
|
|
#include "wx/datetime.h"
|
|
#include "wx/ffile.h"
|
|
#include "wx/filename.h"
|
|
#include "wx/debugrpt.h"
|
|
#include "wx/dynlib.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
|
|
|
|
#ifndef wxHAS_IMAGES_IN_RESOURCES
|
|
#include "../sample.xpm"
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// custom debug reporting class
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// 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
|
|
(
|
|
"http://your.url.here/",
|
|
"report:file",
|
|
"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) wxOVERRIDE
|
|
{
|
|
if ( reply.IsEmpty() )
|
|
{
|
|
wxLogError("Didn't receive the expected server reply.");
|
|
return false;
|
|
}
|
|
|
|
wxString s("Server replied:\n");
|
|
|
|
const size_t count = reply.GetCount();
|
|
for ( size_t n = 0; n < count; n++ )
|
|
{
|
|
s << '\t' << reply[n] << '\n';
|
|
}
|
|
|
|
wxLogMessage("%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() + " crash report",
|
|
"vadim@wxwindows.org",
|
|
wxEmptyString, // mail body
|
|
wxEmptyString, // from address
|
|
GetCompressedFileName(),
|
|
"crashreport.zip");
|
|
|
|
return wxEmail::Send(msg);
|
|
}
|
|
};
|
|
|
|
#endif // 0
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// helper functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// just some functions to get a slightly deeper stack trace
|
|
static void bar(const char *p)
|
|
{
|
|
char *pc = 0;
|
|
*pc = *p;
|
|
|
|
printf("bar: %s\n", p);
|
|
}
|
|
|
|
void baz(const wxString& s)
|
|
{
|
|
printf("baz: %s\n", (const char*)s.c_str());
|
|
}
|
|
|
|
void foo(int n)
|
|
{
|
|
if ( n % 2 )
|
|
baz("odd");
|
|
else
|
|
bar("even");
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// main window
|
|
// ----------------------------------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
DebugRpt_ListLoadedDLLs = 50,
|
|
DebugRpt_Quit = wxID_EXIT,
|
|
DebugRpt_Crash = 100,
|
|
DebugRpt_Current,
|
|
DebugRpt_Paint,
|
|
DebugRpt_Timer,
|
|
DebugRpt_Upload,
|
|
DebugRpt_About = wxID_ABOUT
|
|
};
|
|
|
|
class MyFrame : public wxFrame
|
|
{
|
|
public:
|
|
MyFrame();
|
|
|
|
private:
|
|
void OnListLoadedDLLs(wxCommandEvent& event);
|
|
void OnQuit(wxCommandEvent& event);
|
|
void OnReportForCrash(wxCommandEvent& event);
|
|
void OnReportForCurrent(wxCommandEvent& event);
|
|
void OnReportPaint(wxCommandEvent& event);
|
|
void OnReportTimer(wxCommandEvent& event);
|
|
void OnReportUpload(wxCommandEvent& event);
|
|
void OnAbout(wxCommandEvent& event);
|
|
|
|
void OnPaint(wxPaintEvent& event);
|
|
|
|
// a timer whose only purpose in life is to crash as soon as it's started
|
|
class BadTimer : public wxTimer
|
|
{
|
|
public:
|
|
BadTimer() { }
|
|
|
|
virtual void Notify() wxOVERRIDE
|
|
{
|
|
foo(8);
|
|
}
|
|
} m_badTimer;
|
|
|
|
// number of lines drawn in OnPaint()
|
|
int m_numLines;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(MyFrame);
|
|
wxDECLARE_EVENT_TABLE();
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// application class
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// this is a usual application class modified to work with debug reporter
|
|
//
|
|
// basically just 2 things are necessary: call wxHandleFatalExceptions() as
|
|
// early as possible and override OnFatalException() to create the report there
|
|
class MyApp : public wxApp
|
|
{
|
|
public:
|
|
// call wxHandleFatalExceptions here
|
|
MyApp();
|
|
|
|
// create our main window here
|
|
virtual bool OnInit() wxOVERRIDE;
|
|
|
|
// called when a crash occurs in this application
|
|
virtual void OnFatalException() wxOVERRIDE;
|
|
|
|
// this is where we really generate the debug report
|
|
void GenerateReport(wxDebugReport::Context ctx);
|
|
|
|
// 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);
|
|
};
|
|
|
|
wxIMPLEMENT_APP(MyApp);
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MyFrame
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
|
EVT_MENU(DebugRpt_ListLoadedDLLs, MyFrame::OnListLoadedDLLs)
|
|
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_Timer, MyFrame::OnReportTimer)
|
|
EVT_MENU(DebugRpt_Upload, MyFrame::OnReportUpload)
|
|
EVT_MENU(DebugRpt_About, MyFrame::OnAbout)
|
|
|
|
EVT_PAINT(MyFrame::OnPaint)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
MyFrame::MyFrame()
|
|
: wxFrame(NULL, wxID_ANY, "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_ListLoadedDLLs, "&List loaded DLLs...\tCtrl-L");
|
|
menuFile->AppendSeparator();
|
|
menuFile->Append(DebugRpt_Quit, "E&xit\tAlt-X");
|
|
|
|
wxMenu *menuReport = new wxMenu;
|
|
menuReport->Append(DebugRpt_Crash, "Report for &crash\tCtrl-C",
|
|
"Provoke a crash inside the program and create report for it");
|
|
menuReport->Append(DebugRpt_Current, "Report for c&urrent context\tCtrl-U",
|
|
"Create report for the current program context");
|
|
menuReport->Append(DebugRpt_Paint, "Report for &paint handler\tCtrl-P",
|
|
"Provoke a repeatable crash in wxEVT_PAINT handler");
|
|
menuReport->Append(DebugRpt_Timer, "Report for &timer handler\tCtrl-T",
|
|
"Provoke a crash in wxEVT_TIMER handler");
|
|
menuReport->AppendSeparator();
|
|
menuReport->AppendCheckItem(DebugRpt_Upload, "Up&load debug report",
|
|
"You need to configure a web server accepting debug report uploads to use this function");
|
|
|
|
wxMenu *menuHelp = new wxMenu;
|
|
menuHelp->Append(DebugRpt_About, "&About\tF1");
|
|
|
|
wxMenuBar *mbar = new wxMenuBar();
|
|
mbar->Append(menuFile, "&File");
|
|
mbar->Append(menuReport, "&Report");
|
|
mbar->Append(menuHelp, "&Help");
|
|
SetMenuBar(mbar);
|
|
|
|
CreateStatusBar();
|
|
|
|
Show();
|
|
}
|
|
|
|
void MyFrame::OnListLoadedDLLs(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
const wxDynamicLibraryDetailsArray loaded = wxDynamicLibrary::ListLoaded();
|
|
const size_t count = loaded.size();
|
|
if ( !count )
|
|
{
|
|
wxLogError("Failed to get the list of loaded dynamic libraries.");
|
|
return;
|
|
}
|
|
|
|
wxArrayString names;
|
|
names.reserve(count);
|
|
for ( size_t n = 0; n < count; n++ )
|
|
{
|
|
names.push_back(loaded[n].GetName());
|
|
}
|
|
|
|
for ( ;; )
|
|
{
|
|
const int sel = wxGetSingleChoiceIndex
|
|
(
|
|
"Choose a library to show more information about it",
|
|
"List of loaded dynamic libraries",
|
|
names,
|
|
this
|
|
);
|
|
if ( sel == wxNOT_FOUND )
|
|
return;
|
|
|
|
const wxDynamicLibraryDetails& det = loaded[sel];
|
|
void *addr = 0;
|
|
size_t len = 0;
|
|
det.GetAddress(&addr, &len);
|
|
wxLogMessage("Full path is \"%s\", memory range %p:%p, version \"%s\"",
|
|
det.GetPath(),
|
|
addr,
|
|
static_cast<void*>(static_cast<char*>(addr) + len),
|
|
det.GetVersion());
|
|
}
|
|
}
|
|
|
|
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::OnReportTimer(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
// this will result in a crash in BadTimer::OnNotify() soon
|
|
m_badTimer.StartOnce(100);
|
|
}
|
|
|
|
void MyFrame::OnReportUpload(wxCommandEvent& event)
|
|
{
|
|
wxGetApp().UploadReport(event.IsChecked());
|
|
}
|
|
|
|
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
wxMessageBox
|
|
(
|
|
"wxDebugReport sample\n(c) 2005 Vadim Zeitlin <vadim@wxwindows.org>",
|
|
"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(), "timestamp.my");
|
|
wxFFile file(fn.GetFullPath(), "w");
|
|
if ( file.IsOpened() )
|
|
{
|
|
wxDateTime dt = wxDateTime::Now();
|
|
file.Write(dt.FormatISODate() + ' ' + dt.FormatISOTime());
|
|
file.Close();
|
|
}
|
|
|
|
report->AddFile(fn.GetFullName(), "timestamp of this report");
|
|
|
|
// can also add an existing file directly, it will be copied
|
|
// automatically
|
|
#ifdef __WXMSW__
|
|
wxString windir;
|
|
if ( !wxGetEnv("WINDIR", &windir) )
|
|
windir = "C:\\Windows";
|
|
fn.AssignDir(windir);
|
|
fn.AppendDir("system32");
|
|
fn.AppendDir("drivers");
|
|
fn.AppendDir("etc");
|
|
#else // !__WXMSW__
|
|
fn.AssignDir("/etc");
|
|
#endif // __WXMSW__/!__WXMSW__
|
|
fn.SetFullName("hosts");
|
|
|
|
if ( fn.FileExists() )
|
|
report->AddFile(fn.GetFullPath(), "Local hosts file");
|
|
|
|
// calling Show() is not mandatory, but is more polite
|
|
if ( wxDebugReportPreviewStd().Show(*report) )
|
|
{
|
|
if ( report->Process() )
|
|
{
|
|
if ( m_uploadReport )
|
|
{
|
|
wxLogMessage("Report successfully uploaded.");
|
|
}
|
|
else
|
|
{
|
|
wxLogMessage("Report generated in \"%s\".",
|
|
report->GetCompressedFileName().c_str());
|
|
report->Reset();
|
|
}
|
|
}
|
|
}
|
|
//else: user cancelled the report
|
|
|
|
delete report;
|
|
}
|
|
|