11.1 Basic debugging
Debugging
A program is a series of instructions (aka statements) a computer executes to perform a calculation, like a recipe is a series of instructions a chef executes to make a meal. Programs are hard to write and often have problems, like displaying wrong output. In a program, a problem’s cause is called a bug, and troubleshooting is called debugging.
A basic debugging process is visual inspection: Looking at each statement one-by-one to try to find a bug. For each statement, this process has the hypothesis: This statement has a bug. The test is a visual inspection of the statement. If a bug is found, the hypothesis is validated. If not, the result in inconclusive (the error has been unnoticed).
Note: Because inspecting each statement is time-consuming, a wise programmer only writes a few statements at a time, and then runs those statements to ensure the statements work correctly. A common mistake by beginning programmers is to write dozens of statements before running; if a problem occurs, debugging is harder due to having to inspect dozens of statements instead of just a few.
Inserting debug output statements
Visual inspection of each statement is a good first debugging process for small programs. However, if visual inspection doesn’t detect a bug, another debugging process is to insert debug output statements whose output helps determine whether the preceding statement has the bug.
Hierarchical debugging
For larger programs with 10s to 100s of statements, adding debug output statements for each statement is time-consuming. A hierarchical approach is beneficial. A programmer may divide the statements into regions, and insert one debug output statement after each region. If a region’s output is bad (doesn’t match expected output), the programmer can then create sub-hypotheses for the statements in that region.
Commonly a programmer strives to find regions of related statements. Ex: A region that gets all programmer input, or a region that together perform one computation.
11.6 Programming knowledge
Knowing how a programming language works is important when debugging a program. Knowledge enables a programmer to create hypotheses of the bug’s cause.
A programmer finds that the statement F = C * (9 / 5) + 32 is not behaving as expected, so the programmer adds debug output statements before and after the statement, as below:
Put "DEBUG: C is " to output Put C to output Put "\n" to output F = C * (9 / 5) + 32 Put "DEBUG: F is " to output Put F to output Put "\n" to output
When the program user enters 100, the program outputs “C is 100” followed by “F is 132”. The C is correct, but the F should be 212. So, the programmer creates a hypothesis “The assignment statement is wrong”. But, a careful visual inspection test yields no bug found. The programmer is stuck.
The programmer thus seeks new knowledge, so searches the web with “Why does my program equation output a wrong value?”. The search results in numerous web pages listing common programming errors. The programmer learns of common errors like “Using a variable whose value wasn’t set”, but variable C was clearly set with 100. Another is “Dividing by 0”, but clearly the denominator in 9 / 5 is not 0.
Another listed common error is “Integer division”, with the explanation “In some programming languages, 1 / 2 is not 0.5, but rather is 0, because 1 and 2 are each integers, so only the result’s integer part is kept”. The user thinks “Hmm, that could be relevant. 9 / 5 may be doing integer division.” That hypothesis can be tested by checking if the incorrect result of 132 could be the result of integer division. 9 / 5 is 1.8. Integer division would ignore the .8, leaving just 1. 100 * (1) + 32 is indeed 132. The hypothesis is validated. The programmer investigates further and learns the solution is to write 9.0 / 5.0, and finds that doing so solves the problem.
A different programmer with the same issue, being a college student, asked a teaching assistant: “I’ve narrowed the bug down to this one statement, and I’ve tried but just can’t find the bug in that statement.” The TA says “I suggest you look up ‘integer division’ in your textbook”. The student learns how integer division works, replaces by 9.0 / 5.0, and solves the problem.
A good programmer strives to know how the language works even before encountering bugs, since such knowledge will help prevent many bugs. Knowing how things work can pay off in the long run.