First version with working support for shared libraries!

master
Marc Wäckerlin 20 years ago
parent 3e155bb303
commit bf87a8a514
  1. 340
      mrw/stacktrace.cpp
  2. 57
      mrw/stacktrace.hpp

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $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 Revision 1.5 2004/10/07 16:59:12 marc
new method createSymtable that takes a list of arguments new method createSymtable that takes a list of arguments
-> untested! -> untested!
@ -31,7 +34,10 @@
*/ */
#include <mrw/stacktrace.hpp> #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 <unistd.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -80,9 +86,8 @@ extern "C" {
#include <iomanip> #include <iomanip>
namespace mrw { 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; if (bfd_get_symbol_leading_char(abfd) == name[0]) ++name;
/* This is a hack for better error reporting on XCOFF, PowerPC64-ELF /* This is a hack for better error reporting on XCOFF, PowerPC64-ELF
@ -101,184 +106,199 @@ namespace mrw {
} }
return name; return name;
} }
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
StackTrace::StackTrace() throw(std::bad_exception) { mrw::StackTrace::StackTrace() throw(std::bad_exception) {
// maximum trace level is limited here to 50, see below why // maximum trace level is limited here to 50, see below why
# if defined(__GLIBC__) # if defined(__GLIBC__)
{ {
const int TRACE_LEVEL(50); const int TRACE_LEVEL(50);
void* ba[TRACE_LEVEL]; void* ba[TRACE_LEVEL];
for (int n(backtrace(ba, TRACE_LEVEL)), i(0); i<n; ++i) for (int n(backtrace(ba, TRACE_LEVEL)), i(0); i<n; ++i)
_trace.push_back(ba[i]); _trace.push_back(ba[i]);
} }
# elif defined(__GNUG__) # elif defined(__GNUG__)
{ {
# define push(i) \ # define push(i) \
(__builtin_return_address(i) ? \ (__builtin_return_address(i) ? \
(_trace.push_back(__builtin_return_address(i)), true) : false) (_trace.push_back(__builtin_return_address(i)), true) : false)
push(0) && push(1) && push(2) && push(3) && push(4) && push(5) && 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(6) && push(7) && push(8) && push(9) && push(10) && push(11) &&
push(12) && push(13) && push(14) && push(15) && push(16) && push(17) push(12) && push(13) && push(14) && push(15) && push(16) && push(17)
&& push(18) && push(19) && push(20) && push(21) && push(22) && && push(18) && push(19) && push(20) && push(21) && push(22) &&
push(23) && push(24) && push(25) && push(26) && push(27) && push(28) push(23) && push(24) && push(25) && push(26) && push(27) && push(28)
&& push(29) && push(30) && push(31) && push(32) && push(33) && && push(29) && push(30) && push(31) && push(32) && push(33) &&
push(34) && push(35) && push(36) && push(37) && push(38) && push(39) push(34) && push(35) && push(36) && push(37) && push(38) && push(39)
&& push(40) && push(41) && push(42) && push(43) && push(44) && && push(40) && push(41) && push(42) && push(43) && push(44) &&
push(45) && push(46) && push(47) && push(48) && push(49); push(45) && push(46) && push(47) && push(48) && push(49);
# undef push # undef push
} }
# else # else
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace" # warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
# endif # endif
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
StackTrace::operator std::string() const throw(std::bad_exception) { mrw::StackTrace::operator std::string() const throw(std::bad_exception) {
static const double LN10(log(10)); static const double LN10(log(10));
std::stringstream s; std::string s;
bool first(true); s<<1;
unsigned int lisz(0), fisz(0); bool first(true);
std::list<CodePos> l; unsigned int lisz(0), fisz(0);
for (AddressTrace::const_reverse_iterator it(_trace.rbegin()); std::list<CodePos> l;
it!=_trace.rend(); ++it, first=false) { for (AddressTrace::const_reverse_iterator it(_trace.rbegin());
CodePos c(translate(*it)); it!=_trace.rend(); ++it, first=false) {
if (log(c.line+1)/LN10 > lisz) lisz = (unsigned int)(log(c.line+1)/LN10); CodePos c(translate(*it));
if (c.file.size() > fisz) fisz = c.file.size(); if (log(c.line+1)/LN10 > lisz) lisz = (unsigned int)(log(c.line+1)/LN10);
l.push_back(c); 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();
} }
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 const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const
throw(std::bad_exception) { throw(std::bad_exception) {
os<<(std::string)*this; os<<(std::string)*this;
return *this; return *this;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
StackTrace::CodePos StackTrace::translate(void* addr) mrw::StackTrace::CodePos mrw::StackTrace::translate(void* addr)
throw(std::bad_exception) { throw(std::bad_exception) {
assert(sizeof(bfd_vma)>=sizeof(void*)); assert(sizeof(bfd_vma)>=sizeof(void*));
bfd_vma vma_addr(reinterpret_cast<bfd_vma>(addr)); bfd_vma vma_addr(reinterpret_cast<bfd_vma>(addr));
if (!_dic.get()) return CodePos(addr, "????", "????", 0); std::map<Translator::key_type, std::string>::iterator
std::vector<Translator::key_type>::iterator it(_addrs.lower_bound(vma_addr));
it(std::lower_bound(_addrs.begin(), _addrs.end(), vma_addr)); if (it==_addrs.begin() || (--it)->first > vma_addr ||
if (it--==_addrs.begin() || *it > vma_addr || _dic.find(it->second)==_dic.end() ||
(*_dic)[*it].first <= vma_addr) return CodePos(addr, "????", "????", 0); _dic[it->second][it->first].first <= vma_addr)
static const char* file(0); return CodePos(addr, "????", "????", 0);
static const char* function(0); static const char* file(0);
unsigned int line; static const char* function(0);
if (!bfd_find_nearest_line(const_cast<bfd*>(static_cast<const bfd*>(_bfd)), unsigned int line;
(*_dic)[*it].second, _syms.get(), if (!bfd_find_nearest_line
vma_addr-*it, &file, &function, &line)) (const_cast<bfd*>(static_cast<const bfd*>(_bfd[it->second])),
return CodePos(addr, "????", "????", 0); _dic[it->second][it->first].second,
return CodePos(addr, mrw::demangle(_bfd, function), file?file:"????", line); _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) bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs)
throw(std::bad_exception) { throw(std::bad_exception) {
if (_dic.get()) return true; if (_dic.find(fname)!=_dic.end()) return true; // already loaded
AutoBfd abfd(bfd_openr((fname!="" ? fname : filename()).c_str(), 0)); if (fname=="") return createSymtable(filename());
long memsz(-1); AutoBfd abfd(bfd_openr(fname.c_str(), 0));
Auto<char**>::Free m(0); long memsz(-1);
if (!abfd || bfd_check_format(abfd, bfd_archive) || Auto<char**>::Free m(0);
!bfd_check_format_matches(abfd, bfd_object, &(m.getClean())) || if (!abfd || bfd_check_format(abfd, bfd_archive) ||
!(bfd_get_file_flags(const_cast<bfd*>(static_cast<const bfd*>(abfd))) !bfd_check_format_matches(abfd, bfd_object, &(m.getClean())) ||
&HAS_SYMS) || !(bfd_get_file_flags(const_cast<bfd*>(static_cast<const bfd*>(abfd)))
(memsz=bfd_get_symtab_upper_bound &HAS_SYMS) ||
(const_cast<bfd*>(static_cast<const bfd*>(abfd))))<0) (memsz=bfd_get_symtab_upper_bound
return false; (const_cast<bfd*>(static_cast<const bfd*>(abfd))))<0)
std::auto_ptr<asymbol*> syms(new asymbol*[memsz]); return false;
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)), mrw::AutoPtr<asymbol*> syms(new asymbol*[memsz]);
syms.get())<0) if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)),
return false; syms)<0)
_bfd = abfd; return false;
_syms = syms; _bfd[fname] = abfd;
_dic = std::auto_ptr<Translator>(new Translator()); _syms[fname] = syms;
bfd_map_over_sections(_bfd, buildSectionMap, 0); _dic[fname];
std::sort(_addrs.begin(), _addrs.end()); std::pair<std::string, void*> fileoffs = std::make_pair(fname, offs);
return true; bfd_map_over_sections(_bfd[fname], buildSectionMap, &fileoffs);
} return true;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool StackTrace::createSymtable(const std::list<std::string>& fnames) bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files)
throw(std::bad_exception) { throw(std::bad_exception) {
if (_dic.get()) return true; bool success(true);
for (std::list<std::string>::const_iterator it(fnames.begin()); for (BinFiles::const_iterator it(files.begin());
it!=fnames.end(); ++it) { it!=files.end(); ++it) {
AutoBfd abfd(bfd_openr(it->c_str(), 0)); if (!createSymtable(it->first, it->second)) success=false;
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;
} }
return success;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
std::string StackTrace::filename() throw(std::bad_exception) { mrw::StackTrace::BinFiles mrw::StackTrace::filename()
std::stringstream s; throw(std::bad_exception) {
s<<"/proc/"<<getpid(); mrw::StackTrace::BinFiles res;
std::string s;
s<<"/proc/"<<getpid();
# if defined(__solaris__) # if defined(__solaris__)
{ {
std::string res; AutoFile fd(open(s.str(), O_RDONLY));
AutoFile fd(open(s.str().c_str(), O_RDONLY)); prpsinfo_t status;
prpsinfo_t status; if (fd==-1 || ioctl(fd, PIOCPSINFO, &status)==-1) return res;
if (fd==-1 || ioctl(fd, PIOCPSINFO, &status)==-1) return res; s = status.pr_psargs;
res = status.pr_psargs; return res<<BinFiles::value_type(s.substr(0, s.find(' ')), 0);
res = res.substr(0, res.find(' ')); }
return res;
}
# elif defined(__linux__) # elif defined(__linux__)
{ {
s<<"/exe"; res<<BinFiles::value_type(s<<"/exe", 0);
return s.str(); 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 # else
{
# warning "Don't know how to get executable file name in your system!" # warning "Don't know how to get executable file name in your system!"
# warning "Impossible to get function names in stack trace!" # warning "Impossible to get function names in stack trace!"
# warning "Give the path to the executable to StackTrace::createSymtable!" # warning "Give the path to the executable to StackTrace::createSymtable!"
# endif abort();
} }
# endif
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void StackTrace::buildSectionMap(bfd* abfd, asection* section, void*) void mrw::StackTrace::buildSectionMap(bfd* abfd, asection* section,
throw(std::bad_exception) { void* fileoffs)
if (!abfd || !section) return; throw(std::bad_exception) {
if (!(bfd_get_section_flags(abfd, section)&SEC_ALLOC)) return; if (!abfd || !section || !fileoffs) return;
bfd_vma vma(bfd_get_section_vma(abfd, section)); if (!(bfd_get_section_flags(abfd, section)&SEC_ALLOC)) return;
bfd_size_type sz(bfd_get_section_size_before_reloc(section)); std::pair<std::string, void*> fileoffset =
(*_dic)[vma] = Translator::mapped_type(vma+sz, section); *(std::pair<std::string, void*>*)fileoffs;
_addrs.push_back(vma); 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);
std::auto_ptr<StackTrace::Translator> StackTrace::_dic; _addrs[vma] = fileoffset.first;
std::vector<StackTrace::Translator::key_type> StackTrace::_addrs;
AutoBfd StackTrace::_bfd;
std::auto_ptr<asymbol*> StackTrace::_syms;
} }
//----------------------------------------------------------------------------
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;

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $Log$
Revision 1.7 2004/10/11 15:58:51 marc
First version with working support for shared libraries!
Revision 1.6 2004/10/07 16:59:12 marc Revision 1.6 2004/10/07 16:59:12 marc
new method createSymtable that takes a list of arguments new method createSymtable that takes a list of arguments
-> untested! -> untested!
@ -143,18 +146,21 @@ namespace mrw {
ger symbolic information from libraries linked to the ger symbolic information from libraries linked to the
executable. Perhaps it could be possible, if I'd add a executable. Perhaps it could be possible, if I'd add a
possibility to read and evaluate these libraries, but that's for possibility to read and evaluate these libraries, but that's for
a future release. a future release. (Now, 10/08/2004, I am working on it)
@todo Add support to read debugging information from libraries @todo Add support to read debugging information from libraries
that are linked to the executable. that are linked to the executable. (soon)
@todo Add support for alternative symbol evaluation using @c @todo Add support for alternative symbol evaluation using @c
backtrace_symbols. backtrace_symbols.
*/ */
class StackTrace { class StackTrace {
public: public:
//............................................................... typedefs //............................................................... typedefs
typedef std::vector<void*> AddressTrace; ///< container for the adresses /// container for the adresses
typedef std::vector<void*> AddressTrace;
/// binary file with offset (for shared libraries, 0 for executables)
typedef std::list< std::pair<std::string, void*> > BinFiles;
/// structure to store all evaluated information /// structure to store all evaluated information
struct CodePos { struct CodePos {
CodePos(void* a, std::string fn, std::string fi, unsigned int l) CodePos(void* a, std::string fn, std::string fi, unsigned int l)
@ -187,6 +193,9 @@ namespace mrw {
and Solaris, this can be evaluated automatically, so the and Solaris, this can be evaluated automatically, so the
parameter is optional. parameter is optional.
@param offs Offset of the address space. It is 0 for
executables, but must be given for shared libraries.
@return @c true in case of success. If @c false is returned, @return @c true in case of success. If @c false is returned,
the symbol table was not read and the evaluation cannot be the symbol table was not read and the evaluation cannot be
done. Printing then only prints the raw addresses, without done. Printing then only prints the raw addresses, without
@ -203,15 +212,17 @@ namespace mrw {
are created only the first time, so you don't loose too much are created only the first time, so you don't loose too much
time. time.
*/ */
static bool createSymtable(const std::string& fname="") static bool createSymtable(const std::string& fname="", void* offs=0)
throw(std::bad_exception); throw(std::bad_exception);
/** @brief read the symbol table from a list of an executable file and /** @brief read the symbol table from a list of an executable file and
shared libraries shared libraries
@param fnames The list of file names that must contain the @param files The list of file names that must contain the
executable plus all shared libraries that should be included executable plus all shared libraries that should be included
in stack traces. in stack traces. Every file name has an offset address as
second parameter. This offset must be given for shared
libraries and can be retrieved using the program @c ldd.
@return @c true in case of success. If @c false is returned, @return @c true in case of success. If @c false is returned,
the symbol table was not completely read and the evaluation the symbol table was not completely read and the evaluation
@ -228,8 +239,8 @@ namespace mrw {
@note If this method is called more than once, the symbols @note If this method is called more than once, the symbols
are created only the first time, so you don't loose too much are created only the first time, so you don't loose too much
time. time.
*/ */
static bool createSymtable(const std::list<std::string>& fnames) throw(std::bad_exception); static bool createSymtable(const BinFiles& files) throw(std::bad_exception);
private: private:
//............................................................... typedefs //............................................................... typedefs
@ -237,22 +248,28 @@ namespace mrw {
Translator; Translator;
//.............................................................. variables //.............................................................. variables
AddressTrace _trace; AddressTrace _trace;
static std::auto_ptr<Translator> _dic; static std::map<std::string, Translator> _dic;
static std::vector<Translator::key_type> _addrs; static std::map<Translator::key_type, std::string> _addrs;
static AutoBfd _bfd; static std::map<std::string, AutoBfd> _bfd;
static std::auto_ptr<asymbol*> _syms; static std::map<std::string, mrw::AutoPtr<asymbol*> > _syms;
//................................................................ methods //................................................................ methods
static std::string filename() throw(std::bad_exception); static BinFiles filename() throw(std::bad_exception);
static void buildSectionMap(bfd*, asection*, void*) static void buildSectionMap(bfd*, asection*, void*)
throw(std::bad_exception); throw(std::bad_exception);
}; };
/// evaluate a stack trace and shift it on a stream
inline std::ostream& operator<<(std::ostream& os, const StackTrace& st)
throw(std::bad_exception) {
return os<<(std::string)st;
}
//@} //@}
} }
/** @addtogroup StackTrace */
//@{
/// evaluate a stack trace and shift it on a stream
inline std::ostream& operator<<(std::ostream& os, const mrw::StackTrace& st)
throw(std::bad_exception) {
return os<<(std::string)st;
}
//@}
#endif #endif

Loading…
Cancel
Save