/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.2 2005 / 04 / 07 20 : 42 : 38 marc
renamed loggerhierarchy from mrw . gccfunctiontrace to mrw . fn
Revision 1.1 2005 / 03 / 11 21 : 07 : 54 marc
initial version
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.hpp>
# include <sstream>
# include <iomanip>
# include <mrw/stacktrace.hpp>
# include <log4cxx/logger.h>
// 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 ) ) ;
namespace mrw {
struct ThreadInfo {
int level ;
bool recurse ;
} ;
# ifdef _REENTRANT
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 ) ) ;
} ;
Lock : : Lock ( ) {
info . recurse = true ;
}
Lock : : ~ Lock ( ) {
info . recurse = false ;
}
}
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 .
I use the following log4cxx @ c log4cxx : : PropertyConfigurator
configuration in the test program to enable all function traces ,
except from some third party and standard libraries :
@ verbatim
log4j . rootLogger = OFF , A1
log4j . logger . mrw . fn = DEBUG , A1
log4j . logger . mrw . fn . log4cxx = OFF , A1
log4j . logger . mrw . fn . boost = OFF , A1
log4j . logger . mrw . fn . Thread = OFF , A1
log4j . logger . mrw . fn . mrw = OFF , A1
log4j . logger . mrw . fn . std = OFF , A1
log4j . logger . mrw . fn . new = OFF , A1
log4j . logger . mrw . fn . CppUnit = OFF , A1
log4j . logger . mrw . fn . __gnu_cxx = OFF , A1
log4j . appender . A1 = org . apache . log4j . FileAppender
log4j . appender . A1 . layout = org . apache . log4j . PatternLayout
log4j . appender . A1 . layout . ConversionPattern = % t - % - 41 c % m % n
log4j . appender . A1 . filename = filename . log
@ endverbatim
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 ;
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 ( ' ' , hierarchy . find ( ' < ' ) ) ) ;
if ( p ! = std : : string : : npos ) hierarchy . erase ( 0 , p + 1 ) ;
log4cxx : : Logger * logger
( log4cxx : : Logger : : getLogger ( _T ( " mrw.fn. " ) + hierarchy ) ) ;
if ( logger - > isDebugEnabled ( ) ) {
std : : stringstream ss ;
ss < < std : : setw ( 2 + mrw : : info . level + + ) < < std : : setfill ( ' ' )
< < " \\ " < < pos . function ;
logger - > forcedLog ( : : log4cxx : : Level : : DEBUG , ss . str ( ) ,
pos . file . c_str ( ) , pos . line ) ;
}
}
} 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 ;
{
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 . find ( ' ( ' ) ) ;
std : : string : : size_type p ( hierarchy . rfind ( ' ' , hierarchy . find ( ' < ' ) ) ) ;
if ( p ! = std : : string : : npos ) hierarchy . erase ( 0 , p + 1 ) ;
log4cxx : : Logger * logger
( log4cxx : : Logger : : getLogger ( _T ( " mrw.fn. " ) + hierarchy ) ) ;
if ( logger - > isDebugEnabled ( ) ) {
std : : stringstream ss ;
ss < < std : : setw ( 2 + - - mrw : : info . level ) < < std : : setfill ( ' ' )
< < " / " < < pos . function ;
logger - > forcedLog ( : : log4cxx : : Level : : DEBUG , ss . str ( ) ,
pos . file . c_str ( ) , pos . line ) ;
}
}
} catch ( . . . ) { }
}
//@}
//@}