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 Macrocheck-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-A | Print argument list of function in current frame.
|
Control-Meta-A | Examine or change the n th argument of the current frame.
|
Control-B | Print brief backtrace.
|
Meta-B | Print longer backtrace.
|
Control-C | Attempt to continue.
|
Control-G | Quit to command level.
|
Control-L | Redisplay error message and current frame.
|
Meta-L | Full-screen typeout of current frame.
|
Control-N | Move to next frame. With argument, move down n frames.
|
Meta-N | Move to next frame with full-screen typeout. With argument, move down n frames.
|
Control-P | Move to previous frame. With argument, move up n frames.
|
Meta-P | Move to previous frame with full-screen typeout. With argument, move up n frames.
|
Control-R | Return a value from the current frame.
|
Meta-R | Return several values from the current frame.
|
Control-T | Throw a value to a tag.
|
Control-Z | Throw back to Lisp top level.
|
? or Help | Print 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.