There are a thousand different debugging techniques. The following are those that I have gotten the most mileage out of over the years:
When you are defining a data structure, take a few minutes to create a routine that dumps the contents of the structure, in human readable form, to stdout or stderr (or if debugging Windows® software - use OutputDebugString()
as mentioned below). This may seem like a waste of time, but it makes tracking down dynamic structure allocation problems much easier.
Once your program is released, you will inevitably find that your users report bugs. Unless you can visit each user's site you need some way to get debugging output from your program, while it is running on the user's system.
This is where logging/tracing earn their keep. Build your program from the ground up with a trace/logging facility. Design your program so that a command line argument, environment variable, or menu option will the enable trace output.
Create a set of multi-level logging routines that provide trace output from your program. Design your program so that a command line argument, environment
variable, or menu option will enable trace output. The option to save trace output to a file gives you a way to get program traces from your users. Assign consistent levels to various functions. The higher the traces level the more information that gets dumped. For example:
Level 1 - Function Entrance and Exits
Level 2 - Function Return Values
Level 3 - Function Parameters
Level 4 - Intermediate results
Feel free to assign your own scheme, but do it consistently, across all modules.
Make use of OutputDebugString() to output debug messages. When used in combination with a multi-level logging capability, you have a powerful tool for tracking down software weirdness. Several third party tools are available
which allow you to tap into the output of this routine, and most of these tools allow you to save the output to a file.
When attempting to debug multi-threaded programs, the trace output can be very helpful when trying to resolve thread synchronization issues.
Look at your malloc(), calloc(), realloc(), and free() calls. Look at each and every malloc() or calloc() call, can you easily point to its matching free() call? If not, get suspicious quick! Avoid calls to realloc() unless
you really understand it (and how it behaves on your given operating system). When possible use calloc() instead of malloc(), calloc by default initializes all allocated bytes to a value of 0x0.
No Bugs!, by David Thielen, published by Addison-Wesley, ISBN 0-201-60890-1
Code Complete, by Steve McConnell, published by Microsoft Press, ISBN 1-55615-484-4
Windows is a registered trademark of Microsoft Corporation.