initial version

master
Marc Wäckerlin 20 years ago
parent 40f522eb3a
commit 5fc121989f
  1. 239
      mrw/autofunctiontracelog4cxx.cpp
  2. 22
      mrw/functiontrace.cpp
  3. 274
      mrw/functiontrace.hpp
  4. 116
      mrw/functiontrace_test.cpp
  5. 179
      mrw/mrwautofunctiontracelog4cxx_test.cpp

@ -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++&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.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 &copy; Marc W&auml;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 &copy; Marc W&auml;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 &copy; Marc W&auml;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 &copy; Marc W&auml;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…
Cancel
Save