# 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 " {
# include <demangle.h>
}
# 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 ;
mrw : : AutoFree < char > res ( cplus_demangle ( p , DMGL_ANSI | DMGL_PARAMS ) ) ;
if ( res ) {
/* Now put back any stripped dots. */
if ( p = = name ) return ( char * ) res ;
std : : string add_dots ( ' . ' , p - name ) ;
return add_dots + = ( 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 ( _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 ( 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 ) ;
AutoFree < char * > m ( 0 ) ;
if ( ! abfd | | bfd_check_format ( abfd , bfd_archive ) | |
! bfd_check_format_matches ( abfd , bfd_object , m ) | |
! ( bfd_get_file_flags ( abfd ) & HAS_SYMS ) | |
( memsz = bfd_get_symtab_upper_bound ( abfd ) ) < 0 ) return false ;
std : : auto_ptr < asymbol * > syms ( new asymbol * [ memsz ] ) ;
if ( bfd_canonicalize_symtab ( 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 ;
}
//----------------------------------------------------------------------------
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 ;
}