2005-03-11 21:07:55 +00:00
|
|
|
/** @file
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
$Date$
|
|
|
|
$Author$
|
|
|
|
|
|
|
|
@copy © Marc Wäckerlin
|
|
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
|
|
|
|
1 2 3 4 5 6 7 8
|
|
|
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __GNUG__
|
|
|
|
#error GNU C++ Compiler is required for automatical function trace
|
|
|
|
#endif
|
|
|
|
|
2011-12-10 11:39:09 +00:00
|
|
|
#include <mrw/string.hxx>
|
2005-03-11 21:07:55 +00:00
|
|
|
#include <sstream>
|
|
|
|
#include <iomanip>
|
|
|
|
|
2011-12-10 11:39:09 +00:00
|
|
|
#include <mrw/stacktrace.hxx>
|
2005-03-11 21:07:55 +00:00
|
|
|
#include <log4cxx/logger.h>
|
|
|
|
|
2007-08-20 16:09:31 +00:00
|
|
|
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
|
|
|
|
&& defined(_REENTRANT) && !defined(_MT)
|
2005-11-29 12:42:01 +00:00
|
|
|
#define _MT
|
|
|
|
#endif
|
|
|
|
|
2005-03-11 21:07:55 +00:00
|
|
|
// 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));
|
|
|
|
|
2007-08-05 08:20:01 +00:00
|
|
|
|
|
|
|
// support for log4cxx 0.9.7 and for new CVS version
|
|
|
|
#ifndef LOG4CXX_LOCATION
|
|
|
|
# define MRW_LOG4CXX_LOCATION pos.file.c_str(), pos.line
|
|
|
|
#define MRW_LEVEL_DEBUG ::log4cxx::Level::DEBUG
|
|
|
|
#define MRW_LEVEL_INFO ::log4cxx::Level::INFO
|
|
|
|
#define MRW_LEVEL_WARN ::log4cxx::Level::WARN
|
|
|
|
#define MRW_LEVEL_ERROR ::log4cxx::Level::ERROR
|
|
|
|
#define MRW_LEVEL_FATAL ::log4cxx::Level::FATAL
|
|
|
|
#else
|
|
|
|
# define LOG4CXX_CVS
|
|
|
|
# define MRW_LOG4CXX_LOCATION \
|
|
|
|
::log4cxx::spi::LocationInfo(pos.file.c_str(), pos.function.c_str(), pos.line)
|
|
|
|
#define MRW_LEVEL_DEBUG ::log4cxx::Level::getDebug()
|
|
|
|
#define MRW_LEVEL_INFO ::log4cxx::Level::getInfo()
|
|
|
|
#define MRW_LEVEL_WARN ::log4cxx::Level::getWarn()
|
|
|
|
#define MRW_LEVEL_ERROR ::log4cxx::Level::getError()
|
|
|
|
#define MRW_LEVEL_FATAL ::log4cxx::Level::getFatal()
|
|
|
|
#endif
|
|
|
|
|
2005-03-11 21:07:55 +00:00
|
|
|
namespace mrw {
|
|
|
|
|
|
|
|
struct ThreadInfo {
|
|
|
|
int level;
|
|
|
|
bool recurse;
|
|
|
|
};
|
|
|
|
|
2005-11-29 12:42:01 +00:00
|
|
|
#ifdef _MT
|
2005-03-11 21:07:55 +00:00
|
|
|
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));
|
|
|
|
};
|
2005-11-29 12:42:01 +00:00
|
|
|
// workaround doxygen problem:
|
|
|
|
// - the C++ compiler compiles the following code
|
|
|
|
// - doxygen ignores it
|
|
|
|
#ifndef LET_DOXYGEN_IGNORE_THIS // doxygen can't match with __attribute__ above
|
2005-03-11 21:07:55 +00:00
|
|
|
Lock::Lock() {
|
|
|
|
info.recurse = true;
|
|
|
|
}
|
|
|
|
Lock::~Lock() {
|
|
|
|
info.recurse = false;
|
|
|
|
}
|
2005-11-29 12:42:01 +00:00
|
|
|
#endif
|
2005-03-11 21:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2005-04-07 20:42:38 +00:00
|
|
|
"mrw.fn.<full-method-name>" where @c
|
2005-03-11 21:07:55 +00:00
|
|
|
<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.
|
|
|
|
|
2005-04-14 19:12:18 +00:00
|
|
|
A good default configuration is given in @ref AutoInitLog4cxx.
|
2005-03-11 21:07:55 +00:00
|
|
|
Depending on your log4cxx configuration, a function trace may look
|
|
|
|
e.g. like this (very simple configuration with very few additional
|
|
|
|
information):
|
|
|
|
|
|
|
|
@verbatim
|
2005-04-07 20:42:38 +00:00
|
|
|
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()
|
2005-03-11 21:07:55 +00:00
|
|
|
@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
|
2005-04-14 19:12:18 +00:00
|
|
|
in your @c main().
|
|
|
|
|
|
|
|
@see @ref AutoInitLog4cxx if you also want to automatically
|
|
|
|
configure @c log4cxx.
|
2005-03-11 21:07:55 +00:00
|
|
|
|
|
|
|
@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;
|
2005-11-29 12:42:01 +00:00
|
|
|
const mrw::StackTrace::CodePos& pos(mrw::StackTrace::translate(this_fn));
|
2005-03-11 21:07:55 +00:00
|
|
|
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('('));
|
2005-04-14 19:12:18 +00:00
|
|
|
std::string::size_type p
|
|
|
|
(hierarchy.rfind(' ', std::min(hierarchy.find('<'),
|
|
|
|
hierarchy.find("operator"))));
|
2005-03-11 21:07:55 +00:00
|
|
|
if (p!=std::string::npos) hierarchy.erase(0, p+1);
|
2007-08-05 08:20:01 +00:00
|
|
|
// hierarchy.erase
|
|
|
|
// (hierarchy.find_first_not_of
|
|
|
|
// ("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890_."));
|
|
|
|
if (hierarchy=="") hierarchy = "global";
|
|
|
|
std::string name("mrw.fn."+hierarchy);
|
2005-03-11 21:07:55 +00:00
|
|
|
log4cxx::Logger* logger
|
2011-12-11 11:22:15 +00:00
|
|
|
(log4cxx::Logger::getLogger(log4cxx::LogString(name.begin(), name.end())));
|
2005-03-11 21:07:55 +00:00
|
|
|
if (logger->isDebugEnabled()) {
|
2011-12-11 11:22:15 +00:00
|
|
|
std::basic_ostringstream<log4cxx::LogString::value_type> ss;
|
2007-08-05 08:20:01 +00:00
|
|
|
ss<<std::setw(2+mrw::info.level++)
|
2011-12-11 11:22:15 +00:00
|
|
|
<<std::setfill(log4cxx::LogString::value_type(' '))
|
|
|
|
<<"\\ "<<log4cxx::LogString(pos.function.begin(), pos.function.end());
|
2007-08-05 08:20:01 +00:00
|
|
|
logger->forcedLog(MRW_LEVEL_DEBUG, ss.str(),
|
|
|
|
MRW_LOG4CXX_LOCATION);
|
2005-03-11 21:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
{
|
2005-11-29 12:42:01 +00:00
|
|
|
const mrw::StackTrace::CodePos& pos(mrw::StackTrace::translate(this_fn));
|
2005-03-11 21:07:55 +00:00
|
|
|
std::string hierarchy(pos.function);
|
|
|
|
for (std::string::size_type p(0);
|
|
|
|
(p=hierarchy.find("::", p))!=std::string::npos;
|
|
|
|
hierarchy.replace(p, 2, "."));
|
2007-08-05 08:20:01 +00:00
|
|
|
hierarchy.erase(hierarchy.rfind('('));
|
2005-04-14 19:12:18 +00:00
|
|
|
std::string::size_type p
|
|
|
|
(hierarchy.rfind(' ', std::min(hierarchy.find('<'),
|
|
|
|
hierarchy.find("operator"))));
|
2005-03-11 21:07:55 +00:00
|
|
|
if (p!=std::string::npos) hierarchy.erase(0, p+1);
|
2007-08-05 08:20:01 +00:00
|
|
|
// hierarchy.erase
|
|
|
|
// (hierarchy.find_first_not_of
|
|
|
|
// ("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890_."));
|
|
|
|
if (hierarchy=="") hierarchy = "global";
|
|
|
|
std::string name("mrw.fn."+hierarchy);
|
2005-03-11 21:07:55 +00:00
|
|
|
log4cxx::Logger* logger
|
2011-12-11 11:22:15 +00:00
|
|
|
(log4cxx::Logger::getLogger(log4cxx::LogString(name.begin(), name.end())));
|
2005-03-11 21:07:55 +00:00
|
|
|
if (logger->isDebugEnabled()) {
|
2011-12-11 11:22:15 +00:00
|
|
|
std::basic_ostringstream<log4cxx::LogString::value_type> ss;
|
2007-08-05 08:20:01 +00:00
|
|
|
ss<<std::setw(2+--mrw::info.level)
|
2011-12-11 11:22:15 +00:00
|
|
|
<<std::setfill(log4cxx::LogString::value_type(' '))
|
|
|
|
<<"/ "<<log4cxx::LogString(pos.function.begin(), pos.function.end());
|
2007-08-05 08:20:01 +00:00
|
|
|
logger->forcedLog(MRW_LEVEL_DEBUG, ss.str(),
|
|
|
|
MRW_LOG4CXX_LOCATION);
|
2005-03-11 21:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (...) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|
|
|
|
//@}
|