C Debugging Macros

Any experienced programmer can relate to sprinkling their code with printf statements to try and figure out a NULL pointer, or perhaps whether a function has been reached or not. This is the first step towards debugging code.   Further down the line can include using GDB or Valgrind to check code entry paths, modify variables during runtime or check memory usage or look for memory leaks from non-free malloc’d data upon program exit.  These tools are usually pulled out when the going gets tough!  As an initial pass during basic unit testing debug information is extremely handy.  What I usually attempt to do is create a header file (*.h) that can be included in any (*.c) files I happen to be working on.  This way, if I modify my macro it only requires a change in one place to complete.  For example:

#include <stdio.h>
#include <stdlib.h>
 
#ifdef DEBUG
#define DEBUGP(x, args ...) fprintf(stderr, " [%s(), %s:%u]\n" \
x, __FUNCTION__, __FILE__,__LINE__, ## args)
#else
#define DEBUGP(x, args ...)
#endif
 
void calculation() {
	DEBUGP("data: %u\n",5 * 5);
}
 
int main()
{
	DEBUGP("Program started.\n");
	calculation();
	return 0;
}

Now every time we want to output any debug information we can call DEBUGP(..data..);.  In doing so, we also get the file name, the function it was called in, and what line it occurred at.  This is extremely helpful for debugging purposes.  This macro utilizes the built-in C macros of __LINE____FUNCTION__, and __FILE__.   Another piece to note is, I have #ifdef DEBUG, this means that if this variable is defined then we can reference DEBUGP, if the DEBUG variable is not defined then we call our “#define DEBUGP(x, args …)“, which does not print out any debug data.  This means during compilation we can pass the -DDEBUG flag to the compiler, meaning we can turn on/off our debugging flag effortlessly.

So putting this all together, lets compile the program with the flag enabled.

erik@debian:/debug$ gcc -DDEBUG -o debug debug.c

Now that it is compiled lets run it.

erik@debian:/debug$./debug
[main(), debug.c:18] Program started.
[calculation(), debug.c:12] data: 25

So as we can see, with a little bit of organization and taking advantage of C macros we can make debugging a lot easier for ourselves!