CMU Common Lisp Debugger Practice

CMU Common Lisp Debugger Practice

This exercise will get you familiar with some of the basic features of the debugger. It would probably be helpful to have the Debugger Quick Reference handy; if interested you could also look at details in the Debugger section of the CMU Common Lisp User's Manual.


First, get into CMU Lisp.
  % lisp

At the star prompt, let's define a variable and give it a value:

  (defvar foo 4)

If you simply type the name of the variable, it will be evaluated, and its value will be returned.

  foo

Let's make a deliberate mistake, like mistyping the name of the variable:

  foi

As you can see, the LISP interpreter is now complaining of an "unbound symbol" -- it tried to evaluate the symbol "foi" and couldn't find a value for it. You're not being given many options. At this point, about the only sensible thing to do is ABORT, which gets you out of the debugger and back to the top level of the LISP interpreter.

  abort

Note that you could also have typed 0, to choose option 0, which was ABORT. I prefer to do that, generally, since it takes fewer keystrokes.

Now let's define a simple function that adds two numbers:

  (defun addem (x y) (+ x y))

You can call the function to see that it works as you'd expect:

  (addem 3 5)

Now let's make another deliberate mistake: let's give it too many arguments.

  (addem 3 5 7)
Notice you get a nicely informative error message, saying it wanted 2 arguments but got three. Also notice that it's telling you where this error occurred, namely in a function called %VERIFY-ARGUMENT-COUNT.

To find out how you got to this function, type

  backtrace
in the debugger. The deepest level of the stack is first, i.e. the call to %VERIFY-ARGUMENT-COUNT. At the next level, you can see that this function was called from ADDEM, and that at the next level, that call happened when the INTERACTIVE-EVAL function was called with (ADDEM 3 5 7) as its argument.

There's little else of interest we can do here, so let's abort again and get back out of the debugger:

  quit

Now let's make a different sort of mistake: let's give the ADDEM function an argument of the wrong type.

  (addem 3 "5")
You can't add numbers and strings, so this is bound to cause some interesting sort of havoc, right?

As usual, the debugger is being friendly enough to provide a useful error message: it tells us the argument called Y is not a number. Again, let's use backtrace to figure out where we are:

  backtrace
As you can see, the offending string, "5", was not noticed until the addition function (TWO-ARG-+) was called.

Let's do one final, more interesting thing. Let's define a recursive function that sums up a list of numbers:

  (defun sum (numlist) 
     (if (null numlist)
         0
         (+ (first numlist) (sum (rest numlist)))))

First let's see this work properly:

  (sum '(1 2 3))

Now let's force an error:

  (sum '(1 2 "3"))

Again, the error is that an argument (in this case "3") is a string, not a number. Let's again use backtrace to see where we are on the call stack:

  backtrace

You can see that we're in the midst of some recursive calls here, which can be hard to follow. A useful strategy in these cases is often to quit out of the debugger

  quit
and then TRACE the function you're interested in. "Tracing" means that the interpreter will make a little report to you every time it enters or exits the function(s) you're interested in, showing you the arguments on the way in and the return value on the way out. For example, doing
  (trace sum)
will mark SUM as a function to be traced, so that when you invoke
  (sum '(1 2 "3"))

you can now see the seequence of calls to SUM as they happen. As you can see, at the deepest level, SUM was called with NIL, i.e. the empty list. This is correct, since that indicates that the recursion is over: the sum of the values in an empty list is zero. What happens next, though, as you can see, is that when it tries to add that zero value to the value "3", the interpreter chokes, since you can't add a number (0) to a string ("3").

At this point you know what the problem is. You can quit out of the debugger the usual way (quit) and then to turn tracing of this function off do:

  (untrace sum)