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.
346 lines
12 KiB
346 lines
12 KiB
/** @file |
|
|
|
$Id$ |
|
|
|
$Date$ |
|
$Author$ |
|
|
|
@copy © Marc Wäckerlin |
|
@license LGPL, see file <a href="license.html">COPYING</a> |
|
|
|
*/ |
|
#include <mrw/stacktrace.hpp> |
|
#include <mrw/exec.hpp> |
|
#include <mrw/string.hpp> |
|
#include <mrw/list.hpp> |
|
#include <mrw/stdext.hpp> |
|
#ifndef NO_LTDL |
|
# include <mrw/dynamiclibrary.hpp> |
|
#endif |
|
#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> |
|
#include <iomanip> |
|
#if HAVE_DEMANGLE_H |
|
/* the defines work around wrong definitions in libiberty.h */ |
|
#define HAVE_DECL_BASENAME 1 |
|
#define HAVE_DECL_FFS 1 |
|
#define HAVE_DECL_ASPRINTF 1 |
|
#define HAVE_DECL_VASPRINTF 1 |
|
#define HAVE_DECL_SNPRINTF 1 |
|
#define HAVE_DECL_VSNPRINTF 1 |
|
#define HAVE_DECL_STRVERSCMP 1 |
|
# include <demangle.h> |
|
#else |
|
# define DMGL_PARAMS (1 << 0) /* Include function args */ |
|
# define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
|
extern "C" { |
|
extern char * cplus_demangle(const char *, int); |
|
} |
|
#endif |
|
|
|
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) && _REENTRANT && !_MT |
|
#define _MT |
|
#endif |
|
|
|
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) { |
|
#ifdef _MT |
|
boost::recursive_mutex::scoped_lock lock(_mutex); |
|
#endif |
|
static const CodePos UNKNOWN(addr, "????", "????", 0); |
|
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 UNKNOWN; |
|
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 UNKNOWN; |
|
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) { |
|
#ifdef _MT |
|
boost::recursive_mutex::scoped_lock lock(_mutex); |
|
#endif |
|
if (_dic.find(fname)!=_dic.end()) return true; // already loaded |
|
try { |
|
#if NO_LTDL |
|
static bfd*(*bfd_openr)(const char*, const char*) = |
|
::bfd_openr; |
|
static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) = |
|
::bfd_check_format; |
|
static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) = |
|
::bfd_check_format_matches; |
|
static void(*bfd_map_over_sections) |
|
(bfd*, void(*)(bfd*, asection*, void*), void*) = |
|
::bfd_map_over_sections; |
|
#else |
|
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"); |
|
#endif |
|
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) { |
|
_error = "cannot map bfd symbols - 'bfd_get_file_flags' failed " |
|
"for file: \""+fname+'"'; |
|
return false; |
|
} |
|
mrw::AutoPtr<asymbol*> syms(new asymbol*[memsz]); |
|
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)), |
|
syms)<0) { |
|
_error = "cannot map bfd symbols - 'bfd_canonicalize_symtab' failed" |
|
"for file: \""+fname+'"'; |
|
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 (const std::exception& x) { |
|
_error = std::string("exception received: \"")+x.what()+"\", " |
|
"while processing file: \""+fname+'"'; |
|
return false; // shared library for bfd not found or similar |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files) |
|
throw(std::bad_exception) { |
|
#ifdef _MT |
|
boost::recursive_mutex::scoped_lock lock(_mutex); |
|
#endif |
|
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 { |
|
#if NO_LTDL |
|
static int(*bfd_close)(bfd*) = ::bfd_close; |
|
#else |
|
static DynamicLibrary lib(""); |
|
static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close"); |
|
#endif |
|
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; |
|
// added check: no names in brackets: [lib], because there are |
|
// [heap], [stack] and [vdso] that cannot be loaded |
|
// question: should only files with ending '.so' be loaded? |
|
if (lib.size() && lib[0]!='[' && lib[lib.size()-1]!=']' && 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; |
|
#ifdef _MT |
|
boost::recursive_mutex mrw::StackTrace::_mutex; |
|
#endif |
|
std::string mrw::StackTrace::_error;
|
|
|