Trailing-Edge
-
PDP-10 Archives
-
decus_20tap1_198111
-
decus/20-0004/15lisp.doc
There are no other files named 15lisp.doc in the archive.
SECTION 15
1
DEBUGGING - THE BREAK PACKAGE
15.1 Debugging Facilities
Debugging a collection of LISP functions involves isolating problems
within particular functions and/or determining when and where incorrect
data are being generated and transmitted. In the INTERLISP system,
there are three facilities which allow the user to (temporarily) modify
selected function definitions so that he can follow the flow of control
in his programs, and obtain this debugging information. These three
facilities together are called the break package. All three redefine
functions in terms of a system function, break1 described below.
Break modifies the definition of its argument, a function fn, so that if
a break condition (defined by the user) is satisfied, the process is
halted temporarily on a call to fn. The user can then interrogate the
state of the machine, perform any computation, and continue or return
from the call.
Trace modifies a definition of a function fn so that whenever fn is
called, its arguments (or some other values specified by the user) are
printed. When the value of fn is computed it is printed also. (trace
is a special case of break).
Breakin allows the user to insert a breakpoint inside an expression
defining a function. When the breakpoint is reached and if a break
condition (defined by the user) is satisfied, a temporary halt occurs
and the user can again investigate the state of the computation.
The following two examples illustrate these facilities. In the first
example, the user traces the function factorial. trace redefines
factorial so that it calls break1 in such a way that it prints some
information, in this case the arguments and value of factorial, and then
goes on with the computation. When an error occurs on the fifth
recursion, break1 reverts to interactive mode, and a full break occurs.
The situation is then the same as though the user had originally
performed BREAK(FACTORIAL) instead of TRACE(FACTORIAL), and the user can
evaluate various INTERLISP forms and direct the course of the
computation. In this case, the user examines the variable n, and
------------------------------------------------------------------------
1
The break package was written by W. Teitelman.
15.1
instructs break1 to return 1 as the value of this cell to factorial.
The rest of the tracing proceeds without incident. The user would then
presumably edit factorial to change L to 1.
In the second example, the user has constructed a non-recursive
definition of factorial. He uses breakin to insert a call to break1
just after the PROG label LOOP. This break is to occur only on the last
two iterations, i.e., when n is less than 2. When the break occurs, the
user looks at the value of n. mistakenly typing NN. However, the break
is maintained and no damage is done. After examining n and m the user
allows the computation to continue by typing OK. A second break occurs
after the next iteration, this time with N=0. When this break is
released, the function factorial returns its value of 120.
_PP FACTORIAL
(FACTORIAL
[LAMBDA (N)
(COND
((ZEROP N
L)
(T (ITIMES N (FACTORIAL (SUB1 N])
FACTORIAL
_TRACE(FACTORIAL)
(FACTORIAL)
_FACTORIAL(4)
FACTORIAL:
N = 4
FACTORIAL:
N = 3
FACTORIAL:
N = 2
FACTORIAL:
N = 1
FACTORIAL:
N = 0
U.B.A.
L
(FACTORIAL BROKEN)
:N
0
:RETURN 1
FACTORIAL = 1
FACTORIAL = 1
FACTORIAL = 2
FACTORIAL = 6
FACTORIAL = 24
24
_
15.2
_PP FACTORIAL
(FACTORIAL
[LAMBDA (N)
(PROG ((M 1))
LOOP(COND
((ZEROP N)
(RETURN M)))
(SETQ M (ITIMES M N))
(SETQ N (SUB1 N))
(GO LOOP])
FACTORIAL
_BREAKIN(FACTORIAL (AFTER LOOP) (ILESSP N 2]
SEARCHING...
FACTORIAL
_FACTORIAL(5)
((FACTORIAL) BROKEN)
:NN
U.B.A.
NN
(FACTORIAL BROKEN AFTER LOOP)
:N
1
:M
120
:OK
(FACTORIAL)
((FACTORIAL) BROKEN)
:N
0
:OK
(FACTORIAL)
120
_
15.2 Break1
The basic function of the break package is break1. Whenever INTERLISP
types a message of the form (- BROKEN) followed by ":" the user is then
"talking to" break1, and we say he is "in a break." break1 allows the
user to interrogate the state of the world and affect the course of the
computation. It uses the prompt character ":" to indicate it is ready
to accept input(s) for evaluation, in the same way as evalqt uses "_".
The user may type in an expression for evaluation as with evalqt, and
the value will be printed out, followed by another :. Or the user can
type in one of the commands specifically recognized by break1 described
below.
Since break1 puts all of the power of INTERLISP at the user's command,
he can do anything he can do at evalqt. For example, he can insert new
breaks on subordinate functions simply by typing:
(BREAK fn1 fn2 ...)
or he can remove old breaks and traces if too much information is being
supplied:
15.3
(UNBREAK fn3 fn4 ...)
He can edit functions, including the one currently broken:
EDITF(fn)
For example, the user might evaluate an expression, see that the value
was incorrect, call the editor, change the function, and evaluate the
expression again, all without leaving the break.
Similarly, the user can prettyprint functions, define new functions or
redefine old ones, load a file, compile functions, time a computation,
etc. In short, anything that he can do at the top level can be done
while inside of the break. In addition the user can examine the
pushdown list, via the functions described in Section 12, and even force
a return back to some higher function via the function retfrom or
reteval.
It is important to emphasize that once a break occurs, the user is in
complete control of the flow of the computation, and the computation
will not proceed without specific instruction from him. If the user
types in an expression whose evaluation causes an error, the break is
2
maintained. Similarly if the user aborts a computation initiated from
within the break, the break is maintained. Only if the user gives one
of the commands that exits from the break, or evaluates a form which
does a retfrom or reteval back out of break1, will the computation
3
continue.
Note that break1 is just another INTERLISP function, not a special
system feature like the interpreter or the garbage collector. It has
arguments which are explained later, and returns a value, the same as
cons or cond or prog or any other function. The value returned by
break1 is called "the value of the break." The user can specify this
value explicitly by using the RETURN command described below. But in
most cases, the value of a is given implicitly, via a GO or OK command,
and is the result of evaluating "the break expression," brkexp, which is
one of the arguments to break1.
The break expression is an expression equivalent to the computation that
would have taken place had no break occurred. For example, if the user
breaks on the function FOO, the break expression is the body of the
definition of FOO. When the user types OK or GO, the body of FOO is
evaluated, and its value returned as the value of the break, i.e., to
whatever function called FOO. The effect is the same as though no break
had occurred. In other words, one can think of break1 as a fancy eval,
------------------------------------------------------------------------
2
By typing control-E, see Section 16.
3
Except that break1 does not "turn off" control-D, i.e., a control-D
will force an immediate return back to the top level.
15.4
which permits interaction before and after evaluation. The break
expression then corresponds to the argument to eval.
Break Commands
GO Releases the break and allows the computation to
proceed. break1 evaluates brkexp, its first
argument, prints the value of the break. brkexp
is set up by the function that created the call
to break1. For break or trace, brkexp is
equivalent to the body of the definition of the
broken function. For breakin, using BEFORE or
AFTER, brkexp is NIL. For breakin AROUND,
brkexp is the indicated expression. See
breakin, page 15.17.
OK Same as GO except the value of brkexp is not
printed.
EVAL Same as GO or OK except that the break is
maintained after the evaluation. The user can
then interrogate the value of the break which is
bound on the variable !value, and continue with
the break. Typing GO or OK following EVAL will
not cause reevaluation but another EVAL will.
EVAL is a useful command when the user is not
sure whether or not the break will produce the
correct value and wishes to be able to do
something about it if it is wrong.
RETURN form The value of the indicated computation is
returned
or as the value of the break.
RETURN fn[args] For example, one might use the EVAL command and
follow this with RETURN (REVERSE !VALUE).
^ Calls error! and aborts the break. i.e., makes
it "go away" without returning a value. This is
a useful way to unwind to a higher level break.
All other errors, including those encountered
while executing the GO, OK, EVAL, and RETURN
commands, maintain the break.
!EVAL function is first unbroken, then the break
expression is evaluated, and then the function
is rebroken. Very useful for dealing with
recursive functions.
!OK Function is first unbroken, evaluated, rebroken,
and then exited, i.e., !OK is equivalent to
!EVAL followed by OK.
15.5
!GO Function is first unbroken, evaluated, rebroken,
and exited with value typed, i.e., !EVAL
followed by GO.
UB unbreaks brkfn, e.g.,
(FOO BROKEN)
:UB
FOO
:
and FOO is now unbroken
@ resets the variable lastpos, which establishes a
context for the commands ?=, ARGS, BT, BTV,
BTV*, and EDIT, and IN? described below.
lastpos is the position of a function call on
the push-down stack. It is initialized to the
function just before the call to break1, i.e.,
stknth[-1;BREAK1]
@ treats the rest of the teletype line as its
argument(s). It first resets lastpos to
stknth[-1;BREAK1] and then for each atom on the
line, @ searches backward, for a call to that
atom. The following atoms are treated
specially:
@ do not reset lastpos to
stknth[-1;BREAK1] but leave it as it
was, and continue searching from that
point.
numbers if negative, move lastpos back that
number of calls, if positive, forward,
i.e., reset lastpos to
stknth[n;lastpos]
/ the next atom is a number and can be
used to specify more than one call
e.g.,
@ FOO / 3 is equivalent to
@ FOO FOO FOO
= resets lastpos to the value of the
next expression, e.g., if the value of
FOO is a stack pointer, @ = FOO FIE
will search for FIE in the environment
specified by FOO.
15.6
Example:
If the push-down stack looks like
BREAK1 (13)
FOO (12)
SETQ (11)
COND (10)
PROG (9)
FIE (8)
COND (7)
FIE (6)
COND (5)
FIE (4)
COND (3)
PROG (2)
FUM (1)
then @ FIE COND will set lastpos to the position
corresponding to (7); @ @ COND will then set
lastpos to (5); and @ FIE / 3 -1 to (3).
If @ cannot successfully complete a search, it
types (fn NOT FOUND), where fn is the name of
the function for which it was searching.
When @ finishes, it types the name of the
function at lastpos, i.e., stkname[lastpos]
@ can be used on brkcoms. In this case, the
next command on brkcoms is treated the same as
the rest of the teletype line.
4
?= This is a multi-purpose command. Its most
common use is to interrogate the value(s) of the
arguments of the broken function, e.g., if FOO
has three arguments (X Y Z), then typing ?= to a
break on FOO, will produce:
:?=
X = value of X
Y = value of Y
Z = value of Z
:
------------------------------------------------------------------------
4
In fact, ?= is a universal mnemonic for displaying argument names
and their corresponding values. In addition to being a break
command, ?= is an edit macro which prints the argument names and
values for the current expression (see Section 9), and a read-macro
(actually ? is the read-macro character) which does the same for the
current level list being read (see Sections 2 and 22).
15.7
?= operates on the rest of the teletype line as
its arguments. If the line is empty, as in the
above case, it prints all of the arguments. If
the user types ?= X (CAR Y), he will see the
value of X, and the value of (CAR Y). The
difference between using ?= and typing X and
(CAR Y) directly to break1 is that ?= evaluates
its inputs as of lastpos, i.e., it uses stkeval.
This provides a way of examing variables or
performing computations as of a particular point
on the stack. For example, @ FOO / 2 followed
by ?= X will allow the user to examine the value
of X in the previous call to FOO, etc.
?= also recognizes numbers as refering to the
correspondingly numbered argument, i.e., it uses
stkarg in this case. Thus
:@ FIE
FIE
:?= 2
will print the name and value of the second
argument of FIE.
?= can also be used on brkcoms, in which case
the next command on brkcoms is treated as the
rest of the teletype line. For example, if
brkcoms is (EVAL ?= (X Y) GO), brkexp will be
evaluated, the values of X and Y printed, and
then the function exited with its value being
printed.
BT Prints a backtrace of function names only
starting at lastpos. (See discussion of @
above) The several nested calls in system
packages such as break, edit, and the top level
executive appear as the single entries
**BREAK**, **EDITOR**, and **TOP** respectively.
BTV Prints a backtrace of function names with
variables beginning at lastpos.
BTV* Same as BTV except also prints arguments of
internal calls to eval (see Section 12).
BTV! Same as BTV except prints everything on stack.
(See Section 12).
BT, BTV, BTV*, and BTV! all permit an optional functional argument which
is a predicate that chooses functions to be skipped on the backtrace,
e.g., BT SUBRP will skip all SUBRs,
BTV (LAMBDA (X) (NOT (MEMB X FOOFNS))) will skip all but those functions
15.8
on FOOFNS. If used as a brkcom the functional argument is no longer
optional, i.e., the next brkcom must either be the functional argument,
or NIL if no functional argument is to be applied.
For BT, BTV, BTV*, and BTV!, if control-P is used to change a printlevel
during the backtrace, the printlevel will be restored after the
backtrace is completed.
ARGS Prints the names of the variables bound at
lastpos, i.e., variables[lastpos] (Section 12).
For most cases, these are the arguments to the
function entered at that position, i.e.,
arglist[stkname[lastpos]].
The following two commands are for use only with unbound atoms or
undefined function breaks (see Section 16).
= form, = fn[args] only for the break following an unbound atom
error. Sets the atom to the value of the form,
or function and arguments, exits from the break
returning that value, and continues the
computation, e.g.,
U.B.A.
(FOO BROKEN)
:= (COPY FIE)
sets FOO and goes on.
-> expr for use either with unbound atom error, or
undefined function error. Replaces the
5
expression containing the error with expr (not
the value of expr) e.g.,
U.D.F.
(FOO1 BROKEN)
:-> FOO
changes the FOO1 to FOO and continues the
computation.
------------------------------------------------------------------------
5
-> does not change just brkexp; it changes the function or
expression containing the erroneous form. In other words, the user
does not have to perform any additional editing.
15.9
expr need not be atomic, e.g.,
U.B.A.
(FOO BROKEN)
:-> (QUOTE FOO)
For U.D.F. breaks, the user can specify a
function and initial arguments, e.g.,
U.D.F.
(MEMBERX BROKEN)
:-> MEMBER X
Note that in the case of a U.D.F. error
occurring immediately following a call to apply,
e.g., (APPLY X Y) where the value of x is FOO
and FOO is undefined, or a U.B.A. error
immediately following a call to eval, e.g.,
(EVAL X), where the value of x is FOO and FOO is
unbound, there is no expression containing the
offending atom. In this case, -> cannot
operate, so ? is printed and no action taken.
EDIT designed for use in conjunction with breaks
caused by errors. Facilitates editing the
expression causing the break:
NON-NUMERIC ARG
NIL
(IPLUS BROKEN)
:EDIT
IN FOO...
(IPLUS X Z)
EDIT
*(3 Y)
*OK
FOO
:
and user can continue by typing OK, EVAL, etc.
This command is very simple conceptually, but complicated in its
implementation by all of the exceptional cases involving interactions
with compiled functions, breaks on user functions, error breaks, breaks
within breaks, et al. Therefore, we shall give the following simplified
explanation which will account for 90% of the situations arising in
actual usage. For those others, EDIT will print an appropriate failure
message and return to the break.
EDIT begins by searching up the stack beginning at lastpos (set by @
command, initially position of the break) looking for a form, i.e., an
internal call to eval. Then EDIT continues from that point looking for
a call to an interpreted function, or to eval. It then calls the editor
on either the EXPR or the argument to eval in such a way as to look for
an expression eq to the form that it first found. It then prints the
form, and permits interactive editing to begin. Note that the user can
then type successive 0's to the editor to see the chain of superforms
for this computation.
15.10
If the user exits from the edit with an OK, the break expression is
reset, if possible, so that the user can continue with the computation
6
by simply typing OK. However, in some situations, the break expression
cannot be reset. For example, if a compiled function FOO incorrectly
called putd and caused the error ARG NOT ATOM followed by a break on
putd, EDIT might be able to find the form headed by FOO, and also find
that form in some higher interpreted function. But after the user
corrected the problem in the FOO-form, if any, he would still not have
in any way informed EDIT what to do about the immediate problem, i.e.,
the incorrect call to putd. However, if FOO were interpreted EDIT would
find the putd form itself, so that when the user corrected that form,
EDIT could use the new corrected form to reset the break expression.
The two cases are shown below:
ARG NOT ATOM ARG NOT ATOM
(FUM) (PUTD BROKEN)
(PUTD BROKEN) :EDIT
:EDIT IN FOO...
IN FIE... (PUTD X)
(FOO X) EDIT
EDIT *(2 (CAR X))
*(2 (CAR X)) *OK
*OK FOO
NOTE: BRKEXP NOT CHANGED :OK
FIE PUTD
:?=
U = (FUM)
:(SETQ U (CAR U))
FUM
:OK
PUTD
IN? similar to EDIT, but just prints parent form,
and superform, but does not call editor, e.g.,
ATTEMPT TO RPLAC NIL
T
(RPLACD BROKEN)
:IN?
FOO: (RPLACD X Z)
Although EDIT and IN? were designed for error breaks, they can also be
useful for user breaks. For example, if upon reaching a break on his
function FOO, the user determines that there is a problem in the call to
FOO, he can edit the calling form and reset the break expression with
one operation by using EDIT. The following two protocol's with and
without the use of EDIT, illustrate this:
------------------------------------------------------------------------
6
Evaluating the new brkexp will involve reevaluating the form that
causes the break, e.g., if (PUTD (QUOTE (FOO)) big-computation) were
handled by EDIT, big-computation would be reevaluated.
15.11
(FOO BROKEN) (FOO BROKEN)
:?= :?=
X = (A B C) X = (A B C)
Y = D Y = D
:B :EDIT
IN FIE...
FOO (FOO V U)
SETQ EDIT
COND find which function *(SW 2 3)
PROG FOO is called from *OK
7
FIE FIE
(aborted with ^E) :OK
:EDITF(FIE) FOO
EDIT
*F FOO P
(FOO V U) edit it
*(SW 2 3)
*OK
FIE
:(SETQ Y X) reset X and Y
(A B C)
:(SETQQ X D)
D
:?=
X = D
Y = (A B C) check them
:OK
FOO
REVERT goes back to position lastpos on stack and
reenters the function called at that point with
8
the arguments found on the stack. If the
function is not already broken, REVERT first
breaks it, and then unbreaks it after it is
reentered.
REVERT is useful for restarting a computation in the situation where a
bug is discovered at some point below where the problem actually
occurred. REVERT essentially says "go back there and start over in a
------------------------------------------------------------------------
7
x and y have not been changed, but brkexp has. See previous
footnote.
8
REVERT can also be given the position using the conventions
described for @ on page 15.6, e.g., REVERT FOO -1 is equivalent to @
FOO -1 followed by REVERT.
15.12
9
break."
? prints the names of the break commands.
Brkcoms
The fourth argument to break1 is brkcoms, a list of break commands that
break1 interprets and executes as though they were teletype input. One
can think of brkcoms as another input file which always has priority
over the teletype. Whenever brkcoms=NIL, break1 reads its next command
from the teletype. Whenever brkcoms is not NIL, break1 takes as its
10
next command car[brkcoms] and sets brkcoms to cdr[brkcoms]. For
example, suppose the user wished to see the value of the variable x
after a function was evaluated. He would set up a break with
brkcoms=(EVAL (PRINT X) OK), which would have the desired effect. The
function trace uses brkcoms: it sets up a break with two commands; the
first one prints the arguments of the function, or whatever the user
specifies, and the second is the command GO, which causes the function
to be evaluated and its value printed.
If brkcoms is not NIL, the value of a break command is not printed. If
you desire to see a value, you must print it yourself, as in the above
example with the command (PRINT X).
Note: whenever an error occurs, brkcoms is set to NIL, and a full
interactive break occurs.
Brkfile
The break package has a facility for redirecting ouput to a file. The
variable brkfile should be set to the name of the file, and the file
must be opened. All output resulting from brkcoms will be output to
brkfile, e.g., output due to TRACE. Output due to user typein is not
affected, and will always go to the terminal. brkfile is initially T.
------------------------------------------------------------------------
9
REVERT will work correctly if the names or arguments to the
function, or even its function type, have been changed.
10
Normally, when a user breaks or traces a function, the value of
brkcoms for the corresponding call to break1 will be defaulted to
NIL. However, it is possible to specify a list of break commands, as
described in the discussion of break and break1 below.
15.13
Breakmacros
Whenever an atomic command is given break1 that it does not recognize,
either via brkcoms or the teletype, it searches the list breakmacros for
the command. The form of breakmacros is
( ... (macro command1 command2 ... commandn) ...). If the command is
defined as a macro, break1 simply appends its definition, which is a
sequence of commands, to the front of brkcoms, and goes on. If the
command is not contained in breakmacros, it is treated as a function or
11
variable as before.
Example: the command ARGS could be defined by including on breakmacros:
(ARGS (PRINT (VARIABLES LASTPOS T)))
Breakresetforms
If the user is developing programs that change the way a user and
INTERLISP normally interact, e.g., change or disable the interrupt or
line-editing characters, turn off echoing, etc., debugging them by
breaking or tracing may be difficult, because INTERLISP might be in a
"funny" state at the time of the break. breakresetforms is designed to
solve this problem. The user puts on breakresetforms expressions
12
suitable for use in conjunction with resetform (section 5). When a
break occurs, break1 evaluates each expression on breakresetforms before
any interaction with the terminal, and saves the values. When the break
expression is evaluated via an EVAL, OK, or GO, break1 first restores
the state of the system with respect to the various expressions on
breakresetforms. When (if) control returns to break1, the expressions
13
on breakresetforms are again evaluated, and their values saved. When
the break is exited via an OK, GO, RETURN, or command, break1 again
restores state. Thus the net effect is to make the break invisible with
respect to the user's programs, but nevertheless allow the user to
interact in the break in the normal fashion.
------------------------------------------------------------------------
11
If the command is not the name of a defined function, bound
variable, or lispx command, break1 will attempt spelling correction
using breakcomslst as a spelling list. If spelling correction is
unsuccessful, break1 will go ahead and call lispx anyway, since the
atom may also be a misspelled history command.
12
i.e., the value of each form is its "previous state," so that the
effect of evaluating the form can be undone by applying car of the
form to the value, e.g., radix, printlevel, linelength,
setreadtable, interruptchar, etc., all have this property.
13
Because a lower function might have changed the state of the system
with respect to one of the these expressions!
15.14
15.3 Break Functions
break1[brkexp;brkwhen;brkfn;brkcoms;brktype]
is an nlambda. brkwhen determines whether a
break is to occur. If its value is NIL, brkexp
is evaluated and returned as the value of
break1. Otherwise a break occurs and an
identifying message is printed using brkfn.
Commands are then taken from brkcoms or the
teletype and interpreted. The commands, GO,
!GO, OK, !OK, RETURN and ^, are the only ways to
leave break1. The command EVAL causes brkexp to
be evaluated, and saves the value on the
variable !value. Other commands can be defined
for break1 via breakmacros. brktype is NIL for
user breaks, INTERRUPT for control-H breaks, and
ERRORX for error breaks.
For error breaks, the input buffer is cleared and saved. (For control-H
breaks, the input buffer was cleared at the time the control-H was
typed, see Section 16.) In both cases, if the break returns a value,
i.e., is not aborted via ^ or control-D, the input buffer will be
restored (see Section 14).
break0[fn;when;coms] sets up a break on the function fn by redefining
fn as a call to break1 with brkexp an equivalent
definition of fn, and when, fn, and coms, as
brkwhen, brkfn, brkcoms. Puts property BROKEN
on property list of fn with value a gensym
defined with the original definition. Puts
property BRKINFO on property list of fn with
value (BREAK0 when coms) (For use in conjunction
with rebreak). Adds fn to the front of the list
brokenfns. Value is fn.
If fn is non-atomic and of the form
(fn1 IN fn2), break0 first calls a function
which changes the name of fn1 wherever it
appears inside of fn2 to that of a new function,
fn1-IN-fn2, which it initially defines as fn1.
Then break0 proceeds to break on fn1-IN-fn2
exactly as described above. This procedure is
useful for breaking on a function that is called
from many places, but where one is only
interested in the call from a specific function,
e.g., (RPLACA IN FOO), (PRINT IN FIE), etc. It
is similar to breakin described below, but can
be performed even when FN2 is compiled or
blockcompiled, whereas breakin only works on
interpreted functions.
If fn1 is not found in fn2, break0 returns the
value (fn1 NOT FOUND IN fn2).
If fn1 is found in fn2, in addition to breaking
fn1-IN-fn2 and adding fn1-IN-fn2 to the list
brokenfns, break0 adds fn1 to the property value
15.15
for the property NAMESCHANGED on the property
list of fn2 and adds the property ALIAS with the
value (fn2 . fn1) to the property list of
fn1-IN-fn2. This will enable unbreak to
recognize what changes have been made and
restore the function fn2 to its original state.
If fn is nonatomic and not of the above form,
break0 is called for each member of fn using the
same values for when, coms, and file specified
in this call to break0. This distributivity
permits the user to specify complicated break
conditions on several functions without
excessive retyping, e.g.,
break0[(FOO1 ((PRINT PRIN1) IN (FOO2 FOO3)));
(NEQ X T);(EVAL ?= (Y Z) OK)]
will break on FOO1, PRINT-IN-FOO2,
PRINT-IN-FOO3, PRIN1-IN-FOO2 and PRIN1-IN-FOO3.
If fn is non-atomic, the value of break0 is a
list of the individual values.
break[x] is a nospread nlambda. For each atomic
argument, it performs break0[atom;T]. For each
list, it performs apply[BREAK0;list]. For
example,
break[FOO1 (FOO2 (GREATERP N 5) (EVAL))] is
equivalent to break0[FOO1,T] and
break0[FOO2; (GREATERP N 5); (EVAL)]
trace[x] is a nospread nlambda. For each atomic
argument, it performs
14
break0[atom;T;(TRACE ?= NIL GO)] For each list
argument, car is the function to be traced, and
cdr the forms the user wishes to see, i.e.,
trace performs:
break0[car[list];T;list[TRACE;?=; cdr[list],GO]]
For example, TRACE(FOO1 (FOO2 Y)) will cause
both FOO1 and FOO2 to be traced. All the
arguments of FOO1 will be printed; only the
value of Y will be printed for FOO2. In the
special case that the user wants to see only the
value, he can perform TRACE((fn)). This sets up
a break with commands (TRACE ?= (NIL) GO).
------------------------------------------------------------------------
14
The flag TRACE is checked for in break1 and causes the message
"function :" to be printed instead of (function BROKEN).
15.16
Note: the user can always call break0 himself to obtain combination of
options of break1 not directly available with break and trace. These
two functions merely provide convenient ways of calling break0, and will
serve for most uses.
Breakin
Breakin enables the user to insert a break, i.e., a call to break1, at a
specified location in an interpreted function. For example, if foo
calls fie, inserting a break in foo before the call to fie is similar to
breaking fie. However, breakin can be used to insert breaks before or
after prog labels, particular SETQ expressions, or even the evaluation
of a variable. This is because breakin operates by calling the editor
and actually inserting a call to break1 at a specified point inside of
the function.
The user specifies where the break is to be inserted by a sequence of
editor commands. These commands are preceded by BEFORE, AFTER, or
AROUND, which breakin uses to determine what to do once the editor has
found the specified point, i.e., put the call to break1 BEFORE that
point, AFTER that point, or AROUND that point. For example,
(BEFORE COND) will insert a break before the first occurrence of cond,
(AFTER COND 2 1) will insert a break after the predicate in the first
cond clause, (AFTER BF (SETQ X &)) after the last place X is set. Note
that (BEFORE TTY:) or (AFTER TTY:) permit the user to type in commands
to the editor, locate the correct point, and verify it for himself using
15
the P command if he desires, and exit from the editor with OK. breakin
then inserts the break BEFORE, AFTER, or AROUND that point.
For breakin BEFORE or AFTER, the break expression is NIL, since the
value of the break is irrelevant. For breakin AROUND, the break
expression will be the indicated form. In this case, the user can use
the EVAL command to evaluate that form, and examine its value, before
allowing the computation to proceed. For example, if the user inserted
a break after a cond predicate, e.g., (AFTER (EQUAL X Y)), he would be
powerless to alter the flow of computation if the predicate were not
true, since the break would not be reached. However, by breaking
(AROUND (EQUAL X Y)), he can evaluate the break expression, i.e.,
(EQUAL X Y), look at its value, and return something else if he wished.
The message typed for a breakin break, is ((fn) BROKEN), where fn is the
name of the function inside of which the break was inserted. Any error,
or typing control-E, will cause the full identifying message to be
printed, e.g., (FOO BROKEN AFTER COND 2 1).
A special check is made to avoid inserting a break inside of an
expression headed by any member of the list nobreaks, initialized to
------------------------------------------------------------------------
15
A STOP command typed to TTY: produces the same effect as an
unsuccessful edit command in the original specification, e.g.,
(BEFORE CONDD). In both cases, the editor aborts, and breakin types
(NOT FOUND).
15.17
(GO QUOTE *), since this break would never be activated. For example,
if (GO L) appears before the label L, breakin (AFTER L) will not insert
the break inside of the GO expression, but skip this occurrence of L and
go on to the next L, in this case the label L. Similarly, for BEFORE or
AFTER breaks, breakin checks to make sure that the break is being
inserted at a "safe" place. For example, if the user requests a break
(AFTER X) in (PROG -- (SETQ X &) --), the break will actually be
inserted AFTER (SETQ X &), and a message printed to this effect, e.g.,
BREAK INSERTED AFTER (SETQ X &).
breakin[fn;where;when;coms]
breakin is an nlambda. when and coms are
similar to when and coms for break0, except that
if when is NIL, T is used. where specifies
where in the definition of fn the call to break1
is to be inserted. (See earlier discussion).
If fn is a compiled function, breakin returns
(fn UNBREAKABLE) as its value.
If fn is interpreted, breakin types SEARCHING...
while it calls the editor. If the location
specified by where is not found, breakin types
(NOT FOUND) and exits. If it is found, breakin
adds the property BROKEN-IN with value T, and
the property BRKINFO with value
(where when coms) to the property list of fn,
and adds fn to the front of the list brokenfns.
Multiple break points, can be inserted with a
single call to breakin by using a list of the
form ((BEFORE ...) .. (AROUND ...)) for where.
It is also possible to call break or trace on a
function which has been modified by breakin, and
conversely to breakin a function which has been
redefined by a call to break or trace.
unbreak[x] unbreak is a nospread nlambda. It takes an
indefinite number of functions modified by
break, trace, or breakin and restores them to
their original state by calling unbreak0. Value
is list of values of unbreak0.
unbreak[] will unbreak all functions on
brokenfns, in reverse order. It first sets
brkinfolst to NIL.
unbreak[T] unbreaks just the first function on
brokenfns, i.e., the most recently broken
function.
unbreak0[fn] restores fn to its original state. If fn was
not broken, value is (NOT BROKEN) and no changes
are made. If fn was modified by breakin,
unbreakin is called to edit it back to its
15.18
original state. If fn was created from
(fn1 IN fn2), i.e., if it has a property ALIAS,
the function in which fn appears is restored to
its original state. All dummy functions that
were created by the break are eliminated. Adds
property value of BRKINFO to (front of)
brkinfolst.
Note: unbreak0[(fn1 IN fn2)] is allowed:
unbreak0 will operate on fn1-IN-fn2 instead.
unbreakin[fn] performs the appropriate editing operations to
eliminate all changes made by breakin. fn may
be either the name or definition of a function.
Value is fn. Unbreakin is automatically called
by unbreak if fn has property BROKEN-IN with
value T on its property list.
rebreak[x] is an nlambda, nospread function for rebreaking
functions that were previously broken without
having to respecify the break information. For
each function on x, rebreak searches brkinfolst
for break(s) and performs the corresponding
operation. Value is a list of values
corresponding to calls to break0 or breakin. If
no information is found for a particular
function, value is
(fn - NO BREAK INFORMATION SAVED).
rebreak[] rebreaks everything on brkinfolst,
i.e., rebreak[] is the inverse of unbreak[].
rebreak[T] rebreaks just the first break on
brkinfolst, i.e., the function most recently
unbroken.
changename[fn;from;to] changes all occurrences of from to to in fn. fn
may be compiled or blockcompiled. Value is fn if
from was found, otherwise NIL. Does not perform
any modifications of property lists. Note that
from and to do not have to be functions, e.g.,
they can be names of variables, or any other
literals.
virginfn[fn;flg] is the function that knows how to restore
functions to their original state regardless of
any amount of breaks, breakins, advising,
compiling and saving exprs, etc. It is used by
prettyprint, define, and the compiler. If
flg=NIL, as for prettyprint, it does not modify
the definition of fn in the process of producing
a "clean" version of the definition, i.e., it
works on a copy. If flg=T as for the compiler
and define, it physically restores the function
15.19
to its original state, and prints the changes it
is making, e.g., FOO UNBROKEN, FOO UNADVISED,
FOO NAMES RESTORED, etc. Value is the virgin
function definition.
baktrace[ipos;epos;skipfn;flags]
prints backtrace from ipos to epos. flags
specifies the options of the backtrace, e.g.,
do/don't print arguments, do/don't print
temporaries of the interpreter, etc., and is the
same as for backtrace (Section 12). baktrace
collapses the sequence of several function calls
corresponding to a call to a system package into
a single "function", e.g., any call to the
editor is printed as **EDITOR**, a break is
printed as **BREAK**, etc. If skipfn is not NIL
and skipfn[stkname[pos]] is T, pos is skipped
(including all variables). baktrace is used by
the BT, BTV, BTV*, and BTV! commands, with
flags=0,1,3, and 7 respectively.
15.20