/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
1 2 3 4 5 6 7 8
5678901234567890123456789012345678901234567890123456789012345678901234567890
*/
# ifndef __GNUG__
# error GNU C++ Compiler is required for automatical function trace
# endif
# include <mrw/string.hxx>
# include <sstream>
# include <iomanip>
# include <mrw/stacktrace.hxx>
# include <log4cxx/logger.h>
# if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
& & defined ( _REENTRANT ) & & ! defined ( _MT )
# define _MT
# endif
// these are special built in functions of GNU Compiler Collection
extern " C " void __cyg_profile_func_enter ( void * , void * ) __attribute__ ( ( no_instrument_function ) ) ;
extern " C " void __cyg_profile_func_exit ( void * , void * ) __attribute__ ( ( no_instrument_function ) ) ;
// support for log4cxx 0.9.7 and for new CVS version
# ifndef LOG4CXX_LOCATION
# define MRW_LOG4CXX_LOCATION pos.file.c_str(), pos.line
# define MRW_LEVEL_DEBUG ::log4cxx::Level::DEBUG
# define MRW_LEVEL_INFO ::log4cxx::Level::INFO
# define MRW_LEVEL_WARN ::log4cxx::Level::WARN
# define MRW_LEVEL_ERROR ::log4cxx::Level::ERROR
# define MRW_LEVEL_FATAL ::log4cxx::Level::FATAL
# else
# define LOG4CXX_CVS
# define MRW_LOG4CXX_LOCATION \
: : log4cxx : : spi : : LocationInfo ( pos . file . c_str ( ) , pos . function . c_str ( ) , pos . line )
# define MRW_LEVEL_DEBUG ::log4cxx::Level::getDebug()
# define MRW_LEVEL_INFO ::log4cxx::Level::getInfo()
# define MRW_LEVEL_WARN ::log4cxx::Level::getWarn()
# define MRW_LEVEL_ERROR ::log4cxx::Level::getError()
# define MRW_LEVEL_FATAL ::log4cxx::Level::getFatal()
# endif
namespace mrw {
struct ThreadInfo {
int level ;
bool recurse ;
} ;
# ifdef _MT
static __thread ThreadInfo info = { 0 , false } ;
# else
static ThreadInfo info = { 0 , false } ;
# endif
static bool mainPassed ( false ) ;
class Lock {
public :
Lock ( ) __attribute__ ( ( no_instrument_function ) ) ;
~ Lock ( ) __attribute__ ( ( no_instrument_function ) ) ;
} ;
// workaround doxygen problem:
// - the C++ compiler compiles the following code
// - doxygen ignores it
# ifndef LET_DOXYGEN_IGNORE_THIS // doxygen can't match with __attribute__ above
Lock : : Lock ( ) {
info . recurse = true ;
}
Lock : : ~ Lock ( ) {
info . recurse = false ;
}
# endif
}
extern " C " int main ( int , char * * ) ;
/** @addtogroup FunctionTrace
@ note There is also a fully automagic function trace , similar to
@ ref AutoTrace . It is described in @ ref AutoFunctionTrace
and requires the GNU Compiler Collection gcc , because it
makes use of a proprietary compiler feature . */
//@{
/** @defgroup AutoFunctionTrace Automatic Function Trace for GNU g++
If you compile your program with GNU g + + & nbsp ; / gcc , and you want
to add function tracing without changing a single line of your
code , here ' s the solution :
If you link to the library @ c libmrwautofunctiontracelog4cxx using
a linker option such as : @ c - lmrwautofunctiontracelog4cxx and you
must enable the GNU Compiler Collection specific function trace
feature with compile and link option @ c - finstrument - functions
then you get an automatical function trace , that traces to level
@ c DEBUG using the log4cxx library . You don ' t need to change a single
line in your code !
This feature depends on :
- the GNU C + + compiler gcc
- @ ref mrw : : StackTrace
- log4cxx http : //logging.apache.org/log4cxx
- optional : boost for thread support http : //boost.org
The logger hiararchy name is : @ c
" mrw.fn.<full-method-name> " where @ c
< full - method - name > is the full name of the method , including
namespace and class name , seperated not by double colon @ c : : but
by single dots @ c . as it is common use in log4cxx . This way , you
can enable or disable the function trace per namespace , per class
or even per method .
The function trace does not start , before the program @ c main is
reached ! Only the calls within the @ c main are traced , but not during
the initialisation and destruction of static members . This is
necessary , because otherwise access the incompletely initialized
MRW - C + + library itself may cause a crash .
A good default configuration is given in @ ref AutoInitLog4cxx .
Depending on your log4cxx configuration , a function trace may look
e . g . like this ( very simple configuration with very few additional
information ) :
@ verbatim
mrw . fn . anotherFunction \ anotherFunction ( )
mrw . fn . HalloWelt . fn1 \ HalloWelt : : fn1 ( )
mrw . fn . HalloWelt . fn \ HalloWelt : : fn ( )
mrw . fn . HalloWelt . A . method \ HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method / HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method \ HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method / HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . fn / HalloWelt : : fn ( )
mrw . fn . HalloWelt . fn \ HalloWelt : : fn ( )
mrw . fn . HalloWelt . A . method \ HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method / HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method \ HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . A . method / HalloWelt : : A : : method ( )
mrw . fn . HalloWelt . fn / HalloWelt : : fn ( )
mrw . fn . HalloWelt . fn1 / HalloWelt : : fn1 ( )
mrw . fn . anotherFunction / anotherFunction ( )
@ endverbatim
@ note The configurator is not installed automatically . If you
want to trace e . g . on the console , you have to call @ c
log4cxx : : BasicConfigurator : : configure ( ) ; as first statement
in your @ c main ( ) .
@ see @ ref AutoInitLog4cxx if you also want to automatically
configure @ c log4cxx .
@ section AutoFunctionTraceThread Multithreading
Multithreading is also supported , then you need to link to the
thread safe compilat of the library @ c
libmrwautofunctiontracelog4cxx - mt with option @ c
- libmrwautofunctiontracelog4cxx - mt and you need the option @ c
- pthread in addition to @ c - finstrument - functions which is still
required .
Thread support requires the boost thread library . See
http : //boost.org for details.
*/
//@{
extern " C " void __cyg_profile_func_enter ( void * this_fn , void * ) {
if ( ! mrw : : mainPassed )
if ( this_fn = = ( void * ) & : : main ) // not ANSI C++ conform...
mrw : : mainPassed = true ;
else
return ;
try {
if ( mrw : : info . recurse ) return ;
mrw : : Lock lock ;
{
static bool init ( mrw : : StackTrace : : createSymtable ( ) ) ;
if ( ! init ) return ;
const mrw : : StackTrace : : CodePos & pos ( mrw : : StackTrace : : translate ( this_fn ) ) ;
std : : string hierarchy ( pos . function ) ;
for ( std : : string : : size_type p ( 0 ) ;
( p = hierarchy . find ( " :: " , p ) ) ! = std : : string : : npos ;
hierarchy . replace ( p , 2 , " . " ) ) ;
hierarchy . erase ( hierarchy . rfind ( ' ( ' ) ) ;
std : : string : : size_type p
( hierarchy . rfind ( ' ' , std : : min ( hierarchy . find ( ' < ' ) ,
hierarchy . find ( " operator " ) ) ) ) ;
if ( p ! = std : : string : : npos ) hierarchy . erase ( 0 , p + 1 ) ;
// hierarchy.erase
// (hierarchy.find_first_not_of
// ("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890_."));
if ( hierarchy = = " " ) hierarchy = " global " ;
std : : string name ( " mrw.fn. " + hierarchy ) ;
log4cxx : : Logger * logger
( log4cxx : : Logger : : getLogger ( log4cxx : : LogString ( name . begin ( ) , name . end ( ) ) ) ) ;
if ( logger - > isDebugEnabled ( ) ) {
std : : basic_ostringstream < log4cxx : : LogString : : value_type > ss ;
ss < < std : : setw ( 2 + mrw : : info . level + + )
< < std : : setfill ( log4cxx : : LogString : : value_type ( ' ' ) )
< < " \\ " < < log4cxx : : LogString ( pos . function . begin ( ) , pos . function . end ( ) ) ;
logger - > forcedLog ( MRW_LEVEL_DEBUG , ss . str ( ) ,
MRW_LOG4CXX_LOCATION ) ;
}
}
} catch ( . . . ) { }
}
extern " C " void __cyg_profile_func_exit ( void * this_fn , void * ) {
if ( ! mrw : : mainPassed )
return ;
else
if ( this_fn = = ( void * ) & : : main ) { // not ANSI C++ conform...
mrw : : mainPassed = false ;
return ;
}
try {
if ( mrw : : info . recurse | | mrw : : info . level = = 0 ) return ;
mrw : : Lock lock ;
{
const mrw : : StackTrace : : CodePos & pos ( mrw : : StackTrace : : translate ( this_fn ) ) ;
std : : string hierarchy ( pos . function ) ;
for ( std : : string : : size_type p ( 0 ) ;
( p = hierarchy . find ( " :: " , p ) ) ! = std : : string : : npos ;
hierarchy . replace ( p , 2 , " . " ) ) ;
hierarchy . erase ( hierarchy . rfind ( ' ( ' ) ) ;
std : : string : : size_type p
( hierarchy . rfind ( ' ' , std : : min ( hierarchy . find ( ' < ' ) ,
hierarchy . find ( " operator " ) ) ) ) ;
if ( p ! = std : : string : : npos ) hierarchy . erase ( 0 , p + 1 ) ;
// hierarchy.erase
// (hierarchy.find_first_not_of
// ("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890_."));
if ( hierarchy = = " " ) hierarchy = " global " ;
std : : string name ( " mrw.fn. " + hierarchy ) ;
log4cxx : : Logger * logger
( log4cxx : : Logger : : getLogger ( log4cxx : : LogString ( name . begin ( ) , name . end ( ) ) ) ) ;
if ( logger - > isDebugEnabled ( ) ) {
std : : basic_ostringstream < log4cxx : : LogString : : value_type > ss ;
ss < < std : : setw ( 2 + - - mrw : : info . level )
< < std : : setfill ( log4cxx : : LogString : : value_type ( ' ' ) )
< < " / " < < log4cxx : : LogString ( pos . function . begin ( ) , pos . function . end ( ) ) ;
logger - > forcedLog ( MRW_LEVEL_DEBUG , ss . str ( ) ,
MRW_LOG4CXX_LOCATION ) ;
}
}
} catch ( . . . ) { }
}
//@}
//@}