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>
$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;

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a>
$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
new method createSymtable that takes a list of arguments
-> untested!
@ -143,18 +146,21 @@ namespace mrw {
ger symbolic information from libraries linked to the
executable. Perhaps it could be possible, if I'd add a
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
that are linked to the executable.
that are linked to the executable. (soon)
@todo Add support for alternative symbol evaluation using @c
backtrace_symbols.
*/
class StackTrace {
public:
public:
//............................................................... 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
struct CodePos {
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
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,
the symbol table was not read and the evaluation cannot be
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
time.
*/
static bool createSymtable(const std::string& fname="")
static bool createSymtable(const std::string& fname="", void* offs=0)
throw(std::bad_exception);
/** @brief read the symbol table from a list of an executable file and
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
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,
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
are created only the first time, so you don't loose too much
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:
//............................................................... typedefs
@ -237,22 +248,28 @@ namespace mrw {
Translator;
//.............................................................. variables
AddressTrace _trace;
static std::auto_ptr<Translator> _dic;
static std::vector<Translator::key_type> _addrs;
static AutoBfd _bfd;
static std::auto_ptr<asymbol*> _syms;
static std::map<std::string, Translator> _dic;
static std::map<Translator::key_type, std::string> _addrs;
static std::map<std::string, AutoBfd> _bfd;
static std::map<std::string, mrw::AutoPtr<asymbol*> > _syms;
//................................................................ methods
static std::string filename() throw(std::bad_exception);
static BinFiles filename() throw(std::bad_exception);
static void buildSectionMap(bfd*, asection*, void*)
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

Loading…
Cancel
Save