/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
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 <sstream>
# 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 <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 {
//----------------------------------------------------------------------------
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 ;
Auto < char * > : : Free res ( cplus_demangle ( p , DMGL_ANSI | DMGL_PARAMS ) ) ;
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 ;
}
//----------------------------------------------------------------------------
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
}
//----------------------------------------------------------------------------
StackTrace : : operator std : : string ( ) const throw ( std : : bad_exception ) {
static const double LN10 ( log ( 10 ) ) ;
std : : stringstream s ;
bool first ( true ) ;
unsigned int 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 ( log ( c . line + 1 ) / LN10 > lisz ) lisz = ( unsigned int ) ( log ( c . line + 1 ) / LN10 ) ;
if ( c . file . size ( ) > fisz ) fisz = c . file . size ( ) ;
l . push_back ( c ) ;
}
for ( std : : list < CodePos > : : iterator it ( l . begin ( ) ) ; it ! = l . end ( ) ; + + it )
s < < " [ " < < it - > address < < " ] "
< < it - > file < < ' : ' < < it - > line
< < std : : setw ( fisz + lisz - it - > file . size ( ) -
( unsigned int ) ( log ( it - > line + 1 ) / LN10 ) - 1 )
< < " " < < it - > function < < std : : endl ;
return s . str ( ) ;
}
//----------------------------------------------------------------------------
const StackTrace & StackTrace : : print ( std : : ostream & os ) const
throw ( std : : bad_exception ) {
os < < ( std : : string ) * this ;
return * this ;
}
//----------------------------------------------------------------------------
StackTrace : : CodePos StackTrace : : translate ( void * addr )
throw ( std : : bad_exception ) {
assert ( sizeof ( bfd_vma ) > = sizeof ( void * ) ) ;
bfd_vma vma_addr ( reinterpret_cast < bfd_vma > ( addr ) ) ;
if ( ! _dic . get ( ) ) return CodePos ( addr , " ???? " , " ???? " , 0 ) ;
std : : vector < Translator : : key_type > : : iterator
it ( std : : lower_bound ( _addrs . begin ( ) , _addrs . end ( ) , vma_addr ) ) ;
if ( it - - = = _addrs . begin ( ) | | * it > vma_addr | |
( * _dic ) [ * it ] . 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 ) ) ,
( * _dic ) [ * it ] . second , _syms . get ( ) ,
vma_addr - * it , & file , & function , & line ) )
return CodePos ( addr , " ???? " , " ???? " , 0 ) ;
return CodePos ( addr , mrw : : demangle ( _bfd , function ) , file ? file : " ???? " , line ) ;
}
//----------------------------------------------------------------------------
bool StackTrace : : createSymtable ( const std : : string & fname )
throw ( std : : bad_exception ) {
if ( _dic . get ( ) ) return true ;
AutoBfd abfd ( bfd_openr ( ( fname ! = " " ? fname : filename ( ) ) . 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 ;
std : : auto_ptr < asymbol * > syms ( new asymbol * [ memsz ] ) ;
if ( bfd_canonicalize_symtab ( const_cast < bfd * > ( static_cast < const bfd * > ( abfd ) ) ,
syms . get ( ) ) < 0 )
return false ;
_bfd = abfd ;
_syms = syms ;
_dic = std : : auto_ptr < Translator > ( new Translator ( ) ) ;
bfd_map_over_sections ( _bfd , buildSectionMap , 0 ) ;
std : : sort ( _addrs . begin ( ) , _addrs . end ( ) ) ;
return true ;
}
//----------------------------------------------------------------------------
bool StackTrace : : createSymtable ( const std : : list < std : : string > & fnames )
throw ( std : : bad_exception ) {
if ( _dic . get ( ) ) return true ;
for ( std : : list < std : : string > : : const_iterator it ( fnames . begin ( ) ) ;
it ! = fnames . end ( ) ; + + it ) {
AutoBfd abfd ( bfd_openr ( it - > 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 ;
std : : auto_ptr < asymbol * > syms ( new asymbol * [ memsz ] ) ;
if ( bfd_canonicalize_symtab ( const_cast < bfd * > ( static_cast < const bfd * > ( abfd ) ) ,
syms . get ( ) ) < 0 )
return false ;
_bfd = abfd ;
_syms = syms ;
if ( ! _dic . get ( ) ) _dic = std : : auto_ptr < Translator > ( new Translator ( ) ) ;
bfd_map_over_sections ( _bfd , buildSectionMap , 0 ) ;
}
std : : sort ( _addrs . begin ( ) , _addrs . end ( ) ) ;
return true ;
}
//----------------------------------------------------------------------------
std : : string StackTrace : : filename ( ) throw ( std : : bad_exception ) {
std : : stringstream s ;
s < < " /proc/ " < < getpid ( ) ;
# if defined(__solaris__)
{
std : : string res ;
AutoFile fd ( open ( s . str ( ) . c_str ( ) , O_RDONLY ) ) ;
prpsinfo_t status ;
if ( fd = = - 1 | | ioctl ( fd , PIOCPSINFO , & status ) = = - 1 ) return res ;
res = status . pr_psargs ;
res = res . substr ( 0 , res . find ( ' ' ) ) ;
return res ;
}
# elif defined(__linux__)
{
s < < " /exe " ;
return s . str ( ) ;
}
# 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
}
//----------------------------------------------------------------------------
void StackTrace : : buildSectionMap ( bfd * abfd , asection * section , void * )
throw ( std : : bad_exception ) {
if ( ! abfd | | ! section ) return ;
if ( ! ( bfd_get_section_flags ( abfd , section ) & SEC_ALLOC ) ) return ;
bfd_vma vma ( bfd_get_section_vma ( abfd , section ) ) ;
bfd_size_type sz ( bfd_get_section_size_before_reloc ( section ) ) ;
( * _dic ) [ vma ] = Translator : : mapped_type ( vma + sz , section ) ;
_addrs . push_back ( vma ) ;
}
//----------------------------------------------------------------------------
std : : auto_ptr < StackTrace : : Translator > StackTrace : : _dic ;
std : : vector < StackTrace : : Translator : : key_type > StackTrace : : _addrs ;
AutoBfd StackTrace : : _bfd ;
std : : auto_ptr < asymbol * > StackTrace : : _syms ;
}