Introduction
Although there shouldn't, there are times when it's uncertain which code points are run through during the execution of a program. There are many professional tools out there which range from static analysis to instrumented measures to get answers to such questions, but on some occasions, an ad hoc solution can be more effective.
Background
The approach to make this work is to make use of the pre program start initialization
of static variables, but since all code points are in functions and
therefore, the "static anker point" has to be put there somehow and so, the C++
feature of delayed initialization of static function variables has to be overcome.
This seems to be possible by adding an indirection through a template
class (
X
) with a
static
member variable (
progloc_
) to enforce the
initialization per template parameter which in turn is a wrapper
struct
which transports the needed information (_.
FILE._ " at line " _.
LINE._).
Putting this together, the most important code to achieve this could look like the following:
template <class T> class X {
public:
static T progloc_;
};
template <class T> T X<T>::progloc_;
#define TRACE_CODE_POINT { \
struct ProgLocation { \
public: \
std::string loc_; \
ProgLocation() : loc_(std::string(__FILE__ " at line " S__LINE__)) \
{ \
TestFw::CodePoints::Test::imHere(loc_); \
} \
}; \
TestFw::CodePoints::X<ProgLocation> dummy; \
TestFw::CodePoints::Test::iGotCalled(dummy.progloc_.loc_); }
The S_._LINE_._
- trick which is used in the ProgLocation
- ctor comes from here on SO.
#define S(x) #x
#define S_(x) S(x)
#define S__LINE__ S_(__LINE__)
To track, the following is used:
class Test
{
private:
typedef std::set<std::string> TFuncs;
static TFuncs registeredFunctions;
static TFuncs calledFunctions;
public:
static int imHere(const std::string fileAndLine)
{
assert(registeredFunctions.find(fileAndLine) == registeredFunctions.end());
registeredFunctions.insert(fileAndLine);
return 0;
}
static void iGotCalled(const std::string fileAndLine)
{
if (calledFunctions.find(fileAndLine) == calledFunctions.end())
calledFunctions.insert(fileAndLine);
}
static void report()
{
for (TFuncs::const_iterator rfIt = registeredFunctions.begin();
rfIt != registeredFunctions.end(); ++rfIt)
if (calledFunctions.find(*rfIt) == calledFunctions.end())
std::cout << (*rfIt) << " didn't get called" << std::endl;
}
};
Using the Code
All it needs to track a code point is to put the macro:
TRACE_CODE_POINT
at the desired places and to call the report()
method of the Test
class at the end of the program.
Points of Interest
The provided solution is very dangerous for several reasons and should be used with care. Neither performance nor thread safety were considered. So far, the code has only been tested on MSVC 2010 and 2013.
If you're going to use the code, you might also consider adding some dispatching to the report method.
The sample source provided in the zip at the start of the tip contains one such reporter and a console and a debug output window sink and also a handy helper to frame the main function.
History
- 2013/12/20: First release