[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43. Errors and Debugging

.setq error-chapter chapter-number

The first section of this chapter explains how programs can handle errors, by means of condition handlers. It also explains how a program can signal an error if it detects something it doesn't like.

The second explains how users can handle errors, by means of an interactive debugger; that is, it explains how to recover if you do something wrong. A new user of the Lisp Machine, or someone who just wants to know how to deal with errors and not how to cause them, should ignore the first section and skip ahead to (debugger).

The remaining sections describe some other debugging facilities. Anyone who is going to be writing programs for the Lisp Machine should familiarize himself with these.

The trace facility provides the ability to perform certain actions at the time a function is called or at the time it returns. The actions may be simple typeout, or more sophisticated debugging functions.

The advise facility is a somewhat similar facility for modifying the behavior of a function.

The step facility allows the evaluation of a form to be intercepted at every step so that the user may examine just what is happening throughout the execution of the form.

The MAR facility provides the ability to cause a trap on any memory reference to a word (or a set of words) in memory. If something is getting clobbered by agents unknown, this can help track down the source of the clobberage.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1 The Error System


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1.1 Conditions

.setq condition section-page

Programmers often want to control what action is taken by their programs when errors or other exceptional situations occur. Usually different situations are handled in different ways, and in order to express what kind of handling each situation should have, each situation must have an associated name. In Zetalisp there is the concept of a condition. Every condition has a name, which is a symbol. When an unusual situation occurs, some condition is signalled, and a handler for that condition is invoked.

When a condition is signalled, the system (essentially) searches up the stack of nested function invocations looking for a handler established to handle that condition. The handler is a function which gets called to deal with the condition. The condition mechanism itself is just a convenient way for finding an appropriate handler function given the name of an exceptional situation. On top of this is built the error-condition system, which defines what arguments are passed to a handler function and what is done with the values returned by a handler function. Almost all current use of the condition mechanism is for errors, but the user may find other uses for the underlying mechanism.

.need 1000 The search for an appropriate handler is done by the function signal:

Function: signal condition-name &rest args
signal searches through all currently-established condition handlers, starting with the most recent. If it finds one that will handle the condition condition-name, then it calls that handler with a first argument of condition-name, and with args as the rest of the arguments. If the first value returned by the handler is nil, signal will continue searching for another handler; otherwise, it will return the first two values returned by the handler. If signal doesn't find any handler that returns a non-nil value, it will return nil.

Condition handlers are established through the condition-bind special form:

Special Form: condition-bind
The condition-bind special form is used for establishing handlers for conditions. It looks like:
 
(condition-bind ((cond-1 hand-1)
                 (cond-2 hand-2)
                 ...)
  body)
Each cond-n is either the name of a condition, or a list of names of conditions, or nil. If it is nil, a handler is set up for all conditions (this does not mean that the handler really has to handle all conditions, but it will be offered the chance to do so, and can return nil for conditions which it is not interested in). Each hand-n is a form which is evaluated to produce a handler function. The handlers are established sequentially such that the cond-1 handler would be looked at first.
 
Example:
(condition-bind ((:wrong-type-argument 'my-wta-handler)
                 ((lossage-1 lossage-2) lossage-handler))
    (princ "Hello there.")
    (= t 69))
This first sets up the function my-wta-handler to handle the :wrong-type-argument condition. Then, it sets up the value of the symbol lossage-handler to handle both the lossage-1 and lossage-2 conditions. With these handlers set up, it prints out a message and then runs headlong into a wrong-type-argument error by calling the function = with an argument which is not a number. The condition handler my-wta-handler will be given a chance to handle the error. condition-bind makes use of ordinary variable binding, so that if the condition-bind form is thrown through, the handlers will be disestablished. This also means that condition handlers are established only within the current stack-group.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1.2 Error Conditions

[This section is incorrect. The mechanism by which errors are signalled does not work. It will be redesigned someday.]

The use of the condition mechanism by the error system defines an additional protocol for what arguments are passed to error-condition handlers and what values they may return.

There are basically four possible responses to an error: proceeding, restarting, throwing, or entering the debugger. The default action, taken if no handler exists or deigns to handle the error (returns non-nil), is to enter the debugger. A handler may give up on the execution that produced the error by throwing (see *throw, (*throw-fun)). Proceeding means to repair the error and continue execution. The exact meaning of this depends on the particular error, but it generally takes the form of supplying a replacement for an unacceptable argument to some function, and retrying the invocation of that function. Restarting means throwing to a special standard catch-tag, error-restart. Handlers cause proceeding and restarting by returning certain special values, described below.

Each error condition is signalled with some parameters, the meanings of which depend on the condition. For example, the condition :unbound-variable, which means that something tried to find the value of a symbol which was unbound, is signalled with at least one parameter, the unbound symbol. It is always all right to signal an error condition with extra parameters beyond those whose meanings are defined by the condition.

An error condition handler is applied to several arguments. The first argument is the name of the condition that was signalled (a symbol). This allows the same function to handle several different conditions, which is useful if the handling of those conditions is very similar. (The first argument is also the name of the condition for non-error conditions.) The second argument is a format control string (see the description of format, on (format-fun)). The third argument is t if the error is proceedable; otherwise it is nil. The fourth argument is t if the error is restartable; otherwise it is nil. The fifth argument is the name of the function that signalled the error, or nil if the signaller can't figure out the correct name to pass. The rest of the arguments are the parameters with which the condition was signalled. If the format control string is used with these parameters, a readable English message should be produced. Since more information than just the parameters might be needed to print a reasonable message, the program signalling the condition is free to pass any extra parameters it wants to, after the parameters which the condition is defined to take. This means that every handler must expect to be called with an arbitrarily high number of arguments, so every handler should have a &rest argument (see (&rest)).

An error condition handler may return any of several values. If it returns nil, then it is stating that it does not wish to handle the condition after all; the process of signalling will continue looking for a prior handler (established farther down on the stack) as if the handler which returned nil had not existed at all. (This is also true for non-error conditions.) If the handler does wish to handle the condition, it can try to proceed from the error if it is proceedable, or restart from it if it is restartable, or it can throw to a catch tag. Proceeding and restarting are done by returning two values. The first value is one of the following symbols:

:return
If the error was signalled by calling cerror, the second value is returned as the value of cerror. If the error was signalled by calling ferror, proceeding is not allowed. If the error was detected by the Lisp system, the error will be proceeded from, using the second value if a data object is needed. For example, for an :undefined-function error, the handler's second value will be used as the function to be called, in place of the non-existent function definition.

eh:return-value
If the error was signalled by calling ferror or cerror, the second value is returned from that function, regardless of whether the error was proceedable. If the error was detected by the Lisp system, the second value is returned as the result of the function in which the error was detected. It should be obvious that :return-value allows you to do things that are totally unanticipated by the program that got the error.

:error-restart
The second value is thrown to the catch tag error-restart.

The condition handler must not return any other sort of values. However, it can legitimately throw to any tag instead of returning at all. If a handler tries to proceed an unproceedable error or restart an unrestartable one, an error is signalled.

Note that if the handler returns nil, it is not said to have handled the error; rather, it has decided not to handle it, but to "continue to signal" it so that someone else may handle it. If an error is signalled and none of the handlers for the condition decide to handle it, the debugger is entered.

Here is an example of an excessively simple handler for the :wrong-type-argument condition.

[Note that this code does not work in system 56.]

 
;;; This function handles the :wrong-type-argument condition,
;;; which takes two defined parameters: a symbol indicating
;;; the correct type, and the bad value.
(defun sample-wta-handler (condition control-string
			   proceedable-flag restartable-flag
			   function correct-type bad-value
			   &rest rest)
 (prog ()
   (format error-output "~%There was an error in ~S~%" function)
   (lexpr-funcall (function format) error-output
		  control-string correct-type bad-value rest)
   (cond ((and proceedable-flag
	       (yes-or-no-p "Do you want use nil instead?"))
	  (return 'return nil))
	 (t (return nil))))) ;don't handle

If an error condition reaches the error handler, the RESUME (or control-C) command may be used to continue from it. If the condition name has a eh:proceed property, that property is called as a function with two arguments, the stack-group and the "ete" (an internal error-handler data structure). Usually it will ignore these arguments. If this function returns, its value will be returned from the ferror or cerror that signalled the condition. If no such property exists, the error-handler asks the user for a form, evaluates it, and causes ferror or cerror to return that value. Putting such a property on can be used to change the prompt for this form, avoid asking the user, or change things in more far-reaching ways.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1.3 Signalling Errors

Some error conditions are signalled by the Lisp system when it detects that something has gone wrong. Lisp programs can also signal errors, by using any of the functions ferror, cerror, or error. ferror is the most commonly used of these. cerror is used if the signaller of the error wishes to make the error be proceedable or restartable, or both. error is provided for Maclisp compatibility.

A ferror or cerror that doesn't have any particular condition to signal should use nil as the condition name. The only kind of handler that will be invoked by the signaller in this case is the kind that handles all conditions, such as is set up by
 
(condition-bind ((nil something) ...) ...)
In practice, the nil condition is used a great deal.

Function: ferror condition-name control-string &rest params
ferror signals the error condition condition-name. The associated error message is obtained by calling format (see (format)) on control-string and params. The error is neither proceedable nor restartable, so ferror will not return unless the user forces it to by intervening with the debugger. In most cases condition-name is nil, which means that no condition-handler is likely to be found and the debugger will be entered.

 
Examples:
(cond ((> sz 60)
       (ferror nil
	       "The size, ~S, was greater than the maximum"
	       sz))
      (t (foo sz)))

(defun func (a b)
   (cond ((and (> a 3) (not (symbolp b)))
          (ferror ':wrong-type-argument
                  "The name, ~1G~S, must be a symbol"
                  'symbolp
                  b))
         (t (func-internal a b))))

If the error is not handled and the debugger is entered, the error message is printed by calling format with control-string as the control string and the elements of params as the additional arguments. Alternatively, the formatted output functions ((format:outfmt-fun)) can be used to generate the error message:

 
(ferror nil
  (format:outfmt "Frob has "
		 (format:plural (format:onum n-elts)
				" element")
		 " which is too few"))
In this case params are not used for printing the error message, and often none are needed. They may still be useful as information for condition handlers, and those that a condition is documented to expect should always be supplied.

Function: cerror proceedable-flag restartable-flag condition-name control-string &rest params
cerror is just like ferror (see above) except for proceedable-flag and restartable-flag. If cerror is called with a non-nil proceedable-flag, the caller should be prepared to accept the returned value of cerror and use it to retry the operation that failed. Similarly, if he passes cerror a non-nil restartable-flag, he should be sure that there is a *catch above him for the tag error-restart.

If proceedable-flag is t and the error goes to the debugger, if the user says to proceed from the error he will be asked for a replacement object which cerror will return. If proceedable-flag is not t and not nil, the user will not be asked for a replacement object and cerror will return no particular value when the error is proceeded.

Note: Many programs that want to signal restartable errors will want to use the error-restart special form; see (error-restart-fun).
 
Example:
(do ()
    ((symbolp a))
  ; Do this stuff until a becomes a symbol.
  (setq a (cerror t nil ':wrong-type-argument
	   "The argument ~2G~A was ~1G~S, which is not ~3G~A"
	   'symbolp a 'a "a symbol")))
Note: the form in this example is so useful that there is a standard special form to do it, called check-arg (see (check-arg-fun)).

Function: error message &optional object interrupt
error is provided for Maclisp compatibility. In Maclisp, the functionality of error is, essentially, that message gets printed, preceeded by object if present, and that interrupt, if present, is a user interrupt channel to be invoked.

In order to fit this definition into Zetalisp way of handling errors, error is defined to be:
 
(cerror (not (null interrupt))
        nil
        (or (get interrupt 'eh:condition-name)
            interrupt)
        (if (missing? object) ;If no object given
	    "~*~A"
	    "~S ~A")
        object
        message)

Here is what that means in English: first of all, the condition to be signalled is nil if interrupt is nil. If there is some condition whose meaning is close to that of one of the Maclisp user interrupt channels, the name of that channel has an eh:condition-name property, and the value of that property is the name of the condition to signal. Otherwise, interrupt is the name of the condition to signal; probably there will be no handler and the debugger will be entered.

If interrupt is specified, the error will be proceedable. The error will not be restartable. The format control string and the arguments are chosen so that the right error message gets printed, and the handler is passed everything there is to pass.

Macro: error-restart body...
error-restart is useful for denoting a section of a program that can be restarted if certain errors occur during its execution. The forms of the body are evaluated sequentially. If an error occurs within the evaluation of the body and is restarted (by a condition handler or the debugger), the evaluation resumes at the beginning of the error-restart's body. The only way a restartable error can occur is if cerror is called with a second argument of t.
 
Example:
(error-restart
  (setq a (* b d))
  (cond ((> a maxtemp)
         (cerror nil t 'overheat
	    "The frammistat will overheat by ~D. degrees!"
	    (- a maxtemp))))
  (setq q (cons a a)))
If the cerror happens, and the handler invoked (or the debugger) restarts the error, then evaluation will continue with the (setq a (* b d)), and the condition (> a maxtemp) will get checked again.

 
error-restart is implemented as a macro that expands into:
(prog ()
 loop (*catch 'error-restart
              (return (progn
                         form-1
                         form-2
                         ...)))
      (go loop))

Macro: check-arg var-name predicate description [type-symbol]
The check-arg form is useful for checking arguments to make sure that they are valid. A simple example is:
 
(check-arg foo stringp "a string")
foo is the name of an argument whose value should be a string. stringp is a predicate of one argument, which returns t if the argument is a string. "a string" is an English description of the correct type for the variable.

The general form of check-arg is
 
(check-arg var-name
           predicate
           description
	   type-symbol)
var-name is the name of the variable whose value is of the wrong type. If the error is proceeded this variable will be setq'ed to a replacement value. predicate is a test for whether the variable is of the correct type. It can be either a symbol whose function definition takes one argument and returns non-nil if the type is correct, or it can be a non-atomic form which is evaluated to check the type, and presumably contains a reference to the variable var-name. description is a string which expresses predicate in English, to be used in error messages. type-symbol is a symbol which is used by condition handlers to determine what type of argument was expected. It may be omitted if it is to be the same as predicate, which must be a symbol in that case.

The use of the type-symbol is not really well-defined yet, but the intention is that if it is numberp (for example), the condition handlers can tell that a number was needed, and might try to convert the actual supplied value to a number and proceed.

[We need to establish a conventional way of "registering" the type-symbols to be used for various expected types. It might as well be in the form of a table right here.]

The predicate is usually a symbol such as fixp, stringp, listp, or closurep, but when there isn't any convenient predefined predicate, or when the condition is complex, it can be a form. In this case you should supply a type-symbol which encodes the type. For example:
 
(check-arg a
           (and (numberp a) ( a 10.) (> a 0.))
           "a number from one to ten"
           one-to-ten)
If this error got to the debugger, the message
 
The argument a was 17, which is not a number from one to ten.
would be printed.

In general, what constitutes a valid argument is specified in three ways in a check-arg. description is human-understandable, type-symbol is program-understandable, and predicate is executable. It is up to the user to ensure that these three specifications agree.

check-arg uses predicate to determine whether the value of the variable is of the correct type. If it is not, check-arg signals the :wrong-type-argument condition, with four parameters. First, type-symbol if it was supplied, or else predicate if it was atomic, or else nil. Second, the bad value. Third, the name of the argument (var-name). Fourth, a string describing the proper type (description). If the error is proceeded, the variable is set to the value returned, and check-arg starts over, checking the type again. Note that only the first two of these parameters are defined for the :wrong-type-argument condition, and so :wrong-type-argument handlers should only depend on the meaning of these two.

Macro: check-arg-type var-name type-name [description]
This is a useful variant of the check-arg form. A simple example is:
 
(check-arg foo :number)
foo is the name of an argument whose value should be a number. :number is a value which is passed as a second argument to typep (see (typep-fun)); that is, it is a symbol that specifies a data type. The English form of the type name, which gets put into the error message, is found automatically.

The general form of check-arg-type is:
 
(check-arg-type var-name
                type-name
                description)
var-name is the name of the variable whose value is of the wrong type. If the error is proceeded this variable will be setq'ed to a replacement value. type-name describes the type which the variable's value ought to have. It can be exactly those things acceptable as the second argument to typep. description is a string which expresses predicate in English, to be used in error messages. It is optional. If it is omitted, and type-name is one of the keywords accepted by :typep, which describes a basic Lisp data type, then the right description will be provided correctly. If it is omitted and type-name describes some other data type, then the description will be the word "a" followed by the printed representation of type-name in lower-case.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1.4 Standard Condition Names

Some condition names are used by the kernel Lisp system, and are documented below; since they are of global interest, they are on the keyword package. Programs outside the kernel system are free to define their own condition names; it is intended that the description of a function include a description of any conditions that it may signal, so that people writing programs that call that function may handle the condition if they desire. When you decide what package your condition names should be in, you should apply the same criteria you would apply for determining which package a function name should be in; if a program defines its own condition names, they should not be on the keyword package. For example, the condition names chaos:bad-packet-format and arpa:bad-packet-format should be distinct. For further discussion, see (package).

The following table lists all standard conditions and the parameters they take; more will be added in the future. These are all error-conditions, so in addition to the condition name and the parameters, the handler receives the other arguments described above.

:wrong-type-argument type-name value
value is the offending argument, and type-name is a symbol for what type is required. Often, type-name is a predicate which returns non-nil if applied to an acceptable value. If the error is proceeded, the value returned by the handler should be a new value for the argument to be used instead of the one which was of the wrong type.

:inconsistent-arguments list-of-inconsistent-argument-values
These arguments were inconsistent with each other, but the fault does not belong to any particular one of them. This is a catch-all, and it would be good to identify subcases in which a more specific categorization can be made. If the error is proceeded, the value returned by the handler will be returned by the function whose arguments were inconsistent.

:wrong-number-of-arguments function number-of-args-supplied list-of-args-supplied
function was invoked with the wrong number of arguments. The elements of list-of-args-supplied have already been evaluated. If the error is proceeded, the value returned should be a value to be returned by function.

:invalid-function function-name
The name had a function definition but it was no good for calling. You can proceed, supplying a value to return as the value of the call to the function.

:invalid-form form
The so-called form was not a meaningful form for eval. Probably it was of a bad data type. If the error is proceeded, the value returned should be a new form; eval will use it instead.

:undefined-function function-name
The symbol function-name was not defined as a function. If the error is proceeded, then the value returned will be used instead of the (non-existent) definition of function-name.

:unbound-variable variable-name
The symbol variable-name had no value. If the error is proceeded, then the value returned will be used instead of the (non-existent) value of variable-name.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.1.5 "Errset"

As in Maclisp, there is an errset facility which allows a very simple form of error handling. If an error occurs inside an errset, and no condition handler handles it, i.e. the debugger would be entered, control is returned (thrown) to the errset. The errset can control whether or not the debugger's error message is printed. All errors are caught by errset, whether they are signalled by ferror, cerror, error, or the Lisp system itself.

A problem with errset is that it is too powerful; it will apply to any unhandled error at all. If you are writing code that anticipates some specific error, you should find out what condition that error signals and set up a handler. If you use errset and some unanticipated error crops up, you may not be told--this can cause very strange bugs. Note that the variable errset allows all errsets to be disabled for debugging purposes.

Special Form: errset form [flag]
The errset special form catches errors during the evaluation of form. If an error occurs, the usual error message is printed unless flag is nil. Then control is thrown and the errset-form returns nil. flag is evaluated first and is optional, defaulting to t. If no error occurs, the value of the errset-form is a list of one element, the value of form.

Special Form: catch-error form [flag]
catch-error is a variant of errset. This special form catches errors during the evaluation of form, and returns two values. Normally the first value is the value of form and the second value is nil. If an error occurs, the usual error message is printed unless flag is nil, and then control is thrown out of the catch-error form, which returns two values: first nil and second a non-nil value that indicates the occurrence of an error. flag is evaluated first and is optional, defaulting to t.

Variable: errset
If this variable is non-nil, errset forms are not allowed to trap errors. The debugger is entered just as if there were no errset. This is intended mainly for debugging. The initial value of errset is nil.

Special Form: err
This is for Maclisp compatibility only and should not be used.

(err) is a dumb way to cause an error. If executed inside an errset, that errset returns nil, and no message is printed. Otherwise an unseen throw-tag error occurs.

(err form) evaluates form and causes the containing errset to return the result. If executed when not inside an errset, an unseen throw-tag error occurs.

(err form flag), which exists in Maclisp, is not supported.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.2 The Debugger

.setq debugger section-page

When an error condition is signalled and no handlers decide to handle the error, an interactive debugger is entered to allow the user to look around and see what went wrong, and to help him continue the program or abort it. This section describes how to use the debugger.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.2.1 Entering the Debugger

There are two kinds of errors; those generated by the Lisp Machine's microcode, and those generated by Lisp programs (by using ferror or related functions). When there is a microcode error, the debugger 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 indicates entry to the debugger and contains some mysterious internal microcode information: the micro program address, the microcode trap name and parameters, and a microcode backtrace. Users can ignore this line in most cases. 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 signalled inside the function *eval, which was called by si:lisp-top-level1.

Here is an example of an error from Lisp code:
 
>>ERROR: The argument X was 1, which is not a symbol,
While in the function FOO  *EVAL  SI:LISP-TOP-LEVEL1

Here the first line contains the English description of the error message, and the second line contains the abbreviated backtrace. foo signalled the error by calling ferror, however ferror is censored out of the backtrace.

After the debugger's initial message it prints the function that got the error and its arguments.

The debugger can be manually entered either by causing an error (e.g. by typing a ridiculous symbol name such as ahsdgf at the Lisp read-eval-print loop) or by typing the BREAK key with the META shift held down while the program is reading from the terminal. Typing the BREAK key with both CONTROL and META held down will force the program into the debugger immediately, even if it is running. If the BREAK key is typed without META, it puts you into a read-eval-print loop using the break function (see (break-fun)) rather into the debugger.

Function: eh process
Stops process and calls the debugger on it so that you can look at its current state. Exit the debugger with the Control-Z command and eh will release the process and return. process can be a window, in which case the window's process will be used.

If process is not a process but a stack group, the current state of the stack group will be examined. The caller should ensure that no one tries to resume that stack group while the debugger is looking at it.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.2.2 How to Use the Debugger

Once inside the debugger, 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 (a Control or Meta character is interpreted as a command, whereas most normal characters are interpreted as the first character of an expression). If you type the HELP key or the ? key, you will get some introductory help with the error handler.

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, with certain exceptions explained below. The result of the evaluation will be printed, and the debugger will prompt again with an arrow. If, during the typing of the form, you change your mind and want to get back to the debugger's command level, type the ABORT key or a Control-G; the debugger will respond with an arrow prompt. In fact, at any time that typein is expected from you, while you are in the debugger, you may type ABORT or 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.

If an error occurs in the evaluation of the Lisp expression you type, you will get into a second error handler looking at the new error. You can abort the computation and get back to the first error by typing the ABORT key (see below). However, if the error is trivial the abort will be done automatically and the original error message will be reprinted.

Various debugger 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 that cannot be typed in (such as arrays). If the form you type is non-trivial (not just a constant form), the debugger 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, (y-or-n-p-fun)), and if you answer negatively it will ask you for another form. To quit out of the command, just type ABORT or Control-G.

When the debugger evaluates a form, the variable bindings at the point of error are in effect with the following exceptions:

terminal-io is rebound to the stream the error handler is using. eh:old-terminal-io is bound to the value terminal-io had at the point of error.

standard-input and standard-output are rebound to be synonymous with terminal-io; their old bindings are saved in eh:old-standard-input and eh:old-standard-output.

+ and * are rebound to the error handler's previous form and previous value. When the debugger is first entered, + is the last form typed, which is typically the one that caused the error, and * is the value of the previous form.

evalhook (see (evalhook-var)) is rebound to nil, turning off the step facility if it had been in use when the error occurred.

Note that the variable bindings are those in effect at the point of error, not those of the current frame being looked at. This may be changed in the future.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.2.3 "Debugger Commands"

All debugger commands are single characters, usually with the Control or Meta bits. The single most useful command is ABORT (or Control-Z), which exits from the debugger and throws out of the computation that got the error. This is the ABORT key, not a 5-letter command. ITS users should note that Control-Z is not CALL. Often you are not interested in using the debugger at all and just want to get back to Lisp top level; so you can do this in one character.

The ABORT command returns control to the most recent read-eval-print loop. This can be Lisp top level, a break, or the debugger command loop associated with another error. Typing ABORT multiple times will throw back to successively older read-eval-print or command loops until top level is reached. Typing Control-Meta-ABORT, on the other hand, will always throw to top level. Control-Meta-ABORT is not a debugger command, but a system command which is always available no matter what program you are in.

Note that typing ABORT in the middle of typing a form to be evaluated by the debugger aborts that form, and returns to the debugger's command level, while typing ABORT as a debugger command returns out of the debugger and the erring program, to the previous command level.

Self-documentation is provided by the HELP or ? command, which types out some documentation on the debugger commands, including any special commands which apply to the particular error currently being handled.

Often you want to try to proceed from the error. To do this, use the RESUME (or Control-C) command. The exact way RESUME works depends on the kind of error that happened. For some errors, there is no standard way to proceed at all, and RESUME will just tell you this and return to the debugger's command level. For the very common "unbound variable" error, it will get a Lisp object from you, which will be used in place of the (nonexistent) value of the symbol. For unbound-variable or undefined-function errors, you can also just type Lisp forms to set the variable or define the function, and then type RESUME; it will proceed without asking anything.

The debugger 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-detected error, or the one which called ferror, cerror, or error. When the debugger starts it up it shows you this frame in the following format:
 
FOO:
   Arg 0 (X): 13
   Arg 1 (Y): 1
and so on. This means that foo 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. These may not be the original arguments if foo happens to setq its argument variables.

The CLEAR-SCREEN (or Control-L) command clears the screen, retypes the error message that was initially printed when the debugger was entered, and then prints out a description of the current frame, in the above format.

Several commands are provided to allow you to examine the Lisp control stack and to make other frames current than the one which got the error. The control stack (or "regular pdl") 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 debugger 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  SI:*EVAL  SI:LISP-TOP-LEVEL1  SI: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; for the example above it might look like:
 
BAZ:
   Arg 0 (X): 13
   Arg 1 (Y): 1

BAR:
   Arg 0 (ADDEND): 13

FOO:
   Arg 0 (FROB): (A B C . D)
and so on.

The Control-N command moves "down" to the "next" frame (that is, it changes the current frame to be the frame which called it), and prints out the frame in this same format. Control-P moves "up" to the "previous" frame (the one which this one called), 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. Control-S asks you for a string, and searches the stack for a frame whose executing function's name contains that string. That frame becomes current and is printed out. These commands are easy to remember since they are analogous to editor commands.

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. Refer to (understanding-compiled-code) for help in reading this machine code.

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. Meta-S is like Control-S but does a full-screen display.

Commands such as Control-N and Meta-N, which are meaningful to repeat, take a prefix numeric argument and repeat that many types. The numeric argument is typed by using Control- or Meta- and the number keys, as in the editor.

Control-E puts you into the editor, looking at the source code for the function in the current frame. This is useful when you have found a function which caused the error and needs to be fixed. The editor command Control-Z will return to the error handler, if it is still there.

Meta-C is similar to Control-C, but in the case of an unbound variable or undefined function, actually setqs the variable or defines the function, so that the error will not happen again. Control-C (or RESUME) provides a replacement value but does not actually change the variable.

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.

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-R is a variation of Control-R; it starts the current frame over with the same function and arguments. If the function has been redefined in the meantime (perhaps you edited it and fixed its bug) the new definition is used. Control-Meta-R asks for confirmation before doing it.

The Control-Meta-N, Control-Meta-P, and Control-Meta-B commands are like the corresponding Control- commands but don't censor the stack. When running interpreted code, the error handler usually tries to skip over frames that belong to functions of the interpreter, such as *eval, prog, and cond, and only show "interesting" functions. Control-Meta-N, Control-Meta-P, and Control-Meta-B show everything. They also show frames that are not yet active; that is, frames whose arguments are still being computed for functions that are going to be called. The Control-Meta-U command goes up the stack to the next interesting function, and makes that the current frame.

Control-Meta-A takes a numeric argument n, and prints out the value of the nth 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 + set to a locative pointing to the argument on the stack, so that you can change that argument (by calling rplacd on the locative). Control-Meta-L is similar, but refers to the nth local variable of the frame. Control-Meta-F is similar but refers to the function executing in the frame; it ignores its numeric argument and doesn't allow you to change the function.

Another way to examine and set the arguments, locals and values of a frame is with the functions eh-arg, eh-loc, eh-val and eh-fun. Use these functions in expressions you evaluate inside the error handler, and they refer to the arguments, locals, values and function, respectively, of the error handler's current frame.

Function: eh-arg arg-number-or-name
When used in an expression evaluated in the error handler, eh-arg returns the value of the specifed argument in the error handler's current frame. Argument names are compared ignoring packages; only the pname of the symbol you supply is relevant. eh-arg can appear in setf and locf to set an argument or get its location.

Function: eh-loc local-numer-or-name
eh-loc is just like eh-arg but accesses the current frame's locals instead of its arguments.

Function: eh-val &optional value-number
eh-val is used in an expression evaluated in the error handler when the current frame is returning multiple values, to examine those values. This is only useful if the function has already begun to return some values (as in a trap-on-exit), since otherwise they are all nil. Which value is specified only by number, because values do not normally have names.

eh-val can be used with setf and locf. You can make a frame return a specific sequence of values by setting all but the last value with eh-val and doing Control-R to return the last value.

eh-val with no argument returns a list of all the values this frame is returning.

eh-val is currently not useful on a frame which has not been asked to return multiple values.

Function: eh-fun
eh-fun can be called in an expression being evalued inside the error handler to return the function-object being called in the current frame. It can be used with setf and locf.

Control-Meta-W calls the window error handler, a display-oriented debugger which is not documented in this manual. It should, however, be usable without further documentation.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.2.4 "Summary of Commands"

Control-A
Print argument list of function in current frame.
Control-Meta-A
Examine or change the nth argument of the current frame.
Control-B
Print brief backtrace.
Meta-B
Print longer backtrace.
Control-Meta-B
Print longer backtrace with no censoring of interpreter functions.
Control-C or RESUME
Attempt to continue.
Meta-C
Attempt to continue, setqing the unbound variable or otherwise "permanently" fixing the error.
Control-Meta-C
Attempt to restart (see the error-restart special form, (error-restart-fun)).
Control-E
Edit the source code for the function in the current frame.
Control-Meta-F
Set * to the function in the current frame.
Control-G or ABORT
Quit to command level. This is not a command, but something you can type to escape from typing in a form.
Control-L or CLEAR SCREEN
Redisplay error message and current frame.
Meta-L
Full-screen typeout of current frame.
Control-Meta-L
Get local variable n.
Control-N or LINE
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-Meta-N
Move to next frame even if it is "uninteresting" or still accumulating arguments. With argument, move down n frames.
Control-P or RETURN
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-Meta-P
Move to previous frame even if it is "uninteresting" or still accumulating arguments. With argument, move up n frames.
Control-R
Return a value from the current frame.
Meta-R
Return multiple values from the current frame (doesn't work currently).
Control-Meta-R
Reinvoke the function in the current frame (throw back to it and start it over at its beginning.)
Control-S
Search for a frame containing a specified function.
Meta-S
Same as control-S but does a full display.
Control-T
Throw a value to a tag.
Control-Meta-U
Move up the stack to the previous "interesting" frame.
Control-Meta-W
Call the window error handler.
Control-Z or ABORT
Abort the computation and throw back to the most recent break or debugger, to the program's "command level", or to Lisp top level.
? or Help
Print a help message.
Meta-<
Go to top of stack.
Meta->
Go to bottom of stack.
Control-0 through Control-Meta-9
Numeric arguments to the following command are specified by typing a decimal number with Control and/or Meta held down.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.3 Tracing Function Execution

.setq trace section-page

The trace facility allows the user to trace some functions. When a function is traced, certain special actions will be taken when it is called, and when it returns. The default tracing action is to print a message when the function is called, showing its name and arguments, and another message when the function returns, showing its name and value(s).

The trace facility is closely compatible with Maclisp. One invokes it through the trace and untrace special forms, whose syntax is described below. Alternatively, you can use the trace system by clicking "trace" in the system menu, or by using the "meta-X Trace" command in the editor. This allows you to select the trace options from a menu instead of having to remember the following syntax.

Special Form: trace
A trace form looks like:
 
(trace spec-1 spec-2 ...)

Each spec can take any of the following forms:

a symbol
This is a function name, with no options. The function will be traced in the default way, printing a message each time it is called and each time it returns.

a list (function-name option-1 option-2 ...)
function-name is a symbol and the options control how it is to be traced. The various options are listed below. Some options take "arguments", which should be given immediately following the option name.

a list (:function function-spec option-1 option-2 ...)
This is like the previous form except that function-spec need not be a symbol (see (function-spec)). It exists because if function-name was a list in the previous form, it would instead be interpreted as the following form:

a list ((function-1 function-2...) option-1 option-2 ...)
All of the functions are traced with the same options. Each function can be either a symbol or a general function-spec.

The following trace options exist:

:break pred
Causes a breakpoint to be entered after printing the entry trace information but before applying the traced function to its arguments, if and only if pred evaluates to non-nil. During the breakpoint, the symbol arglist is bound to a list of the arguments of the function.

:exitbreak pred
This is just like break except that the breakpoint is entered after the function has been executed and the exit trace information has been printed, but before control returns. During the breakpoint, the symbol arglist is bound to a list of the arguments of the function, and the symbol values is bound to a list of the values that the function is returning.

:error
Causes the error handler to be called when the function is entered. Use RESUME (or Control-C) to continue execution of the function. If this option is specified, there is no printed trace output other than the error message printed by the error handler.

:step
Causes the function to be single-stepped whenever it is called. See the documentation on the step facility, (stepper-section).

:entrycond pred
Causes trace information to be printed on function entry only if pred evaluates to non-nil.

:exitcond pred
Causes trace information to be printed on function exit only if pred evaluates to non-nil.

:cond pred
This specifies both :exitcond and :entrycond together.

:wherein function
Causes the function to be traced only when called, directly or indirectly, from the specified function function. One can give several trace specs to trace, all specifying the same function but with different wherein options, so that the function is traced in different ways when called from different functions.

This is different from advise-within, which only affects the function being advised when it is called directly from the other function. The trace :wherein option means that when the traced function is called, the special tracing actions occur if the other function is the caller of this function, or its caller's caller, or its caller's caller's caller, etc.

:argpdl pdl
This specifies a symbol pdl, whose value is initially set to nil by trace. When the function is traced, a list of the current recursion level for the function, the function's name, and a list of arguments is consed onto the pdl when the function is entered, and cdr'ed back off when the function is exited. The pdl can be inspected from within a breakpoint, for example, and used to determine the very recent history of the function. This option can be used with or without printed trace output. Each function can be given its own pdl, or one pdl may serve several functions.

:entryprint form
The form is evaluated and the value is included in the trace message for calls to the function. You can give this option more than once, and all the values will appear, preceded by \\.

:exitprint form
The form is evaluated and the value is included in the trace message for returns from the function. You can give this option more than once, and all the values will appear, preceded by \\.

:print form
The form is evaluated and the value is included in the trace messages for both calls to and returns from the function. You can give this option more than once, and all the values will appear, preceded by \\.

:entry list
This specifies a list of arbitrary forms whose values are to be printed along with the usual entry-trace. The list of resultant values, when printed, is preceded by \\ to separate it from the other information.

:exit list
This is similar to entry, but specifies expressions whose values are printed with the exit-trace. Again, the list of values printed is preceded by \\.

:arg :value :both nil
These specify which of the usual trace printouts should be enabled. If :arg is specified, then on function entry the name of the function and the values of its arguments will be printed. If :value is specified, then on function exit the returned value(s) of the function will be printed. If :both is specified, both of these will be printed. If nil is specified, neither will be printed. If none of these four options are specified the default is to :both. If any further options appear after one of these, they will not be treated as options! Rather, they will be considered to be arbitrary forms whose values are to be printed on entry and/or exit to the function, along with the normal trace information. The values printed will be preceded by a //, and follow any values specified by :entry or :exit. Note that since these options "swallow" all following options, if one is given it should be the last option specified.

.vindex arglist If the variable arglist is used in any of the expressions given for the :cond, :break, :entry, or :exit options, or after the :arg, :value, :both, or nil option, when those expressions are evaluated the value of arglist will be bound to a list of the arguments given to the traced function. Thus
 
(trace (foo :break (null (car arglist))))
would cause a break in foo if and only if the first argument to foo is nil. If the :break or :error option is used, the variable arglist will be valid inside the break-loop. If you setq arglist, the arguments seen by the function will change. arglist should perhaps have a colon, but it can be omitted because this is the name of a system function and therefore global.

.vindex values Similarly, the variable values will be a list of the resulting values of the traced function. For obvious reasons, this should only be used with the :exit option. If the :exitbreak option is used, the variables values and arglist are valid inside the break-loop. If you setq values, the values returned by the function will change. values should perhaps have a colon, but it can be omitted because this is the name of a system function and therefore global.

The trace specifications may be "factored", as explained above. For example,
 
(trace ((foo bar) :break (bad-p arglist) :value))
is equivalent to
(trace (foo :break (bad-p arglist) :value)
       (bar :break (bad-p arglist) :value))
Since a list as a function name is interpreted as a list of functions, non-atomic function names (see (function-spec)) are specified as follows:
 
(trace (:function (:method flavor :message) :break t))

trace returns as its value a list of names of all functions it traced. If called with no arguments, as just (trace), it returns a list of all the functions currently being traced.

If you attempt to trace a function already being traced, trace calls untrace before setting up the new trace.

Tracing is implemented with encapsulation (see (encapsulate)), so if the function is redefined (e.g. with defun or by loading it from a QFASL file) the tracing will be transferred from the old definition to the new definition.

Tracing output is printed on the stream which is the value of trace-output. This is synonymous with terminal-io unless you change it.

Special Form: untrace
untrace is used to undo the effects of trace and restore functions to their normal, untraced state. untrace will take multiple specifications, e.g. (untrace foo quux fuphoo). Calling untrace with no arguments will untrace all functions currently being traced.

Unlike Maclisp, if there is an error trace (or untrace) will invoke the error system and give an English message, instead of returning lists with question marks in them. Also, the remtrace function is not provided, since it is unnecessary.

Variable: trace-compile-flag
If the value of trace-compile-flag is non-nil, the functions created by trace will get compiled, allowing you to trace special forms such as cond without interfering with the execution of the tracing functions. The default value of this flag is nil.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.4 Advising a Function

.setq advise section-page

To advise a function is to tell it to do something extra in addition to its actual definition. It is done by means of the function advise. The something extra is called a piece of advice, and it can be done before, after, or around the definition itself. The advice and the definition are independent, in that changing either one does not interfere with the other. Each function can be given any number of pieces of advice.

Advising is fairly similar to tracing, but its purpose is different. Tracing is intended for temporary changes to a function to give the user information about when and how the function is called and when and with what value it returns. Advising is intended for semi-permanent changes to what a function actually does. The differences between tracing and advising are motivated by this difference in goals.

Advice can be used for testing out a change to a function in a way which is easy to retract. In this case, you would call advise from the terminal. It can also be used for customizing a function which is part of a program written by someone else. In this case you would be likely to put a call to advise in one of your source files or your login init file (see (login-fun)), rather than modifying the other person's source code.

Advising is implemented with encapsulation (see (encapsulate)), so if the function is redefined (e.g. with defun or by loading it from a QFASL file) the advice will be transferred from the old definition to the new definition.

Special Form: advise
A function is advised by the special form
 
(advise function class name position
  form1 form2...)
None of this is evaluated. function is the function to put the advice on. It is usually a symbol, but any function spec is allowed (see (function-spec)). The forms are the advice; they get evaluated when the function is called. class should be either :before, :after, or :around, and says when to execute the advice (before, after, or around the execution of the definition of the function). The meaning of :around advice is explained a couple of sections below.

name is used to keep track of multiple pieces of advice on the same function. name is an arbitrary symbol which is remembered as the name of this particular piece of advice. If you have no name in mind, use nil; then we say the piece of advice is anonymous. A given function and class can have any number of pieces of anonymous advice, but it can have only one piece of named advice for any one name. If you try to define a second one, it replaces the first. Advice for testing purposes is usually anonymous. Advice used for customizing someone else's program should usually be named so that multiple customizations to one function have separate names. Then, if you reload a customization that is already loaded, it does not get put on twice.

position says where to put this piece of advice in relation to others of the same class already present on the same function. If position is nil, the new advice goes in the default position: it usually goes at the beginning (where it is executed before the other advice), but if it is replacing another piece of advice with the same name, it goes in the same place that the old piece of advice was in.

If you wish to specify the position, position can be the numerical index of which existing piece of advice to insert this one before. Zero means at the beginning; a very large number means at the end. Or, position can be the name of an existing piece of advice of the same class on the same function; the new advice is inserted before that one.

 
For example,
(advise factorial :before negative-arg-check nil
  (if (minusp (first arglist))
      (ferror nil "factorial of negative argument")))
This modifies the factorial function so that if it is called with a negative argument it signals an error instead of running forever.

Special Form: unadvise
 
(unadvise function class position)
removes pieces of advice. None of its "arguments" are evaluated. function and class have the same meaning as they do in the function advise. position specifies which piece of advice to remove. It can be the numeric index (zero means the first one) or it can be the name of the piece of advice.

unadvise can remove more than one piece of advice if some of its arguments are missing. If position is missing or nil, then all advice of the specified class on the specified function is removed. If class is missing or nil as well, then all advice on the specified function is removed. (unadvise) removes all advice on all functions, since function is not specified.

The following are the primitive functions for adding and removing advice. Unlike the above special forms, these are functions and can be conveniently used by programs. advise and unadvise are actually macros which expand into calls to these two.

Function: si:advise-1 function class name position forms
Adds advice. The arguments have the same meaning as in advise. Note that the forms argument is not a &rest argument.

Function: si:unadvise-1 function &optional class position
Removes advice. If class or position is nil or unspecified, all classes of advice or advice at all positions/with all names is removed.

You can find out manually what advice a function has with grindef, which grinds the advice on the function as forms which are calls to advise. These are in addition to the definition of the function.

To poke around in the advice structure with a program, you must work with the encapsulation mechanism's primitives. See (encapsulate).

Variable: si:advised-functions
A list of all functions which have been advised.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.4.1 Designing the Advice

For advice to interact usefully with the definition and intended purpose of the function, it must be able to interface to the data flow and control flow through the function. We provide conventions for doing this.

The list of the arguments to the function can be found in the variable arglist. :before advice can replace this list, or an element of it, to change the arguments passed to the definition itself. If you replace an element, it is wise to copy the whole list first with
 
(setq arglist (copylist arglist))
After the function's definition has been executed, the list of the values it returned can be found in the variable values. :after advice can set this variable or replace its elements to cause different values to be returned.

All the advice is executed within a prog, so any piece of advice can exit the entire function with return. The arguments of the return will be returned as the values of the function. No further advice will be executed. If a piece of :before advice does this, then the function's definition will not even be called.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.4.2 :around Advice

A piece of :before or :after advice is executed entirely before or entirely after the definition of the function. :around advice is wrapped around the definition; that is, the call to the original definition of the function is done at a specified place inside the piece of :around advice. You specify where by putting the symbol :do-it in that place.

For example, (+ 5 :do-it) as a piece of :around advice would add 5 to the value returned by the function. This could also be done by (setq values (list (+ 5 (car values)))) as :after advice.

When there is more than one piece of :around advice, they are stored in a sequence just like :before and :after advice. Then, the first piece of advice in the sequence is the one started first. The second piece is substituted for :do-it in the first one. The third one is substituted for :do-it in the second one. The original definition is substituted for :do-it in the last piece of advice.

:around advice can access arglist, but values is not set up until the outermost :around advice returns. At that time, it is set to the value returned by the :around advice. It is reasonable for the advice to receive the values of the :do-it (e.g. with multiple-value-list) and fool with them before returning them (e.g. with values-list).

:around advice can return from the prog at any time, whether the original definition has been executed yet or not. It can also override the original definition by failing to contain :do-it. Containing two instances of :do-it may be useful under peculiar circumstances. If you are careless, the original definition may be called twice, but something like
 
(if (foo) (+ 5 :do-it) (* 2 :do-it))
will certainly work reasonably.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.4.3 Advising One Function Within Another

It is possible to advise the function foo only for when it is called directly from a specific other function bar. You do this by advising the function specifier (:within bar foo). That works by finding all occurrences of foo in the definition of bar and replacing them with altered-foo-within-bar. This can be done even if bar's definition is compiled code. The symbol altered-foo-within-bar starts off with the symbol foo as its definition; then the symbol altered-foo-within-bar, rather than foo itself, is advised. The system remembers that foo has been replaced inside bar, so that if you change the definition of bar, or advise it, then the replacement is propagated to the new definition or to the advice. If you remove all the advice on (:within bar foo), so that its definition becomes the symbol foo again, then the replacement is unmade and everything returns to its original state.

(grindef bar) will print foo where it originally appeared, rather than altered-foo-within-bar, so the replacement will not be seen. Instead, grindef will print out calls to advise to describe all the advice that has been put on foo or anything else within bar.

An alternate way of putting on this sort of advice is to use advise-within.

Special Form: advise-within
 
(advise-within within-function function-to-advise
	       class name position
	forms...)
advises function-to-advise only when called directly from the function within-function. The other arguments mean the same thing as with advise. None of them are evaluated.

To remove advice from (:within bar foo), you can use unadvise on that function specifier. Alternatively, you can use unadvise-within.

Special Form: unadvise-within
 
(unadvise-within within-function function-to-advise class position)
removes advice which has been placed on (:within within-function function-to-advise). The arguments class and position are interpereted as for unadvise. For example, if those two are omitted, then all advice placed on function-to-advise within within-function is removed. Additionally, if function-to-advise is omitted, all advise on any function within within-function is removed. If there are no arguments, than all advice on one function within another is removed. Other pieces of advice, which have been placed on one function and not limited to within another, are not removed.

(unadvise) removes absolutely all advice, including advice for one function within another.

The function versions of advise-within and unadvise-within are called si:advise-within-1 and si:unadvise-within-1. advise-within and unadvise-within are macros which expand into calls to the other two.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.5 Stepping Through an Evaluation

.setq stepper-section section-page

The Step facility gives you the ability to follow every step of the evaluation of a form, and examine what is going on. It is analogous to a single-step proceed facility often found in machine-language debuggers. If your program is doing something strange, and it isn't obvious how it's getting into its strange state, then the stepper is for you.

There are two ways to enter the stepper. One is by use of the step function.

Function: step form
This evaluates form with single stepping. It returns the value of form.

For example, if you have a function named foo, and typical arguments to it might be t and 3, you could say
 
(step '(foo t 3))
and the form (foo t 3) will be evaluated with single stepping.

The other way to get into the stepper is to use the :step option of trace (see (trace-fun)). If a function is traced with the :step option, then whenever that function is called it will be single stepped.

Note that any function to be stepped must be interpreted; that is, it must be a lambda-expression. Compiled code cannot be stepped by the stepper.

When evaluation is proceeding with single stepping, before any form is evaluated, it is (partially) printed out, preceded by a forward arrow (7) character When a macro is expanded, the expansion is printed out preceded by a double arrow (7) character. When a form returns a value, the form and the values are printed out preceded by a backwards arrow (7) character; if there is more than one value being returned, an and-sign (7) character is printed between the values.

Since the forms may be very long, the stepper does not print all of a form; it truncates the printed representation after a certain number of characters. Also, to show the recursion pattern of who calls whom in a graphic fashion, it indents each form proportionally to its level of recursion.

After the stepper prints any of these things, it waits for a command from the user. There are several commands to tell the stepper how to proceed, or to look at what is happening. The commands are:

Control-N (Next)
Step to the Next thing. The stepper continues until the next thing to print out, and it accepts another command.

Space
Go to the next thing at this level. In other words, continue to evaluate at this level, but don't step anything at lower levels. This is a good way to skip over parts of the evaluation that don't interest you.

Control-U (Up)
Continue evaluating until we go up one level. This is like the space command, only more so; it skips over anything on the current level as well as lower levels.

Control-X (eXit)
Exit; finish evaluating without any more stepping.

Control-T (Type)
Retype the current form in full (without truncation).

Control-G (Grind)
Grind (i.e. prettyprint) the current form.

Control-E (Editor)
Editor escape (enter the editor).

Control-B (Breakpoint)
Breakpoint. This command puts you into a breakpoint (i.e. a read-eval-print loop) from which you can examine the values of variables and other aspects of the current environment. From within this loop, the following variables are available:

step-form
which is the current form.
step-values
which is the list of returned values.
step-value
which is the first returned value.

If you change the values of these variables, it will work.

Control-L
Clear the screen and redisplay the last 10. pending forms (forms which are being evaluated).

Meta-L
Like Control-L, but doesn't clear the screen.

Control-Meta-L
Like Control-L, but redisplays all pending forms.

? or Help
Prints documentation on these commands.

It is strongly suggested that you write some little function and try the stepper on it. If you get a feel for what the stepper does and how it works, you will be able to tell when it is the right thing to use to find bugs.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.6 "Evalhook"

.setq evalhook-section section-page

The evalhook facility provides a "hook" into the evaluator; it is a way you can get a Lisp form of your choice to be executed whenever the evaluator is called. The stepper uses evalhook, and usually it is the only thing that ever needs to. However, if you want to write your own stepper or something similar, this is the primitive facility that you can use to do so. The way this works is a bit hairy, but unless you need to write your own stepper you don't have to worry about it.

Variable: evalhook
If the value of evalhook is non-nil, then special things happen in the evaluator. When a form (any form, even a number or a symbol) is to be evaluated, evalhook is bound to nil and the function which was evalhook's value is applied to one argument--the form that was trying to be evaluated. The value it returns is then returned from the evaluator.

evalhook is bound to nil by break and by the error handler, and setq'ed to nil when errors are dismissed by throwing to the Lisp top level loop. This provides the ability to escape from this mode if something bad happens.

In order not to impair the efficiency of the Lisp interpreter, several restrictions are imposed on evalhook. It only applies to evaluation--whether in a read-eval-print loop, internally in evaluating arguments in forms, or by explicit use of the function eval. It does not have any effect on compiled function references, on use of the function apply, or on the "mapping" functions. (In Zetalisp, as opposed to Maclisp, it is not necessary to do (*rset t) nor (sstatus evalhook t).) (Also, Maclisp's special-case check for store is not implemented.)

Function: evalhook form hook
evalhook is a function which helps exploit the evalhook feature. The form is evaluated with evalhook lambda-bound to the function hook. The checking of evalhook is bypassed in the evaluation of form itself, but not in any subsidiary evaluations, for instance of arguments in the form. This is like a "one-instruction proceed" in a machine-language debugger.
 
Example:
;; This function evaluates a form while printing debugging information.
(defun hook (x)
   (terpri)
   (evalhook x 'hook-function))

;; Notice how this function calls evalhook to evaluate the form f,
;; so as to hook the sub-forms.
(defun hook-function (f)
   (let ((v (evalhook f 'hook-function)))
     (format t "form: ~s~%value: ~s~%" f v)
     v))

;; This isn't a very good program, since if f uses multiple
;; values, it will not work.

The following output might be seen from (hook '(cons (car '(a . b)) 'c)):
 
form: (quote (a . b))
value: (a . b)
form: (car (quote (a . b)))
value: a
form: (quote c)
value: c
(a . c)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.7 "The MAR"

.setq mar section-page

The MAR facility allows any word or contiguous set of words to be monitored constantly, and can cause an error if the words are referenced in a specified manner. The name MAR is from the similar device on the ITS PDP-10's; it is an acronym for "Memory Address Register". The MAR checking is done by the Lisp Machine's memory management hardware, and so the speed of general execution when the MAR is enabled is not significantly slowed down. However, the speed of accessing pages of memory containing the locations being checked is slowed down somewhat, since every reference involves a microcode trap.

These are the functions that control the MAR:

Function: set-mar location cycle-type &optional n-words
The set-mar function clears any previous setting of the MAR, and sets the MAR on n-words words, starting at location. location may be any object. Often it will be a locative pointer to a cell, probably created with the locf special form. n-words currently defaults to 1, but eventually it may default to the size of the object. cycle-type says under what conditions to trap. :read means that only reading the location should cause an error, :write means that only writing the location should, t means that both should. To set the MAR to detect setq (and binding) of the variable foo, use
 
(set-mar (value-cell-location 'foo) ':write)

Function: clear-mar
This turns off the MAR. Warm-booting the machine disables the MAR but does not turn it off, i.e. references to the MARed pages are still slowed down. clear-mar does not currently speed things back up until the next time the pages are swapped out; this may be fixed some day.

Function: mar-mode
(mar-mode) returns a symbol indicating the current state of the MAR. It returns one of:

nil
The MAR is not set.
:read
The MAR will cause an error if there is a read.
:write
The MAR will cause an error if there is a write.
t
The MAR will cause an error if there is any reference.

Note that using the MAR makes the pages on which it is set somewhat slower to access, until the next time they are swapped out and back in again after the MAR is shut off. Also, use of the MAR currently breaks the read-only feature if those pages were read-only.

Proceeding from a MAR break allows the memory reference that got an error to take place, and continues the program with the MAR still effective. When proceeding from a write, the error handler asks you whether to allow the write to take place or to inhibit it, leaving the location with its old contents.

Most--but not all--write operations first do a read. setq and rplaca are examples. This means that if the MAR is in :read mode it will catch writes as well as reads, however they will trap during the reading phase, and consequently the data to be written will not be displayed. This also means that setting the MAR to t mode causes most writes to trap twice, first for a read and then again for a write. So when the MAR says that it trapped because of a read, this means a read at the hardware level, which may not look like a read in your program.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

43.8 Variable Monitoring

Function: monitor-variable var &optional current-value-cell-only-p monitor-function
Calls a given function just after a given special variable is setq'ed (by compiled code or otherwise). Does not trigger on binding of the variable. The function is given both the old and new values as arguments. It does not get the name of the variable as an argument, so it is usually necessary to use a closure as monitor-function in order to remember this. The old value will be nil if the variable had been unbound.

The default monitoring function just prints the symbol and the old and new values. This behavior can be changed by specifying the monitor-function argument.

Normally this feature applies to all setq's, but if current-value-cell-only-p is specified non-nil, it applies only to those setq's which would alter the variable's currently active value cell. This is only relevant when var is subject to a closure.

Don't try to use this with variables that are forwarded to A memory (e.g. inhibit-scheduling-flag).

Function: unmonitor-variable &optional var
If var is being monitored, it is restored to normal. If no var is specified, all variables that have been monitored are unmonitored.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Brad Parker on June, 13 2006 using texi2html