Dynamic parameters

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;
	}
	


Next Prev Main Top Feedback