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.
388 lines
14 KiB
388 lines
14 KiB
20 years ago
|
/** @file
|
||
|
|
||
|
$Id$
|
||
|
|
||
|
$Date$
|
||
|
$Author$
|
||
|
|
||
|
@copy © Marc Wäckerlin
|
||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||
|
|
||
|
*/
|
||
13 years ago
|
#include <mrw/stacktrace.hxx>
|
||
|
#include <mrw/exec.hxx>
|
||
|
#include <mrw/string.hxx>
|
||
|
#include <mrw/list.hxx>
|
||
|
#include <mrw/stdext.hxx>
|
||
19 years ago
|
#ifndef NO_LTDL
|
||
13 years ago
|
# include <mrw/dynamiclibrary.hxx>
|
||
19 years ago
|
#endif
|
||
21 years ago
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <assert.h>
|
||
|
#include <math.h>
|
||
20 years ago
|
#include <fstream>
|
||
21 years ago
|
#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>
|
||
19 years ago
|
#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
|
||
|
|
||
17 years ago
|
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
|
||
|
&& defined(_REENTRANT) && !defined(_MT)
|
||
19 years ago
|
#define _MT
|
||
|
#endif
|
||
21 years ago
|
|
||
|
namespace mrw {
|
||
|
//----------------------------------------------------------------------------
|
||
20 years ago
|
static std::string demangle(bfd* abfd, const char* name) {
|
||
21 years ago
|
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. */
|
||
20 years ago
|
const char* p(name);
|
||
21 years ago
|
while (p && *p == '.') ++p;
|
||
20 years ago
|
# 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
|
||
21 years ago
|
if (res) {
|
||
|
/* Now put back any stripped dots. */
|
||
20 years ago
|
if (p==name) return static_cast<const char*>(res);
|
||
21 years ago
|
std::string add_dots('.', p-name);
|
||
20 years ago
|
return add_dots+=static_cast<const char*>(res);
|
||
21 years ago
|
}
|
||
|
return name;
|
||
|
}
|
||
20 years ago
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
mrw::StackTrace::StackTrace() throw(std::bad_exception) {
|
||
|
// maximum trace level is limited here to 50, see below why
|
||
18 years ago
|
# if defined(__GNUG__)
|
||
|
{
|
||
|
# define PUSH(i) \
|
||
|
(__builtin_frame_address(i)!=0 ? \
|
||
|
(_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
|
||
|
}
|
||
|
# elif defined(__GLIBC__)
|
||
20 years ago
|
{
|
||
|
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]);
|
||
|
}
|
||
21 years ago
|
# else
|
||
|
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
|
||
|
# endif
|
||
20 years ago
|
}
|
||
21 years ago
|
|
||
18 years ago
|
mrw::StackTrace::~StackTrace() throw() {
|
||
|
// maximum trace level is limited here to 50, see below why
|
||
|
# if !defined(__GNUG__) && defined(__GLIBC__)
|
||
|
/* GLIBC backtrace seems to leak memory, but I don't see why, so
|
||
|
GNUG part is prefered. According to the man-page, backtrace does
|
||
|
not allocate memory, so what happens here? Valgrind message:
|
||
|
|
||
|
==19829== 28 bytes in 1 blocks are still reachable in loss record 1 of 1
|
||
|
==19829== at 0x40233F0: malloc (in /usr/lib/valgrind/x86-linux/vgpreload_memcheck.so)
|
||
|
==19829== by 0x400CE43: _dl_map_object_deps (in /lib/ld-2.5.so)
|
||
|
==19829== by 0x4011E34: dl_open_worker (in /lib/ld-2.5.so)
|
||
|
==19829== by 0x400E025: _dl_catch_error (in /lib/ld-2.5.so)
|
||
|
==19829== by 0x4011928: _dl_open (in /lib/ld-2.5.so)
|
||
|
==19829== by 0x44AFF01: do_dlopen (in /lib/libc-2.5.so)
|
||
|
==19829== by 0x400E025: _dl_catch_error (in /lib/ld-2.5.so)
|
||
|
==19829== by 0x44B0000: dlerror_run (in /lib/libc-2.5.so)
|
||
|
==19829== by 0x44B012A: __libc_dlopen_mode (in /lib/libc-2.5.so)
|
||
|
==19829== by 0x448E2A8: init (in /lib/libc-2.5.so)
|
||
|
==19829== by 0x448E452: backtrace (in /lib/libc-2.5.so)
|
||
|
==19829== by 0x40E7E53: mrw::StackTrace::StackTrace() (in libmrw.so.3.2.0)
|
||
|
{
|
||
|
<insert a suppression name here>
|
||
|
Memcheck:Leak
|
||
|
fun:_vgrZU_libcZdsoZa_malloc
|
||
|
fun:_dl_map_object_deps
|
||
|
fun:dl_open_worker
|
||
|
fun:_dl_catch_error
|
||
|
fun:_dl_open
|
||
|
fun:do_dlopen
|
||
|
fun:_dl_catch_error
|
||
|
fun:dlerror_run
|
||
|
fun:__libc_dlopen_mode
|
||
|
fun:init
|
||
|
fun:backtrace
|
||
|
fun:_ZN3mrw10StackTraceC1Ev
|
||
|
}
|
||
|
|
||
|
*/
|
||
|
# endif
|
||
|
}
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
mrw::StackTrace::operator std::string() const throw(std::bad_exception) {
|
||
20 years ago
|
std::stringstream s;
|
||
20 years ago
|
bool first(true);
|
||
20 years ago
|
unsigned int fusz(0), adsz(0); //lisz(0), fisz(0)
|
||
20 years ago
|
std::list<CodePos> l;
|
||
|
for (AddressTrace::const_reverse_iterator it(_trace.rbegin());
|
||
|
it!=_trace.rend(); ++it, first=false) {
|
||
|
CodePos c(translate(*it));
|
||
20 years ago
|
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();
|
||
20 years ago
|
l.push_back(c);
|
||
21 years ago
|
}
|
||
20 years ago
|
for (std::list<CodePos>::iterator it(l.begin()); it!=l.end(); ++it)
|
||
20 years ago
|
s<<'['<<std::setw(adsz)<<it->address<<"] "
|
||
|
<<it->function<<std::setw(fusz-it->function.size()+1)<<' '
|
||
|
<<it->file<<':'<<it->line<<std::endl;
|
||
|
return s.str();
|
||
20 years ago
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const
|
||
|
throw(std::bad_exception) {
|
||
|
os<<(std::string)*this;
|
||
|
return *this;
|
||
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
mrw::StackTrace::CodePos mrw::StackTrace::translate(void* addr)
|
||
|
throw(std::bad_exception) {
|
||
19 years ago
|
#ifdef _MT
|
||
20 years ago
|
boost::recursive_mutex::scoped_lock lock(_mutex);
|
||
|
#endif
|
||
19 years ago
|
static const CodePos UNKNOWN(addr, "????", "????", 0);
|
||
20 years ago
|
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)
|
||
19 years ago
|
return UNKNOWN;
|
||
20 years ago
|
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))
|
||
19 years ago
|
return UNKNOWN;
|
||
20 years ago
|
return CodePos(addr, mrw::demangle(_bfd[it->second], function),
|
||
|
file?file:"????", line);
|
||
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs)
|
||
20 years ago
|
throw(std::bad_exception) {
|
||
19 years ago
|
#ifdef _MT
|
||
20 years ago
|
boost::recursive_mutex::scoped_lock lock(_mutex);
|
||
|
#endif
|
||
20 years ago
|
if (_dic.find(fname)!=_dic.end()) return true; // already loaded
|
||
20 years ago
|
try {
|
||
19 years ago
|
#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
|
||
20 years ago
|
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");
|
||
19 years ago
|
#endif
|
||
20 years ago
|
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
|
||
19 years ago
|
(const_cast<bfd*>(static_cast<const bfd*>(abfd))))<0) {
|
||
|
_error = "cannot map bfd symbols - 'bfd_get_file_flags' failed "
|
||
|
"for file: \""+fname+'"';
|
||
20 years ago
|
return false;
|
||
19 years ago
|
}
|
||
18 years ago
|
mrw::AutoPtrAry<asymbol*> syms(new asymbol*[memsz]);
|
||
20 years ago
|
if (bfd_canonicalize_symtab(const_cast<bfd*>(static_cast<const bfd*>(abfd)),
|
||
19 years ago
|
syms)<0) {
|
||
|
_error = "cannot map bfd symbols - 'bfd_canonicalize_symtab' failed"
|
||
|
"for file: \""+fname+'"';
|
||
20 years ago
|
return false;
|
||
19 years ago
|
}
|
||
20 years ago
|
_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;
|
||
19 years ago
|
} catch (const std::exception& x) {
|
||
|
_error = std::string("exception received: \"")+x.what()+"\", "
|
||
|
"while processing file: \""+fname+'"';
|
||
20 years ago
|
return false; // shared library for bfd not found or similar
|
||
|
}
|
||
20 years ago
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files)
|
||
|
throw(std::bad_exception) {
|
||
19 years ago
|
#ifdef _MT
|
||
20 years ago
|
boost::recursive_mutex::scoped_lock lock(_mutex);
|
||
|
#endif
|
||
20 years ago
|
bool success(true);
|
||
|
for (BinFiles::const_iterator it(files.begin());
|
||
|
it!=files.end(); ++it) {
|
||
|
if (!createSymtable(it->first, it->second)) success=false;
|
||
20 years ago
|
}
|
||
20 years ago
|
return success;
|
||
|
}
|
||
20 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
int mrw::StackTrace::bfdClose(bfd* abfd) throw() {
|
||
|
try {
|
||
19 years ago
|
#if NO_LTDL
|
||
|
static int(*bfd_close)(bfd*) = ::bfd_close;
|
||
|
#else
|
||
20 years ago
|
static DynamicLibrary lib("");
|
||
|
static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close");
|
||
19 years ago
|
#endif
|
||
20 years ago
|
return (*bfd_close)(abfd);
|
||
|
} catch (...) {
|
||
|
return -1; // dynamic library loading problems
|
||
|
}
|
||
|
}
|
||
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
mrw::StackTrace::BinFiles mrw::StackTrace::filename()
|
||
|
throw(std::bad_exception) {
|
||
|
mrw::StackTrace::BinFiles res;
|
||
20 years ago
|
# if defined(__solaris__)
|
||
20 years ago
|
{
|
||
20 years ago
|
std::string s;
|
||
|
s<<"/proc/"<<getpid();
|
||
20 years ago
|
AutoFile fd(open(s.c_str(), O_RDONLY));
|
||
20 years ago
|
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);
|
||
|
}
|
||
20 years ago
|
# elif defined(__linux__)
|
||
20 years ago
|
{
|
||
20 years ago
|
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;
|
||
20 years ago
|
while (getline(is, s)) try {
|
||
20 years ago
|
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;
|
||
19 years ago
|
// 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);
|
||
20 years ago
|
} catch (...) {} // ignore non matching lines
|
||
20 years ago
|
return res;
|
||
|
}
|
||
20 years ago
|
# else
|
||
20 years ago
|
{
|
||
21 years ago
|
# 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!"
|
||
20 years ago
|
return res; // empty
|
||
21 years ago
|
}
|
||
20 years ago
|
# endif
|
||
20 years ago
|
}
|
||
21 years ago
|
|
||
20 years ago
|
//----------------------------------------------------------------------------
|
||
|
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));
|
||
20 years ago
|
#ifdef bfd_get_section_size
|
||
20 years ago
|
bfd_size_type sz(bfd_get_section_size(section));
|
||
20 years ago
|
#elif defined bfd_get_section_size_before_reloc
|
||
20 years ago
|
bfd_size_type sz(bfd_get_section_size_before_reloc(section));
|
||
|
#else
|
||
|
bfd_size_type sz(section->size);
|
||
|
#endif
|
||
20 years ago
|
_dic[fileoffset.first][vma] = Translator::mapped_type(vma+sz, section);
|
||
|
_addrs[vma] = fileoffset.first;
|
||
21 years ago
|
}
|
||
20 years ago
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
std::map<std::string, mrw::StackTrace::Translator> mrw::StackTrace::_dic;
|
||
|
std::map<mrw::StackTrace::Translator::key_type, std::string>
|
||
|
mrw::StackTrace::_addrs;
|
||
20 years ago
|
std::map<std::string, mrw::StackTrace::AutoBfd> mrw::StackTrace::_bfd;
|
||
18 years ago
|
std::map<std::string, mrw::AutoPtrAry<asymbol*> > mrw::StackTrace::_syms;
|
||
19 years ago
|
#ifdef _MT
|
||
20 years ago
|
boost::recursive_mutex mrw::StackTrace::_mutex;
|
||
|
#endif
|
||
19 years ago
|
std::string mrw::StackTrace::_error;
|