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.
355 lines
13 KiB
355 lines
13 KiB
/** @file |
|
|
|
$Id$ |
|
|
|
$Date$ |
|
$Author$ |
|
|
|
@copy © Marc Wäckerlin |
|
@license LGPL, see file <a href="license.html">COPYING</a> |
|
|
|
$Log$ |
|
Revision 1.13 2005/02/28 07:28:37 marc |
|
typo |
|
|
|
Revision 1.12 2005/02/28 07:14:03 marc |
|
change in getting section size for SUN Solaris (old bfd.h) |
|
|
|
Revision 1.11 2005/02/18 15:48:56 marc |
|
Dynamic loading of libbfd, no more dependency on specific libbfd version! |
|
|
|
Revision 1.10 2005/01/28 07:51:24 marc |
|
improved and corrected trace formatting |
|
|
|
Revision 1.9 2005/01/07 00:34:38 marc |
|
some changes for solaris |
|
|
|
Revision 1.8 2004/12/20 13:22:25 marc |
|
mrw string now throws exceptions, catch needed |
|
|
|
Revision 1.7 2004/10/13 10:47:15 marc |
|
no more need for ldd in StackTrace, read from /proc/self/maps |
|
|
|
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! |
|
|
|
Revision 1.4 2004/10/07 09:32:45 marc |
|
correction in parameter (const&) |
|
|
|
Revision 1.3 2004/08/28 16:21:25 marc |
|
mrw-c++-0.92 (mrw) |
|
- new file: version.cpp |
|
- new file header for all sources |
|
- work around warning in mrw::auto<T> |
|
- possibility to compile without log4cxx |
|
- work around bugs in demangle.h and libiberty.h |
|
- corrections in documentation |
|
- added simple tracing mechanism |
|
- more warnings |
|
- small corrections in Auto<>::Free and a new test for it |
|
- possibility to compile without stack trace |
|
|
|
*/ |
|
#include <mrw/stacktrace.hpp> |
|
#include <mrw/exec.hpp> |
|
#include <mrw/string.hpp> |
|
#include <mrw/list.hpp> |
|
#include <mrw/stdext.hpp> |
|
#include <mrw/dynamiclibrary.hpp> |
|
#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 <fstream> |
|
#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" { |
|
|
|
/// @bug redefined in libiberty.h |
|
# define HAVE_DECL_BASENAME 1 |
|
/// @bug redefined in libiberty.h |
|
# define HAVE_DECL_ASPRINTF 1 |
|
/// @bug redefined in libiberty.h |
|
# define HAVE_DECL_VASPRINTF 1 |
|
/** @bug |
|
|
|
- in file file: /usr/include/demangle.h |
|
- of package: binutils-2.15.90.0.1.1-31 |
|
|
|
An idiot unfortunately abused the C++ keyword @c typename as |
|
variable name to @c cplus_demangle_fill_builtin_type, so I have |
|
to work around it. |
|
*/ |
|
# define typename anotherNameThatsNotAKeyword |
|
# include <demangle.h> |
|
# undef typename |
|
// // copied from demangle.h because of compiler warnings in libliberty.h |
|
// // (... throws different exception) |
|
// #define DMGL_PARAMS (1 << 0) /* Include function args */ |
|
// #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
|
// /// @bug demangle.h includes libiberty.h which throws different |
|
// /// exceptions for the same functions than other standard libraries, |
|
// /// so I can't include demangle.h! |
|
// extern char * cplus_demangle(const char *, int); |
|
} |
|
#include <iomanip> |
|
|
|
namespace mrw { |
|
//---------------------------------------------------------------------------- |
|
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 |
|
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; |
|
# ifdef __solaris__ |
|
char res[1024]; |
|
if (cplus_demangle(p, res, 1024)) return name; |
|
# else |
|
Auto<char*>::Free res(cplus_demangle(p, DMGL_ANSI | DMGL_PARAMS)); |
|
# endif |
|
if (res) { |
|
/* Now put back any stripped dots. */ |
|
if (p==name) return static_cast<const char*>(res); |
|
std::string add_dots('.', p-name); |
|
return add_dots+=static_cast<const char*>(res); |
|
} |
|
return name; |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
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]); |
|
} |
|
# 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 |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
mrw::StackTrace::operator std::string() const throw(std::bad_exception) { |
|
std::stringstream s; |
|
bool first(true); |
|
unsigned int fusz(0), adsz(0); //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 (((std::stringstream&)(std::stringstream()<<c.address)).str().size() |
|
> adsz) |
|
adsz = |
|
((std::stringstream&)(std::stringstream()<<c.address)).str().size(); |
|
if (c.function.size() > fusz) fusz = c.function.size(); |
|
l.push_back(c); |
|
} |
|
for (std::list<CodePos>::iterator it(l.begin()); it!=l.end(); ++it) |
|
s<<'['<<std::setw(adsz)<<it->address<<"] " |
|
<<it->function<<std::setw(fusz-it->function.size()+1)<<' ' |
|
<<it->file<<':'<<it->line<<std::endl; |
|
return s.str(); |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const |
|
throw(std::bad_exception) { |
|
os<<(std::string)*this; |
|
return *this; |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
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 mrw::StackTrace::createSymtable(const std::string& fname, void* offs) |
|
throw(std::bad_exception) { |
|
if (_dic.find(fname)!=_dic.end()) return true; // already loaded |
|
try { |
|
static DynamicLibrary lib("libbfd"); |
|
static bfd*(*bfd_openr)(const char*, const char*) = |
|
(bfd*(*)(const char*, const char*))lib.symbol("bfd_openr"); |
|
static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) = |
|
(bfd_boolean(*)(bfd*, bfd_format))lib.symbol("bfd_check_format"); |
|
static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) = |
|
(bfd_boolean(*)(bfd*, bfd_format, char***)) |
|
lib.symbol("bfd_check_format_matches"); |
|
static void(*bfd_map_over_sections) |
|
(bfd*, void(*)(bfd*, asection*, void*), void*) = |
|
(void(*)(bfd*, void(*)(bfd*, asection*, void*), void*)) |
|
lib.symbol("bfd_map_over_sections"); |
|
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; |
|
} catch (...) { |
|
return false; // shared library for bfd not found or similar |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
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; |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
int mrw::StackTrace::bfdClose(bfd* abfd) throw() { |
|
try { |
|
static DynamicLibrary lib(""); |
|
static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close"); |
|
return (*bfd_close)(abfd); |
|
} catch (...) { |
|
return -1; // dynamic library loading problems |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
mrw::StackTrace::BinFiles mrw::StackTrace::filename() |
|
throw(std::bad_exception) { |
|
mrw::StackTrace::BinFiles res; |
|
# if defined(__solaris__) |
|
{ |
|
std::string s; |
|
s<<"/proc/"<<getpid(); |
|
AutoFile fd(open(s.c_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__) |
|
{ |
|
res<<BinFiles::value_type("/proc/self/exe", 0); |
|
std::ifstream is("/proc/self/maps"); |
|
std::string s; |
|
std::string range, perm, x1, x2, size, lib; |
|
while (getline(is, s)) try { |
|
range = perm = x1 = x2 = size = lib = "????"; |
|
s>>range>>perm>>x1>>x2>>size>>lib; |
|
range.resize(range.find_first_not_of("0123456789abcdefABCDEF")); |
|
void* addr(0); |
|
range>>addr; |
|
if (lib.size() && addr>0) res<<BinFiles::value_type(lib, addr); |
|
} catch (...) {} // ignore non matching lines |
|
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!" |
|
return res; // empty |
|
} |
|
# endif |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
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)); |
|
#ifdef bfd_get_section_size |
|
bfd_size_type sz(bfd_get_section_size(section)); |
|
#elif defined bfd_get_section_size_before_reloc |
|
bfd_size_type sz(bfd_get_section_size_before_reloc(section)); |
|
#else |
|
bfd_size_type sz(section->size); |
|
#endif |
|
_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::StackTrace::AutoBfd> mrw::StackTrace::_bfd; |
|
std::map<std::string, mrw::AutoPtr<asymbol*> > mrw::StackTrace::_syms;
|
|
|