// g++ -Wall -D__SOLARIS__ -g -I /home/public/freeware/include -L /home/public/freeware/lib -I . stacktrace.cxx -lbfd -liberty
# ifndef __MRW_STACKTRACE_HPP__
# define __MRW_STACKTRACE_HPP__
# include <mrw/auto.hpp>
# include <vector>
# 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
@ subsection sttech Technology
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 ( ) .
@ subsection stdrawbacks Draw Backs
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 .
@ todo Add support to read debugging information from libraries
that are linked to the executable .
@ todo Add support for alternative symbol evaluation using @ c
backtrace_symbols .
*/
//@{
/** @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 .
*/
class StackTrace {
public :
//............................................................... typedefs
typedef std : : vector < void * > AddressTrace ; ///< container for the adresses
/// 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 std : : string The file name of the executable . On Linux
and Solaris , this can be evaluated automatically , so the
parameter is optional .
@ 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 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 ( std : : string = " " ) throw ( std : : bad_exception ) ;
private :
//............................................................... typedefs
typedef std : : map < bfd_vma , std : : pair < bfd_vma , asection * > >
Translator ;
//.............................................................. variables
AddressTrace _trace ;
static std : : auto_ptr < Translator > _dic ;
static std : : vector < Translator : : key_type > _addrs ;
static AutoBfd _bfd ;
static std : : auto_ptr < asymbol * > _syms ;
//................................................................ methods
static std : : string filename ( ) throw ( std : : bad_exception ) ;
static void buildSectionMap ( bfd * , asection * , void * )
throw ( std : : bad_exception ) ;
} ;
/// evaluate a stack trace and shift it on a stream
inline std : : ostream & operator < < ( std : : ostream & os , const StackTrace & st )
throw ( std : : bad_exception ) {
return os < < ( std : : string ) st ;
}
//@}
}
# endif