Previous Next Table of Contents

4. Debugging: Theory

Debugging is a science. You formulate a hypothesis, make predictions based on the hypothesis, run the program and provide it experimental input, observe its behavior, and confirm or refute the hypothesis.

A good hypothesis is one which makes surprising predictions which then come true; predictions that other hypotheses don't make.

The first step in debugging is not to write bugs in the first place. This sounds obvious, but sadly, is all too often ignored.

If you build a program, and you get any errors or any warnings, you should fix them before continuing. C was designed so that many buggy ways of writing code are legal, but will draw warnings from a suitably smart compiler (such as ``gcc'' with the -Wall flag enabled). It takes only minutes to check your warnings and to fix the code that generates them, but it takes hours to find bugs otherwise.

``Desk checking'' (proof reading) is almost a lost art these days. Too bad. You should desk check your code before even compiling it, and desk-check it again periodically to keep it fresh in mind and find new errors. If you have someone in your group whose only job it is to desk-check other people's code, that person will find and fix more bugs than everyone else combined.

One can desk-check several hundred lines of code per hour. A top-flight software engineer will write, roughly, 99% accurate code on the first pass, which still means one bug per hundred lines. And you are not top flight. So ... you will find several bugs per hour by desk checking. This is a very rapid bug fixing technique. Compare that to all the hours you spend screwing around with broken programs trying to find one bug at a time.

The next technique beyond desk-checking is the time-honored technique of inserting ``print'' statements into the code, and then watching the logged values. Within Circle code, you can call printf(), fprintf(), or log() to dump interesting values at interesting times. Where and when to dump these values is an art, which you will learn only with practice.

If you don't already know how to redirect output in your operating system, now is the time to learn. On Unix, type the command ``man csh'', and read the part about the ``>'' operator. You should also learn the difference between ``standard output'' (for example, output from ``printf'') and ``standard error'' (for example, output from ``fprintf(stderr, ...)'').

Ultimately, you cannot fix a program unless you understand how it's operating in the first place. Powerful debugging tools will help you collect data, but they can't interpret it, and they can't fix the underlying problems. Only you can do that.

When you find a bug ... your first impulse will be to change the code, kill the manifestation of the bug, and declare it fixed. Not so fast! The bug you observe is often just the symptom of a deeper bug. You should keep pursuing the bug, all the way down. You should grok the bug and cherish it in fullness before causing its discorporation.

Also, when finding a bug, ask yourself two questions: ``What design and programming habits led to the introduction of the bug in the first place?'' And: ``What habits would systematically prevent the introduction of bugs like this?''

Previous Next Table of Contents