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