From: davidm@consilium.com (David S. Masterson)
Newsgroups: comp.lang.c++
Subject: Re: Debugging output
Date: 8 Nov 92 00:35:28 GMT
Distribution: comp
Organization: Consilium Inc., Mountain View, California
In-reply-to: jbn@lulea.trab.se's message of 6 Nov 92 18:05:07 GMT
X-Posting-Software: GNUS 3.13  [ NNTP-based News Reader for GNU Emacs ]

>>>>> On 6 Nov 92 18:05:07 GMT, jbn@lulea.trab.se (Johan Bengtsson) said:

> I much prefer saying

> DEBUG( cerr << "here\n" );

> Much easier to see what is going on (IMHO), and useful for other things
> than printouts.

> #ifndef NDEBUG
> #  define DEBUG(expr) if ( debug ) (expr)
> #else
> #  define DEBUG(expr) (0)
> #endif

> Note that DEBUG code is generated per default.  Reversing that is trivial.

A very simple approach to debugging, but a very useful one.  In that vein, I
got the idea for the below Tracer object from the ARM (chapter 3, I think) a
few years ago.  Since you wanted to know about debugging and I saw this idea
recently discussed by Andrew Koenig in JOOP, I thought I'd post this version
of the Tracer object.  The capabilities of this object are:

1. Print out function entry/exit of interesting functions.
2. Print out of interesting information.
3. Attach #2 messages to a bit flag.
4. Print out messages of specific bit flag(s).
5. Print out messages within context of a function.
6. Turn on and off tracing temporarily.
7. Compile out tracing code altogether.

I've added a number of things since this version of the code:

1. Messages can be output to other than cout.
2. Environment variables can override initial tracing state.
   (tried reading environment variable often so tracing could be changed,
    but the getenv() cost was too great  ;-)

Anyway, here's the code.  Enjoy!

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 11/08/1992 00:17 UTC by davidm@prometheus
# Source directory /slab/davidm/tmp
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   5066 -rw-r--r-- tracer.cxx
#   4466 -rw-r--r-- tracer.hxx
#
# ============= tracer.cxx ==============
if test -f 'tracer.cxx' -a X"$1" != X"-c"; then
	echo 'x - skipping tracer.cxx (File already exists)'
else
echo 'x - extracting tracer.cxx (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'tracer.cxx' &&
/*
** File - tracer.cxx
*/
X
#ifndef TRACER_HXX
#include "tracer.hxx"
#endif
X
#include <iostream.h>
#include <string.h>
X
X
// ****************************************************************:
//      Tracer
//
// Description:
//      Initialize local as well as static information.  Set up
//      for total cleanup at destruction.  Also outputs Tracer
//      to stream using operator<<.  Implicit assumption is that
//      this is only called once per run of a program.
// ****************************************************************:
Tracer::Tracer(char* s, const int n)
{
X        prog = func = s;        // Set program name
X        mode = n;               // Initialize the tracing mode (no default).
X        lmode = 1;              // Recognize this as top-level.
X
X        if (mode)               // Should we output?
X                cout << *this << ": entered" << endl;
}
X
X
// ****************************************************************:
//      Tracer
//
// Description:
//      Initialize only local information.  No total cleanup
//      at destuction.  Also outputs Tracer to stream using
//      operator<<.  Also checks to see if input character
//      string contains information that Tracer is watching
//      for and turns tracing on if it does.  Implicit assumption
//      is that this is the way Tracer will be set up when needed.
// ****************************************************************:
Tracer::Tracer(char* s)
{
X        func = s;               // Leave program alone, but set function
X
X        if (watch != 0)                 // Watching something?
X        {
X                if (strstr(func, watch) != 0)   // Watching for this?
X                {                       // yes...
X                        lmode = mode;   // remember global mode locally
X                        mode = -1;      // turn tracing on
X                }
X        }
X        else                            // no...
X                lmode = 0;              // set local mode to not top level
X
X        if (mode)                       // Should we output?
X                cout << *this << ": entered" << endl;
}
X
X
// ****************************************************************:
//      ~Tracer
//
// Description:
//      Check for totalcleanup flag and cleanup.
//      Also outputs Tracer to stream using operator<<.
// ****************************************************************:
Tracer::~Tracer()
{
X        if (mode)                       // Should we output?
X                cout << *this << ": exitted" << endl;
X
X        if (watch != 0)                 // Watching something?
X        {
X                if (strstr(func, watch) != 0)   // Watching for the function?
X                {                       // yes...
X                        mode = lmode;   // reset remembered global mode
X                        lmode = 0;      // this is not the top level
X                }
X        }
X
X        if (lmode)                      // Is this top level?
X                TotalCleanup();
}
X
X
// ****************************************************************:
//              TotalCleanup
//
// Description:
//      Do what's needed to finally cleanup Tracer.
//      For instance, might be a close of a log file.
// ****************************************************************:
void
Tracer::TotalCleanup()
{
X        if (watch)
X                delete watch;
}
X
X
// ****************************************************************:
//              SetMode
//
// Description:
//      Change the global mode of Tracer.
// ****************************************************************:
void
Tracer::SetMode(const int n)
{
X        mode = n;               // new mode
}
X
X
// ****************************************************************:
//              GetMode
//
// Description:
//      Determine the current global mode of Tracer.
// ****************************************************************:
int
Tracer::GetMode()
{
X        return mode;            // what is the mode?
}
X
X
// ****************************************************************:
//              SetWatch
//
// Description:
//      Set a function name to watch trace on.
// ****************************************************************:
void
Tracer::SetWatch(char* name)
{
X        if (watch)
X                delete watch;
X
X        watch = new char[strlen(name)];
X        strcpy(watch, name);
}
X
X
// ****************************************************************:
//              GetWatch
//
// Description:
//      Return the current function being watched for.
// ****************************************************************:
char*
Tracer::GetWatch()
{
X        return watch;           // what we're watching for
}
X
X
// ****************************************************************:
//              Operator<<
//
// Description:
//      Puts Tracer into an output stream.
// ****************************************************************:
ostream&
operator<<(ostream& str, Tracer& x)
{
X        return str << x.func;   // output the Tracer object to stream
}
SHAR_EOF
chmod 0644 tracer.cxx ||
echo 'restore of tracer.cxx failed'
Wc_c="`wc -c < 'tracer.cxx'`"
test 5066 -eq "$Wc_c" ||
	echo 'tracer.cxx: original size 5066, current size' "$Wc_c"
fi
# ============= tracer.hxx ==============
if test -f 'tracer.hxx' -a X"$1" != X"-c"; then
	echo 'x - skipping tracer.hxx (File already exists)'
else
echo 'x - extracting tracer.hxx (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'tracer.hxx' &&
/*
** Tracer object
**
** Purpose:
**      Mostly for tracing function calls, but may be adapted for
**      logging a trace of a program to somewhere else.
*/
X
#ifndef TRACER_HXX
#define TRACER_HXX
X
// .NAME Tracer - class to manage providing a trace of functions.
// .LIBRARY util
// .HEADER Utility Classes
// .INCLUDE tracer.hxx
// .FILE tracer.hxx
// .FILE tracer.cxx
X
// .SECTION Description
// This class makes use of some of the semantics of C++ to provide
// a convenient way of tracing function calls.  This code was initially
// suggested by Bjarne Stroustrup, but has now been modified to allow
// control of when tracing takes place.  Tracing messages are sprinkled
// about the user's code and, at the user's choosing, the type of
// tracing is enabled.  Users of this object class will rarely (if
// ever) directly see the class.  Instead, they will use the macro
// interface defined with this class.
X
// .SECTION Tracing Types
// Tracer may be set to output its tracing information according to the
// type of mode it has been put into.  At the simplest level, Tracer may
// be set to output all tracing information by setting its type to -1
// (which is what TRACE_ON does).  At the next level, messages that
// Tracer is told about may be given a positive integer type < 32.
// Tracer may also be set to output only messages that are of certain
// types (via bitwise AND of current type with message type).  Users
// can, therefore, monitor all types of messages, some of the types,
// or just one of the types depending on the user's interest.  The final
// level is that a user may turn on monitoring of a function call by
// telling Tracer the name of the function to WATCH.  When Tracer hits
// the function in question, it turns TRACE_ON and turns TRACE_OFF when
// it leaves the function (so, all lower functions are also traced).
X
#include <iostream.h>
X
X
class Tracer
{
public:
X        Tracer(char*, const int);       // Full tracer invocation
X        Tracer(char*);                  // Lower level invocation
X
X        ~Tracer();                      // Close tracer invocation
X
X        void    TotalCleanup();         // Clean up when fully done
X
X        void    SetMode(const int);     // Set current mode of tracing
X        int     GetMode();              // Determine current tracing mode
X
X        void    SetWatch(char*);        // Function name to begin tracing in
X        char*   GetWatch();             // What we are watching for?
X
X        friend ostream& operator<<(ostream&, Tracer&);
X                                        // Output tracing information
X
private:
X        char*           func;           // function being worked on
X        int             lmode;          // local mode of Tracer
X        static char*    prog;           // program being worked on
X        static int      mode;           // global mode of Tracer
X        static char*    watch;          // function to watch for and trace
X
};
X
X
// ****************************************************************
//              Macro Interface to Tracer Object
// ****************************************************************
#ifndef NDEBUG
X
#define FTRACER(s,n)    /* Trace program s with type n */\
X        Tracer  _trace(s, n);
X
#define TRACER(s)       /* Trace function s */\
X        Tracer  _trace(s);
X
#define TRACE(s)        /* If TRACE_ON then output s */\
X        if (_trace.GetMode()) cout << _trace << ": " << s << endl;
X
#define TRACEF(f)       /* If TRACE_ON then call function f */\
X        if (_trace.GetMode()) f;
X
#define TRACE_ON        /* Turn tracing on */\
X        _trace.SetMode(-1);
X
#define TRACE_OFF       /* Turn tracing off */\
X        _trace.SetMode(0);
X
#define LTRACE(l,s)     /* If (TRACE_ON || type & l) then output s */\
X        if (_trace.GetMode() < 0 || _trace.GetMode() & l) \
X                cout << _trace << ": " << s << endl;
X
#define LTRACEF(l,f)    /* If (TRACE_ON || type & l) then call function f */\
X        if (_trace.GetMode() < 0 || _trace.GetMode() & l) f;
X
#define STRACE(m)       /* Set tracing to type m */\
X        _trace.SetMode(m);
X
#define WATCH(s)        /* Set function to watch to s */\
X        _trace.SetWatch(s);
X
#else
X
#define FTRACER(s,n)    /**/
#define TRACER(s)       /**/
#define TRACE(s)        /**/
#define TRACE_ON        /**/
#define TRACE_OFF       /**/
#define LTRACE(l,s)     /**/
#define LTRACEF(l,s)    /**/
#define STRACE(m)       /**/
#define WATCH(s)        /**/
X
#endif
X
#endif
SHAR_EOF
chmod 0644 tracer.hxx ||
echo 'restore of tracer.hxx failed'
Wc_c="`wc -c < 'tracer.hxx'`"
test 4466 -eq "$Wc_c" ||
	echo 'tracer.hxx: original size 4466, current size' "$Wc_c"
fi
exit 0
--
====================================================================
David Masterson					Consilium, Inc.
(415) 691-6311					640 Clyde Ct.
davidm@consilium.com				Mtn. View, CA  94043
====================================================================
People were out there looting their asses off... When they saw us,
they shouted, `Viva Bush!'
                -- A US soldier present at the invasion of Panama.