Friday, March 28, 2008

Debugging Messages in C

Ah the great feeling when, after days of work, your program finally compiles. There's nothing as rewarding as going through and checking off every cryptic "Symbol % not expected before or after ^ unless this is an odd numbered Tuesday or your last name beings with W" error message.

So now you have your compiled program; you run it... and nothing. DOA. Or worse, it does something you didn't expect. (I heard a tale this week of a program that read in and displayed user input, but was accidentally off by one: enter a, get garbage, enter b, get a) So then the question is: How do you go about dissecting this program and figuring out where the computer didn't understand what you meant? (aka you f%$&ed up) My solution is slapping this bit of code at the beginning of every program I write:

#ifdef DEBUG
#define ErR(a) fprintf(stderr, "%s\n", a)
#else
#ifdef DEBUG_SLOW
#define ErR(a) fprintf(stderr, "%s\n", a); sleep(1)
#else
#define ErR(a)
#endif
#endif

It may look nasty, but that's only because the C preprocessor lacks an elseifdef scenario. In reality, this bit of code works magic. Everywhere in code that I think would be of interest, I simply add:
ErR("Debugging message here");
And go about my business. Then when I got to compile the program, the real magic begins.

When compiling, there is a way to define symbols for the preprocessor from the command line with the -D flag. As can be seen in the snippet above, if DEBUG is defined, it will replace every ErR with a fprintf and display the message. If DEBUG_SLOW is defined, it will replace ErR with a fprintf, as well as adding in a one second pause at each message.

Sample code - hello.c:
main() {
ErR("Beginning of program");
printf("Hello World!\n");
ErR("End of program");
}

Compile with:
cc -o hello hello.c -DDEBUG
and get:
Beginning of program
Hello World!
End of program

Compile with:
cc -o hello hello.c -DDEBUG_SLOW
and get:
Beginning of program *pause*
Hello World!
End of program *pause*


Granted, this debugging macro only works for flow control problems, which is usually my problem. If you're expecting a function to call another function only every third time, with this it will be real easy to watch to see how many times the second function gets called. Then when you're done debugging, just compile the code without DEBUG or DEBUG_SLOW defined, and all the debug info goes away with no performance hit.

An extension to this would be to let the programmer call ErR with more arguments to display along with the message:
ErR("Current location: ", x, y);
But I rarely need more than the basic message. Just need to see how far the program gets before segmentation faulting.

No comments:

Post a Comment