Applied wxStackWalker improvement part of
item #1591718, [wxGTK] Native assert dialog and optimized stack walker git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@43346 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -82,13 +82,15 @@ This function must be overrided to process the given frame.
|
|||||||
|
|
||||||
\membersection{wxStackWalker::Walk}\label{wxstackwalkerwalk}
|
\membersection{wxStackWalker::Walk}\label{wxstackwalkerwalk}
|
||||||
|
|
||||||
\func{void}{Walk}{\param{size\_t }{skip = 1}}
|
\func{void}{Walk}{\param{size\_t }{skip = 1}, \param{size\_t }{maxDepth = 200}}
|
||||||
|
|
||||||
Enumerate stack frames from the current location, skipping the initial
|
Enumerate stack frames from the current location, skipping the initial
|
||||||
number of them (this can be useful when Walk() is called from some known
|
number of them (this can be useful when Walk() is called from some known
|
||||||
location and you don't want to see the first few frames anyhow; also
|
location and you don't want to see the first few frames anyhow; also
|
||||||
notice that Walk() frame itself is not included if skip $\ge 1$).
|
notice that Walk() frame itself is not included if skip $\ge 1$).
|
||||||
|
|
||||||
|
Up to \arg{maxDepth} frames are walked from the innermost to the outermost one.
|
||||||
|
|
||||||
|
|
||||||
\membersection{wxStackWalker::WalkFromException}\label{wxstackwalkerwalkfromexception}
|
\membersection{wxStackWalker::WalkFromException}\label{wxstackwalkerwalkfromexception}
|
||||||
|
|
||||||
|
@@ -90,7 +90,7 @@ public:
|
|||||||
// only
|
// only
|
||||||
wxStackWalker(const char * WXUNUSED(argv0) = NULL) { }
|
wxStackWalker(const char * WXUNUSED(argv0) = NULL) { }
|
||||||
|
|
||||||
virtual void Walk(size_t skip = 1);
|
virtual void Walk(size_t skip = 1, size_t maxDepth = 200);
|
||||||
virtual void WalkFromException();
|
virtual void WalkFromException();
|
||||||
|
|
||||||
|
|
||||||
|
@@ -130,7 +130,7 @@ public:
|
|||||||
// number of them (this can be useful when Walk() is called from some known
|
// number of them (this can be useful when Walk() is called from some known
|
||||||
// location and you don't want to see the first few frames anyhow; also
|
// location and you don't want to see the first few frames anyhow; also
|
||||||
// notice that Walk() frame itself is not included if skip >= 1)
|
// notice that Walk() frame itself is not included if skip >= 1)
|
||||||
virtual void Walk(size_t skip = 1) = 0;
|
virtual void Walk(size_t skip = 1, size_t maxDepth = 200) = 0;
|
||||||
|
|
||||||
// enumerate stack frames from the location of uncaught exception
|
// enumerate stack frames from the location of uncaught exception
|
||||||
//
|
//
|
||||||
|
@@ -18,30 +18,38 @@
|
|||||||
|
|
||||||
class WXDLLIMPEXP_BASE wxStackFrame : public wxStackFrameBase
|
class WXDLLIMPEXP_BASE wxStackFrame : public wxStackFrameBase
|
||||||
{
|
{
|
||||||
|
friend class wxStackWalker;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// arguments are the stack depth of this frame, its address and the return
|
// arguments are the stack depth of this frame, its address and the return
|
||||||
// value of backtrace_symbols() for it
|
// value of backtrace_symbols() for it
|
||||||
//
|
//
|
||||||
// NB: we don't copy syminfo pointer so it should have lifetime at least as
|
// NB: we don't copy syminfo pointer so it should have lifetime at least as
|
||||||
// long as ours
|
// long as ours
|
||||||
wxStackFrame(size_t level, void *address, const char *syminfo)
|
wxStackFrame(size_t level = 0, void *address = NULL, const char *syminfo = NULL)
|
||||||
: wxStackFrameBase(level, address)
|
: wxStackFrameBase(level, address)
|
||||||
{
|
{
|
||||||
m_hasName =
|
|
||||||
m_hasLocation = false;
|
|
||||||
|
|
||||||
m_syminfo = syminfo;
|
m_syminfo = syminfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnGetName();
|
virtual void OnGetName();
|
||||||
virtual void OnGetLocation();
|
|
||||||
|
// optimized for the 2 step initialization done by wxStackWalker
|
||||||
|
void Set(const wxString &name, const wxString &filename, const char* syminfo,
|
||||||
|
size_t level, size_t numLine, void *address)
|
||||||
|
{
|
||||||
|
m_level = level;
|
||||||
|
m_name = name;
|
||||||
|
m_filename = filename;
|
||||||
|
m_syminfo = syminfo;
|
||||||
|
|
||||||
|
m_line = numLine;
|
||||||
|
m_address = address;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char *m_syminfo;
|
const char *m_syminfo;
|
||||||
|
|
||||||
bool m_hasName,
|
|
||||||
m_hasLocation;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -60,13 +68,30 @@ public:
|
|||||||
ms_exepath = wxString::FromAscii(argv0);
|
ms_exepath = wxString::FromAscii(argv0);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Walk(size_t skip = 1);
|
~wxStackWalker()
|
||||||
|
{
|
||||||
|
FreeStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Walk(size_t skip = 1, size_t maxDepth = 200);
|
||||||
virtual void WalkFromException() { Walk(2); }
|
virtual void WalkFromException() { Walk(2); }
|
||||||
|
|
||||||
static const wxString& GetExePath() { return ms_exepath; }
|
static const wxString& GetExePath() { return ms_exepath; }
|
||||||
|
|
||||||
|
|
||||||
|
// these two may be used to save the stack at some point (fast operation)
|
||||||
|
// and then process it later (slow operation)
|
||||||
|
void SaveStack(size_t maxDepth);
|
||||||
|
void ProcessFrames(size_t skip);
|
||||||
|
void FreeStack();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo);
|
||||||
|
|
||||||
static wxString ms_exepath;
|
static wxString ms_exepath;
|
||||||
|
static void *ms_addresses[];
|
||||||
|
static char **ms_symbols;
|
||||||
|
static int m_depth;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _WX_UNIX_STACKWALK_H_
|
#endif // _WX_UNIX_STACKWALK_H_
|
||||||
|
@@ -326,7 +326,7 @@ void wxStackWalker::WalkFromException()
|
|||||||
WalkFrom(wxGlobalSEInformation, 0);
|
WalkFrom(wxGlobalSEInformation, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxStackWalker::Walk(size_t skip)
|
void wxStackWalker::Walk(size_t skip, size_t WXUNUSED(maxDepth))
|
||||||
{
|
{
|
||||||
// to get a CONTEXT for the current location, simply force an exception and
|
// to get a CONTEXT for the current location, simply force an exception and
|
||||||
// get EXCEPTION_POINTERS from it
|
// get EXCEPTION_POINTERS from it
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "wx/stackwalk.h"
|
#include "wx/stackwalk.h"
|
||||||
|
#include "wx/stdpaths.h"
|
||||||
|
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
|
|
||||||
@@ -73,23 +74,20 @@ private:
|
|||||||
// implementation
|
// implementation
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
wxString wxStackWalker::ms_exepath;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// wxStackFrame
|
// wxStackFrame
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void wxStackFrame::OnGetName()
|
void wxStackFrame::OnGetName()
|
||||||
{
|
{
|
||||||
if ( m_hasName )
|
if ( !m_name.empty() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_hasName = true;
|
// we already tried addr2line in wxStackWalker::InitFrames: it always
|
||||||
|
// gives us demangled names (even if __cxa_demangle is not available) when
|
||||||
// try addr2line first because it always gives us demangled names (even if
|
// the function is part of the ELF (when it's in a shared object addr2line
|
||||||
// __cxa_demangle is not available) and because it seems less error-prone
|
// will give "??") and because it seems less error-prone.
|
||||||
// when it works, backtrace_symbols() sometimes returns incorrect results
|
// when it works, backtrace_symbols() sometimes returns incorrect results
|
||||||
OnGetLocation();
|
|
||||||
|
|
||||||
// format is: "module(funcname+offset) [address]" but the part in
|
// format is: "module(funcname+offset) [address]" but the part in
|
||||||
// parentheses can be not present
|
// parentheses can be not present
|
||||||
@@ -138,102 +136,174 @@ void wxStackFrame::OnGetName()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxStackFrame::OnGetLocation()
|
|
||||||
{
|
|
||||||
if ( m_hasLocation )
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_hasLocation = true;
|
|
||||||
|
|
||||||
// we need to launch addr2line tool to get this information and we need to
|
|
||||||
// have the program name for this
|
|
||||||
wxString exepath = wxStackWalker::GetExePath();
|
|
||||||
if ( exepath.empty() )
|
|
||||||
{
|
|
||||||
if ( !wxTheApp || !wxTheApp->argv )
|
|
||||||
return;
|
|
||||||
exepath = wxTheApp->argv[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
wxStdioPipe fp(wxString::Format(_T("addr2line -C -f -e \"%s\" %p"),
|
|
||||||
exepath.c_str(), m_address).mb_str(),
|
|
||||||
"r");
|
|
||||||
|
|
||||||
if ( !fp )
|
|
||||||
return;
|
|
||||||
|
|
||||||
// parse addr2line output
|
|
||||||
char buf[1024];
|
|
||||||
if ( !fgets(buf, WXSIZEOF(buf), fp) )
|
|
||||||
{
|
|
||||||
wxLogDebug(_T("Empty addr2line output?"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1st line has function name
|
|
||||||
if ( GetName().empty() )
|
|
||||||
{
|
|
||||||
m_name = wxString::FromAscii(buf);
|
|
||||||
m_name.RemoveLast(); // trailing newline
|
|
||||||
|
|
||||||
if ( m_name == _T("??") )
|
|
||||||
m_name.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2nd one -- the file/line info
|
|
||||||
if ( fgets(buf, WXSIZEOF(buf), fp) )
|
|
||||||
{
|
|
||||||
wxString output(wxString::FromAscii(buf));
|
|
||||||
output.RemoveLast();
|
|
||||||
|
|
||||||
const size_t posColon = output.find(_T(':'));
|
|
||||||
if ( posColon != wxString::npos )
|
|
||||||
{
|
|
||||||
m_filename.assign(output, 0, posColon);
|
|
||||||
if ( m_filename == _T("??") )
|
|
||||||
{
|
|
||||||
m_filename.clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned long line;
|
|
||||||
if ( wxString(output, posColon + 1, wxString::npos).
|
|
||||||
ToULong(&line) )
|
|
||||||
m_line = line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxLogDebug(_T("Unexpected addr2line format: \"%s\""),
|
|
||||||
output.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// wxStackWalker
|
// wxStackWalker
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void wxStackWalker::Walk(size_t skip)
|
// that many frames should be enough for everyone
|
||||||
{
|
#define MAX_FRAMES 200
|
||||||
// that many frames should be enough for everyone
|
|
||||||
void *addresses[200];
|
|
||||||
|
|
||||||
int depth = backtrace(addresses, WXSIZEOF(addresses));
|
// we need a char buffer big enough to contain a call to addr2line with
|
||||||
if ( !depth )
|
// up to MAX_FRAMES addresses !
|
||||||
|
// NB: %p specifier will print the pointer in hexadecimal form
|
||||||
|
// and thus will require 2 chars for each byte + 3 for the
|
||||||
|
// " 0x" prefix
|
||||||
|
#define CHARS_PER_FRAME (sizeof(void*) * 2 + 3)
|
||||||
|
|
||||||
|
// BUFSIZE will be 2250 for 32 bit machines
|
||||||
|
#define BUFSIZE (50 + MAX_FRAMES*CHARS_PER_FRAME)
|
||||||
|
|
||||||
|
// static data
|
||||||
|
void *wxStackWalker::ms_addresses[MAX_FRAMES];
|
||||||
|
char **wxStackWalker::ms_symbols = NULL;
|
||||||
|
int wxStackWalker::m_depth = 0;
|
||||||
|
wxString wxStackWalker::ms_exepath;
|
||||||
|
static char g_buf[BUFSIZE];
|
||||||
|
|
||||||
|
|
||||||
|
void wxStackWalker::SaveStack(size_t maxDepth)
|
||||||
|
{
|
||||||
|
// read all frames required
|
||||||
|
maxDepth = wxMin(WXSIZEOF(ms_addresses)/sizeof(void*), maxDepth);
|
||||||
|
m_depth = backtrace(ms_addresses, maxDepth*sizeof(void*));
|
||||||
|
if ( !m_depth )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char **symbols = backtrace_symbols(addresses, depth);
|
ms_symbols = backtrace_symbols(ms_addresses, m_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxStackWalker::ProcessFrames(size_t skip)
|
||||||
|
{
|
||||||
|
wxStackFrame frames[MAX_FRAMES];
|
||||||
|
|
||||||
|
if (!ms_symbols || !m_depth)
|
||||||
|
return;
|
||||||
|
|
||||||
// we have 3 more "intermediate" frames which the calling code doesn't know
|
// we have 3 more "intermediate" frames which the calling code doesn't know
|
||||||
// about., account for them
|
// about, account for them
|
||||||
skip += 3;
|
skip += 3;
|
||||||
|
|
||||||
for ( int n = skip; n < depth; n++ )
|
// call addr2line only once since this call may be very slow
|
||||||
|
// (it has to load in memory the entire EXE of this app which may be quite
|
||||||
|
// big, especially if it contains debug info and is compiled statically!)
|
||||||
|
int towalk = InitFrames(frames, m_depth - skip, &ms_addresses[skip], &ms_symbols[skip]);
|
||||||
|
|
||||||
|
// now do user-defined operations on each frame
|
||||||
|
for ( int n = 0; n < towalk - (int)skip; n++ )
|
||||||
|
OnStackFrame(frames[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxStackWalker::FreeStack()
|
||||||
|
{
|
||||||
|
// ms_symbols has been allocated by backtrace_symbols() and it's the responsibility
|
||||||
|
// of the caller, i.e. us, to free that pointer
|
||||||
|
if (ms_symbols)
|
||||||
|
free( ms_symbols );
|
||||||
|
ms_symbols = NULL;
|
||||||
|
m_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo)
|
||||||
|
{
|
||||||
|
// we need to launch addr2line tool to get this information and we need to
|
||||||
|
// have the program name for this
|
||||||
|
wxString exepath = wxStackWalker::GetExePath();
|
||||||
|
if ( exepath.empty() )
|
||||||
{
|
{
|
||||||
wxStackFrame frame(n - skip, addresses[n], symbols[n]);
|
exepath = wxStandardPaths::Get().GetExecutablePath();
|
||||||
OnStackFrame(frame);
|
if ( exepath.empty() )
|
||||||
|
{
|
||||||
|
wxLogDebug(wxT("Cannot parse stack frame because the executable ")
|
||||||
|
wxT("path could not be detected"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build the (long) command line for executing addr2line in an optimized way
|
||||||
|
// (e.g. use always chars, even in Unicode build: popen() always takes chars)
|
||||||
|
int len = snprintf(g_buf, BUFSIZE, "addr2line -C -f -e \"%s\"", exepath.mb_str());
|
||||||
|
len = (len <= 0) ? strlen(g_buf) : len; // in case snprintf() is broken
|
||||||
|
for (size_t i=0; i<n; i++)
|
||||||
|
{
|
||||||
|
snprintf(&g_buf[len], BUFSIZE - len, " %p", addresses[i]);
|
||||||
|
len = strlen(g_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//wxLogDebug(wxT("piping the command '%s'"), g_buf); // for debug only
|
||||||
|
|
||||||
|
wxStdioPipe fp(g_buf, "r");
|
||||||
|
if ( !fp )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// parse addr2line output (should be exactly 2 lines for each address)
|
||||||
|
// reusing the g_buf used for building the command line above
|
||||||
|
wxString name, filename;
|
||||||
|
unsigned long line, curr=0;
|
||||||
|
for (size_t i=0; i<n; i++)
|
||||||
|
{
|
||||||
|
// 1st line has function name
|
||||||
|
if ( fgets(g_buf, WXSIZEOF(g_buf), fp) )
|
||||||
|
{
|
||||||
|
name = wxString::FromAscii(g_buf);
|
||||||
|
name.RemoveLast(); // trailing newline
|
||||||
|
|
||||||
|
if ( name == _T("??") )
|
||||||
|
name.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxLogDebug(_T("cannot read addr2line output for %d-th stack frame!"), i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd one -- the file/line info
|
||||||
|
if ( fgets(g_buf, WXSIZEOF(g_buf), fp) )
|
||||||
|
{
|
||||||
|
filename = wxString::FromAscii(g_buf);
|
||||||
|
filename.RemoveLast();
|
||||||
|
|
||||||
|
const size_t posColon = filename.find(_T(':'));
|
||||||
|
if ( posColon != wxString::npos )
|
||||||
|
{
|
||||||
|
// parse line number
|
||||||
|
if ( !wxString(filename, posColon + 1, wxString::npos).
|
||||||
|
ToULong(&line) )
|
||||||
|
line = 0;
|
||||||
|
|
||||||
|
// remove line number from 'filename'
|
||||||
|
filename.erase(posColon);
|
||||||
|
if ( filename == _T("??") )
|
||||||
|
filename.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxLogDebug(_T("Unexpected addr2line format: \"%s\" - ")
|
||||||
|
_T("the semicolon is missing"),
|
||||||
|
filename.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name.empty() || !filename.empty())
|
||||||
|
{
|
||||||
|
// now we've got enough info to initialize curr-th stack frame:
|
||||||
|
arr[curr++].Set(name, filename, syminfo[i], i, line, addresses[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxStackWalker::Walk(size_t skip, size_t maxDepth)
|
||||||
|
{
|
||||||
|
// read all frames required
|
||||||
|
SaveStack(maxDepth);
|
||||||
|
|
||||||
|
// process them
|
||||||
|
ProcessFrames(skip);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
FreeStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // wxUSE_STACKWALKER
|
#endif // wxUSE_STACKWALKER
|
||||||
|
Reference in New Issue
Block a user