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.
 
 
 
 
 

395 lines
14 KiB

/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
*/
#include <mrw/stacktrace.hxx>
#include <mrw/exec.hxx>
#include <mrw/string.hxx>
#include <mrw/list.hxx>
#include <mrw/stdext.hxx>
#ifndef NO_LTDL
# include <mrw/dynamiclibrary.hxx>
#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>
#ifdef 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) \
&& defined(_REENTRANT) && !defined(_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() {
// 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]);
}
// crash ...?
# elif defined(__GNUG__)
{
# define __MRW_PUSH(i) \
(__builtin_frame_address(i)!=0 && __builtin_return_address(i)!=0? \
(_trace.push_back(__builtin_return_address(i)), true) : false)
__MRW_PUSH(0) && __MRW_PUSH(1) && __MRW_PUSH(2) && __MRW_PUSH(3)
&& __MRW_PUSH(4) && __MRW_PUSH(5) && __MRW_PUSH(6) && __MRW_PUSH(7)
&& __MRW_PUSH(8) && __MRW_PUSH(9) && __MRW_PUSH(10) && __MRW_PUSH(11)
&& __MRW_PUSH(12) && __MRW_PUSH(13) && __MRW_PUSH(14) && __MRW_PUSH(15)
&& __MRW_PUSH(16) && __MRW_PUSH(17) && __MRW_PUSH(18) && __MRW_PUSH(19)
&& __MRW_PUSH(20) && __MRW_PUSH(21) && __MRW_PUSH(22) && __MRW_PUSH(23)
&& __MRW_PUSH(24) && __MRW_PUSH(25) && __MRW_PUSH(26) && __MRW_PUSH(27)
&& __MRW_PUSH(28) && __MRW_PUSH(29) && __MRW_PUSH(30) && __MRW_PUSH(31)
&& __MRW_PUSH(32) && __MRW_PUSH(33) && __MRW_PUSH(34) && __MRW_PUSH(35)
&& __MRW_PUSH(36) && __MRW_PUSH(37) && __MRW_PUSH(38) && __MRW_PUSH(39)
&& __MRW_PUSH(40) && __MRW_PUSH(41) && __MRW_PUSH(42) && __MRW_PUSH(43)
&& __MRW_PUSH(44) && __MRW_PUSH(45) && __MRW_PUSH(46) && __MRW_PUSH(47)
&& __MRW_PUSH(48) && __MRW_PUSH(49);
# undef __MRW_PUSH
}
# else
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
# endif
}
mrw::StackTrace::~StackTrace() noexcept {
// 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
}
//----------------------------------------------------------------------------
mrw::StackTrace::operator std::string() const {
std::stringstream s;
std::string::size_type fusz(0), adsz(0); //lisz(0), fisz(0)
std::list<CodePos> l;
for (AddressTrace::const_reverse_iterator it(_trace.rbegin());
it!=_trace.rend(); ++it) {
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((int)adsz)<<it->address<<"] "
<<it->function<<std::setw(int(fusz-it->function.size()+1))<<' '
<<it->file<<':'<<it->line<<std::endl;
return s.str();
}
//----------------------------------------------------------------------------
const mrw::StackTrace& mrw::StackTrace::print(std::ostream& os) const
{
os<<(std::string)*this;
return *this;
}
//----------------------------------------------------------------------------
mrw::StackTrace::CodePos mrw::StackTrace::translate(void* addr)
{
#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)
{
#ifdef _MT
boost::recursive_mutex::scoped_lock lock(_mutex);
#endif
if (_dic.find(fname)!=_dic.end()) return true; // already loaded
try {
#ifdef 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::AutoPtrAry<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)
{
#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) noexcept {
try {
#ifdef 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()
{
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;
res<<BinFiles::value_type(s.substr(0, s.find(' ')), (void*)0);
}
# elif defined(__linux__)
{
res<<BinFiles::value_type("/proc/self/exe", (void*)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;
if (mrw::to<int>(size)>0) {
s>>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
}
# 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
return res;
}
//----------------------------------------------------------------------------
void mrw::StackTrace::buildSectionMap(bfd* abfd, asection* section,
void* fileoffs)
{
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::AutoPtrAry<asymbol*> > mrw::StackTrace::_syms;
#ifdef _MT
boost::recursive_mutex mrw::StackTrace::_mutex;
#endif
std::string mrw::StackTrace::_error;