initial version
This commit is contained in:
239
mrw/autofunctiontracelog4cxx.cpp
Normal file
239
mrw/autofunctiontracelog4cxx.cpp
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/** @file
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
$Date$
|
||||||
|
$Author$
|
||||||
|
|
||||||
|
@copy © Marc Wäckerlin
|
||||||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||||
|
|
||||||
|
$Log$
|
||||||
|
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++ / 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.gccfunctiontrace.<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.gccfunctiontrace = DEBUG
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.log4cxx = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.boost = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.Thread = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.mrw = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.std = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.new = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.CppUnit = OFF
|
||||||
|
log4j.logger.mrw.gccfunctiontrace.__gnu_cxx = OFF
|
||||||
|
log4j.appender.A1 = org.apache.log4j.FileAppender
|
||||||
|
log4j.appender.A1.layout = org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.A1.layout.ConversionPattern = %t-%-41c%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.gccfunctiontrace.anotherFunction \ anotherFunction()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn1 \ HalloWelt::fn1()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn \ HalloWelt::fn()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method \ HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method / HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method \ HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method / HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn / HalloWelt::fn()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn \ HalloWelt::fn()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method \ HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method / HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method \ HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.A.method / HalloWelt::A::method()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn / HalloWelt::fn()
|
||||||
|
mrw.gccfunctiontrace.HalloWelt.fn1 / HalloWelt::fn1()
|
||||||
|
mrw.gccfunctiontrace.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().
|
||||||
|
|
||||||
|
@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.gccfunctiontrace.")+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.gccfunctiontrace.")+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 (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
//@}
|
22
mrw/functiontrace.cpp
Normal file
22
mrw/functiontrace.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/** @file
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
$Date$
|
||||||
|
$Author$
|
||||||
|
|
||||||
|
@copy © Marc Wäckerlin
|
||||||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||||
|
|
||||||
|
$Log$
|
||||||
|
Revision 1.1 2005/03/11 21:07:54 marc
|
||||||
|
initial version
|
||||||
|
|
||||||
|
|
||||||
|
1 2 3 4 5 6 7 8
|
||||||
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mrw/functiontrace.hpp>
|
||||||
|
|
||||||
|
unsigned int mrw::FnTrace::_level(0);
|
274
mrw/functiontrace.hpp
Normal file
274
mrw/functiontrace.hpp
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/** @file
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
$Date$
|
||||||
|
$Author$
|
||||||
|
|
||||||
|
@copy © Marc Wäckerlin
|
||||||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||||
|
|
||||||
|
$Log$
|
||||||
|
Revision 1.1 2005/03/11 21:07:54 marc
|
||||||
|
initial version
|
||||||
|
|
||||||
|
|
||||||
|
1 2 3 4 5 6 7 8
|
||||||
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MRW_FUNCTIONTRACE_HPP__
|
||||||
|
#define __MRW_FUNCTIONTRACE_HPP__
|
||||||
|
|
||||||
|
#include <log4cxx/logger.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace mrw {
|
||||||
|
|
||||||
|
/** @defgroup FunctionTrace Function Tracing (using log4cxx)
|
||||||
|
@pre #include <mrw/functiontrace.hpp>
|
||||||
|
|
||||||
|
Place the macro @ref METHOD as first line of all methods you want to
|
||||||
|
trace, and macro @ref FUNCTION as first line of all functions to be
|
||||||
|
traced.
|
||||||
|
|
||||||
|
There are alternative macros @ref METHOD2 and @ref FUNCTION2,
|
||||||
|
that allow you to also specify the logging hierarchy. The
|
||||||
|
default for the hierarchy string is @c "mrw.fntrace". The
|
||||||
|
logging level is always @c DEBUG and cannot be changed.
|
||||||
|
|
||||||
|
@attention Be careful when you copy paste the declarations from
|
||||||
|
one method to the other! Don't forget to correctly change the
|
||||||
|
method's name! I recommend to use a tool like OpenC++
|
||||||
|
(http://opencxx.sf.net) to automatically place function trace
|
||||||
|
statements into classes, so that the coder does not have to care
|
||||||
|
about. I have already partly written such a module and I will
|
||||||
|
provide it for free (GPL) when it is good enough.
|
||||||
|
|
||||||
|
The trace of the following code:
|
||||||
|
|
||||||
|
@code
|
||||||
|
#include <mrw/functiontrace.hpp>
|
||||||
|
#include <log4cxx/basicconfigurator.h>
|
||||||
|
void fn(int i=0) {
|
||||||
|
FUNCTION("fn(int)"); // trace entry and exit
|
||||||
|
if (i<4) fn(++i);
|
||||||
|
}
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
A() { // not traced
|
||||||
|
method();
|
||||||
|
}
|
||||||
|
void method() {
|
||||||
|
METHOD("A::method()"); // trace entry and exit
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
int main(int, char**) {
|
||||||
|
log4cxx::BasicConfigurator::configure();
|
||||||
|
FUNCTION("main(int, char**)");
|
||||||
|
A().method();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Produces this output:
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
1 [1078671488] DEBUG mrw.fntrace - \ main(int, char**)
|
||||||
|
1 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: \ A::method()
|
||||||
|
1 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
2 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: / A::method()
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: \ A::method()
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
3 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - \ fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - / fn(int)
|
||||||
|
4 [1078671488] DEBUG mrw.fntrace - 0xbfffef3f: / A::method()
|
||||||
|
5 [1078671488] DEBUG mrw.fntrace - / main(int, char**)
|
||||||
|
@endverbatim
|
||||||
|
|
||||||
|
The indentation is according to the call stack depth level, the
|
||||||
|
name is preceded by a @c \\ on entry and by a @c / on exit. The
|
||||||
|
methods are preceded by the address of @c this, so that
|
||||||
|
different instances can be distinguished. All before and
|
||||||
|
including the dash @c - is configurable through the normal
|
||||||
|
log4cxx trace patterns.
|
||||||
|
*/
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/** @brief Declare method entrance, place as first line in method.
|
||||||
|
|
||||||
|
Place this macro as first statement in each method that should
|
||||||
|
write a function trace.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
@code
|
||||||
|
namespace xxx {
|
||||||
|
namespace yyy {
|
||||||
|
class Zzz {
|
||||||
|
public: void doIt(int, char) {
|
||||||
|
METHOD("xxx::yyy::Zzz::doIt(int, char)");
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param name the full name of the method, including namespaces
|
||||||
|
and class name
|
||||||
|
*/
|
||||||
|
#define METHOD(name) mrw::FnTrace fnTrace(this, name, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
/** @brief Declare function entrance, place as first line in functions.
|
||||||
|
|
||||||
|
Place this macro as first statement in each function that should
|
||||||
|
write a function trace.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
@code
|
||||||
|
namespace xxx {
|
||||||
|
namespace yyy {
|
||||||
|
void doIt(int, char) {
|
||||||
|
FUNCTION("xxx::yyy::doIt(int, char)");
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param name the full name of the function, including namespaces
|
||||||
|
*/
|
||||||
|
#define FUNCTION(name) mrw::FnTrace fnTrace(name, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
/** @brief Declare method entrance, place as first line in method.
|
||||||
|
|
||||||
|
Place this macro as first statement in each method that should
|
||||||
|
write a function trace.
|
||||||
|
|
||||||
|
This macro allows you to define a logging hierarchy string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
@code
|
||||||
|
namespace xxx {
|
||||||
|
namespace yyy {
|
||||||
|
class Zzz {
|
||||||
|
public: void doIt(int, char) {
|
||||||
|
METHOD2("xxx::yyy::Zzz::doIt(int, char)", "fn.xxx.yyy.Zzz");
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param name the full name of the method, including namespaces
|
||||||
|
and class name
|
||||||
|
@param tracer the tracer hierarchy
|
||||||
|
*/
|
||||||
|
#define METHOD2(name, tracer) \
|
||||||
|
mrw::FnTrace fnTrace(this, name, __FILE__, __LINE__, tracer)
|
||||||
|
|
||||||
|
/** @brief Declare function entrance, place as first line in functions.
|
||||||
|
|
||||||
|
Place this macro as first statement in each function that should
|
||||||
|
write a function trace.
|
||||||
|
|
||||||
|
This macro allows you to define a logging hierarchy string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
@code
|
||||||
|
namespace xxx {
|
||||||
|
namespace yyy {
|
||||||
|
void doIt(int, char) {
|
||||||
|
FUNCTION2("xxx::yyy::doIt(int, char)", "fn.xxx.yyy");
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param name the full name of the function, including namespaces
|
||||||
|
@param tracer the tracer hierarchy
|
||||||
|
*/
|
||||||
|
#define FUNCTION2(name, tracer) \
|
||||||
|
mrw::FnTrace fnTrace(name, __FILE__, __LINE__, tracer)
|
||||||
|
|
||||||
|
class FnTrace {
|
||||||
|
public:
|
||||||
|
FnTrace(const void* addr, const std::string& name,
|
||||||
|
const std::string& file, unsigned long line,
|
||||||
|
const std::string& tracer = "mrw.fntrace") throw():
|
||||||
|
_addr(addr), _name(name), _file(file), _line(line), _tracer(tracer) {
|
||||||
|
log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer));
|
||||||
|
if (logger->isDebugEnabled()) {
|
||||||
|
std::stringstream oss;
|
||||||
|
oss<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec
|
||||||
|
<<std::setw(2+_level)<<std::setfill(' ')<<"\\ "<<_name;
|
||||||
|
logger->forcedLog(log4cxx::Level::DEBUG, oss.str(),
|
||||||
|
_file.c_str(), _line);
|
||||||
|
++_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FnTrace(const std::string& name,
|
||||||
|
const std::string& file, unsigned long line,
|
||||||
|
const std::string& tracer = "mrw.fntrace") throw():
|
||||||
|
_addr(0), _name(name), _file(file), _line(line), _tracer(tracer) {
|
||||||
|
log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer));
|
||||||
|
if (logger->isDebugEnabled()) {
|
||||||
|
std::stringstream oss;
|
||||||
|
oss<<std::setw(17)<<' '
|
||||||
|
<<std::setw(2+_level)<<std::setfill(' ')<<"\\ "<<_name;
|
||||||
|
logger->forcedLog(log4cxx::Level::DEBUG, oss.str(),
|
||||||
|
_file.c_str(), _line);
|
||||||
|
++_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~FnTrace() throw() {
|
||||||
|
log4cxx::Logger* logger(log4cxx::Logger::getLogger(_tracer));
|
||||||
|
if (logger->isDebugEnabled()) {
|
||||||
|
--_level;
|
||||||
|
std::stringstream oss;
|
||||||
|
if (_addr)
|
||||||
|
oss<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec;
|
||||||
|
else
|
||||||
|
oss<<std::setw(17)<<' ';
|
||||||
|
oss<<std::setw(2+_level)<<std::setfill(' ')<<"/ "<<_name;
|
||||||
|
logger->forcedLog(log4cxx::Level::DEBUG, oss.str(),
|
||||||
|
_file.c_str(), _line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
const void* _addr;
|
||||||
|
const std::string _name;
|
||||||
|
const std::string _file;
|
||||||
|
unsigned long _line;
|
||||||
|
const std::string _tracer;
|
||||||
|
/** @todo for multithreading, use thread specific storage */
|
||||||
|
static unsigned int _level;
|
||||||
|
};
|
||||||
|
//@}
|
||||||
|
}
|
||||||
|
#endif
|
116
mrw/functiontrace_test.cpp
Normal file
116
mrw/functiontrace_test.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/** @file
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
$Date$
|
||||||
|
$Author$
|
||||||
|
|
||||||
|
@copy © Marc Wäckerlin
|
||||||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||||
|
|
||||||
|
$Log$
|
||||||
|
Revision 1.1 2005/03/11 21:07:55 marc
|
||||||
|
initial version
|
||||||
|
|
||||||
|
|
||||||
|
1 2 3 4 5 6 7 8
|
||||||
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mrw/functiontrace.hpp>
|
||||||
|
#include <mrw/regexp.hpp>
|
||||||
|
#include <mrw/file.hpp>
|
||||||
|
#include <log4cxx/propertyconfigurator.h>
|
||||||
|
#include <log4cxx/helpers/properties.h>
|
||||||
|
#include <cppunit/TestFixture.h>
|
||||||
|
#include <cppunit/ui/text/TestRunner.h>
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
A() {
|
||||||
|
METHOD("A::A()");
|
||||||
|
}
|
||||||
|
~A() {
|
||||||
|
METHOD("A::~A()");
|
||||||
|
}
|
||||||
|
void fn1() {
|
||||||
|
METHOD("A::fn1()");
|
||||||
|
fn2();
|
||||||
|
}
|
||||||
|
void fn2() {
|
||||||
|
METHOD("A::fn2()");
|
||||||
|
fn3();
|
||||||
|
fn4();
|
||||||
|
}
|
||||||
|
void fn3() {
|
||||||
|
METHOD("A::fn3()");
|
||||||
|
fn4();
|
||||||
|
}
|
||||||
|
void fn4(bool flag=true) {
|
||||||
|
METHOD("A::fn4()");
|
||||||
|
if (flag) fn4(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void fn(A a) {
|
||||||
|
FUNCTION("fn(A)");
|
||||||
|
a.fn1();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FunctionTraceTest: public CppUnit::TestFixture {
|
||||||
|
public:
|
||||||
|
void Init() {
|
||||||
|
try {mrw::File::remove("functiontrace_test.log");} catch (...) {}
|
||||||
|
log4cxx::helpers::Properties properties;
|
||||||
|
properties.setProperty("log4j.rootLogger",
|
||||||
|
"DEBUG, A1");
|
||||||
|
properties.setProperty("log4j.appender.A1",
|
||||||
|
"org.apache.log4j.FileAppender");
|
||||||
|
properties.setProperty("log4j.appender.A1.layout",
|
||||||
|
"org.apache.log4j.PatternLayout");
|
||||||
|
properties.setProperty("log4j.appender.A1.layout.ConversionPattern",
|
||||||
|
"%F:%L - %m%n");
|
||||||
|
properties.setProperty("log4j.appender.A1.filename",
|
||||||
|
"functiontrace_test.log");
|
||||||
|
log4cxx::PropertyConfigurator::configure(properties);
|
||||||
|
}
|
||||||
|
void Calls() {
|
||||||
|
fn(A());
|
||||||
|
mrw::RegExp match
|
||||||
|
("functiontrace_test.cpp:30 - *0x[0-9a-fA-F]+: \\\\ A::A\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:30 - *0x[0-9a-fA-F]+: / A::A\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:55 - \\\\ fn\\(A\\)\n"
|
||||||
|
"functiontrace_test.cpp:36 - *0x[0-9a-fA-F]+: \\\\ A::fn1\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:40 - *0x[0-9a-fA-F]+: \\\\ A::fn2\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:45 - *0x[0-9a-fA-F]+: \\\\ A::fn3\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: \\\\ A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: \\\\ A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: / A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: / A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:45 - *0x[0-9a-fA-F]+: / A::fn3\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: \\\\ A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: \\\\ A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: / A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:49 - *0x[0-9a-fA-F]+: / A::fn4\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:40 - *0x[0-9a-fA-F]+: / A::fn2\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:36 - *0x[0-9a-fA-F]+: / A::fn1\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:55 - / fn\\(A\\)\n"
|
||||||
|
"functiontrace_test.cpp:33 - *0x[0-9a-fA-F]+: \\\\ A::~A\\(\\)\n"
|
||||||
|
"functiontrace_test.cpp:33 - *0x[0-9a-fA-F]+: / A::~A\\(\\)\n");
|
||||||
|
CPPUNIT_ASSERT(match(mrw::File::read("functiontrace_test.log")));
|
||||||
|
mrw::File::remove("functiontrace_test.log");
|
||||||
|
}
|
||||||
|
CPPUNIT_TEST_SUITE(FunctionTraceTest);
|
||||||
|
CPPUNIT_TEST(Init);
|
||||||
|
CPPUNIT_TEST(Calls);
|
||||||
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
};
|
||||||
|
CPPUNIT_TEST_SUITE_REGISTRATION(FunctionTraceTest);
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
CppUnit::TextUi::TestRunner runner;
|
||||||
|
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
|
||||||
|
return runner.run() ? 0 : 1;
|
||||||
|
}
|
179
mrw/mrwautofunctiontracelog4cxx_test.cpp
Normal file
179
mrw/mrwautofunctiontracelog4cxx_test.cpp
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/** @file
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
$Date$
|
||||||
|
$Author$
|
||||||
|
|
||||||
|
@copy © Marc Wäckerlin
|
||||||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||||
|
|
||||||
|
$Log$
|
||||||
|
Revision 1.1 2005/03/11 21:07:55 marc
|
||||||
|
initial version
|
||||||
|
|
||||||
|
|
||||||
|
1 2 3 4 5 6 7 8
|
||||||
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <log4cxx/propertyconfigurator.h>
|
||||||
|
#include <log4cxx/helpers/properties.h>
|
||||||
|
#include <cppunit/TestFixture.h>
|
||||||
|
#include <cppunit/ui/text/TestRunner.h>
|
||||||
|
#include <cppunit/extensions/HelperMacros.h>
|
||||||
|
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||||
|
#include <mrw/file.hpp>
|
||||||
|
#include <mrw/regexp.hpp>
|
||||||
|
#include <mrw/string.hpp>
|
||||||
|
#include <mrw/stacktrace.hpp>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace HalloWelt {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public:
|
||||||
|
void method() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void fn() {
|
||||||
|
A().method();
|
||||||
|
A().method();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fn1() {
|
||||||
|
fn();
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void anotherFunction() {
|
||||||
|
HalloWelt::fn1();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
#include <boost/thread/thread.hpp>
|
||||||
|
//#include <unistd.h> // sleep, the one from boost::thread does not work!
|
||||||
|
class Thread {
|
||||||
|
public:
|
||||||
|
void operator()() {
|
||||||
|
anotherFunction();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace mrw {
|
||||||
|
class AutoFunctionTraceLog4CxxTest: public CppUnit::TestFixture {
|
||||||
|
public:
|
||||||
|
void testcase() {
|
||||||
|
mrw::StackTrace::createSymtable();
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
try {mrw::File::remove("mrwautofunctiontracelog4cxx_test-mt.log");}
|
||||||
|
#else
|
||||||
|
try {mrw::File::remove("mrwautofunctiontracelog4cxx_test.log");}
|
||||||
|
#endif
|
||||||
|
catch (...) {}
|
||||||
|
log4cxx::helpers::Properties properties;
|
||||||
|
properties.setProperty("log4j.rootLogger",
|
||||||
|
"OFF, A1");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace",
|
||||||
|
"DEBUG");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.log4cxx",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.boost",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.Thread",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.mrw",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.std",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.new",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.CppUnit",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.logger.mrw.gccfunctiontrace.__gnu_cxx",
|
||||||
|
"OFF");
|
||||||
|
properties.setProperty("log4j.appender.A1",
|
||||||
|
"org.apache.log4j.FileAppender");
|
||||||
|
properties.setProperty("log4j.appender.A1.layout",
|
||||||
|
"org.apache.log4j.PatternLayout");
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
properties.setProperty("log4j.appender.A1.layout.ConversionPattern",
|
||||||
|
"%t-%-41c%m%n");
|
||||||
|
properties.setProperty("log4j.appender.A1.filename",
|
||||||
|
"mrwautofunctiontracelog4cxx_test-mt.log");
|
||||||
|
#else
|
||||||
|
properties.setProperty("log4j.appender.A1.layout.ConversionPattern",
|
||||||
|
"%-41c%m%n");
|
||||||
|
properties.setProperty("log4j.appender.A1.filename",
|
||||||
|
"mrwautofunctiontracelog4cxx_test.log");
|
||||||
|
#endif
|
||||||
|
log4cxx::PropertyConfigurator::configure(properties);
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
// sleep(4); // to be reproducable, wait for "main" flag
|
||||||
|
Thread threadFunction;
|
||||||
|
boost::thread::thread thread1(threadFunction);
|
||||||
|
boost::thread::thread thread2(threadFunction);
|
||||||
|
boost::thread::thread thread3(threadFunction);
|
||||||
|
#endif
|
||||||
|
anotherFunction();
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
thread1.join();
|
||||||
|
thread2.join();
|
||||||
|
thread3.join();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void checkfile() {
|
||||||
|
mrw::RegExp match
|
||||||
|
("^mrw\\.gccfunctiontrace\\.anotherFunction ( ? ? ?)\\\\ anotherFunction\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn1 \\1 \\\\ HalloWelt::fn1\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn \\1 \\\\ HalloWelt::fn\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 \\\\ HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 / HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 \\\\ HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 / HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn \\1 / HalloWelt::fn\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn \\1 \\\\ HalloWelt::fn\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 \\\\ HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 / HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 \\\\ HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.A\\.method \\1 / HalloWelt::A::method\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn \\1 / HalloWelt::fn\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.HalloWelt\\.fn1 \\1 / HalloWelt::fn1\\(\\)\n"
|
||||||
|
"mrw\\.gccfunctiontrace\\.anotherFunction \\1/ anotherFunction\\(\\)\n$");
|
||||||
|
#ifdef _REENTRANT
|
||||||
|
std::string log(mrw::File::read("mrwautofunctiontracelog4cxx_test-mt.log"));
|
||||||
|
std::map<unsigned long, std::string> logs;
|
||||||
|
for (std::string::size_type pos(0), last(0);
|
||||||
|
(pos=log.find('\n', pos+1))!=std::string::npos; last=pos) {
|
||||||
|
std::string::size_type dash(log.find('-', last));
|
||||||
|
CPPUNIT_ASSERT(dash!=std::string::npos);
|
||||||
|
logs[mrw::to<unsigned long>(log.substr(last, dash-last))] +=
|
||||||
|
log.substr(dash+1, pos-dash);
|
||||||
|
}
|
||||||
|
CPPUNIT_ASSERT(logs.size()==4); // 4 threads
|
||||||
|
for (std::map<unsigned long, std::string>::iterator it(logs.begin());
|
||||||
|
it!=logs.end(); ++it)
|
||||||
|
CPPUNIT_ASSERT(match(it->second));
|
||||||
|
mrw::File::remove("mrwautofunctiontracelog4cxx_test-mt.log");
|
||||||
|
#else
|
||||||
|
CPPUNIT_ASSERT(match(mrw::File::read("mrwautofunctiontracelog4cxx_test.log")));
|
||||||
|
mrw::File::remove("mrwautofunctiontracelog4cxx_test.log");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
CPPUNIT_TEST_SUITE(AutoFunctionTraceLog4CxxTest);
|
||||||
|
CPPUNIT_TEST(testcase);
|
||||||
|
CPPUNIT_TEST(checkfile);
|
||||||
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
};
|
||||||
|
CPPUNIT_TEST_SUITE_REGISTRATION(AutoFunctionTraceLog4CxxTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
CppUnit::TextUi::TestRunner runner;
|
||||||
|
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
|
||||||
|
return runner.run() ? 0 : 1;
|
||||||
|
}
|
Reference in New Issue
Block a user