We generally need a way to control the dynamic behavior of the program by setting the values of various parameters. One set of parameters that need to be controlled are the debug and tracing values. For large programs, there can be a large number of such parameters.
One way that works very well is to treat the set of parameters as a "data-base". We populate this data-base through some means such as command-line arguments, environment variables or init files. Then, the various modules can "query" this data-base to see if the control variables of interest have been defined and with what values.
The minimal API supported by the dbgdb
module is as follows:
int add_dbgdb(const char * key,
int nparams, const char * params[]);
int in_dbgdb(const char * key);
int nparams_dbgdb(const char * key);
char * param_dbgdb(const char * key, int param_num);
add_dbgdb("foo", 2, foo_opts)
, where foo_opts
is { "3", "none"}
adds a record to the data base with the key "foo"
and 2 parameters, "3"
and "none"
. It returns 0 if it succeeds, otherwise a non-zero error code. Note that all entries in the data-base are strings.
The three functions in_dbgdb()
, nparams_dbgdb()
and param_dbgdb()
are used to query the data-base. They all pass in the key as their first argument, and all return 0 if there is no record with that key in the data-base. Otherwise nparams_dbgdb()
returns the number of parameters in the data-base. As long as the value of param_num
is less than the number of parameters for the key param_dbgdb()
returns that parameter, otherwise it returns 0.
For a concrete example, we could use this interface to setup the value of lcl_dbg_int_stack
as follows:
if( in_dbgdb("dbg_int_stack") ) {
/* user wants some debugging; check how much */
char * val = param_dbgdb("dbg_int_stack", 0);
if( val ) {
lcl_dbg_int_stack = atoi(val);
}
else {
/* default: minimal debugging */
lcl_dbg_int_stack = 1;
}
}
else {
/* default: no debugging */
lcl_dbg_int_stack = 0;
}
This function queries the data-base to see if "dbg_int_stack"
has been defined. If so, it looks to see if a particular value was setup for it. If so, it uses that value, otherwise it sets it to a default 1.
Our experience has shown that this idiom is so common that we added another function to the API.
/* check to see if key present.
* if not return no_key_default.
* otherwise check to see if parameter param_num was defined.
* if not return no_param_default.
* otherwise return atoi() of the parameter
*/
int int_dbgdb(const char * key, int no_key_default,
int param_num, int no_param_default);
Using this function, we rewrite the code above as:
lcl_dbg_int_stack = int_dbgdb("dbg_int_stack", 0, 0, 1);
The data-base needs to be populated from outside. Typically, we read strings from the command-line, or the environment, or a configuration/init file, parse the strings and pass the parsed value to add_dbgdb()
. It seems best if the parsing is also done inside dbgdb
. So, we also add the following function to the API:
/* parse the string and add the entries to the data-base */
parse_dbgdb(const char * str);
The grammar we use in our projects is the following:
STRING ::= [RECORD] {':' ENTRY}*
RECORD ::= KEY ['=' { ',' [PARAM] }* ]
Thus the string passed to parse_dbgdb()
consists of a set of records separated by ':'. Each record, if non-empty, consists of a key optionally followed by a set of parameters. The key is separated from the parameters by an '=', and the parameters are separated from each other with ','. Thus a legitimate string would be "foo=3,gah:dbg_int_stack".
An implementation of dbgdb
is shown below. One thing to note: all memory in the implementation, including the string copy area, is statically defined. This is because this module is intended for use in debugging. If it used dynamically allocated memory, it would change the memory allocation pattern, thereby possibly changing the behavior of the program.
#include "dbgdb.h"
#include <string.h>
#include <stdlib.h>
#define MAX_KEYS_DBGDB 256
#define MAX_PARAMS_DBGDB 8
#define BYTES_POOL_DBGDB 8192
static struct {
char * key;
int nparams;
char * params[MAX_PARAMS_DBGDB];
} tbl_dbgdb[MAX_KEYS_DBGDB];
static int at_tbl_dbgdb = 0;
static char pool_dbgdb[BYTES_POOL_DBGDB];
static int at_pool_dbgdb = 0;
static int
index_dbgdb(
const char * key
)
{
int i;
for( i = 0; i < at_tbl_dbgdb; i++ ) {
if( strcmp(tbl_dbgdb[i].key, key) == 0 ) {
return i;
}
}
return -1;
}
static int
do_add_dbgdb(
char * key,
int nparams,
char * params[]
)
{
int i;
int j;
i = index_dbgdb(key);
if( i == -1 ) {
if( at_tbl_dbgdb == MAX_KEYS_DBGDB ) {
return 1;
}
i = at_tbl_dbgdb;
at_tbl_dbgdb++;
}
tbl_dbgdb[i].key = key;
tbl_dbgdb[i].nparams = nparams;
for( j = 0; j < nparams; j++ ) {
tbl_dbgdb[i].params[j] = params[j];
}
return 0;
}
int
add_dbgdb(
const char * key,
int nparams,
const char * params[]
)
{
int len;
int i;
char * ikey;
char * iparams[MAX_PARAMS_DBGDB];
if( nparams > MAX_PARAMS_DBGDB ) {
return 2;
}
len = strlen(key)+1;
for( i = 0; i < nparams; i++ ) {
len += strlen(params[i])+1;
}
if( at_pool_dbgdb + len > BYTES_POOL_DBGDB ) {
return 3;
}
ikey = pool_dbgdb + at_pool_dbgdb;
at_pool_dbgdb += strlen(key)+1;
strcpy(ikey, key);
for( i = 0; i < nparams; i++ ) {
iparams[i] = pool_dbgdb + at_pool_dbgdb;
at_pool_dbgdb += strlen(params[i])+1;
strcpy(iparams[i], params[i]);
}
return do_add_dbgdb(ikey, nparams, iparams);
}
int
in_dbgdb(
const char * key
)
{
return index_dbgdb(key) != -1;
}
int
nparams_dbgdb(
const char * key
)
{
int i = index_dbgdb(key);
if( i == - 1 ) {
return 0;
}
else {
return tbl_dbgdb[i].nparams;
}
}
char *
param_dbgdb(
const char * key,
int num_param
)
{
int i = index_dbgdb(key);
if( i == - 1 ) {
return 0;
}
else if( tbl_dbgdb[i].nparams <= num_param ) {
return 0;
}
else {
return tbl_dbgdb[i].params[num_param];
}
}
int
int_dbgdb(
const char * key,
int no_key_default,
int num_param,
int no_param_default
)
{
int i = index_dbgdb(key);
if( i == - 1 ) {
return no_key_default;
}
else if( tbl_dbgdb[i].nparams <= num_param ) {
return no_param_default;
}
else {
return atoi(tbl_dbgdb[i].params[num_param]);
}
}
int
parse_dbgdb(
const char * str
)
{
int len = strlen(str) + 1;
char * istr;
char * ikey;
char * iparams[MAX_PARAMS_DBGDB];
int i;
int key_sep;
int param_sep;
int status;
if( at_pool_dbgdb + len > BYTES_POOL_DBGDB ) {
return 4;
}
istr = pool_dbgdb + at_pool_dbgdb;
at_pool_dbgdb += len;
while( 1 ) {
ikey = istr;
/* read till end of key */
while( *istr != 0 && *istr != ':' && *istr != '=' ) {
istr++;
}
key_sep = *istr;
*istr = 0;
istr++;
/* read till end of parameters */
i = 0;
if( key_sep == '=' ) {
while( 1 ) {
if( i == MAX_PARAMS_DBGDB ) {
return 5;
}
iparams[i] = istr;
while( *istr != 0 && *istr != ',' && *istr != ':' ) {
istr++;
}
param_sep = *istr;
*istr = 0;
istr++;
i++;
if( param_sep == 0 || param_sep == ':' ) {
key_sep = param_sep;
break;
}
}
}
status = do_add_dbgdb(ikey, i, iparams);
if( status ) {
return status;
}
if( key_sep == 0 ) {
break;
}
}
return 0;
}