The Trace Module

Introduction

The trace (trace.h, trace.cc) module is designed to support a refinement of printf-style debugging. While I do not use printf-style debugging exclusively, it is nevertheless an important and often effective technique.

The main difficulty with traditional printf is that you can't easily turn the output on and off. Hence, the central feature of the trace module is a set of tracing flags, set at run-time, that control tracing output. There is also a compile-time flag, NDEBUG, to disable them completely.

Basic Use

An example tracing directive is the following:

  TRACE("weapons", "about to fire proton torpedo " << torpNum);
If NDEBUG is #defined, this expands to nothing. But if it is not, then this expands to something like:
  if (tracingSys("weapons")) {
    cout << "%%% weapons: " << "about to fire proton torpedo " << torpNum << endl;
  }

Several things are noteworthy about the expansion:

Now, the above is a slight simplification. In fact, the second argument to TRACE is actually evaluated regardless of whether the flag is turned on. This is so that if the evaluation itself has a bug (e.g. it segfaults), it will be found quickly, rather than waiting to bite someone who is trying to debug something else.

Enabling Flags

There are several ways to turn on a tracing flag.

Trace Flag Naming Convention

In my projects, most of my tracing flags are given the name of the module they are in (e.g. TRACE("foo", ...) in foo.cc). Module-level tracing is to report events likely to be relevant to users and casual maintainers of the module. For more detail, I typically add another word to name the task at hand, e.g. TRACE("fooInit").

Debugging, Testing and the Trace Module

How should this module be used in the larger process context? First, some terminology:

I advocate using the tracing module for debugging only. Since the tracing activity (even when no output is produced) is typically prohibitively expensive, one cannot ship to users an executable that has them in. And, since it is a good idea to "ship what you test", testing should be done with NDEBUG turned on, and hence tracing disabled. (See also the note about this in xassert.h.)

Of course, there is nothing wrong with doing some testing with tracing enabled. In fact, when I am the one developing the code, I do most of my testing with it enabled. But the program must also be tested with tracing disabled if that's how users will use it, and to the extent there is dedicated QA activities in your project I recommend they be performed on the NDEBUG build.


Back to smbase.

Valid HTML 4.01!