X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d2dfef5f3f0152ad9b30092168869fec0698e6bf..acd32ffcdb319f162633c20e0202db3f8542998a:/src/unix/stackwalk.cpp diff --git a/src/unix/stackwalk.cpp b/src/unix/stackwalk.cpp index ae48c30a31..d65700afa6 100644 --- a/src/unix/stackwalk.cpp +++ b/src/unix/stackwalk.cpp @@ -33,6 +33,7 @@ #endif #include "wx/stackwalk.h" +#include "wx/stdpaths.h" #include @@ -66,30 +67,27 @@ public: private: FILE *m_fp; - DECLARE_NO_COPY_CLASS(wxStdioPipe) + wxDECLARE_NO_COPY_CLASS(wxStdioPipe); }; // ============================================================================ // implementation // ============================================================================ -wxString wxStackWalker::ms_exepath; - // ---------------------------------------------------------------------------- // wxStackFrame // ---------------------------------------------------------------------------- void wxStackFrame::OnGetName() { - if ( m_hasName ) + if ( !m_name.empty() ) return; - m_hasName = true; - - // try addr2line first because it always gives us demangled names (even if - // __cxa_demangle is not available) and because it seems less error-prone + // we already tried addr2line in wxStackWalker::InitFrames: it always + // gives us demangled names (even if __cxa_demangle is not available) when + // the function is part of the ELF (when it's in a shared object addr2line + // will give "??") and because it seems less error-prone. // when it works, backtrace_symbols() sometimes returns incorrect results - OnGetLocation(); // format is: "module(funcname+offset) [address]" but the part in // parentheses can be not present @@ -138,102 +136,174 @@ void wxStackFrame::OnGetName() } } -void wxStackFrame::OnGetLocation() + +// ---------------------------------------------------------------------------- +// wxStackWalker +// ---------------------------------------------------------------------------- + +// that many frames should be enough for everyone +#define MAX_FRAMES 200 + +// we need a char buffer big enough to contain a call to addr2line with +// 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; + + ms_symbols = backtrace_symbols(ms_addresses, m_depth); +} + +void wxStackWalker::ProcessFrames(size_t skip) { - if ( m_hasLocation ) + wxStackFrame frames[MAX_FRAMES]; + + if (!ms_symbols || !m_depth) return; - m_hasLocation = true; + // we have 3 more "intermediate" frames which the calling code doesn't know + // about, account for them + skip += 3; + + // 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() ) { - if ( !wxTheApp || !wxTheApp->argv ) - return; - exepath = wxTheApp->argv[0]; + exepath = wxStandardPaths::Get().GetExecutablePath(); + if ( exepath.empty() ) + { + wxLogDebug(wxT("Cannot parse stack frame because the executable ") + wxT("path could not be detected")); + return 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) ) + // 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\"", (const char*) exepath.mb_str()); + len = (len <= 0) ? strlen(g_buf) : len; // in case snprintf() is broken + for (size_t i=0; i