301 lines
11 KiB
C++
301 lines
11 KiB
C++
/** @file
|
|
|
|
$Id$
|
|
|
|
$Date$
|
|
$Author$
|
|
|
|
@copy © Marc Wäckerlin
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
$Log$
|
|
Revision 1.7 2004/10/13 10:47:15 marc
|
|
no more need for ldd in StackTrace, read from /proc/self/maps
|
|
|
|
Revision 1.6 2004/10/11 15:58:51 marc
|
|
First version with working support for shared libraries!
|
|
|
|
Revision 1.5 2004/10/07 16:59:12 marc
|
|
new method createSymtable that takes a list of arguments
|
|
-> untested!
|
|
|
|
Revision 1.4 2004/10/07 09:32:45 marc
|
|
correction in parameter (const&)
|
|
|
|
Revision 1.3 2004/08/28 16:21:25 marc
|
|
mrw-c++-0.92 (mrw)
|
|
- new file: version.cpp
|
|
- new file header for all sources
|
|
- work around warning in mrw::auto<T>
|
|
- possibility to compile without log4cxx
|
|
- work around bugs in demangle.h and libiberty.h
|
|
- corrections in documentation
|
|
- added simple tracing mechanism
|
|
- more warnings
|
|
- small corrections in Auto<>::Free and a new test for it
|
|
- possibility to compile without stack trace
|
|
|
|
*/
|
|
#include <mrw/stacktrace.hpp>
|
|
#include <mrw/exec.hpp>
|
|
#include <mrw/string.hpp>
|
|
#include <mrw/list.hpp>
|
|
#include <mrw/stdext.hpp>
|
|
#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>
|
|
#include <algorithm>
|
|
#include <list>
|
|
#if defined(__solaris__)
|
|
#include <sys/old_procfs.h>
|
|
#endif
|
|
#if defined (__GLIBC__)
|
|
#include <execinfo.h>
|
|
#endif
|
|
#include <bfd.h>
|
|
extern "C" {
|
|
|
|
/// @bug redefined in libiberty.h
|
|
# define HAVE_DECL_BASENAME 1
|
|
/// @bug redefined in libiberty.h
|
|
# define HAVE_DECL_ASPRINTF 1
|
|
/// @bug redefined in libiberty.h
|
|
# define HAVE_DECL_VASPRINTF 1
|
|
/** @bug
|
|
|
|
- in file file: /usr/include/demangle.h
|
|
- of package: binutils-2.15.90.0.1.1-31
|
|
|
|
An idiot unfortunately abused the C++ keyword @c typename as
|
|
variable name to @c cplus_demangle_fill_builtin_type, so I have
|
|
to work around it.
|
|
*/
|
|
# define typename anotherNameThatsNotAKeyword
|
|
# include <demangle.h>
|
|
# undef typename
|
|
// // copied from demangle.h because of compiler warnings in libliberty.h
|
|
// // (... throws different exception)
|
|
// #define DMGL_PARAMS (1 << 0) /* Include function args */
|
|
// #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
|
|
// /// @bug demangle.h includes libiberty.h which throws different
|
|
// /// exceptions for the same functions than other standard libraries,
|
|
// /// so I can't include demangle.h!
|
|
// extern char * cplus_demangle(const char *, int);
|
|
}
|
|
#include <iomanip>
|
|
|
|
namespace mrw {
|
|
//----------------------------------------------------------------------------
|
|
static std::string demangle(bfd* abfd, const char* name) {
|
|
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);
|
|
while (p && *p == '.') ++p;
|
|
|
|
Auto<char*>::Free res(cplus_demangle(p, DMGL_ANSI | DMGL_PARAMS));
|
|
if (res) {
|
|
/* Now put back any stripped dots. */
|
|
if (p==name) return static_cast<const char*>(res);
|
|
std::string add_dots('.', p-name);
|
|
return add_dots+=static_cast<const char*>(res);
|
|
}
|
|
return name;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
mrw::StackTrace::StackTrace() throw(std::bad_exception) {
|
|
// maximum trace level is limited here to 50, see below why
|
|
# if 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]);
|
|
}
|
|
# elif defined(__GNUG__)
|
|
{
|
|
# define push(i) \
|
|
(__builtin_return_address(i) ? \
|
|
(_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
|
|
}
|
|
# else
|
|
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
|
|
# endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
mrw::StackTrace::operator std::string() const throw(std::bad_exception) {
|
|
static const double LN10(log(10));
|
|
std::string s;
|
|
s<<1;
|
|
bool first(true);
|
|
unsigned int 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 (log(c.line+1)/LN10 > lisz) lisz = (unsigned int)(log(c.line+1)/LN10);
|
|
if (c.file.size() > fisz) fisz = c.file.size();
|
|
l.push_back(c);
|
|
}
|
|
for (std::list<CodePos>::iterator it(l.begin()); it!=l.end(); ++it)
|
|
s<<"["<<it->address<<"] "
|
|
<<it->file<<':'<<it->line
|
|
<<std::setw(fisz+lisz-it->file.size()-
|
|
(unsigned int)(log(it->line+1)/LN10)-1)
|
|
<<" "<<it->function<<"\n";
|
|
return s;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const
|
|
throw(std::bad_exception) {
|
|
os<<(std::string)*this;
|
|
return *this;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
mrw::StackTrace::CodePos mrw::StackTrace::translate(void* addr)
|
|
throw(std::bad_exception) {
|
|
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 CodePos(addr, "????", "????", 0);
|
|
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 CodePos(addr, "????", "????", 0);
|
|
return CodePos(addr, mrw::demangle(_bfd[it->second], function),
|
|
file?file:"????", line);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs)
|
|
throw(std::bad_exception) {
|
|
if (_dic.find(fname)!=_dic.end()) return true; // already loaded
|
|
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)
|
|
return false;
|
|
mrw::AutoPtr<asymbol*> syms(new asymbol*[memsz]);
|
|
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)),
|
|
syms)<0)
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files)
|
|
throw(std::bad_exception) {
|
|
bool success(true);
|
|
for (BinFiles::const_iterator it(files.begin());
|
|
it!=files.end(); ++it) {
|
|
if (!createSymtable(it->first, it->second)) success=false;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
mrw::StackTrace::BinFiles mrw::StackTrace::filename()
|
|
throw(std::bad_exception) {
|
|
mrw::StackTrace::BinFiles res;
|
|
# if defined(__solaris__)
|
|
{
|
|
std::string s;
|
|
s<<"/proc/"<<getpid();
|
|
AutoFile fd(open(s.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);
|
|
}
|
|
# 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)) {
|
|
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;
|
|
if (lib.size() && addr>0) res<<BinFiles::value_type(lib, addr);
|
|
}
|
|
return res;
|
|
}
|
|
# else
|
|
{
|
|
# 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
|
|
}
|
|
# endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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));
|
|
bfd_size_type sz(bfd_get_section_size_before_reloc(section));
|
|
_dic[fileoffset.first][vma] = Translator::mapped_type(vma+sz, section);
|
|
_addrs[vma] = fileoffset.first;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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::AutoBfd> mrw::StackTrace::_bfd;
|
|
std::map<std::string, mrw::AutoPtr<asymbol*> > mrw::StackTrace::_syms;
|