/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.7 2004 / 10 / 11 15 : 58 : 51 marc
First version with working support for shared libraries !
Revision 1.6 2004 / 10 / 07 16 : 59 : 12 marc
new method createSymtable that takes a list of arguments
- > untested !
Revision 1.5 2004 / 10 / 07 09 : 32 : 45 marc
correction in parameter ( const & )
Revision 1.4 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
*/
# ifndef __MRW_STACKTRACE_HPP__
# define __MRW_STACKTRACE_HPP__
# include <mrw/auto.hpp>
# include <vector>
# include <list>
# include <map>
# include <string>
# include <memory>
# include <sys/mman.h>
# include <bfd.h>
# ifdef __REENTRANT
# warning "mrw::StackTrace is not thread safe yet!"
# warning "It should work, but is at least untested..."
# endif
namespace mrw {
/** @defgroup StackTrace Collect and Format a Stack Trace
Somewhere in a program , there is a fatal error , e . g . an
unexpected exception is thrown . How is it possible to debug the
problem in such a case ? Sometimes you can start a debugger and
trace the execution of your program . But what if it occurs only
once a week , or if you cannot set a breakpoint , because you
don ' t know where the problem is located , or because only the
1000 th run of a method causes a problem , or what if the problem
occurs only at your customers installation ?
One way to solve these problems is to do logging , or even
function tracing , so you can narrow down the lines of code ,
where the problem occurs . But sometimes this is not enough ,
especially with exceptions . One of the worst things with
exceptions is , you can catch an exception somewhere , but you
don ' t know where it was thrown . Here it is very handy , to be
able to write a stacktrace to a logging device .
For logging , I recommend log4cxx on page :
- http : //logging.apache.org/log4cxx
These classes are for collecting a stack trace and later for
formatting with source code file name , line number and the
method name .
For collecting the stack trace ( the addresses ) :
- either the GNU gcc compiler is required
- or the GNU glibc library function @ c backtrace
For extracting information from an address , the ELF library is required .
@ note For all features and full operation , this class requires :
- either a GNU glibc bases system ( LINUX ) , or the GNU gcc compiler
- a system with ELF binaries ( LINUX , Solaris , . . . )
- debug information , compile option @ c - g
- it must be linked with @ c - libery and @ c - lbfd
*/
//@{
/** @brief store and print a stack trace of the actual position in code
@ pre # include < mrw / stacktrace . hpp >
In the constructor , a stack trace is stored , but not yet
evaluated . Therefore storing a stack trace is relatively
fast . The evaluation is done when the stack trace is printed on
a stream or converted to a string . " Evaluation " means , that the
addresses are mapped to the correspoding symbols , the method
names , sorce file names and line numbers are evaluated .
@ note Method StackTrace : : createSymtable must be called exactely
once , before evaluating the first stack trace . Best place is the
first line of the @ c main function .
@ note This class requires libbfd an libiberty . Debug information
is required for compiling . You nee the compile option @ c - g , or
even better @ c - ggdb3 . To link , you need @ c - lmrw , @ c - lbfd and
@ c - liberty .
@ note The stack trace is known to work perfectly on Linux and
Solaris both with GNU gcc compiler . But it should work with the
GNU compiler on all systems , or wherever there is a glibc
library .
@ note Symbol evaluation requires the ELF library and an ELF system .
< h3 > Technology < / h3 >
On GNU glibc based systems ( Linux ) , the stack trace is collected
with GNU glibc ' s function @ c backtrace ( ) . On other systems
( Solaris ) it is collected using the GNU gcc ' s internal function @ c
__builtin_return_address ( ) . With both functions , at most 50 steps
back are collected .
The evaluation is not done with the glibc library function @ c
backtrace_symbols ( ) , because this function is unable to print
the source file name and line number information . Instead of
this , the executable binary is loaded into the memory and
evaluated using the bdf library functions . For this the stack
tracer needs to know how to find out which executable is
running . It is possible to get this information automatically on
Linux and Solaris . On other systems , I don ' t have this
information , but you can either tell me , and I integrate support
for your system ( when I have time to do it ) , or provide the
executable file name as an argument to @ c
mrw : : StackTrace : : createSymtable ( ) .
< h3 > Draw Backs < / h3 >
Unfortunately it is not possible to extract the source file name
and line number information if the executable was not compiled
with debug option @ c - g . But what ' s worse , it is not possible to
ger symbolic information from libraries linked to the
executable . Perhaps it could be possible , if I ' d add a
possibility to read and evaluate these libraries , but that ' s for
a future release . ( Now , 10 / 08 / 2004 , I am working on it )
@ todo Add support to read debugging information from libraries
that are linked to the executable . ( soon )
@ todo Add support for alternative symbol evaluation using @ c
backtrace_symbols .
*/
class StackTrace {
public :
//............................................................... typedefs
/// container for the adresses
typedef std : : vector < void * > AddressTrace ;
/// binary file with offset (for shared libraries, 0 for executables)
typedef std : : list < std : : pair < std : : string , void * > > BinFiles ;
/// structure to store all evaluated information
struct CodePos {
CodePos ( void * a , std : : string fn , std : : string fi , unsigned int l )
throw ( std : : bad_exception ) :
address ( a ) , function ( fn ) , file ( fi ) , line ( l ) {
}
void * address ; ///< the address pointer
std : : string function ; ///< function/method name
std : : string file ; ///< code file name
unsigned int line ; ///< code line number
} ;
//................................................................ methods
/// the constructor stores the actual stack trace
StackTrace ( ) throw ( std : : bad_exception ) ;
/// evaluates the symbol table and returns the formatted stack trace
operator std : : string ( ) const throw ( std : : bad_exception ) ;
/** @return list of raw stack addresses */
operator const AddressTrace & ( ) const throw ( std : : bad_exception ) {
return _trace ;
}
/// evaluate the stack trace and print it to a stream
const StackTrace & print ( std : : ostream & os ) const throw ( std : : bad_exception ) ;
/// evaluates and returns all information from a raw address
static CodePos translate ( void * addr )
throw ( std : : bad_exception ) ;
/** @brief read the symbol table from the executable file
@ param fname The file name of the executable . On Linux
and Solaris , this can be evaluated automatically , so the
parameter is optional .
@ param offs Offset of the address space . It is 0 for
executables , but must be given for shared libraries .
@ return @ c true in case of success . If @ c false is returned ,
the symbol table was not read and the evaluation cannot be
done . Printing then only prints the raw addresses , without
file , line nmber information and method names .
@ note This method must be executed once before a stack trace
is printed the very first time . For storing a stack trace
( that means for the creation of a mrw : : StackTrace object ) a
call to this method is not yet needed .
@ note Call only one of both createSymtable methods !
@ note If this method is called more than once , the symbols
are created only the first time , so you don ' t loose too much
time .
*/
static bool createSymtable ( const std : : string & fname = " " , void * offs = 0 )
throw ( std : : bad_exception ) ;
/** @brief read the symbol table from a list of an executable file and
shared libraries
@ param files The list of file names that must contain the
executable plus all shared libraries that should be included
in stack traces . Every file name has an offset address as
second parameter . This offset must be given for shared
libraries and can be retrieved using the program @ c ldd .
@ return @ c true in case of success . If @ c false is returned ,
the symbol table was not completely read and the evaluation
cannot be done . Printing then only prints the raw addresses ,
without file , line nmber information and method names .
@ note This method must be executed once before a stack trace
is printed the very first time . For storing a stack trace
( that means for the creation of a mrw : : StackTrace object ) a
call to this method is not yet needed .
@ note Call only one of both createSymtable methods !
@ note If this method is called more than once , the symbols
are created only the first time , so you don ' t loose too much
time .
*/
static bool createSymtable ( const BinFiles & files ) throw ( std : : bad_exception ) ;
private :
//............................................................... typedefs
typedef std : : map < bfd_vma , std : : pair < bfd_vma , asection * > >
Translator ;
//.............................................................. variables
AddressTrace _trace ;
static std : : map < std : : string , Translator > _dic ;
static std : : map < Translator : : key_type , std : : string > _addrs ;
static std : : map < std : : string , AutoBfd > _bfd ;
static std : : map < std : : string , mrw : : AutoPtr < asymbol * > > _syms ;
//................................................................ methods
static BinFiles filename ( ) throw ( std : : bad_exception ) ;
static void buildSectionMap ( bfd * , asection * , void * )
throw ( std : : bad_exception ) ;
} ;
//@}
}
/** @addtogroup StackTrace */
//@{
/// evaluate a stack trace and shift it on a stream
inline std : : ostream & operator < < ( std : : ostream & os , const mrw : : StackTrace & st )
throw ( std : : bad_exception ) {
return os < < ( std : : string ) st ;
}
//@}
# endif