Introduction
Sometimes in complex applications large number of messages are to be processed and there are a large number of functions for handling requests and events. Sometimes it is confusing to see what are the functions called by a given function. What are the functions that will lead to a call to a particular function. What is the sequence of calling of all these functions. If some programmer is supposed to continue an unfinished work of another programmer, in a badly documented system it will be a nightmare. Even the same programmer who had previously made all this can be confused. I experienced this problem in one of my projects. I searched for a solution on the Internet but I was unlucky, so I started to do it myself. I wrote a class to trace methods in the application and log all the sequence to a file in a readable format.
The idea
The idea of my solution is to use a global object of a small class having some static member functions and static member variables. In an early point in the application (in the constructor of the application class, for example) we specify the file that will hold the sequence log. This is done using a static function. The class contains some private static member variables to maintain the current level of the call. At the beginning of a function an instance of this class is instantiated in the stack using the constructor, which takes as a parameter the text that should be logged. At the end of the function, this class instance will be out of scope so the destructor of the class will be called and the level will decrease.
Implementation
The following is the header file of the SequenceTracer
class:
#ifndef _SEQUENCE_TRACER
#define _SEQUENCE_TRACER
#include <fstream> // for std::ofstream
class SequenceTracer
{
public:
static void Start(const char * fileName);
SequenceTracer(const char * text);
virtual ~SequenceTracer();
protected:
static int Stack[256];
static int Level;
static std::ofstream * OutStream;
};
#endif
And this is the implementation file for the class:
#include "SequenceTracer.h"
int SequenceTracer::Level;
std::ofstream * SequenceTracer::OutStream;
int SequenceTracer::Stack[256];
void SequenceTracer::Start(const char *fileName)
{
OutStream = new std::ofstream (fileName);
}
SequenceTracer::SequenceTracer(const char * text)
{
if (!OutStream)
return;
for (int i = 0; i < Level; i++)
*OutStream << '\t';
Stack[Level]++;
for (i = 0; i < Level, Stack[i] > 0; i++)
*OutStream << Stack[i] << '.';
*OutStream << " ";
*OutStream << text << "\n";
Level++;
}
SequenceTracer::~SequenceTracer()
{
Stack[Level] = 0;
Level--;
if (Level < 0)
{
delete OutStream;
OutStream = 0;
}
}
Using the class
Using this class is very easy simply in three steps:
- Include the SequencTracer.h in CPP files in which you want to trace function sequence
- In an early point in the application (The application constructor for example) call the static function
Start
and give this function the name of the file to be created and will store all the sequence
- In the beginning of any function you would like to figure out the usage, instantiate an object of the class in the stack
After you run the application and the level is less than 0, you can see the contents of the sequence file. It will look like:
I made an application specially to test the class. I made two helper macros to further simplify using the class:
#ifdef _DEBUG
#define SEQ_TRACE(txt) SequenceTracer unique_variable_name##__LINE__ (txt)
#define SEQ_START(fileName) SequenceTracer::Start(fileName)
#else
#define SEQ_TRACE(txt)
#define SEQ_START(fileName)
#endif
History
- 16-8-2003: Article submitted, minor updates.