/** @file

    $Id: autofunctiontracelog4cxx.cxx,v 1.3 2005/04/14 19:12:18 marc Exp $

    $Date: 2005/04/14 19:12:18 $
    $Author: marc $

    @copy &copy; Marc W&auml;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

#include <mrw/string.hxx>
#include <mrw/stacktrace.hxx>
#include <iostream>
#include <iomanip>

#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
  && defined(_REENTRANT) && !defined(_MT)
#define _MT
#endif

// 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 {
  
  // workaround doxygen problem:
  //  - the C++ compiler compiles the following code
  //  - doxygen ignores it
#ifndef LET_DOXYGEN_IGNORE_THIS // no matching class member found for
                                // int mrw::ThreadInfo::level()
  struct ThreadInfo {
      int level;
      bool recurse;
  };
#endif

#ifdef _MT
  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));
  };
  // 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
  Lock::Lock() {
    info.recurse = true;
  }
  Lock::~Lock() {
    info.recurse = false;
  }
#endif
}

extern "C" int main(int, char**);

/** @addtogroup FunctionTrace */
//@{

/** @defgroup AutoFunctionTraceStdlog Automatic Function Trace to standard out for GNU g++

    Same as @ref AutoFunctionTrace, but traces to @c stdlog.

    If you link to the library @c libmrwautofunctiontracestdlog using
    a linker option such as: @c -lmrwautofunctiontracestdlog 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 @c
    stdlog. You don't need to change a single line in your code!

    */
//@{

//@}

//@}

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::clog<<std::setw(2+mrw::info.level++)<<std::setfill(' ')
               <<"\\ "<<pos.function<<" ("<<pos.file<<':'<<pos.line<<')'
               <<std::endl;
    }
  } 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::clog<<std::setw(2+--mrw::info.level)<<std::setfill(' ')
               <<"/ "<<pos.function<<" ("<<pos.file<<':'<<pos.line<<')'
               <<std::endl;
    }
  } catch (...) {}
}
