/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
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 ) ) ;
# elsifdef 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 ;