C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.9 KiB
190 lines
6.9 KiB
21 years ago
|
#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;
|
||
|
|
||
|
}
|