Back to libsm overview

libsm : Debugging and Tracing

$Id: debug.html,v 1.9 2002/02/02 16:50:56 ca Exp $


The debug and trace package provides abstractions for writing trace messages, and abstractions for enabling and disabling debug and trace code at run time.

Sendmail 8.11 and earlier has a -d option which lets you turn on debug and trace code. Debug categories are integers from 0 to 99, with the sole exception of "ANSI", which is a named debug category.

The libsm debug package supports named debug categories. Debug category names have the form of C identifiers. For example, sm_trace_heap controls the output of trace messages from the sm heap package, while sm_check_heap controls the argument validity checking and memory leak detection features of the sm heap package.

In sendmail 8.12, the -d flag is generalized to support both the original style numeric categories, for backwards compatibility, and the new style named categories implemented by libsm. With this change, "-dANSI" is implemented using a libsm named debug category. You will be able to set a collection of named debug categories to the same activation level by specifying a glob pattern. For example,

sets the named category "ANSI" to level 1,
sets all named categories matching the glob pattern "foo_*" to level 3,
sets the numeric categories 0 through 99 to level 1, and
does all of the above.


#include <sm/debug.h>

**  abstractions for printing trace messages
void sm_dprintf(char *fmt, ...)
void sm_dflush()
void sm_debug_setfile(SM_FILE_T *)

**  abstractions for setting and testing debug activation levels
void sm_debug_addsettings(char *settings)
void sm_debug_addsetting(char *pattern, int level)

typedef struct sm_debug SM_DEBUG_T;
SM_DEBUG_T dbg = SM_DEBUG_INITIALIZER("name", "@(#)$Debug: name - description $");

bool sm_debug_active(SM_DEBUG_T *debug, int level)
int  sm_debug_level(SM_DEBUG_T *debug)
bool sm_debug_unknown(SM_DEBUG_T *debug)

Naming Conventions

All debug categories defined by libsm have names of the form sm_*. Debug categories that turn on trace output have names of the form *_trace_*. Debug categories that turn on run time checks have names of the form *_check_*. Here are all of the libsm debug categories as of March 2000:
Variable name Category name Meaning
SmExpensiveAssert sm_check_assert enable expensive SM_ASSERT checking
SmExpensiveRequire sm_check_require enable expensive SM_REQUIRE checking
SmExpensiveEnsure sm_check_ensure enable expensive SM_ENSURE checking
SmHeapTrace sm_trace_heap trace sm_{malloc,realloc,free} calls
SmHeapCheck sm_check_heap enable checking and memory leak detection in sm_{malloc,realloc,free}

Function Reference

To create a new debug category, use the SM_DEBUG_INITIALIZER macro to initialize a static variable of type SM_DEBUG_T. For example,
	    "@(#)$Debug: ANSI - enable reverse video in debug output $");
There is no centralized table of category names that needs to be edited in order to add a new debug category. The sole purpose of the second argument to SM_DEBUG_INITIALIZER is to provide an easy way to find out what named debug categories are present in a sendmail binary. You can use:
ident /usr/sbin/sendmail | grep Debug
what /usr/sbin/sendmail | grep Debug
void sm_debug_addsetting(char *pattern, int level)
All debug categories default to activation level 0, which means no activity. This function updates an internal database of debug settings, setting all categories whose name matches the specified glob pattern to the specified activation level. The level argument must be >= 0.

void sm_debug_addsettings(char *settings)
This function is used to process the -d command line option of Sendmail 9.x, and of other programs that support the setting of named debug categories. The settings argument is a comma-separated list of settings; each setting is a glob pattern, optionally followed by a '.' and a decimal numeral.

bool sm_debug_active(SM_DEBUG_T *debug, int level)
This macro returns true if the activation level of the statically initialized debug structure debug is >= the specified level. The test is performed very efficiently: in the most common case, when the result is false, only a single comparison operation is performed.

This macro performs a function call only if the debug structure has an unknown activation level. All debug structures are in this state at the beginning of program execution, and after a call to sm_debug_addsetting.

int sm_debug_level(SM_DEBUG_T *debug)
This macro returns the activation level of the specified debug structure. The comparison
sm_debug_level(debug) >= level
is slightly less efficient than, but otherwise semantically equivalent to
sm_debug_active(debug, level)

bool sm_debug_unknown(SM_DEBUG_T *debug)
This macro returns true if the activation level of the specified debug structure is unknown. Here is an example of how the macro might be used:
if (sm_debug_unknown(&FooDebug))
	if (sm_debug_active(&FooDebug, 1))
		... perform some expensive data structure initializations
		... in order to enable the "foo" debugging mechanism
		... disable the "foo" debugging mechanism
The purpose of using sm_debug_unknown in the above example is to avoid performing the expensive initializations each time through the code. So it's a performance hack. A debug structure is in the "unknown" state at the beginning of program execution, and after a call to sm_debug_addsetting. A side effect of calling sm_debug_active is that the activation level becomes known.

void sm_dprintf(char *fmt, ...)
This function is used to print a debug message. The standard idiom is
if (sm_debug_active(&BarDebug, 1))
	sm_dprintf("bar: about to test tensile strength of bar %d\n", i);

void sm_dflush()
Flush the debug output stream.

void sm_debug_setfile(SM_FILE_T *file)
This function lets you specify where debug output is printed. By default, debug output is written to standard output.

We want to allow you to direct debug output to syslog. The current plan is to provide a standard interface for creating an SM_FILE_T object that writes to syslog.