C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
239 lines
8.4 KiB
239 lines
8.4 KiB
/** @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 (...) {} |
|
} |
|
|
|
//@} |
|
|
|
//@}
|
|
|