233 lines
7.8 KiB
C++
233 lines
7.8 KiB
C++
/** @file
|
|
|
|
$Id$
|
|
|
|
$Date$
|
|
$Author$
|
|
|
|
@copy © Marc Wäckerlin
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
$Log$
|
|
Revision 1.3 2005/04/14 19:12:18 marc
|
|
better formatting for operators
|
|
|
|
Revision 1.2 2005/04/07 20:42:38 marc
|
|
renamed loggerhierarchy from mrw.gccfunctiontrace to mrw.fn
|
|
|
|
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.fn.<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.
|
|
|
|
A good default configuration is given in @ref AutoInitLog4cxx.
|
|
Depending on your log4cxx configuration, a function trace may look
|
|
e.g. like this (very simple configuration with very few additional
|
|
information):
|
|
|
|
@verbatim
|
|
mrw.fn.anotherFunction \ anotherFunction()
|
|
mrw.fn.HalloWelt.fn1 \ HalloWelt::fn1()
|
|
mrw.fn.HalloWelt.fn \ HalloWelt::fn()
|
|
mrw.fn.HalloWelt.A.method \ HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method / HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method \ HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method / HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.fn / HalloWelt::fn()
|
|
mrw.fn.HalloWelt.fn \ HalloWelt::fn()
|
|
mrw.fn.HalloWelt.A.method \ HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method / HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method \ HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.A.method / HalloWelt::A::method()
|
|
mrw.fn.HalloWelt.fn / HalloWelt::fn()
|
|
mrw.fn.HalloWelt.fn1 / HalloWelt::fn1()
|
|
mrw.fn.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().
|
|
|
|
@see @ref AutoInitLog4cxx if you also want to automatically
|
|
configure @c log4cxx.
|
|
|
|
@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(' ', std::min(hierarchy.find('<'),
|
|
hierarchy.find("operator"))));
|
|
if (p!=std::string::npos) hierarchy.erase(0, p+1);
|
|
log4cxx::Logger* logger
|
|
(log4cxx::Logger::getLogger(_T("mrw.fn.")+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(' ', std::min(hierarchy.find('<'),
|
|
hierarchy.find("operator"))));
|
|
if (p!=std::string::npos) hierarchy.erase(0, p+1);
|
|
log4cxx::Logger* logger
|
|
(log4cxx::Logger::getLogger(_T("mrw.fn.")+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 (...) {}
|
|
}
|
|
|
|
//@}
|
|
|
|
//@}
|