Files
mrw-cxx/src/stacktrace.cxx

388 lines
14 KiB
C++
Raw Normal View History

/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
*/
#include <mrw/stacktrace.hxx>
#include <mrw/exec.hxx>
#include <mrw/string.hxx>
#include <mrw/list.hxx>
#include <mrw/stdext.hxx>
#ifndef NO_LTDL
# include <mrw/dynamiclibrary.hxx>
#endif
2004-04-21 06:39:20 +00:00
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <assert.h>
#include <math.h>
#include <fstream>
2004-04-21 06:39:20 +00:00
#include <algorithm>
#include <list>
#if defined(__solaris__)
#include <sys/old_procfs.h>
#endif
#if defined (__GLIBC__)
#include <execinfo.h>
#endif
#include <bfd.h>
#include <iomanip>
#if HAVE_DEMANGLE_H
/* the defines work around wrong definitions in libiberty.h */
#define HAVE_DECL_BASENAME 1
#define HAVE_DECL_FFS 1
#define HAVE_DECL_ASPRINTF 1
#define HAVE_DECL_VASPRINTF 1
#define HAVE_DECL_SNPRINTF 1
#define HAVE_DECL_VSNPRINTF 1
#define HAVE_DECL_STRVERSCMP 1
# include <demangle.h>
#else
# define DMGL_PARAMS (1 << 0) /* Include function args */
# define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
extern "C" {
extern char * cplus_demangle(const char *, int);
}
#endif
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
&& defined(_REENTRANT) && !defined(_MT)
#define _MT
#endif
2004-04-21 06:39:20 +00:00
namespace mrw {
//----------------------------------------------------------------------------
static std::string demangle(bfd* abfd, const char* name) {
2004-04-21 06:39:20 +00:00
if (bfd_get_symbol_leading_char(abfd) == name[0]) ++name;
/* This is a hack for better error reporting on XCOFF, PowerPC64-ELF
or the MS PE format. These formats have a number of leading '.'s
on at least some symbols, so we remove all dots to avoid
confusing the demangler. */
const char* p(name);
2004-04-21 06:39:20 +00:00
while (p && *p == '.') ++p;
2005-01-07 00:34:38 +00:00
# ifdef __solaris__
char res[1024];
if (cplus_demangle(p, res, 1024)) return name;
# else
Auto<char*>::Free res(cplus_demangle(p, DMGL_ANSI | DMGL_PARAMS));
# endif
2004-04-21 06:39:20 +00:00
if (res) {
/* Now put back any stripped dots. */
if (p==name) return static_cast<const char*>(res);
2004-04-21 06:39:20 +00:00
std::string add_dots('.', p-name);
return add_dots+=static_cast<const char*>(res);
2004-04-21 06:39:20 +00:00
}
return name;
}
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
mrw::StackTrace::StackTrace() throw(std::bad_exception) {
// maximum trace level is limited here to 50, see below why
# if defined(__GNUG__)
{
# define PUSH(i) \
(__builtin_frame_address(i)!=0 ? \
(_trace.push_back(__builtin_return_address(i)), true) : false)
PUSH(0) && PUSH(1) && PUSH(2) && PUSH(3) && PUSH(4) && PUSH(5) &&
PUSH(6) && PUSH(7) && PUSH(8) && PUSH(9) && PUSH(10) && PUSH(11) &&
PUSH(12) && PUSH(13) && PUSH(14) && PUSH(15) && PUSH(16) && PUSH(17)
&& PUSH(18) && PUSH(19) && PUSH(20) && PUSH(21) && PUSH(22) &&
PUSH(23) && PUSH(24) && PUSH(25) && PUSH(26) && PUSH(27) && PUSH(28)
&& PUSH(29) && PUSH(30) && PUSH(31) && PUSH(32) && PUSH(33) &&
PUSH(34) && PUSH(35) && PUSH(36) && PUSH(37) && PUSH(38) && PUSH(39)
&& PUSH(40) && PUSH(41) && PUSH(42) && PUSH(43) && PUSH(44) &&
PUSH(45) && PUSH(46) && PUSH(47) && PUSH(48) && PUSH(49);
# undef PUSH
}
# elif defined(__GLIBC__)
{
const int TRACE_LEVEL(50);
void* ba[TRACE_LEVEL];
for (int n(backtrace(ba, TRACE_LEVEL)), i(0); i<n; ++i)
_trace.push_back(ba[i]);
}
2004-04-21 06:39:20 +00:00
# else
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
# endif
}
2004-04-21 06:39:20 +00:00
mrw::StackTrace::~StackTrace() throw() {
// maximum trace level is limited here to 50, see below why
# if !defined(__GNUG__) && defined(__GLIBC__)
/* GLIBC backtrace seems to leak memory, but I don't see why, so
GNUG part is prefered. According to the man-page, backtrace does
not allocate memory, so what happens here? Valgrind message:
==19829== 28 bytes in 1 blocks are still reachable in loss record 1 of 1
==19829== at 0x40233F0: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
==19829== by 0x400CE43: _dl_map_object_deps (in /lib/ld-2.5.so)
==19829== by 0x4011E34: dl_open_worker (in /lib/ld-2.5.so)
==19829== by 0x400E025: _dl_catch_error (in /lib/ld-2.5.so)
==19829== by 0x4011928: _dl_open (in /lib/ld-2.5.so)
==19829== by 0x44AFF01: do_dlopen (in /lib/libc-2.5.so)
==19829== by 0x400E025: _dl_catch_error (in /lib/ld-2.5.so)
==19829== by 0x44B0000: dlerror_run (in /lib/libc-2.5.so)
==19829== by 0x44B012A: __libc_dlopen_mode (in /lib/libc-2.5.so)
==19829== by 0x448E2A8: init (in /lib/libc-2.5.so)
==19829== by 0x448E452: backtrace (in /lib/libc-2.5.so)
==19829== by 0x40E7E53: mrw::StackTrace::StackTrace() (in libmrw.so.3.2.0)
{
<insert a suppression name here>
Memcheck:Leak
fun:_vgrZU_libcZdsoZa_malloc
fun:_dl_map_object_deps
fun:dl_open_worker
fun:_dl_catch_error
fun:_dl_open
fun:do_dlopen
fun:_dl_catch_error
fun:dlerror_run
fun:__libc_dlopen_mode
fun:init
fun:backtrace
fun:_ZN3mrw10StackTraceC1Ev
}
*/
# endif
}
//----------------------------------------------------------------------------
mrw::StackTrace::operator std::string() const throw(std::bad_exception) {
std::stringstream s;
bool first(true);
unsigned int fusz(0), adsz(0); //lisz(0), fisz(0)
std::list<CodePos> l;
for (AddressTrace::const_reverse_iterator it(_trace.rbegin());
it!=_trace.rend(); ++it, first=false) {
CodePos c(translate(*it));
if (((std::stringstream&)(std::stringstream()<<c.address)).str().size()
> adsz)
adsz =
((std::stringstream&)(std::stringstream()<<c.address)).str().size();
if (c.function.size() > fusz) fusz = c.function.size();
l.push_back(c);
2004-04-21 06:39:20 +00:00
}
for (std::list<CodePos>::iterator it(l.begin()); it!=l.end(); ++it)
s<<'['<<std::setw(adsz)<<it->address<<"] "
<<it->function<<std::setw(fusz-it->function.size()+1)<<' '
<<it->file<<':'<<it->line<<std::endl;
return s.str();
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const
throw(std::bad_exception) {
os<<(std::string)*this;
return *this;
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
mrw::StackTrace::CodePos mrw::StackTrace::translate(void* addr)
throw(std::bad_exception) {
#ifdef _MT
boost::recursive_mutex::scoped_lock lock(_mutex);
#endif
static const CodePos UNKNOWN(addr, "????", "????", 0);
assert(sizeof(bfd_vma)>=sizeof(void*));
bfd_vma vma_addr(reinterpret_cast<bfd_vma>(addr));
std::map<Translator::key_type, std::string>::iterator
it(_addrs.lower_bound(vma_addr));
if (it==_addrs.begin() || (--it)->first > vma_addr ||
_dic.find(it->second)==_dic.end() ||
_dic[it->second][it->first].first <= vma_addr)
return UNKNOWN;
static const char* file(0);
static const char* function(0);
unsigned int line;
if (!bfd_find_nearest_line
(const_cast<bfd*>(static_cast<const bfd*>(_bfd[it->second])),
_dic[it->second][it->first].second,
_syms[it->second],
vma_addr - it->first,
&file, &function, &line))
return UNKNOWN;
return CodePos(addr, mrw::demangle(_bfd[it->second], function),
file?file:"????", line);
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs)
throw(std::bad_exception) {
#ifdef _MT
boost::recursive_mutex::scoped_lock lock(_mutex);
#endif
if (_dic.find(fname)!=_dic.end()) return true; // already loaded
try {
#if NO_LTDL
static bfd*(*bfd_openr)(const char*, const char*) =
::bfd_openr;
static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) =
::bfd_check_format;
static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) =
::bfd_check_format_matches;
static void(*bfd_map_over_sections)
(bfd*, void(*)(bfd*, asection*, void*), void*) =
::bfd_map_over_sections;
#else
static DynamicLibrary lib("libbfd");
static bfd*(*bfd_openr)(const char*, const char*) =
(bfd*(*)(const char*, const char*))lib.symbol("bfd_openr");
static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) =
(bfd_boolean(*)(bfd*, bfd_format))lib.symbol("bfd_check_format");
static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) =
(bfd_boolean(*)(bfd*, bfd_format, char***))
lib.symbol("bfd_check_format_matches");
static void(*bfd_map_over_sections)
(bfd*, void(*)(bfd*, asection*, void*), void*) =
(void(*)(bfd*, void(*)(bfd*, asection*, void*), void*))
lib.symbol("bfd_map_over_sections");
#endif
if (fname=="") return createSymtable(filename());
AutoBfd abfd((*bfd_openr)(fname.c_str(), 0));
long memsz(-1);
Auto<char**>::Free m(0);
if (!abfd || (*bfd_check_format)(abfd, bfd_archive) ||
!(*bfd_check_format_matches)(abfd, bfd_object, &(m.getClean())) ||
!(bfd_get_file_flags(const_cast<bfd*>(static_cast<const bfd*>(abfd)))
&HAS_SYMS) ||
(memsz=bfd_get_symtab_upper_bound
(const_cast<bfd*>(static_cast<const bfd*>(abfd))))<0) {
_error = "cannot map bfd symbols - 'bfd_get_file_flags' failed "
"for file: \""+fname+'"';
return false;
}
mrw::AutoPtrAry<asymbol*> syms(new asymbol*[memsz]);
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)),
syms)<0) {
_error = "cannot map bfd symbols - 'bfd_canonicalize_symtab' failed"
"for file: \""+fname+'"';
return false;
}
_bfd[fname] = abfd;
_syms[fname] = syms;
_dic[fname];
std::pair<std::string, void*> fileoffs = std::make_pair(fname, offs);
(*bfd_map_over_sections)(_bfd[fname], buildSectionMap, &fileoffs);
return true;
} catch (const std::exception& x) {
_error = std::string("exception received: \"")+x.what()+"\", "
"while processing file: \""+fname+'"';
return false; // shared library for bfd not found or similar
}
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files)
throw(std::bad_exception) {
#ifdef _MT
boost::recursive_mutex::scoped_lock lock(_mutex);
#endif
bool success(true);
for (BinFiles::const_iterator it(files.begin());
it!=files.end(); ++it) {
if (!createSymtable(it->first, it->second)) success=false;
}
return success;
}
//----------------------------------------------------------------------------
int mrw::StackTrace::bfdClose(bfd* abfd) throw() {
try {
#if NO_LTDL
static int(*bfd_close)(bfd*) = ::bfd_close;
#else
static DynamicLibrary lib("");
static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close");
#endif
return (*bfd_close)(abfd);
} catch (...) {
return -1; // dynamic library loading problems
}
}
//----------------------------------------------------------------------------
mrw::StackTrace::BinFiles mrw::StackTrace::filename()
throw(std::bad_exception) {
mrw::StackTrace::BinFiles res;
2005-01-07 00:34:38 +00:00
# if defined(__solaris__)
{
std::string s;
s<<"/proc/"<<getpid();
2005-01-07 00:34:38 +00:00
AutoFile fd(open(s.c_str(), O_RDONLY));
prpsinfo_t status;
if (fd==-1 || ioctl(fd, PIOCPSINFO, &status)==-1) return res;
s = status.pr_psargs;
return res<<BinFiles::value_type(s.substr(0, s.find(' ')), 0);
}
2005-01-07 00:34:38 +00:00
# elif defined(__linux__)
{
res<<BinFiles::value_type("/proc/self/exe", 0);
std::ifstream is("/proc/self/maps");
std::string s;
std::string range, perm, x1, x2, size, lib;
while (getline(is, s)) try {
range = perm = x1 = x2 = size = lib = "????";
s>>range>>perm>>x1>>x2>>size>>lib;
range.resize(range.find_first_not_of("0123456789abcdefABCDEF"));
void* addr(0);
range>>addr;
// added check: no names in brackets: [lib], because there are
// [heap], [stack] and [vdso] that cannot be loaded
// question: should only files with ending '.so' be loaded?
if (lib.size() && lib[0]!='[' && lib[lib.size()-1]!=']' && addr>0)
res<<BinFiles::value_type(lib, addr);
} catch (...) {} // ignore non matching lines
return res;
}
2005-01-07 00:34:38 +00:00
# else
{
2004-04-21 06:39:20 +00:00
# warning "Don't know how to get executable file name in your system!"
# warning "Impossible to get function names in stack trace!"
# warning "Give the path to the executable to StackTrace::createSymtable!"
return res; // empty
2004-04-21 06:39:20 +00:00
}
2005-01-07 00:34:38 +00:00
# endif
}
2004-04-21 06:39:20 +00:00
//----------------------------------------------------------------------------
void mrw::StackTrace::buildSectionMap(bfd* abfd, asection* section,
void* fileoffs)
throw(std::bad_exception) {
if (!abfd || !section || !fileoffs) return;
if (!(bfd_get_section_flags(abfd, section)&SEC_ALLOC)) return;
std::pair<std::string, void*> fileoffset =
*(std::pair<std::string, void*>*)fileoffs;
bfd_vma vma(bfd_get_section_vma(abfd, section)+
reinterpret_cast<bfd_vma>(fileoffset.second));
#ifdef bfd_get_section_size
bfd_size_type sz(bfd_get_section_size(section));
2005-02-28 07:28:37 +00:00
#elif defined bfd_get_section_size_before_reloc
bfd_size_type sz(bfd_get_section_size_before_reloc(section));
#else
bfd_size_type sz(section->size);
#endif
_dic[fileoffset.first][vma] = Translator::mapped_type(vma+sz, section);
_addrs[vma] = fileoffset.first;
2004-04-21 06:39:20 +00:00
}
//----------------------------------------------------------------------------
std::map<std::string, mrw::StackTrace::Translator> mrw::StackTrace::_dic;
std::map<mrw::StackTrace::Translator::key_type, std::string>
mrw::StackTrace::_addrs;
std::map<std::string, mrw::StackTrace::AutoBfd> mrw::StackTrace::_bfd;
std::map<std::string, mrw::AutoPtrAry<asymbol*> > mrw::StackTrace::_syms;
#ifdef _MT
boost::recursive_mutex mrw::StackTrace::_mutex;
#endif
std::string mrw::StackTrace::_error;