diff --git a/mrw/autofunctiontracelog4cxx.cpp b/mrw/autofunctiontracelog4cxx.cpp new file mode 100644 index 0000000..b004c18 --- /dev/null +++ b/mrw/autofunctiontracelog4cxx.cpp @@ -0,0 +1,239 @@ +/** @file + + $Id$ + + $Date$ + $Author$ + + @copy © Marc Wäckerlin + @license LGPL, see file COPYING + + $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 +#include +#include + +#include +#include + +// 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." where @c + 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<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<forcedLog(::log4cxx::Level::DEBUG, ss.str(), + pos.file.c_str(), pos.line); + } + } + } catch (...) {} +} + +//@} + +//@} diff --git a/mrw/functiontrace.cpp b/mrw/functiontrace.cpp new file mode 100644 index 0000000..92dff82 --- /dev/null +++ b/mrw/functiontrace.cpp @@ -0,0 +1,22 @@ +/** @file + + $Id$ + + $Date$ + $Author$ + + @copy © Marc Wäckerlin + @license LGPL, see file COPYING + + $Log$ + Revision 1.1 2005/03/11 21:07:54 marc + initial version + + + 1 2 3 4 5 6 7 8 + 5678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +#include + +unsigned int mrw::FnTrace::_level(0); diff --git a/mrw/functiontrace.hpp b/mrw/functiontrace.hpp new file mode 100644 index 0000000..f782635 --- /dev/null +++ b/mrw/functiontrace.hpp @@ -0,0 +1,274 @@ +/** @file + + $Id$ + + $Date$ + $Author$ + + @copy © Marc Wäckerlin + @license LGPL, see file COPYING + + $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 +#include +#include +#include + +namespace mrw { + + /** @defgroup FunctionTrace Function Tracing (using log4cxx) + @pre #include + + 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 + #include + 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<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<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<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 diff --git a/mrw/functiontrace_test.cpp b/mrw/functiontrace_test.cpp new file mode 100644 index 0000000..14e6d57 --- /dev/null +++ b/mrw/functiontrace_test.cpp @@ -0,0 +1,116 @@ +/** @file + + $Id$ + + $Date$ + $Author$ + + @copy © Marc Wäckerlin + @license LGPL, see file COPYING + + $Log$ + Revision 1.1 2005/03/11 21:07:55 marc + initial version + + + 1 2 3 4 5 6 7 8 + 5678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/mrw/mrwautofunctiontracelog4cxx_test.cpp b/mrw/mrwautofunctiontracelog4cxx_test.cpp new file mode 100644 index 0000000..b247d1d --- /dev/null +++ b/mrw/mrwautofunctiontracelog4cxx_test.cpp @@ -0,0 +1,179 @@ +/** @file + + $Id$ + + $Date$ + $Author$ + + @copy © Marc Wäckerlin + @license LGPL, see file COPYING + + $Log$ + Revision 1.1 2005/03/11 21:07:55 marc + initial version + + + 1 2 3 4 5 6 7 8 + 5678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HalloWelt { + + class A { + public: + void method() { + } + }; + + void fn() { + A().method(); + A().method(); + } + + void fn1() { + fn(); + fn(); + } + +} + +void anotherFunction() { + HalloWelt::fn1(); +} + +#ifdef _REENTRANT +#include +//#include // 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 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(log.substr(last, dash-last))] += + log.substr(dash+1, pos-dash); + } + CPPUNIT_ASSERT(logs.size()==4); // 4 threads + for (std::map::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; +}