This minitutorial discusses several topics:

Dropping into the Debugger

Every Lisp Environment comes with a debugger builtin. Given the fact that all environments have the capability to function as "dumb" interpreter ( i.e. do not incrementally compile functions on-the-fly ), this really isn't surprising. Most Lisp "Hackers" like environments that allow them to develop code incrementally with maximum opportunity to "explore".

By now I'm sure all of you have manage to drop into the HLW debugger. You can do something as simple as:

     CL-USER 5 > car
     Error: The variable CAR is unbound
     1 (continue) Try evaluating it again
     2 Return a value to use
     3 Return a value to set it to
     4 (abort) Return to level 0.
     5 return to top loop level 0.

     Type :c followed by a number to proceed or type :? for other options

     CL-USER 12 : 1 > 
Note that the Listener's cursor has changed from being 'CL-USER XX >' to 'CL-USER XX : 1 >' . This means that you have entered the debugger and are at "break level" one. If you where to type car again you would end up at "break level" two, 'CL-USER XX : 2 >'.
       CL-USER 13 : 2  > :a
       CL-USER 14 : 1  > 

Meaning, if you were to enter ':a' at this point you would drop back to level one.

It is not uncommon to wander the lab and see people at "break level" 23 or some such. This isn't really a good idea since there is a bunch of stuff on the "Stack" waiting for you to resolve the breaks. Unfortunately, ( or not depending on your point of view) you can type in anything at the break prompt that you could type in at the top level Listener prompt.

HLW's debugger has numerous commands. To get a short listing of the possible commands type ':help' which will produce the following list:

:lf      look for symbol
:bug-form  Print a bug report form
:bb      Print a full backtrace suitable for a bug report
:bb      Print a full backtrace suitable for a bug report
:ed      Edit the function associated with the frame
:pp      Print the source code for this frame
:v       Print the current frame
:bq      Print a quick backtrace of interesting call frames
:b       Printout a backtrace from the current stack
:a       Abort up one level
:error   Print the error and how to continue
:n       Go down the stack
:p       Go up the stack
:c       Continue from error
:ret     Return from frame
:res     Restart frame
:trap    Cause the debugger to be re-entered on exit from this frame.
:<       Go to the top of the stack
:>       Go to the bottom of the stack
:cc      Gets the current condition object
:all     Set the debugger options to show all call frames
:lambda  Show the lambda expression for an anonymous interepreted frame
:redo &optional <command identifier> 
         Redo a previous command, identified by its number or a substring.
:get <variable> <command identifier>
         Get a command from the history list and put it in a variable.
:help    Produce this list.
:use <new form> <form to replace> &optional <command identifier> 
         Replace one form with another form in a previous command and redo the command.
:his     List the command history.

I'm only going to explain a few commands on this list.

You can choose any item on the list of options the debugger gives you by just typing ':c' and that number at the debugger prompt. For example

  1  (continue) Try evaluating it again
  2  Return the value of :BAR instead
  3  Return a value to use
  4  Return a value to set it to
  5  (abort) Return to level 2.
  6  return to debug level 2.
  7  Try evaluating it again
  8  Return a value to use
  9  Return a value to set it to
  10 Return to level 1.
  11 return to debug level 1.
  12 Try evaluating it again
  13 Return a value to use
  14 Return a value to set it to
  15 Return to level 0.
  16 return to top loop level 0.

  Type :c followed by a number to proceed or type :? for other options

CL-USER 12 : 3 >
Here I've managed to drop three levels deep into the debugger. If I wished to jump back up to Level 1 , I need only enter a ':c 10 ' at the prompt. Entering ':c 16' would go to Lisp Top Level.

You can always recover the error message and options available by typing ':error'

You can always type ':a' to exit the current break level and move to the next one up.

Dropping into the Debugger on purpose...

You can drop into the debugger on purpose if you wish. Now why would you want to do that? Don't you remember debugging with breakpoints with your favorite procedural debugger ( e.g. gdb or dbx on a Unix box )? Sometimes you would like to see how your function is behaving at a certain point in time.
        > (defun  debug-me ( int  num-times  result  )
            (declare ( notinline debug-me ) ) 
		(cond ((= num-times 0 ) result )
	                 ((= num-times 2 ) (break "Help this doesn't work"  ) 
                   (debug-me int num-times ( + int  result)))
	                 ( t (debug-me int (- num-times 1 ) (+ int result ) ) ) ) )
If you now invoke (debug-me 3 5 0 ) the function will run until num-times is equal to 2 and then drop into the debugger. The message that you pass to break is optional [ break takes an optional format string and args for the format string if any ]. At this point you can enter ':c 1" to continue evaluation.
Which will drop you back into the debugger since the invocation on that condition is remiss. You'll stay at "break level" one since you are purposely invoking break. [ Don't abort and go on to next section. ]

Stack Backtrace

Sometimes you land in the debugger and you have NO idea how or why you got there. At this time invoke a Stack Backtrace. You can do this by typing ':b' . This will show all the frames back to the Lisp Top Level. If you just want to see the frames back to where a particular function (eval for example)got called the type ':b eval'.
Additionally, you type ':b 3' to look back just three frames. The command ':error' will display the same message you got when you dropped into the debugger at the current frame.

You can look at the local variable bindings entering

            CL-USER 23 : 1  >  :n  ; move up the stack from the break.
            CL-USER 24 : 1  >  :v
	    Interpreted call to DEBUG-ME : 
		   INT       : 3
		   NUM-TIMES : 2
		   RESULT    : 9
Or at a particular local variable name:
            CL-USER 25 : 1  >   num-times 
                 2

You could temporarly fix this by calling
         CL-USER 26 : 1 > (setf  num-times   1 )  ; but we're not suppose 
                                                     ;to use setf. :-)

and then typing

         CL-USER 27 : 1  > :c  1
At which point the functions exits "normally". If you reinvoke that function again and call up the stack backtrace you can examine the previous invocations. You can go up to the top of the stack by typing ':p' and back down ( towards the initial caller ) by typing ':n'. If you are looking at the stack backtrace just think of the emacs and 'p' to go to previous ( up ) and 'n' to go to next (down).

What else can I do?

You can go on to the minitutorial on stepping and tracing.
Placing (break) in your code can be a pain since your have to re-evaluate the function to great the compiler to remove the "breakpoint". However, this recompilation process is quite fast. Or you can try to use the trace facility more effectively. See the trace minitutorial for more info.
Back to HLW and Tool Time

Last modified: by Lyman S. Taylor(lyman@cc.gatech.edu)
(c) copyright Lyman S. Taylor 1995, All rights reserved