Function tracer

Posted by Nick Gammon on Sat 10 Jul 2004 05:51 AM — 2 posts, 13,122 views.

Australia Forum Administrator #0
The code below is for a simple function tracer. The idea is to output debugging information to somewhere (eg. standard output) to show when a function has been called. The constructor supplies the function name and echoes "begin name" to the output stream. The destruction echoes "--end name" when the function terminates.

It also uses a map of function names so that it can count how many times a function was called. You could use this for some simple profiling.

To use it, just include the class in your project, and then put something like:


trace x ("func_name");


at the start of your functions. This will then echo "begin func_name" when that line is reached.

It is important to give the class variable a name (like "x" in the example). Without it, like this:


trace ("func_name"); // don't do this!


the compiler will construct a temporary object which will immediately be destroyed, thus showing the "--end func_name" message at the wrong time.

You can use any standard ostream as the recipient of the trace messages (eg. cout, cerr). For no tracing (eg. in production) use 0 as the output stream.

You can turn tracing on and off with the "reset" member function. eg.


trace::reset (0); // turn tracing off
trace::reset (&cout); // turn tracing on, output to cout


To show the summaries of counts just call:


trace::show_counts ();





#ifdef WIN32
  #pragma warning( disable : 4786)
#endif

#include <iostream>

#include <map>
#include <string>

using namespace std;

class trace
  {
  const string name;
  const int which;

  static ostream * os;
  static map<string, int> invoc_count;

  public:

    // constructor shows beginning of function
    trace (const string & s)
      : name (s), which (++invoc_count [s])
      {
      if (os)
        *os << "begin " << name << " #" << which << endl;
      }

    // destructor shows end of function
     ~trace ()
      {
      if (os)
        *os << "--end " << name << " #" << which << endl;
      }

     // change output stream to something else (eg. &cout, or 0)
     static void reset (ostream * o) 
       {
       os = o;
       }

     // show counts of how many times every function called
     static void show_counts ()
       {
       if (os)
         for (map<string, int>::const_iterator i = invoc_count.begin ();
              i != invoc_count.end ();
              i++)
            *os << i->second << " " << i->first << endl;          
       }

     // reset all counts
     static void reset_counts ()
       {
       invoc_count.clear ();
       }

  };  // end of trace

// initialise static variables
ostream * trace::os = &cout;
map<string, int> trace::invoc_count;


void g ()
  {
  trace t ("g");
  }

void f ()
  {
  trace t ("f");
  g ();
  g ();
  }

int main (void)
  {
  trace t ("main");
  f ();
  f ();
  f ();


  trace::reset (0);

  {
  trace t ("foo");
  }

  trace::reset (&cout);

  {
  trace t ("bah");
  }

  trace::show_counts ();

  return 0;
  }



Output


begin main #1
begin f #1
begin g #1
--end g #1
begin g #2
--end g #2
--end f #1
begin f #2
begin g #3
--end g #3
begin g #4
--end g #4
--end f #2
begin f #3
begin g #5
--end g #5
begin g #6
--end g #6
--end f #3
begin bah #1
--end bah #1
1 bah
3 f
1 foo
6 g
1 main
--end main #1


The output shows the call sequence (eg. main calls f which calls g).

The summary at the end shows the count of each function call.
Amended on Sat 10 Jul 2004 06:07 AM by Nick Gammon
Australia Forum Administrator #1
A neat variation is to output to a stringstream, this could then be sent somewhere else (eg. written to a file, sent on the Internet, eaten by wild goats).

Here is an example, modifying the "main" function above:




#include <sstream>

int main (void)
  {
  ostringstream to;  // trace output

  trace::reset (&to);  // direct to our string stream

  trace t ("main");
  f ();
  f ();
  f ();
 
  trace::show_counts ();  // output function counts

  // show results (string in 'to')

  cout << "trace output = " << endl << to.str ();

  return 0;
  }



Output

trace output =
begin main #1
begin f #1
begin g #1
--end g #1
begin g #2
--end g #2
--end f #1
begin f #2
begin g #3
--end g #3
begin g #4
--end g #4
--end f #2
begin f #3
begin g #5
--end g #5
begin g #6
--end g #6
--end f #3
3 f
6 g
1 main