"You know, my dear Watson," he said, pausing to produce another billow of aromatic smoke, "a computer program can be debugged using the same methods that are applied to solving a mystery."
"You mean observation and deduction, don't you Holmes?"
"Precisely, Watson. When a program stops unexpectedly or prints out unanticipated messages, the programmer can safely assume that the program has met with foul play. In other words, it has a bug."
"But how does one debug a large program? So many things could go wrong. The task of righting all of them seems insurmountable."
"It would be impossible indeed if you tried to solve all the problems at once. No, my friend, one should attack the matter bit by bit. Try to identify the little problems and solve them. Do not search for `the magic solution' that solves everything. Debugging means careful work. We would do well to remember the French philosopher Voltaire:
`Le programme ne le raccommode pas,'
which means, loosely, `PROGRAMS DO NOT DEBUG THEMSELVES.'"
"Did Voltaire really say that?"
"No, but he would have if he had thought of it. But nonetheless, when a program dies, the programmer has immediately before him or her the single greatest clue to the problem: the characters on the screen. Sometimes the program itself will print out messages of significance to alert the programmer to errors."
"But that requires the programmer to think ahead and include some diagnostic messages in the program, doesn't it Holmes?"
"Of course, Watson. But when no such messages are available, one can still learn quite a bit from the messages (or lack of them) from DOS."
"Is that all there is to debugging?"
"Most certainly not. In some cases, the cause of the error can be found by inspection, such as a typographical error or the use of the wrong addressing mode. But more often, one must use a debugger to determine the circumstances that led to the error in order to understand the problem."
"The circumstances, Holmes?"
"Yes, Watson. In particular, the contents of the registers tell a great deal about what the program has been doing. When subroutines or interrupts are used, one register is extremely helpful."
"You mean the stack pointer."
"Exactly. But it is not the contents of the stack pointer itself that is of so great importance, but rather the contents of the stack around the offset specified by the stack pointer. There is an old Hungarian saying:
`Kerek egy kis uveg konyakot,'
which means `The stack contains a history of your program,' or `Bring me a small flask of brandy;' I can never remember which. By examining the contents of the stack it is possible to locate previous register contents (saved by PUSH) and return offsets. These return offsets form a path through the program that can be followed."
"There may not be many return offsets on the stack. How is one to follow the 'flow' of the program up to the error?"
"The BREAKPOINT and TRACE operations hold the key to that problem. The judicious choice of breakpoints permits the programmer to isolate a region in which the error may be located. When this suspicious region is isolated, it is possible to single step through the code and carefully observe the changes in the registers and memory variables."
"Can you be more explicit?" asked Watson, still perplexed.
"There is no method that works all the time, but there are several heuristics," replied Holmes. "In the CONTRAST method, the programmer should compare what the value should be with what the value is. Also, the INPUT/OUTPUT method can help. Examine the variables upon starting a block of code (the input), and examine them again upon coming out of the block (the output). The programmer must then ask the following two questions:
Is the input correct? If not, a problem lies before this block.
Does the output correspond to the input? If not, a problem lies within this block.
If both are true, then the problem lies further on. Using the debugger's memory window, the programmer can even force execution of a block of code with specific values in memory variables. There is no mechanical process that will automatically solve every problem. The programmer must think. Remember:
`Mind is like parachute: works best when open.'"
"Confucius?"
"No, Charlie Chan. But the point is that the programmer must think about the program in order to debug it."
"But after one has located the problem, it is so time consuming to re-edit and re-assemble and re-link the program. Surely there must be a better way to make small changes."
"There is, Watson. With the debugger one can perform a limited amount of editing of the machine code by assembling new instructions in place. One can even replace unwanted code with NOPs (null operations). After these small changes, the programmer can continue to run the program in the debugger. Since these changes are not saved, however, the programmer must still edit the source code to make the changes permanent."
"Won't this take a lot of time?"
"You will have all the time you need, if you start early."