1. Why using printf debugging when debuggers are available?
Certain cases of socket, multi-threaded, or asynchronous programming make extremely difficult, by design, the use of debuggers or logging libraries. This is when printf debugging is the most useful.
2. Limitations of printf debugging
The two main issues with printf debugging is when you want to enable or disable a particular debugging print, or when you want to remove all the debugging prints. Even with a powerful text search, the only text you can search on is the name of the print function, that is to say “print” or “printf”, and you are going to burn minutes skimming over the code in search of the exact prints that need to be removed. Those minutes will sum to hours, not even taking into account that the search process itself is quite distracting from the actual coding/debugging task. I came up with a few tricks that really improve printf debugging and I felt like sharing those. Do not except anything outstanding, just a few good practices.
3. The solution: for each bug, create a unique function that prints the debugging messages
Instead of calling the print function directly like this (this is roughly Python code),
[some code] print "Now in function stuff, with value %d" % variable [some code]
you want to create a unique function that calls the print function, and which is named "bug_bugname", where "bugname" briefly describes the bug, (ex: "memcheck", "db_utf8", etc.):
def bug_bugname( message ): print 'bug_bugname', message ... [some code] bug_bugname( "Now in function stuff, with value %d" % variable ) [some code]
And for any additional message that has to be printed during the debugging process, just add a call to "bug_bugname()".
By having a unique name for the debugging function, you can do a text search and find only the calls to this function, which will save you time. In addition, having an indirect call to the print function through the debugging function allows you to remove all the prints at once by simply commenting the call to the print function in the debugging function!
This requires some discipline, as one is often tempted to just put a call to the print function thinking "I'll just use one print anyway, so I don't need an indirect debugging function". But I have found that in most cases, I ended up using several prints during a debugging session, and then wasted time on trying to find those prints.
Finally, when coding with other programmers on a code base, it is always good to include one's name in the debugging messages. In the bug_bugname() function above, "bug_bugname" would become "bug_bugname codername". With that, other coders know instantly who is responsible for this new print when they run the program.
Still not convinced about printf debugging? I'll use an argument by authority and leave you with this quote:
The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.
Brian Kernighan, "Unix for Beginners" (1979)
You can receive a notification email every time a new article is posted on Code Capsule, you can subscribe to the newsletter by filling up the form at the top right corner of the blog.