TRACE.CPP: A debugging aid for C++
By Marco Hyman
Computer Language, October 1989

The growth of C++ translators and compilers has outstripped that of modern
debugging tools. So what's a poor programmer to do?

There's always the old standby -- printf statements sprinkled throughout
the code -- but this doesn't use C++'s inherent abilities to their best
advantage. The class mechanism allows debugging information to be integral
part of a program.

The Trace class implemented in this code is designed to be a permanent part
of a program. Trace debug output is normally suppressed but can be enabled
from the command line or the environment. Trace output is directed to stderr,
which can be redirected to a file (DOS users may require a utility such as
the ERROUT program that comes with Microsoft C) that can be perused at
leisure.

The Trace class divides debug output into four basic types: trace, assert,
error, and info. Each can be enabled by setting an environment variable
called DEBUGMODE or by calling Trace::setTraceMode within the program.
Trace::setTraceMode is provided to allow trace output to be enabled via a
command-line switch.

To use the Trace class, a Trace object is defined in each procedure using the
syntax:

    Trace trace = "class::procedure";

Of course, the name of the object (trace) and the format of the initialization
string can be anything. If trace output is enabled, the initialization string
will be putput to stderr and an indent level is incremented whenever the
procedure is called. The output is used to say "I'm here" and the object is
used to make other trace-debug output.

Suppose you wish to trace a path through a procedure. The trace object
defined in the procedure is used as a function and is passed the message to
output. The code would look like this:

    if (condition) {
        trace("condition true path");
        ...
    } else {
        trace("condition false path");
    }

The messages are output at the current indent level so it's easy to see what
procedure was active when the messages were generated. The trace output would
look like this:

    class::procedure
         condition true path

assert output is generated by the assert macro, which is similar to the one
found in assert.h. In Trace, assert doesn't terminate the program.

Putting an assert in each place where you're sure the return value will
never be wrong can help pinpoint the source of impossible errors. In this
code fragment:

    Trace trace = "class::procedure";
    ...
    int err = fseek(file,offset,0);
    assert(trace,err == 0);

an assert message is displayed when assert output is enabled and the fseek
call returns a nonzero value.

If an environment variable named DEBUGMODE exists and its value is numeric,
the low-order four bits of the value are used as enable flags for the various
output modes. The values are:

    assert       1
    trace        2
    error        4
    info         8

To enable all trace-debug output, DEBUGMODE should have a value of 15.

TEST.CC (Listing 1) is a simple program that copies stdin to stdout while
showing how the Trace class can be used. When executed with DEBUGMODE set to
15 and a command line of "test garbage," the output shown in Listing 2 is
displayed.

TRACE.H (Listing 3) is a specification of the Trace class. The class contains
two constructors. The first initializes the class. The second is the standard
constructor called when Trace objects are defined.

Trace was written using v. 1.35.0 of G++ from the Free Software Foundation.
It should work with C++ v. 1.2 and 2.0 with no difficulties.

