/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.13 2005 / 02 / 18 15 : 48 : 56 marc
Dynamic loading of libbfd , no more dependency on specific libbfd version !
Revision 1.12 2005 / 01 / 28 12 : 13 : 11 marc
interference between group name StackTrace and class name StackTrace
Revision 1.11 2005 / 01 / 28 12 : 13 : 11 marc
interference between group name StackTrace and class name StackTrace
Revision 1.10 2004 / 11 / 25 18 : 30 : 20 marc
bug fixed ( put solution in the documentation )
Revision 1.9 2004 / 10 / 13 10 : 47 : 15 marc
no more need for ldd in StackTrace , read from / proc / self / maps
Revision 1.8 2004 / 10 / 11 16 : 49 : 32 marc
Better comment for new shared library feature
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 grpStackTrace 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
*/
//@{
/** @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 Debug information is required for compiling . You nee the
compile option @ c - g , or even better @ c - ggdb3 .
@ 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 .
@ note If file and line is wrong , but the function name is
correct , try to build without optimizations . For GNU compiler ,
this means no option @ c - O or @ c - O0 and enable the debug
information with @ c - g and don ' t inline functions with compiler
option @ c - fno - inline . To build this project , you may enter :
@ code
CXXFLAGS = " -g -fno-inline " . / configure & & make clean check
@ endcode
@ 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 or a
shared library
On Solaris and Linux , the executable is automatically
detected through the @ c / proc file system . Only on Linux the
shared libraries are detected through @ c / proc / self / maps . So
you may leave the parameter empty on these systems .
@ param fname The file name of the executable or a shared
library . 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 . Use @ c
ldd on the executable to find out the offset .
@ 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 createSymtable must be executed at least 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 In general , call only one of both createSymtable
methods ! But instead of callin the other method once , you
may also call this one several times .
@ note If this method is called more than once for the same
file , 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 In general , call only one of both createSymtable methods !
@ note This method calls the other one for all files in
parameter @ c files .
*/
static bool createSymtable ( const BinFiles & files )
throw ( std : : bad_exception ) ;
private :
//............................................................... typedefs
typedef std : : map < bfd_vma , std : : pair < bfd_vma , asection * > > Translator ;
static int bfdClose ( bfd * ) throw ( ) ;
typedef mrw : : AutoResource < bfd * , int ( * ) ( bfd * ) , StackTrace : : bfdClose , int >
AutoBfd ;
//.............................................................. 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 grpStackTrace */
//@{
/// 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