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.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
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 (...) {} |
|
} |
|
|
|
//@} |
|
|
|
//@}
|
|
|