## CS 307: Debugging

Debugging programs is an important skill for a computer scientist. Here are some hints about debugging:

1. Document clearly what each function should do. Write a comment (after a semicolon) that states what the function does. Write a comment describing each argument, unless it is obvious. Write a comment with an example of a call to the function.
```; Compute the value of an investment of a fixed contribution for a
;    given number of time periods at a specified interest rate.
; contribution = amount added at the end of a time period
; time         = number of time periods
; interest     = interest paid per time period, e.g. 6% = 0.06
; Example: (invest 1040 60 0.06)  ; \$1040 per year for 60 years at 6%
(define (invest contribution time interest) ...)
```
These comments are a specification of your program. One source of bugs is an unclear (poorly understood, unwritten) specification. Sometimes, a clear specification will make it obvious how to write the program. One of the best ways to debug is not to write bugs; a good specification helps.

2. Test the function on an easy case first. Often, the easy case is the base case of a recursive function. Make sure the answer is correct for the easy case. Then try a slightly harder case -- one for which you understand the answer.
```> (invest 100 0 0.06)
0

> (invest 100 1 0.06)
100.

> (invest 100 2 0.06)
206.
```
Investing \$100 zero times gives zero. Investing \$100 at the end of one time period leaves us with the given \$100. Investing \$100 for two time periods gives us interest of \$6 on \$100 during the second time period.

If your function works on the base case and a few more cases, it probably works for all cases; if not, it will be easier to understand and fix the function when the case is an easy one.

3. Trace your functions. Use (trace fn ) to trace the function fn. You can turn off a trace with (untrace fn). The trace will show the arguments of a function on entry and the result on exit. The entry and exit are matched by vertical alignment of the trace printouts.

4. As soon as you see a bug, track it down and fix it. Develop a keen eye for anything that looks unusual in your output; if you see something abnormal, focus on it.

5. Test your functions one at a time. This is easy in Lisp: simply type in a call to a function with test arguments, even if it is not the ``main'' function. It is easy to test a single function in isolation, but hard to test a large system.

6. Use rapid prototyping and evolve your programs: write a program that does part of the job, test it, then expand it. When you get part of the program working, you can expand on that model; this is better than repeating the same mistake many times in your code before doing any testing.

7. If your program ``cannot possibly be doing what it is doing'', the bug is probably something that you are not looking at. Take a wider view and look at other things.

8. Instrument your code with debugging printouts: print out internal values and look for abnormalities. Sometimes students stay up all night trying to reason about why their program does not work, when a simple printout would make the problem obvious.

You can detect internal errors and stop your program (and then get variable values) using an (error   "message") call.

```   (if stopping-condition
(error "The elusive bug has occurred!"))
```

9. When you get an error break, DrScheme can provide additional information:
• Click the function name to get instant documentation of the function in which the error occurred.

• Click the Bug symbol to get backtrace information. This will show the sequence of calls that reached the error and variable values.