This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
363 lines
11 KiB
C++
363 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/unix/stackwalk.cpp
|
|
// Purpose: wxStackWalker implementation for Unix/glibc
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 2005-01-18
|
|
// Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_STACKWALKER
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/string.h"
|
|
#include "wx/app.h"
|
|
#include "wx/log.h"
|
|
#include "wx/utils.h"
|
|
#endif
|
|
|
|
#include "wx/stackwalk.h"
|
|
#include "wx/stdpaths.h"
|
|
|
|
#include <execinfo.h>
|
|
|
|
#ifdef HAVE_CXA_DEMANGLE
|
|
#include <cxxabi.h>
|
|
#endif // HAVE_CXA_DEMANGLE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// tiny helper wrapper around popen/pclose()
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxStdioPipe
|
|
{
|
|
public:
|
|
// ctor parameters are passed to popen()
|
|
wxStdioPipe(const char *command, const char *type)
|
|
{
|
|
m_fp = popen(command, type);
|
|
}
|
|
|
|
// conversion to stdio FILE
|
|
operator FILE *() const { return m_fp; }
|
|
|
|
// dtor closes the pipe
|
|
~wxStdioPipe()
|
|
{
|
|
if ( m_fp )
|
|
pclose(m_fp);
|
|
}
|
|
|
|
private:
|
|
FILE *m_fp;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxStdioPipe);
|
|
};
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxStackFrame
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxStackFrame::OnGetName()
|
|
{
|
|
if ( !m_name.empty() )
|
|
return;
|
|
|
|
// 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
|
|
|
|
// format is: "module(funcname+offset) [address]" but the part in
|
|
// parentheses can be not present
|
|
wxString syminfo = wxString::FromAscii(m_syminfo);
|
|
const size_t posOpen = syminfo.find(wxT('('));
|
|
if ( posOpen != wxString::npos )
|
|
{
|
|
const size_t posPlus = syminfo.find(wxT('+'), posOpen + 1);
|
|
if ( posPlus != wxString::npos )
|
|
{
|
|
const size_t posClose = syminfo.find(wxT(')'), posPlus + 1);
|
|
if ( posClose != wxString::npos )
|
|
{
|
|
if ( m_name.empty() )
|
|
{
|
|
m_name.assign(syminfo, posOpen + 1, posPlus - posOpen - 1);
|
|
|
|
#ifdef HAVE_CXA_DEMANGLE
|
|
int rc = -1;
|
|
char *cppfunc = __cxxabiv1::__cxa_demangle
|
|
(
|
|
m_name.mb_str(),
|
|
NULL, // output buffer (none, alloc it)
|
|
NULL, // [out] len of output buffer
|
|
&rc
|
|
);
|
|
if ( rc == 0 )
|
|
m_name = wxString::FromAscii(cppfunc);
|
|
|
|
free(cppfunc);
|
|
#endif // HAVE_CXA_DEMANGLE
|
|
}
|
|
|
|
unsigned long ofs;
|
|
if ( wxString(syminfo, posPlus + 1, posClose - posPlus - 1).
|
|
ToULong(&ofs, 0) )
|
|
m_offset = ofs;
|
|
}
|
|
}
|
|
|
|
m_module.assign(syminfo, posOpen);
|
|
}
|
|
#ifndef __WXOSX__
|
|
else // not in "module(funcname+offset)" format
|
|
{
|
|
m_module = syminfo;
|
|
}
|
|
#endif // !__WXOSX__
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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)
|
|
{
|
|
wxStackFrame frames[MAX_FRAMES];
|
|
|
|
if (!ms_symbols || !m_depth)
|
|
return;
|
|
|
|
// we are another level down from Walk(), so adjust the number of stack
|
|
// frames to skip accordingly
|
|
skip += 1;
|
|
|
|
// 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 numFrames = InitFrames(frames, m_depth - skip,
|
|
&ms_addresses[skip], &ms_symbols[skip]);
|
|
|
|
// now do user-defined operations on each frame
|
|
for ( int n = 0; n < numFrames; 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;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
// Helper function to read a line from the file and return it without the
|
|
// trailing newline. Line number is only used for error reporting.
|
|
bool ReadLine(FILE* fp, unsigned long num, wxString* line)
|
|
{
|
|
if ( !fgets(g_buf, WXSIZEOF(g_buf), fp) )
|
|
{
|
|
wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
|
|
num);
|
|
return false;
|
|
}
|
|
|
|
*line = wxString::FromAscii(g_buf);
|
|
line->RemoveLast();
|
|
|
|
return true;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
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() )
|
|
{
|
|
exepath = wxStandardPaths::Get().GetExecutablePath();
|
|
if ( exepath.empty() )
|
|
{
|
|
wxLogDebug(wxT("Cannot parse stack frame because the executable ")
|
|
wxT("path could not be detected"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// build the command line for executing addr2line or atos under OS X using
|
|
// char* directly to avoid the conversions from Unicode
|
|
#ifdef __WXOSX__
|
|
int len = snprintf(g_buf, BUFSIZE, "atos -p %d", (int)getpid());
|
|
#else
|
|
int len = snprintf(g_buf, BUFSIZE, "addr2line -C -f -e \"%s\"", (const char*) exepath.mb_str());
|
|
#endif
|
|
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 the output reusing the same buffer to avoid any big memory
|
|
// allocations which could fail if our program is in a bad state
|
|
wxString name, filename;
|
|
unsigned long line = 0,
|
|
curr = 0;
|
|
for ( size_t i = 0; i < n; i++ )
|
|
{
|
|
#ifdef __WXOSX__
|
|
wxString buffer;
|
|
if ( !ReadLine(fp, i, &buffer) )
|
|
return false;
|
|
|
|
line = 0;
|
|
filename.clear();
|
|
|
|
// We can get back either the string in the following format:
|
|
//
|
|
// func(args) (in module) (file:line)
|
|
//
|
|
// or just the same address back if it couldn't be resolved.
|
|
const size_t posIn = buffer.find("(in ");
|
|
if ( posIn != wxString::npos )
|
|
{
|
|
name.assign(buffer, 0, posIn);
|
|
|
|
size_t posAt = buffer.find(") (", posIn + 3);
|
|
if ( posAt != wxString::npos )
|
|
{
|
|
posAt += 3; // Skip ") ("
|
|
|
|
// Discard the two last characters which are ")\n"
|
|
wxString location(buffer, posAt, buffer.length() - posAt - 2);
|
|
|
|
wxString linenum;
|
|
filename = location.BeforeFirst(':', &linenum);
|
|
if ( !linenum.empty() )
|
|
linenum.ToULong(&line);
|
|
}
|
|
}
|
|
#else // !__WXOSX__
|
|
// 1st line has function name
|
|
if ( !ReadLine(fp, i, &name) )
|
|
return false;
|
|
|
|
name = wxString::FromAscii(g_buf);
|
|
name.RemoveLast(); // trailing newline
|
|
|
|
if ( name == wxT("??") )
|
|
name.clear();
|
|
|
|
// 2nd one -- the file/line info
|
|
if ( !ReadLine(fp, i, &filename) )
|
|
return false;
|
|
|
|
const size_t posColon = filename.find(wxT(':'));
|
|
if ( posColon != wxString::npos )
|
|
{
|
|
// parse line number (it's ok if it fails, this will just leave
|
|
// line at its current, invalid, 0 value)
|
|
wxString(filename, posColon + 1, wxString::npos).ToULong(&line);
|
|
|
|
// remove line number from 'filename'
|
|
filename.erase(posColon);
|
|
if ( filename == wxT("??") )
|
|
filename.clear();
|
|
}
|
|
else
|
|
{
|
|
wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
|
|
wxT("the semicolon is missing"),
|
|
filename.c_str());
|
|
}
|
|
#endif // __WXOSX__/!__WXOSX__
|
|
|
|
// now we've got enough info to initialize curr-th stack frame
|
|
// (at worst, only addresses[i] and syminfo[i] have been initialized,
|
|
// but wxStackFrame::OnGetName may still be able to get function name):
|
|
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
|