Don't hard code the number of stack frames after wxOnAssert().

The number of frames between the code containing the assert and the code
generating the stack trace is not the same under different platforms and so
hardcoding 8 for it in wxAppTraitsBase::GetAssertStackTrace() worked for wxMSW
but not e.g. wxGTK.

Instead, just ignore all frames up to and including the one for wxOnAssert()
itself. This makes the code work correctly on all platforms and it also won't
need to be modified whenever any extra functions are added/removed
(wxGTK-specific code in utilsgtk.cpp used wrong number of frames too, even
though it was presumably correct once before).
This commit is contained in:
Vadim Zeitlin
2015-07-05 18:33:49 +02:00
parent 6c43aa90b6
commit b7f1ac40f4
2 changed files with 73 additions and 32 deletions

View File

@@ -1012,25 +1012,37 @@ wxString wxAppTraitsBase::GetAssertStackTrace()
#endif // !__WINDOWS__
wxString stackTrace;
class StackDump : public wxStackWalker
{
public:
StackDump() { }
StackDump() { m_numFrames = 0; }
const wxString& GetStackTrace() const { return m_stackTrace; }
protected:
virtual void OnStackFrame(const wxStackFrame& frame) wxOVERRIDE
{
m_stackTrace << wxString::Format
(
wxT("[%02d] "),
wx_truncate_cast(int, frame.GetLevel())
);
// don't show more than maxLines or we could get a dialog too tall
// to be shown on screen: 20 should be ok everywhere as even with
// 15 pixel high characters it is still only 300 pixels...
if ( m_numFrames++ > 20 )
return;
m_stackTrace << wxString::Format(wxT("[%02u] "), m_numFrames);
const wxString name = frame.GetName();
if ( name.StartsWith("wxOnAssert") )
{
// Ignore all frames until the wxOnAssert() one, they are
// internal to wxWidgets and not interesting for the user
// (but notice that if we never find the wxOnAssert() frame,
// e.g. because we don't have symbol info at all, we would show
// everything which is better than not showing anything).
m_stackTrace.clear();
m_numFrames = 0;
return;
}
wxString name = frame.GetName();
if ( !name.empty() )
{
m_stackTrace << wxString::Format(wxT("%-40s"), name.c_str());
@@ -1053,22 +1065,12 @@ wxString wxAppTraitsBase::GetAssertStackTrace()
private:
wxString m_stackTrace;
unsigned m_numFrames;
};
// don't show more than maxLines or we could get a dialog too tall to be
// shown on screen: 20 should be ok everywhere as even with 15 pixel high
// characters it is still only 300 pixels...
static const int maxLines = 20;
StackDump dump;
dump.Walk(8, maxLines); // 8 is chosen to hide all OnAssert() calls
stackTrace = dump.GetStackTrace();
const int count = stackTrace.Freq(wxT('\n'));
for ( int i = 0; i < count - maxLines; i++ )
stackTrace = stackTrace.BeforeLast(wxT('\n'));
return stackTrace;
dump.Walk();
return dump.GetStackTrace();
#else // !wxDEBUG_LEVEL
// this function is still present for ABI-compatibility even in debug level
// 0 build but is not used there and so can simply do nothing

View File

@@ -20,6 +20,7 @@
#include "wx/apptrait.h"
#include "wx/process.h"
#include "wx/sysopt.h"
#include "wx/vector.h"
#include "wx/gtk/private/timer.h"
#include "wx/evtloop.h"
@@ -254,30 +255,68 @@ class StackDump : public wxStackWalker
public:
StackDump(GtkAssertDialog *dlg) { m_dlg=dlg; }
void ShowStackInDialog()
{
ProcessFrames(0);
for ( wxVector<Frame>::const_iterator it = m_frames.begin();
it != m_frames.end();
++it )
{
gtk_assert_dialog_append_stack_frame(m_dlg,
it->name.utf8_str(),
it->file.utf8_str(),
it->line);
}
m_frames.clear();
}
protected:
virtual void OnStackFrame(const wxStackFrame& frame) wxOVERRIDE
{
wxString fncname = frame.GetName();
// append this stack frame's info in the dialog
if (!frame.GetFileName().empty() || !fncname.empty())
const wxString name = frame.GetName();
if ( name.StartsWith("wxOnAssert") )
{
gtk_assert_dialog_append_stack_frame(m_dlg,
fncname.utf8_str(),
frame.GetFileName().utf8_str(),
frame.GetLine());
// Ignore all frames until the wxOnAssert() one, just as we do in
// wxAppTraitsBase::GetAssertStackTrace().
m_frames.clear();
return;
}
// Also ignore frames which don't have neither the function name nor
// the file name, showing them in the dialog wouldn't provide any
// useful information.
if ( name.empty() && frame.GetFileName().empty() )
return;
m_frames.push_back(Frame(frame));
}
private:
GtkAssertDialog *m_dlg;
struct Frame
{
explicit Frame(const wxStackFrame& f)
: name(f.GetName()),
file(f.GetFileName()),
line(f.GetLine())
{
}
wxString name;
wxString file;
int line;
};
wxVector<Frame> m_frames;
};
static void get_stackframe_callback(void* p)
{
StackDump* dump = static_cast<StackDump*>(p);
// skip over frames up to including wxOnAssert()
dump->ProcessFrames(6);
dump->ShowStackInDialog();
}
#endif // wxDEBUG_LEVEL && wxUSE_STACKWALKER