Files
mrw-cxx/mrw/stacktrace.cpp

190 lines
6.9 KiB
C++
Raw Normal View History

2004-04-21 06:39:20 +00:00
#include <mrw/stacktrace.hpp>
#include <sstream>
#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 <algorithm>
#include <list>
#if defined(__solaris__)
#include <sys/old_procfs.h>
#endif
#if defined (__GLIBC__)
#include <execinfo.h>
#endif
#include <bfd.h>
extern "C" {
#include <demangle.h>
}
#include <iomanip>
namespace mrw {
//----------------------------------------------------------------------------
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;
mrw::AutoFree<char> res(cplus_demangle(p, DMGL_ANSI | DMGL_PARAMS));
if (res) {
/* Now put back any stripped dots. */
if (p==name) return (char*)res;
std::string add_dots('.', p-name);
return add_dots+=(char*)res;
}
return name;
}
//----------------------------------------------------------------------------
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
}
//----------------------------------------------------------------------------
StackTrace::operator std::string() const throw(std::bad_exception) {
static const double LN10(log(10));
std::stringstream s;
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<<std::endl;
return s.str();
}
//----------------------------------------------------------------------------
const StackTrace& StackTrace::print(std::ostream& os) const
throw(std::bad_exception) {
os<<(std::string)*this;
return *this;
}
//----------------------------------------------------------------------------
StackTrace::CodePos StackTrace::translate(void* addr)
throw(std::bad_exception) {
assert(sizeof(bfd_vma)>=sizeof(void*));
bfd_vma vma_addr(reinterpret_cast<bfd_vma>(addr));
if (!_dic.get()) return CodePos(addr, "????", "????", 0);
std::vector<Translator::key_type>::iterator
it(std::lower_bound(_addrs.begin(), _addrs.end(), vma_addr));
if (it--==_addrs.begin() || *it > vma_addr ||
(*_dic)[*it].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(_bfd, (*_dic)[*it].second, _syms.get(),
vma_addr-*it, &file, &function, &line))
return CodePos(addr, "????", "????", 0);
return CodePos(addr, mrw::demangle(_bfd, function), file?file:"????", line);
}
//----------------------------------------------------------------------------
bool StackTrace::createSymtable(std::string fname) throw(std::bad_exception) {
if (_dic.get()) return true;
AutoBfd abfd(bfd_openr((fname!="" ? fname : filename()).c_str(), 0));
long memsz(-1);
AutoFree<char*> m(0);
if (!abfd || bfd_check_format(abfd, bfd_archive) ||
!bfd_check_format_matches(abfd, bfd_object, m) ||
!(bfd_get_file_flags(abfd)&HAS_SYMS) ||
(memsz=bfd_get_symtab_upper_bound(abfd))<0) return false;
std::auto_ptr<asymbol*> syms(new asymbol*[memsz]);
if (bfd_canonicalize_symtab(abfd, syms.get())<0) return false;
_bfd = abfd;
_syms = syms;
_dic = std::auto_ptr<Translator>(new Translator());
bfd_map_over_sections(_bfd, buildSectionMap, 0);
std::sort(_addrs.begin(), _addrs.end());
return true;
}
//----------------------------------------------------------------------------
std::string StackTrace::filename() throw(std::bad_exception) {
std::stringstream s;
s<<"/proc/"<<getpid();
# if defined(__solaris__)
{
std::string res;
AutoFile fd(open(s.str().c_str(), O_RDONLY));
prpsinfo_t status;
if (fd==-1 || ioctl(fd, PIOCPSINFO, &status)==-1) return res;
res = status.pr_psargs;
res = res.substr(0, res.find(' '));
return res;
}
# elif defined(__linux__)
{
s<<"/exe";
return s.str();
}
# 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!"
# endif
}
//----------------------------------------------------------------------------
void StackTrace::buildSectionMap(bfd* abfd, asection* section, void*)
throw(std::bad_exception) {
if (!(bfd_get_section_flags(abfd, section)&SEC_ALLOC)) return;
bfd_vma vma(bfd_get_section_vma(abfd, section));
bfd_size_type sz(bfd_get_section_size_before_reloc(section));
(*_dic)[vma] = Translator::mapped_type(vma+sz, section);
_addrs.push_back(vma);
}
//----------------------------------------------------------------------------
std::auto_ptr<StackTrace::Translator> StackTrace::_dic;
std::vector<StackTrace::Translator::key_type> StackTrace::_addrs;
AutoBfd StackTrace::_bfd;
std::auto_ptr<asymbol*> StackTrace::_syms;
}