Copyright (C) 2003-2005 by David J. Hardy.
All rights reserved.


Debug Module
------------


Files:

debug/debug.h


Purpose:

This is really just a header file that defines useful macros.
All of these macros print the filename and line number where
they were called.  The error catching macros conclude with a
call to abort(); the variable watch macros print information
to stderr.  


Enabling support:

To activate the error checking macros, you must #define DEBUG_SUPPORT
before #include "debug/debug.h" statement.  Alternatively, when
compiling a debugging version of MDX, just enable support with
the -D compiler flag (i.e. add the line

  CPPFLAGS += -DDEBUG_SUPPORT

to the end of your ./arch/*_debug.mk file).  If not enabled, the
macros vanish into empty (;) statements (except for BUG(msg)).

In order to enable debug variable and expression watches, you must 
#define DEBUG_WATCH in addition to #define DEBUG_SUPPORT.  You will
typically want this enabled only for interactively debugging a small
subset of the source files for which DEBUG_SUPPORT is enabled
(perhaps as few as one source file at a time).


Trapping impossible conditions:

BUG(msg) - print nil-terminated msg string to stderr and abort.
  Use for bug catching, for things that should never go wrong.
  Activated whether or not debugging support is on.

  One such use is to catch impossible values in a switch statement:

  switch (foo) {
    case BAR:
      stmt;
      break;
    ...
    default:
      BUG("illegal switch value");
  }

(The rest of the macros must be enabled.  Otherwise they compile
into empty ; statements.)


Trapping error conditions:

ASSERT(expr) - assert macro, expr is evaluated once as (boolean) integer.
  Tests to see if asserted condition is true.  If it *is not* true, print
  error diagnostics to stderr and abort.

  Standard use is to catch function and code section preconditions and
  postconditions:

  double inner_product(double u[], int ulen, double v[], int vlen) {
    ASSERT(u != NULL);
    ASSERT(v != NULL);
    ASSERT(u > 0);
    ASSERT(v > 0);
    ASSERT(ulen == vlen);
    ...
  }


COND(expr) - exceptional condition macro, expr is evaluated once as
  (boolean) integer.  Tests to catch an exception.  If condition *is*
  true, print error diagnostics to stderr and abort.  This is the
  opposite behavior of ASSERT().

  This can be placed within a conditional before any standard error
  handling takes place.  For example, suppose we have an array class
  in which you pass an index in order to index a value.  With range
  checking on the index, the code might be:

  double array_val(Array *a, int k) {
    ASSERT(a != NULL);
    if (k >= a->max) {
      COND(k >= a->max);
      return NaN;
    }
    return a->u[k];
  }

  By calling COND() the code aborts at the immediate location, and an
  examination of the core file in a debugger will let you see the stack
  trace of who called array_val() with an out-of-range index.


ERRCOND(expr) - exceptional condition macro, expr is evaluated once
  as (boolean) integer, also calls strerror(errno) for diagnostics.
  Tests to catch an exception from C system library call.  Same behavior
  as COND() but diagnostics also returns strerror(errno) message.

  This is used to trap after failed system library calls:

  if ((p = malloc(n)) == NULL) {
    ERRCOND(p == NULL);
    return FAIL;
  }


Watching variables:

The debug watches below print to stderr the value of strings, evaluate
and print arithmetic expressions, or print pointer addresses, depending
on the macro.  These macros require you to #define DEBUG_WATCH in
addition to #define DEBUG_SUPPORT.  Although it is useful to compile a
"debugging version" of all source modules with DEBUG_SUPPORT defined
so that the error checking macros silently test assertions and other
conditions, you will typically want to enable DEBUG_WATCH by hand for
interactively debugging a single source file in order to view variables
and expressions during program execution.

You can customize the debug watches for various sections of code by
redefining the DEBUG_TITLE label (set to "DEBUG" by default).  This
title is used in the output of all of the watches below.  These macros
all share a global integer counter DEBUG_COUNTER that is incremented
after every call.  The counter is printed as part of the title.  Also
printed are the filename and line number where the call occurs.  These
macros provide a quick way to examine values computed in your code
without having to write a lot of printf() statements or step through
the code with a debugger utility.

TEXT(msg) - print text message to stderr.  Argument should be a
  nil-terminated string literal.

STR(s) - print to stderr value from string variable.  Argument should be
  a variable of type char * that points to a nil-terminated string (or
  pointer could be NULL).

INT(i) - print to stderr value from an integer expression.

PTR(p) - print to stderr address of pointer (evaluate as hex expression).

FLT(x) - print to stderr value from a float or double expression.

VEC(v) - print to stderr component values of 3D vector struct (like
  mdtypes.h Fvec or Dvec).
