|
|
|
@ -9,6 +9,9 @@ |
|
|
|
|
@license LGPL, see file <a href="license.html">COPYING</a> |
|
|
|
|
|
|
|
|
|
$Log$ |
|
|
|
|
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! |
|
|
|
@ -31,7 +34,10 @@ |
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
#include <mrw/stacktrace.hpp> |
|
|
|
|
#include <sstream> |
|
|
|
|
#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> |
|
|
|
@ -80,9 +86,8 @@ extern "C" { |
|
|
|
|
#include <iomanip> |
|
|
|
|
|
|
|
|
|
namespace mrw { |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
std::string demangle(bfd* abfd, const char* name) { |
|
|
|
|
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
|
|
|
|
@ -101,184 +106,199 @@ namespace mrw { |
|
|
|
|
} |
|
|
|
|
return name; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
StackTrace::StackTrace() throw(std::bad_exception) { |
|
|
|
|
// maximum trace level is limited here to 50, see below why
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
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]); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
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); |
|
|
|
|
{ |
|
|
|
|
# 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(); |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
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 StackTrace& StackTrace::print(std::ostream& os) const |
|
|
|
|
throw(std::bad_exception) { |
|
|
|
|
os<<(std::string)*this; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
const mrw::StackTrace& mrw::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(const_cast<bfd*>(static_cast<const bfd*>(_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); |
|
|
|
|
} |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
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 StackTrace::createSymtable(const 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); |
|
|
|
|
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; |
|
|
|
|
std::auto_ptr<asymbol*> syms(new asymbol*[memsz]); |
|
|
|
|
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(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; |
|
|
|
|
} |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
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 StackTrace::createSymtable(const std::list<std::string>& fnames) |
|
|
|
|
throw(std::bad_exception) { |
|
|
|
|
if (_dic.get()) return true; |
|
|
|
|
for (std::list<std::string>::const_iterator it(fnames.begin()); |
|
|
|
|
it!=fnames.end(); ++it) { |
|
|
|
|
AutoBfd abfd(bfd_openr(it->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; |
|
|
|
|
std::auto_ptr<asymbol*> syms(new asymbol*[memsz]); |
|
|
|
|
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)), |
|
|
|
|
syms.get())<0) |
|
|
|
|
return false; |
|
|
|
|
_bfd = abfd; |
|
|
|
|
_syms = syms; |
|
|
|
|
if (!_dic.get()) _dic = std::auto_ptr<Translator>(new Translator()); |
|
|
|
|
bfd_map_over_sections(_bfd, buildSectionMap, 0); |
|
|
|
|
} |
|
|
|
|
std::sort(_addrs.begin(), _addrs.end()); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
std::string StackTrace::filename() throw(std::bad_exception) { |
|
|
|
|
std::stringstream s; |
|
|
|
|
s<<"/proc/"<<getpid(); |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
mrw::StackTrace::BinFiles mrw::StackTrace::filename() |
|
|
|
|
throw(std::bad_exception) { |
|
|
|
|
mrw::StackTrace::BinFiles res; |
|
|
|
|
std::string 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; |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
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__) |
|
|
|
|
{ |
|
|
|
|
s<<"/exe"; |
|
|
|
|
return s.str(); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
res<<BinFiles::value_type(s<<"/exe", 0); |
|
|
|
|
try { |
|
|
|
|
mrw::Exec ldd = (mrw::Cmd("/usr/bin/ldd"), s).execute(); |
|
|
|
|
for (std::string lddres(ldd.result()); |
|
|
|
|
lddres.size(); |
|
|
|
|
lddres.erase(0, min(lddres.find('\n')+1, std::string::npos))) { |
|
|
|
|
std::string line(lddres.substr(0, lddres.find('\n'))); |
|
|
|
|
std::string::size_type pos = line.find(" => "); |
|
|
|
|
if (pos<std::string::npos) { |
|
|
|
|
pos += 4; |
|
|
|
|
std::string file = line.substr(pos, (pos=line.find(' ', pos)-pos)); |
|
|
|
|
std::stringstream addr; |
|
|
|
|
addr<<line.substr(++(pos=line.find('(', pos)), |
|
|
|
|
line.find(')', pos)-pos); |
|
|
|
|
void* address(0); |
|
|
|
|
addr>>address; |
|
|
|
|
if (file.size()) res<<BinFiles::value_type(file, address); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch (mrw::ExecutionFailedExc&) {} |
|
|
|
|
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!" |
|
|
|
|
# endif |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
# endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
void StackTrace::buildSectionMap(bfd* abfd, asection* section, void*) |
|
|
|
|
throw(std::bad_exception) { |
|
|
|
|
if (!abfd || !section) return; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
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; |
|
|
|
|