parent
40f522eb3a
commit
5fc121989f
5 changed files with 830 additions and 0 deletions
@ -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 (...) {} |
||||
} |
||||
|
||||
//@}
|
||||
|
||||
//@}
|
@ -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); |
@ -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 |
@ -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; |
||||
} |
@ -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; |
||||
} |
Loading…
Reference in new issue