views:

620

answers:

2

I set out to get myself a neat C debugging macro, not really sure what I really wanted (and being clueless when it comes to macros) I turned to google. Some time later and I now think I know what I want, but not how it works. I haven't had much luck with getting decent information about macros and techniques for debugging.

What I've been using in the past have been something like this:

#ifdef DEBUG
 #define DBG(x) printf x
#else
 #define DBG(x) /* nothing */
#endif

Problem is that it can get quite messy and ultimately you end up commenting out old debug messages eventhough you probably will need them later on.

The best example I found was from some slides from an advanced c course, which can be found here: http://www.mpi-inf.mpg.de/departments/rg1/teaching/advancedc-ws08/script/lecture07.pdf (the relevant parts are slide 19-23, but most of it is included below)

Being lecture slides they unfortunately need some explaining to go. But they mention something that seems quite useful:

DBG((MOD_PARSER , "z = %d\n", z));

Where MOD_PARSER is a debug module/category and the rest of the arguments are meant to be given to printf.

And the implementation of DBG:

#ifdef PRGDEBUG
 #define DBG(x) dbg_printer x
#else
 #define DBG(x) /* nothing */
#endif
void dbg_printer(int module , const char *fmt, ...);

Problem #1 is to write the dbg_printer function, I'm not sure how to pass the variable number of arguments to an printf statement.

The slides go on to discuss how to elegantly add new modules and I'm fairly certain that I haven't understood this at all, but anyway...

*How to add new modules elegantly
*Add a file debug_modules.def 
ADD_MOD(0, PARSER) 
ADD_MOD(1, SOLVER) 
ADD_MOD(2, PRINTER)

...

*“Generate” an enum with debug modules: debug.h
...
#define ADD_MOD(num, id) MOD_ ## id = 1 << num,
enum _debug_modules_t {
#include "debug_modules.def"
};
#undef ADD_MOD
...

...

*Preprocessor yields enum _debug_modules_t {
 MOD_PARSER = 1 << 0,
 MOD_SOLVER = 1 << 1,
 MOD_PRINTER = 1 << 2,
};

I don't see why you would left-shift the values of the enumeration elements, some nifty trick I'm missing?

Aside from the slides above I haven't seen a single example or article/post/whatever even mentioning this so maybe this isn't even suitable for my purposes. Does this sound reasonable and are similar techniques actually used?

As of now the question is how to implement dbg_printer and really how the debug modules enumeration is supposed to work, but seeing as how I might have misunderstood everything that might change :(

+2  A: 

To pass the variable number of arguments from dbg_printer, you call vfprintf instead of fprintf. Something like this:

#include <stdarg.h>

extern int dbg_mod_stderr;
extern int dbg_mod_file;
extern FILE *dbg_file;

void dbg_printer(int module , const char *fmt, ...)
{
    if (dbg_mod_stderr & module)
    {
        va_list ap;

        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
        va_end(ap);
    }

    if (dbg_file != NULL && (dbg_mod_file & module))
    {
        va_list ap;

        va_start(ap, fmt);
        vfprintf(dbg_file, fmt, ap);
        va_end(ap);
    }
}

(This example show how to allow one set of debugging messages to go to stderr, and a different set to a debug file)

The left-shifting of the constants allows you to have any subset of modules selected, not just a single one. For example, with the above code, you would turn on debugging of the parser and solver to stderr like so:

dbg_mod_stderr |= MOD_PARSER;
dbg_mod_stderr |= MOD_SOLVER;

and you could later turn off debugging of the solver like this:

dbg_mod_stderr &= ~MOD_SOLVER;

(This works because each MOD_ constant has only a single, unique bit set in the binary representation).

caf
fprintf(dbg_file, fmt, ap); should be vfprintf ???
Dipstick
Oops! Now fixed
caf
+3  A: 

I don't see why you would left-shift the values of the enumeration elements, some nifty trick I'm missing?

This is most likely so that more than one of the elements can be indicated by joining them with bitwise-or.

My preferred debug-logging macro does not have the module-specificity, but adds the filename and line number to the output and is relatively simple:

    #ifdef DEBUG
        #define DLOG(fmt, args...) printf("%s:%d "fmt,__FILE__,__LINE__,args)
    #else
        #define DLOG(fmt, args...)
    #endif
Isaac
+1 for c99 __FILE__ and __LINE__ -- dont forget __func__
Tom Dignan
comments have the underscores stripped. i believe its <under><under>func<under><under>
Tom Dignan
I do similar, but with some compilers "__FILE__" is the whole filename, including path, so is often unusable on large projects with complicated source code trees. Instead I define DBG_MODULE which might be a file or package and use that along with "__func__". If debugging macros are to be left in the code to report problems then line number is also not that useful as you would have to dig out the exact version of code being used (with the same debugging macros in!!!). In general I find DBG_MODULE/"__func__" far more useful and usable than "__FILE__"/"__LINE__".
Dipstick
Tom Dignan: You can put items in comments in backticks, so they appear like this: `__func__`.
caf
Very good points about `__func__` being more useful and the length of `__FILE__` making it hard to use. Since I'm predominantly in Objective-C/Mac OS X, `__func__` does not give terribly usable results.
Isaac
@Isaac - most name mangling compilers provide `__PRETTY_FUNCTION__` as an alternative to `__func__` or `__FUNC__` to unmangle names. I'm fairly sure Objective-C has it.
Dipstick
@chrisharris: Yes, `__PRETTY_FUNCTION__` works, at least in Objective-C on 10.6. Thanks for the tip! (I only wish I'd known about it a year ago.)
Isaac