C++ function tracer
While debugging a multithreaded program in C++, I wanted the ability to trace entry and exit of functions. This was tricky because exceptions were thrown left and right, caught at the very top of each function and transferred to the main thread to be re-thrown there, so the exit path of a function wasn't always clear.
I was after a feature similar to Common Lisp's TRACE macro. I didn't
find anything promising in the manual of GDB, so I decided to
implement this little gadget.
It relies on the resource acquisition is initialization (RAII) idiom. The resource in question is the stack frame. The destructor is called when control exits the stack frame, printing the message.
__PRETTY_FUNCTION__ is a preprocessor macro that contains the name
of the current function (full prototype). It is a GNU extension.
__func__ is a standard (C99) alternative, but less useful in C++, as
it contains only the base name of the function (not the namespaces nor
the types of arguments).
// tracer.hh #ifndef TRACER_HH_INCLUDED #include <iostream> #include <sstream> #include <string> #include <thread> class Tracer { public: Tracer(std::string fname): fname_{ std::move(fname) } { print_("enter"); } ~Tracer() { print_("exit"); } private: void print_(const char *msg) const noexcept { std::ostringstream oss; oss << std::showbase << std::hex << tid_ << '\t' << msg << '\t' << fname_ << '\n'; std::cerr << oss.str(); } std::string fname_; std::thread::id tid_ = std::this_thread::get_id(); }; #define TRACE() Tracer tracer{ __PRETTY_FUNCTION__ } #endif
This requires a C++11 compiler (for the <thread> abstraction); it
could be replaced by a direct call to pthread_self(3), but
pthread_t is not specified so it would be cumbersome to print
portably.
Example usage:
// example.cc #include <iostream> #include <thread> #include "tracer.hh" namespace a { void f(int x) { TRACE(); std::cerr << x << '\n'; } } // namespace a namespace b { void g(double y) { TRACE(); std::cerr << y << '\n'; } } // namespace b int main() { std::thread t1{ a::f, 42 }; std::thread t2{ b::g, -35.43 }; t1.join(); t2.join(); }
$ g++ example.cc -pthread $ ./a.out 0x7f2d67921700 enter void b::g(double) 0x7f2d682b2700 enter void a::f(int) 42 0x7f2d682b2700 exit void a::f(int) -35.43 0x7f2d67921700 exit void b::g(double)