27. The Error Handler
The Lisp Machine Error Handler is an interactive subsystem which can be used to examine and alter the state of the Lisp environment, and to aid in debugging. This chapter explains how to enter the error handler, and how to use it.
27.1 Entering the Error Handler
When an error is detected, the error handler is entered. There are two kinds of errors: those detected by the Lisp Machine's microcode, and those caused by Lisp functions which have called a function such as ferror or error . When there is a microcode error, the error handler prints out a message such as the following:
>>TRAP 5543 (TRANS-TRAP)
The symbol FOOBAR is unbound.
While in the function *EVAL ← SI:LISP-TOP-LEVEL1
The first line of this error message simply indicates entry to the error handler; the mysterious numbers and so on are internal information which may be useful for the maintainers of the error system in case there are bugs in it. The second line contains a description of the error in English. The third line indicates where the error happened by printing a very abbreviated "backtrace" of the stack (see below); in the example, it is saying that the error was taken by the function *eval , which was called by si:lisp-top-level1 . Here is an example of a macrocode error.
>>ERROR: The argument X was 1, which is not a symbol,
While in the function { FERROR ← } FOO ← *EVAL
Here the first line contains the English description of the error message, and the second line contains the abbreviated backtrace. The backtrace here indicates that the function which actually entered the error handler was ferror , but that function is indicated in braces because it is not very important; the useful information here is that the function foo is what called ferror and thus generated the error. There is not any good way to manually get into the error handler; I think when we have interrupts and so on we will need something to let you interrupt the machine and examine the stack and so on. If you are writing a program which has detected an erroneous condition, and wish to signal the error, one of the following two functions can be used.
ferror code format-control-string &rest args
ferror signals an error, causing the error handler to be entered. The error message printed is based on the arguments: format-control-string should be a control string to be given to the function format (q.v.), and args are the rest of the arguments for format . code is used to give the error system some idea of how to treat the error in various ways. For example, it can tell the error system how to continue from the error. Currently it is not implemented, and all callers of ferror should pass nil as the code argument. Example:
(ferror nil "The symbol ~S is not EQ to ~S" 'x 'y)
from inside the function FOO which was called by the function BAR would produce the error message
>>ERROR: The symbol X is not EQ to Y,
While in the function { FERROR ← } FOO ← BAR
Note: the following function is provided primarily for Maclisp compatibility. New programs should use the ferror function as it is capable of producing better formatted and more readable messages.
error message object &optional code
error signals an error, causing the error handler to be entered. The error message printed is object followed by message , as in Maclisp. The code argument is interpreted the same way as in ferror . Example:
(error "The symbols are not EQ" (list 'x 'y))
from inside the function FOO which was called by the function BAR would produce the error message
>>ERROR: (X Y) The symbols are not EQ
While in the function { ERROR ← } FOO ← BAR
check-args Macro
check-args is a macro designed to make it convenient to check the arguments of a function to see if they are reasonable. A check-args form looks like this:
(check-args var  predicate  message )
var is the name of the variable you want to check. predicate is used to determine whether the value of var is O.K. or not. predicate may either be the name of a predicate (.e.g symbolp ) of one argument, which will be applied to the value of var , or if non-atomic it is an arbitrary form which will be evaluated. If predicate returns t , then var is O.K.; otherwise, an error is signalled. message should be a string which describes what the value of var should be. For example, if some argument a of a function must be an array, and some other argument b must be a fixnum between 1 and 6 inclusive, one could write:
(check-arg a arrayp "an array")
(check-arg b (and (fixp b) (>= b 1) (<= b 6)) 
           "a fixnum between 1 and 6 inclusive")
27.2 How to use the Error Handler.
Once inside the error hander, the user may give a wide variety of commands. This section describes how to give the commands, and then explains them in approximate order of usefulness. A summary is provided at the end of the listing. When the error hander is waiting for a command, it prompts with an arrow:
At this point, you may either type in a Lisp expression, or type a command. If you type a Lisp expression, it will be interpreted as a Lisp form, and will be evaluated in the context of the function which got the error. (That is, all bindings which were in effect at the time of the error will be in effect when your form is evaluated.) The result of the evaluation will be printed, and the error handler will prompt again with an arrow. If, during the typeing of the form, you change your mind and want to get back to the error handler's command level, type a Control-G; the error handler will respond with an arrow prompt. In fact, at any time that typein is expected from you, you may type a Control-G to flush what you are doing and get back to command level. This read-eval-print loop maintains the values of + , * , and - just as the top-level one does. Various error handler commands ask for Lisp objects, such as an object to return, or the name of a catch-tag. Whenever it tries to get a Lisp object from you, it expects you to type in a form ; it will evaluate what you type in. This provides greater generality, since there are objects to which you might want to refer which cannot by typed in (such as arrays). If the form you type is non-trivial (not just a constant form), the error handler will show you the result of the evaluation, and ask you if it is what you intended. It expects a Y or N answer (see the function y-or-n-p ), and if you answer negatively it will ask you for another form. To quit out of the command, just type Control-G.
27.3 Error Handler Commands
All error handler commands are single characters, usually with the Control or Meta bits. The single most useful command is Control-Z, which exits from the error handler and throws back to the Lisp top level loop. ITS users should note that Control-Z is not Call. Often you are not interested in using the error handler at all and just want to get back to Lisp top level; so you can do this in one character. This is similar to Control-G in Maclisp. Self-documentation is provided by the Help or "?" command, which types out some documentation on the error handler commands. Often you want to try to continue from the error. To do this, use the Control-C command. The exact way Control-C works depends on the kind of error that happened. For some errors, there is no standard way to continue at all, and Control-C will just tell you this and return to command level. For the very common "unbound symbol" error, it will get a Lisp object from you, which it will store back into the symbol. Then it will continue as if the symbol had been bound to that object in the first place. For unbound-variable or undefined-function errors, you can just type Lisp forms to set the variable or define the function, and then type Control-C; it will proceed without asking anything. Several command are provided to allow you to examine the Lisp control stack (regular pdl), which keeps a record of all functions which are currently active. If you call foo at Lisp's top level, and it calls bar , which in turn calls baz , and baz gets an error, then a backtrace (a backwards trace of the stack) would show all of this information. The error handler has two backtrace commands. Control-B simply prints out the names of the functions on the stack; in the above example it would print
BAZ ← BAR ← FOO ← *EVAL ← LISP-TOP-LEVEL1 ← LISP-TOP-LEVEL
The arrows indicate the direction of calling. The Meta-B command prints a more extensive backtrace, indicating the names of the arguments to the functions and their current values, and also the saved address at which the function was executing (in case you want to look at the code generated by the compiler); for the example above it might look like:
FOO: (P.C. = 23)
   Arg 0 (X): 13
   Arg 1 (Y): 1

BAR: (P.C. = 120)
   Arg 0 (ADDEND): 13
and so on. This means that FOO was executing at instruction 23, and was called with two arguments, whose names (in the Lisp source code) are X and Y. The current values of X and Y are 13 and 1 respectively. The error handler knows about a "current stack frame", and there are several commands which use it. The initially "current" stack frame is the one which signalled the error; either the one which got the microcode error, or the one which called ferror or error . The command Control-L (or Form) clears the screen, retypes the error message that was initially printed when the error handler was entered, and then prints out a description of the current frame, in the format used by Meta-B. The Control-N command moves to the "next" frame (that is, it changes the current frame to be the frame which is the one following the current frame, moving towards the base of the stack), and prints out the frame in this same format. Control-P moves to the "previous" frame, and prints out the frame in the same format. Meta-< moves to the top of the stack, and Meta-> to the bottom; both print out the new current frame. Meta-L prints out the current frame in "full screen" format, which shows the arguments and their values, the local variables and their values, and the machine code with an arrow pointing to the next instruction to be executed. Meta-N moves to the next frame and prints it out in full-screen format, and Meta-P moves to the previous frame and prints it out in full-screen format. Control-A prints out the argument list for the function of the current frame, as would be returned by the function arglist (q.v.). Control-R is used to return a value from the current frame; the frame that called that frame continues running as if the function of the current frame had returned. This command prompts you for a form, which it will evaluate; it returns the resulting value, possibly after confirming it with you. Meta-R is used to return multiple values from the current frame, but it is not currently implemented. The Control-T command does a throw to a given tag with a given value; you are prompted for the tag and the value. Control-Meta-A takes a numeric argument n , and prints out the value of the n th argument of the current frame. It leaves * set to the value of the argument, so that you can use the Lisp read-eval-print loop to examine it. It also leaves + bound to a locative pointing to the n th argument on the stack, so that you can change that argument (by calling rplaca or rplacd on the locative). Control-Meta-L is similar, but refers to the n th local variable of the frame.
27.4 Summary of Commands:
Control-APrint argument list of function in current frame.
Control-Meta-AExamine or change the n th argument of the current frame.
Control-BPrint brief backtrace.
Meta-BPrint longer backtrace.
Control-CAttempt to continue.
Control-GQuit to command level.
Control-LRedisplay error message and current frame.
Meta-LFull-screen typeout of current frame.
Control-NMove to next frame. With argument, move down n frames.
Meta-NMove to next frame with full-screen typeout. With argument, move down n frames.
Control-PMove to previous frame. With argument, move up n frames.
Meta-PMove to previous frame with full-screen typeout. With argument, move up n frames.
Control-RReturn a value from the current frame.
Meta-RReturn several values from the current frame.
Control-TThrow a value to a tag.
Control-ZThrow back to Lisp top level.
? or HelpPrint a help message.
Meta-<Go to top of stack.
Meta->Go to bottom of stack.
27.5 Miscellany
Sometimes, e.g. when the error handler is running, microcode trapping is "disabled": any attempt by the microcode to trap will cause the machine to halt.
trapping-enabled-p message object &optional code
This predicate returns t if trapping is enabled; otherwise it returns nil .
enable-trapping &optional (arg 1)
If arg is 1 , trapping is enabled. If it is 0 , trapping is disabled.