Tuesday, May 20, 2008

A simple but EFFECTIVE debugging technique

Debugging with printf:
================

Even with all the sophisticated debugging tools out there, as developers I guess all of us will agree that printf still remains one of the most convenient and heavily used debugging aids. Whether you are writting new code or trying to debug existing one, there is nothing like good old printf :)

printf not acceptable in production:
=========================

Having said that, we all would agree that nobody would want printfs or fprintf's in their production code. A few reasons for that being:

1. printf messages clutters the stdout and considering that these messages are coming straight from developers hearts, you would DEFINITELY not want to expose them to your clients

2. You must be thinking, "well I can use fprintf instead and redirect the debug messages to a file and add a secret command line parameter to run the program in debug mode". There are still two issues with this;
(i) Your source code will be cluttered with alot of if statements. Even worst the program will have to perform the if (mode == DEBUG) type of tests where ever you have printed debug message(s). If your application is large and runs in real time these tests could prove to be a considerable performance bottleneck.
(ii) (i) is bad but it isn't the worst part. The worst is that the if statements and their corresponding bodies will contribute to the binary size, thus killing the performance of your program (see 3 for details)

3. Debug messages will increase the size of the code section part of the binary, which means that they would considerably effect performance.

Preprocessor directives to the rescue:
===========================

Thus the question is "how do I add debug statements to my program, but at the same time not add debug statements to my program :)". Well the answer to this dilemma is, the use of preprocessor directives (#if). Add the debugging code under a preprocessor directive and compile it conditionally.

#if DEBUG
/* print crazy debug messages or perform some other debugging operations */

#endif

Now whenever I need debug messages I can turn them on by compiling my program with the DEBUG macro defined.

The following small program demonstrates the use of this technique:

3
4 #define DEBUG 1
5
6 int main(int argc, char *argv[])
7 {
8 printf("Hello World\n");
9 #if DEBUG
10 printf("Debugging is enabled\n");
11 #endif
12 return 0;
13 }


Note how we are defining DEBUG at line 4. Also note how we are using it in a preprocessor directive at line 9

9 #if DEBUG
10 printf("Debugging is enabled\n");
11 #endif


Compiling and running the program would yield the following output

Hello World
Debugging is enabled


Now undefine DEBUG by updating line 4 as follows

4 #define DEBUG 0


Compile and execute the program. You should get the following output

Hello World


Not only did the execution of what ever was between #if DEBUG ... #endif was avoided, but it was not even included in the compiled binary. Hence we achieved our goal, i.e. "we added debug statements to our program, but at the same time did not add debug statements" :)

There is a much elegant want to define the DEBUG marco; Remove line 4 of the above program and compile it with gcc's -D option:

gcc -o test -DDEBUG=1 test.c


You can now include the debug messages in your production level code and commit it to your SCM's release branch(however DO make sure to write it under preprocess directives).

Please note that I am in no way denying the importance of debuggers, they are a VERY important component of developing stable software, however there are scenarios where outputing messages are more convenient and that is where this approach comes handy.

No comments: