/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; 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>
# 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) \
& & 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 ( ) 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 ] ) ;
}
// crash ...?
// # elif 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
// }
# else
# warning "You need GNU gcc or GNU glibc to be able to use mrw::StackTrace"
# endif
}
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
}
//----------------------------------------------------------------------------
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 : : 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 )
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 ;
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
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 : : AutoPtrAry < asymbol * > > mrw : : StackTrace : : _syms ;
# ifdef _MT
boost : : recursive_mutex mrw : : StackTrace : : _mutex ;
# endif
std : : string mrw : : StackTrace : : _error ;