;;; -*- Mode:gate; Fonts:(HL12 HL12I HL12B CPTFONTB HL12BI HL12B HL12I ) -*- =Node: 4Stepping Through an Evaluation* =Text: 3STEPPING THROUGH AN EVALUATION* 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 2step* function. 3step* 1form* This evaluates 1form* with single stepping. It returns the value of 1form*. For example, if you have a function named 2foo*, and typical arguments to it might be 2t* and 23*, you could say 3(step '(foo t 3))* to evaluate the form 2(foo t 3)* with single stepping. The other way to get into the stepper is to use the 2:step* option of 2trace* (see 4(DEBUGGING-5)Tracing Function Execution*). If a function is traced with the 2: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 (3*) character When a macro is expanded, the expansion is printed out preceded by a double arrow (3*) character. When a form returns a value, the form and the values are printed out preceded by a backwards arrow (3*) character; if there is more than one value being returned, an and-sign (3*) character is printed between the values. When the stepper has evaluated the args to a form and is about to apply the function, it prints a lambda (3*) because entering the lambda is the next thing to be done. 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: 2Control-N (Next)* Steps to the Next event, then asks for another command. Events include beginning to evaluate a form at any level or finishing the evaluation of a form at any level. 2Space* .br Steps to the next event 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. 2Control-A (Args)* Skips over the evaluation of the arguments of this form, but pauses in the stepper before calling the function that is the car of the form. 2Control-U (Up)* Continues 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. 2Control-X (eXit)* Exits; finishes evaluation without any more stepping. 2Control-T (Type)* Retypes the current form in full (without truncation). 2Control-G (Grind)* Grinds (i.e. prettyprints) the current form. 2Control-E (Editor)* Switches windows, to the editor. 2Control-B (Breakpoint)* Enters a 2break* 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: 2step-form* the current form. 2step-values* the list of returned values. 2step-value* the first returned value. If you change the values of these variables, you will affect execution. 2Control-L* Clears the screen and redisplays the last 10. pending forms (forms that are being evaluated). 2Meta-L* Like Control-L, but doesn't clear the screen. 2Control-Meta-L* Like Control-L, but redisplays all pending forms. 2? 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. =Node: 4Evalhook* =Text: 3EVALHOOK* The 2evalhook* 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 2evalhook*, 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. 3evalhook* 1Variable* 3*evalhook** 1Variable* If the value of 2evalhook* is non-2nil*, then special things happen in the evaluator. Its value is called the 1hook function*. When a form (any form, even a number or a symbol) is to be evaluated, the hook function is called instead. Whatever values the hook function returns are taken to be the results of the evaluation. Both 2evalhook* and 2applyhook* are bound to 2nil* before the hook function is actually called. The hook function receives two arguments: the form that was to be evaluated, and the lexical environment of evaluation. These two arguments allow the hook function to perform later, if it wishes, the very same evaluation that the hook was called instead of. 3applyhook* 1Variable* 3*applyhook** 1Variable* If the value of 2applyhook* is non-2nil*, it is called the next time the interpreter is about to apply a function to its evaluated arguments. Whatever values the apply hook function returns are taken to be the results of calling the other function. Both 2evalhook* and 2applyhook* are bound to 2nil* before the hook function is actually called. The hook function receives three arguments: the function that was going to be called, the list of arguments it was going to receive, and the lexical environment of evaluation. These arguments allow the hook function to perform later, if it wishes, the very same evaluation that the hook was called instead of. When either the evalhook or the applyhook is called, both variables are bound to 2nil*. They are also rebound to 2nil* by 2break* and by the debugger, and 2setq*'ed to 2nil* 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 the evalhook and applyhook. They apply only to evaluation--whether in a read-eval-print loop, internally in evaluating arguments in forms, or by explicit use of the function 2eval*. They 1do not* have any effect on compiled function references, on use of the function 2apply*, or on the mapping functions. 3evalhook* 1form* 1evalhook* 1applyhook* &optional 1environment* Evaluates 1form* in the specified 1environment*, with 1evalhook* and 1applyhook* in effect for all recursive evaluations of subforms of 1form*. However, the 1evalhook* is not called for the evaluation of 1form* itself. 1environment* is a list which represents the lexical environment to be in effect for the evaluation of 1form*. 2nil* means an empty lexical environment, in which no lexical bindings exist. This is the environment used when 2eval* itself is called. Aside from 2nil*, the only reasonable way to get a value to pass for 1environment* is to use the last argument passed to a hook function. You must take care not to use it after the context in which it was made is exited, because environments normally contain stack lists which become garbage after their stack frames are popped. 1environment* has no effect on the evaluation of a variable which is regarded as special. This is always done by examining the value cell. However, environment contains the record of the local special declarations currently in effect, so it does enter in the decision of whether a variable is special. Here is an example of the use of 3evalhook*: 2;; This function evaluates a form while printing debugging information.* 3(defun hook (x)* 3 (terpri)* 3 (evalhook x 'hook-function nil))* 2;; Notice how this function calls evalhook to evaluate the form f,* 2;; so as to hook the sub-forms.* 3(defun hook-function (f env)* 3 (let ((v (multiple-value-list* 3 (evalhook f 'hook-function nil env))))* 3 (format t "form: ~S~%values: ~S~%" f v)* 3 (values-list v)))* The following output might be seen from 2(hook '(cons (car '(a . b)) 'c))*: 3form: (quote (a . b))* 3values: ((a . b))* 3form: (car (quote (a . b)))* 3values: (a)* 3form: (quote c)* 3values: (c)* 3(a . c) applyhook* 1function* 1list-of-args* 1evalhook* 1applyhook* &optional 1environment* Applies 1function* to 1list-of-args* in the specified 1environment*, with 1evalhook* and 1applyhook* in effect for all recursive evaluations of subforms of 1function*'s body. However, 1applyhook* is not called for this application of function itself. For more information, refer to the definition of 2evalhook*, immediately above. =Node: 4The MAR* =Text: 3THE MAR* 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, so the speed of general execution is not significantly slowed down when the MAR is enabled. 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: 3set-mar* 1location* 1cycle-type* &optional 1n-words* Sets the MAR on 1n-words* words, starting at 1location*. 1location* may be any object. Often it will be a locative pointer to a cell, probably created with the 2locf* special form. 1n-words* currently defaults to 1, but eventually it may default to the size of the object. 1cycle-type* says under what conditions to trap. 2:read* means that only reading the location should cause an error, 2:write* means that only writing the location should, 2t* means that both should. To set the MAR to detect 2setq* (and binding) of the variable 2foo*, use 3(set-mar (variable-location foo) :write) clear-mar* 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. 2clear-mar* does not currently speed things back up until the next time the pages are swapped out; this may be fixed some day. 3mar-mode* 2(mar-mode)* returns a symbol indicating the current state of the MAR. It returns one of: 2nil* The MAR is not set. 2:read* The MAR will cause an error if there is a read. 2:write* The MAR will cause an error if there is a write. 2t* 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, you have the choice of whether to allow the write to take place or to inhibit it, leaving the location with its old contents. 3sys:mar-break* (3condition*) 1Condition* This is the condition, not an error, signaled by a MAR break. The condition instance supports these operations: 2:object* The object one of whose words was being referenced. 2:offset* The offset within the object of the word being referenced. 2:value* The value read, or to be written. 2:direction* Either 2:read* or 2:write*. The proceed type 2:no-action* simply proceeds, continuing with the interrupted program as if the MAR had not been set. If the trap was due to writing, the proceed type 2:proceed-no-write* is also provided, and causes the program to proceed but does not store the value in the memory location. Most--but not all--write operations first do a read. 2setq* and 2rplaca* both do. This means that if the MAR is in 2:read* mode it catches writes as well as reads; however, they trap during the reading phase, and consequently the data to be written are not yet known. This also means that setting the MAR to 2t* 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.