#include <oskit/debug.h>
This file contains simple macros and functions to assist in debugging. Many of these facilities are intended to be used to ``annotate'' programs permanently or semi-permanently in ways that reflect the code's proper or desired behavior. These facilities typically change their behavior depending on whether the preprocessor symbol DEBUG is defined: if it is defined, then extra code is introduced to check invariants and such; when DEBUG is not defined, all of this debugging code is ``compiled out'' so that it does not result in any size increase or efficiency loss in the resulting compiled code.The following macros and functions are intended to be used as permanent- or semi-permanent annotations to be sprinkled throughout ordinary code to increase its robustness and clarify its invariants and assumptions to human readers:
- assert(cond)
- This is a standard assert macro, like (and compatible with) the one provided in oskit/c/assert.h. If DEBUG is defined, this macro produces code that evaluates cond and calls panic (see Section 9.7.3) if the result is false (zero). When an assertion fails and causes a panic, the resulting message includes the source file name and line number of the assertion that failed, as well as the text of the cond expression used in the assertion. If DEBUG is not defined, this macro evaluates to nothing (an empty statement), generating no code.
Assertions are typically used to codify assumptions made by a code sequence, e.g., about the parameters to a function or the conditions on entry to or exit from a loop. By placing explicit assert statements in well-chosen locations to verify that the code's invariants indeed hold, a thicker ``safety net'' is woven into the code, which tends to make bugs manifest themselves earlier and in much more obvious ways, rather than allowing incorrect results to ``trickle'' through the program's execution for a long time, sometimes resulting in completely baffling behavior. Assertions can also act as a form of documentation, clearly describing to human readers the exact requirements and assumptions in a piece of code.
- otsan()
- If DEBUG is defined, this macro unconditionally causes a panic with the message ``off the straight and narrow!,'' along with the source file name and line number, if it is ever executed. It is intended to be placed at code locations that should never be reached if the code is functioning properly; e.g., as the default case of a switch statement for which the result of the conditional expression should always match one of the explicit case values. If DEBUG is not defined, this macro evaluates to nothing.
- do_debug(stmt)
- If DEBUG is defined, this macro evaluates to stmt; otherwise it evaluates to nothing. This macro is useful in situations where an #ifdef DEBUG #endif block would otherwise be used over just a few lines of code or a single statement: it produces the same effect, but is smaller and less visually intrusive.
The following macros and functions are primarily intended to be used as temporary scaffolding during debugging, and removed from production code:
- void dump_stack_trace(void)
- This function dumps a human-readable backtrace of the current function call stack to the console, using printf. The exact content and format of the printed data is architecture-specific; however, the output is typically a list of instruction pointer or program counter values, each pointing into a function on the call stack, presumably to the return point after the function call to the next level. You can find out what function these addresses reside in by running the Unix nm utility on the appropriate executable file image, sorting the resulting symbol list if necessary, and looking up the address in the sorted list. Alternatively, for more precise details, you can look up the exact instruction addresses in a disassembly of the executable file, e.g., by using GNU objdump with the `-d' option.
- here()
- This macro generates code that simply prints the source file name and line number at which the macro was used. This macro can be extremely useful when trying to nail down the precise time or code location at which a particular bug manifests itself, or to determine the sequence of events leading up to it. By sprinkling around calls to the here macro in appropriate places, the program will dump regular status reports of its location every time it hits one of these macros, effectively producing a log of ``interesting'' events (``interesting'' being defined according to the placement of the here macro invocations). Using the here macro this way is equivalent to the common practice of sprinkling printf's around and watching the output, except it is easier because the here invocation in each place does not have to be ``tailored'' to make it distinguishable from the other locations: each use of the here macro is self-identifying.
If DEBUG is not defined, the here macro is not defined at all; this makes it obvious when you've accidentally left invocations of this macro in a piece of code after it has been debugged.
- debugmsg(printfargs)
- This macro is similar to here, except it allows a formatted message to be printed along with the source file name and line number. printfargs is a complete set of arguments to be passed to the printf function, including parentheses: for example, `debugmsg(("foo is %d", foo));'. A newline is automatically appended to the end of the message. This macro is generally useful as a wrapper for printf for printing temporary run-time status messages during execution of a program being debugged.
As with here, if DEBUG is not defined, the debugmsg macro is not defined at all, in order to make it obvious if any invocations are accidentally left in production code.
Note that only panic and dump_stack_trace are real functions; the others are simply macros.