DrC's Inline Debugger

Author: Charles E. Campbell
Copyright 2012

What Does it Do?

DrC's Inline Debugger facilitates debugging C and C++ code. The programmer instruments his or her code with "disappearing macros" which give the programmer flexibility:
the debugger calls are compiled in if -DDEBUG is in the compile command, or
the debugger calls are compiled in if #define DEBUG is at the top of the file,
otherwise the code is compiled as if the debugger's calls are not present!

Your source code should have

#include "dbg.h"
in every file using DrChip's debugging calls.

The debugger, even if compiled in, doesn't activate unless a -@ command is given on the command line. Then the programmer may issue commands to turn debugging on selectively by function and detail level, directing debugging output to one or more files.

DrC's internal inline debugger allows programmers to install debugging-smarts into their code without impacting the final production executables. As a result debugging output can be formatted to improve usability to the programmer. Often a problem's effect is found thousands of executed lines after the cause, and external debuggers don't allow easy backtracking. By redirecting debugging output to a file with DrC's internal debugger, one may have a history of helpful comments and important variable states recorded while suppressing unwanted debugger output.

You may get the source by clicking on DrC's Inline Debugger Source.

What's the Output Look Like?

The Makefile included with the debugger's source code distribution makes the "xtdio.a" library and compiles an example using DrC's Inline Debugger. If you run this example as shown (by selectively turning on debugging for function f3), you'll see:

  example -@ "on f3 1"
    f3(j=3){
    |[res=1]*=[j=3]=3
    |[res=3]*=[j=2]=6
    |[res=6]*=[j=1]=6
    |return f3 6 }

I also have a Vim-based syntax highlighting file for DrC's Inline Debugger; you may get it by clicking on dbg.vim.gz. The following shows what the output of the debugger looks like in Vim:

  f3(j=3){
  |[res=1]*=[j=3]=3
  |[res=3]*=[j=2]=6
  |[res=6]*=[j=1]=6
  |return f3 6 }

Instrumenting the Code

Typically one instruments the source code with calls like

   Edbg(("funcname(arg=%d)",arg));

at the beginning of every function and

   Rdbg(("funcname %d",retval));

just before every return point in each function.

Note that the string and arguments style is patterned off of C-style printf(). In addition, where one thinks it appropriate, put in additional debugging statements like

   Dprintf((1,"whatever=%d",iwhatever));

The 1 is a debugging level; at runtime, you will be able to control the level of debugging detail coming out of the debugger (see edbg, rdbg, and dprintf).

How to use the Inline Debugger After Instrumentation

The debugging system is internally energized by calling

    initdbg(&argc,argv);

early on in the main program. Externally, when you run the program,

    -@ dbgfile [dbgfile2 ...] [# [#]]

or

   -@ "{on|ondis|keep|off} funcname [*] [detail-level] [:dtst1 [:dtst2]] [preq list]"

will enable the debugger. The initdbg() looks for that -@ and processes a dbgfile in the first case and actual debugger commands in the second case. A dbgfile merely consists of one or more debugger command lines and simplifies keeping specialized groups of debugging commands. The two optional numbers :dtst1 and :dtst2 are also read into the global variables dtst1 and dtst2. If the optional numbers are not present on the command line, then both will be set to DBG_DTSTOFF, which currently is -2. Note: debug files may not begin with an integer or minus sign.

The presence of a debugger command as opposed to the name of a dbgfile is detected by determining if the dbgfile-name/debugger-command string began with "on" or "off"; if so, the string is treated as a debugger command and passed on to initdbg3().

The debugging system exhibits function-level control over printing of debugging information. The dbgmode contains three pieces of information: a detail level (between 0 to 15), whether or not debugging is currently active (ie. on), and whether debugging is to be inherited by deeper functions. Debugging output is preceded by a number of bars indicating the current debugging function depth.

The debug file contains lines following one of the following forms:

   {on|ondis|off} funcname [*] [detail-level] [:dtst1 [:dtst2]] [preq list] [> file]
   keep funcname [:dtst1 [:dtst2]]
   del [funcname | *]

       Debugging Command : Description
       on|off : during program flow, debugging will be turned on/off
       if the associated function name (funcname) is encountered.
       keep : don't modify debugging status, but allow changes to dtst1,2
       ondis : debugging for the given function is unchanged, but
       dbgswitch() can be used to turn it on (on but disabled)
       funcname : debugging information concerns this function name
       * : debugging mode will not be inherited by deeper
       routines (default: debug mode will be inherited)
       detail-level : debugging detail levels are in the interval [0,15],
       with 0 as default (and least detail).
       :dtst1 : a colon-number signifies a dtst1 value
       :dtst2 : a colon-number signifies a dtst2 value
       preq-list : a list of function names, optionally preceded with no
       intervening white space by a "!". These functions must
       be present in the current routine stack (or not present
       if preceded by a "!") for the debugging command to be fire.
       > filename : debugging output will be directed to this file. It
       will be opened once, and debugging output appended to,
       the given file.
       del funcname : delete debugging info on funcname from debugging system
       del * : delete all debugging info from debugging system

The Inline-Debugging Library

There's more than edbg(), rdbg(), and dprintf()! Each of these functions have "macro((...))" variants which are "disappearing" versions controlled via the C/C++ preprocessor (in particular, Edbg(()), Rdbg(()), and Dprintf(()) ).

That is, if -DDEBUG is on the command line (or #define DEBUG is in the code), the inline debugger's "disappearing macros" are available, otherwise the preprocessor will eliminate them. Makes it easy to get that production version but keep the inline debugger instrumentation available for when you really need it!

Dbgswitch((...))
int dbgswitch(int on)

If dtst1 and optionally dtst2 were entered on the command line, then dbgswitch can be used to further delineate debugging selection. Basically, dbgswitch takes a true/false (0=false, other=true) and will turn dbgmode on/off as indicated. dtst1 and dtst2 may, of course, be used in the test.

Dbgtst((...))
int dbgtst(int detail)

This function tests to see if a dprintf() at the specified detail level would print or not.

Dprintf((...))
int dprintf(int detail,char *fmt,...)

dprintf(detail-level,"...",...): except for the first integer argument, this function appears like a member of the printf() family. The detail-level is an integer between 0 to 15. The data will be printed with the usual leading dots indicating routine depth. This function returns a 1 if debugging is enabled, 0 else.

Edbg((...))
void edbg(char *fmt,...)

edbg("funcname(...)",...): the function appears like a member of the printf() family, except that the string must begin with a funcname. The funcname is checked against the debug file info and, if the funcname appears in the debugging database, the dbgmode is modified appropriately. This call is to be placed at the beginning of every function (to be debugged); it signifies that the function has been entered. The string will be printed if the dbgmode so indicates at a detail-level of zero.

Initdbg((...))
void initdbg(int *argc,char **argv)

Call initdbg("argc,argv) at the beginning of the main program.

Initdbg2((...))
void initdbg2(char *dbgfile)

The function processes all the lines in the dbgfile using initdbg3. It is called by initdbg().

Initdbg3((...))
void initdbg3(char *buf)

The function processes a single line, usually provided by initdbg2(), and modifies the debugging database.

Prt_dbgstr((...))
void prt_dbgstr()

Prints out the entire debugging database. This function is useful for documenting a particular debugging run with what debugging information was used.

Prt_traceback((...))
void prt_traceback()

prints the current routine stack (quaintly known as a traceback)

Rdbg((...))
void rdbg(char *fmt,...)

rdbg("funcname ...",...): the function appears like a member of the printf() family, except that the format string must begin with the current funcname. This call is to be placed just before every return point; it signifies that the function is returning. The string will be printed if the dbgmode so indicates at a detail-level of zero.


Rom 6:23
For the wages of sin is death, but the gift of God is eternal life in Christ Jesus our Lord.


Last Modified Aug 22, 2016 09:43:20 PM © 2012, Charles E Campbell