Trailing-Edge
-
PDP-10 Archives
-
decuslib10-04
-
43,50322/break.doc
There are no other files named break.doc in the archive.
DEBUGGING FACILITIES
Introduction
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 UCI LISP system, there
are five facilities which aid the user in monitoring his
program. One of these is the Error Package which takes
control whenever an error occurs in a program and which
allows the user to examine the state of the world (see
section on 'ERROR PACKAGE'). Another facility allows the
user to temporarily interrupt his computation and examine
its progress. The other three facilities (BREAK, TRACE and
BREAKIN) allow the user to (temporarily) modify selected
function definitions so that he can follow the flow of
control in his programs. All of these facilities use the
same system function, BREAK1, as the user interface.
BREAK, BREAKIN and TRACE together are called the Break
Package. BREAK and TRACE can be used on compiled and system
functions as well as EXPR's, FEXPR's and MACRO's. BREAKIN
can be used only with interpreted functions.
BREAK modifies the definition of a function FN, so that
if a break condition (defined by the user) is satisified,
the process is halted temporarily on a call to FN. The user
can then interrogate the state of the machine, perform any
computations, 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.
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 two examples on pages 1.3 and 1.4 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
1 . 1
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 LISP
forms and direct the course of the computation. In this
case, the user examines the variable N, instructs BREAK1 to
change L to 1 and continue. The > command, following an
UNBOUND ATOM or UNDEFINED FUNCTION error, tells BREAK1 to
use the next expression instead of the atom which caused the
error. The > command does a destructive replacement of, in
this case, 1 for L, and saves an edit step by correcting the
typo in the function definition. The rest of the tracing
proceeds without incident. The function UNTRACE restores
FACTORIAL to its original definition.
In the second example, the user has written Ackermann's
function. He then uses BREAK to place a call to BREAK1
around the body of the function. He indicates that ACK is
to be broken when M equals N and that before the break
occurs, the arguments to ACK are to be printed. While
calculating (ACK 2 1), ACK is called twice when M = N.
During the first of these breaks, the user prints out a
backtrace of the function names and variable bindings. He
continues the computation with a GO which causes the value
of (ACK 1 1), 3, to be printed before the break is released.
The second break is released with an OK which does not print
the result of (ACK 1 1). The function UNBREAK with an
argument T restores the latest broken or traced function to
its original definition.
For further information on how to use BREAK, TRACE and
BREAKIN, see the section on The Break Package.
1 . 2
*(DE FACTORIAL (N)
(COND ((ZEROP N) L)
(T (TIMES N (FACTORIAL (SUB1 N))))))
FACTORIAL
*(TRACE FACTORIAL)
(FACTORIAL)
*(FACTORIAL 4)
ENTER FACTORIAL:
! N = 4
! ENTER FACTORIAL:
! ! N = 3
! ! ENTER FACTORIAL:
! ! ! N = 2
! ! ! ENTER FACTORIAL:
! ! ! ! N = 1
! ! ! ! ENTER FACTORIAL:
! ! ! ! ! N = 0
L
UNBOUND VARIABLE - EVAL
(L BROKEN)
1:N
0
1:> 1
! ! ! ! FACTORIAL = 1
! ! ! FACTORIAL = 1
! ! FACTORIAL = 2
! FACTORIAL = 6
FACTORIAL = 30
30
(UNTRACE FACTORIAL)
(FACTORIAL)
*(FACTORIAL 4)
30
1 . 3
*(DE ACK (M N)
(COND ((ZEROP M) (ADD1 N))
((ZEROP N) (ACK (SUB1 M) 1))
(T (ACK (SUB1 M) (ACK M (SUB1 N))))))
ACK
*(BREAK (ACK (EQ N M) (ARGS)))
(ACK)
*(ACK 2 1)
M = 1
N = 1
(ACK BROKEN)
1:BKFV
M = 1
N = 1
ACK
M = 2
N = 0
ACK
M = 2
N = 1
ACK
1:GO
3
M = 1
N = 1
(ACK BROKEN)
1:OK
5
*(UNBREAK T)
(ACK)
1 . 4
Interrupting a computation-REE and DDT
A useful feature for debugging is a way to temporarily
suspend computation. If the user wishes to know how his
computation is proceeding (i.e. is he in an infinite loop
or is system response poor). Then type Control-C twice
(which will cause a return to the monitor) followed by
either REE or DDT. After typing REE the user must respond
with one of the following control characters; Control-H,
Control-B, Control-G, Control-E or Control-Z. Typing DDT is
equivalent to typing REE followed by Control-H.
1. Control-H: This will cause the computation to continue,
but a break will occur the next time a function is called
(except for a compiled function called by a compiled
function). A message of the form (-- BROKEN) is typed and
the user is in BREAK1 (see the next section). He can
examine the state of the world and continue or stop his
computation using any of the BREAK1 commands. WARNING It is
possible to get into an infinite loop that does not include
calls to functions other than compiled functions called by
compiled functions. These will continue to run. (In such
cases, type Control-C twice, followed by REE, followed by
one of the other control characters).
2. Control-B: This will cause the system to back up to the
last expression to be evaluated and cause a break (putting
the user in BREAK1 with all the power of BREAK1 at the
user's command. This does not include calls to compiled
functions by other compiled functions.
3. Control-G: This causes an (ERR ERRORX) which returns to
the last (ERRSET ERRORX). This enables the user to
Control-C out of the Break package or the Editor, reenter
and return to the appropriate command level. (i.e. if the
user were several levels deep in the Editor for example,
Control-G will return him to the correct command level of
the Editor).
1 . 5
4. Control-E: This does an (ERR NIL), which return NIL to
the last ERRSET. (See section on changes to ERR and
ERRSET).
5. Control-Z: This returns the user to the top-level of
LISP, (i.e. either the READ-EVAL-PRINT loop or the current
INITFN).
6. Control-R: This restores the normal system OBLIST.
Another of the above control characters must be typed after
this character is typed. This will often recover after a
GARBAGED OBLIST message.
1 . 5 . 1
BREAK1
The heart of the debugging package is a function called
BREAK1. BREAK and TRACE redefine your functions in terms of
BREAK1. When an error occurs control is passed to BREAK1.
The DDT break feature is also implemented using BREAK1.
Whenever LISP types a message of the form (-- BROKEN)
followed by 'n:' the user is then 'talking to' BREAK1, and
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 the top level of LISP uses '*'. The n before the ':'
is the level number which indicates how many levels of
BREAK1 are currently open. The user may type in an
expression for evaluation and the value will be printed out,
followed by another ':'. Or the user can type in one of the
commands described below which are specifically recognized
by BREAK1 (for summary of commands see Table I, page 1.25).
Since BREAK1 puts all of the power of LISP at the
user's command, he can do anything he can do at the top
level of LISP. For example, he can define new functions or
edit existing ones, set breaks, or trace functions. The
user may evaluate an expression, see that the value was
incorrect, call the editor, change a function, and evaluate
the expression again, all without leaving the break.
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. Only if the user gives one
of the commands that exits from the break (GO, OK, RETURN,
FROM?=, EX) will the computation continue. If the user
wants to abort the computation, this also can be done (using
^ or ^^).
Note that BREAK1 is just another LISP function, not a
special system feature like the interpreter or the garbage
collector. It has arguments and returns a value, the same
as any other function. A call to BREAK1 has the form
(BREAK1 BRKEXP BRKWHEN BRKFN BRKCOMS BRKTYPE)
The arguments to BREAK1 are: BRKWHEN is a LISP function
which is evaluated to determine if a break will occur. If
1 . 6
BRKWHEN returns NIL, BRKEXP is evaluated and returned as the
value of the BREAK1. Otherwise a break occurs. BRKFN is
the name of the function being broken and is used to print
an identifying message. BRKCOMS is a list of command lines
(as returned by READLINE) which are executed as if they had
been typed in from the teletype. The command lines on
BRKCOMS are executed before commands are accepted from the
teletype, so that if one of the commands on BRKCOMS causes a
return, a break occurs without the need for teletype
interaction. BRKTYPE identifies the type of the break. It
is used primarily by the error package and in all cases the
user can use NIL for this argument.
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. In most cases,
however, the value of the break is given implicitly, via a
GO or OK command, and is the result of evaluating 'the break
expression,' BRKEXP.
BRKEXP is, in general, an expression
equivalent to the computation that would have
taken place had no break occurred. In other
words, one can think of BREAK1 as a fancy EVAL,
which permits interaction before and after
evaluation. The break expression then corresponds
to the argument to EVAL. For BREAK and TRACE,
BRKEXP is a form equivalent to that of the
function being traced or broken. For errors,
BRKEXP is the form which caused the error. For
DDT breaks, BRKEXP is the next form to be
evaluated.
1 . 7
WHAT YOU CAN DO IN A BREAK
Break Commands
Once in a break, in addition to evaluating expressions,
the user can ask BREAK1 to perform certain useful actions by
giving it atomic items as "break commands". The following
commands can be typed in by the user or may be put on the
list BRKCOMS. TABLE I (page 1.25) is a summary of these
commands.
All printing in BREAK1 is done by calling (%PRINFN
expr). %PRINFN is an atom (not a function) which should
evaluate to the name of a printing function of one argument.
%PRINFN is initialized to use PRINTLEV because it can print
circular lists, which quite often result from errors.
PRINTLEV only prints lists to a depth of 6. This depth
parameter may be changed by setting the value of %LOOKDPTH.
PRINTLEV is necessarily slow and if you are not printing
circular structures, traces can be speeded up greatly by
changing the value of %PRINFN to PRIN1.
GO
Releases the break and allows the computation
to proceed. BREAK1 evaluates BRKEXP, its first
argument, prints the value, and returns it as the
value of the break. BRKEXP is the expression set up
by the function that called BREAK1. For BREAK or
TRACE, BRKEXP is equivalent to the body of the
definition of the broken function. For the error
package, BRKEXP is the expression in which the error
occurred. For DDT breaks, it is the next form to be
evaluated.
OK
Same as GO except that the value of BRKEXP is
not printed.
EVAL
Causes BRKEXP to be evaluated. The break is
maintained and the value of the evaluation is
printed and bound on the variable !VALUE. Typing GO
or OK will not cause reevaluation of BRKEXP
following EVAL 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
1 . 8
wishes to be able to do something about it if it is
wrong.
RETURN form
The form is evaluated and its value is returned
as the value of the break. For example, one might
use the EVAL command and follow this with
RETURN (REVERSE !VALUE).
FROM?= form
This permits the user to release the break and
return to a previous context with form to be
evaluated. For details see context commands.
> [or ->] expr
For use either with UNBOUND ATOM error or
UNDEFINED FUNCTION error. Replaces the expression
containing the error with expr (not the value of
expr) e.g.,
FOO1
UNDEFINED FUNCTION
(FOO1 BROKEN)
1:> FOO
changes FOO1 to FOO and continues the computation.
Expr need not be atomic, e.g.,
FOO
UNBOUND ATOM
(FOO BROKEN)
1:> (QUOTE FOO)
For UNDEFINED FUNCTION breaks, the user can specify
a function and its first argument, e.g.,
MEMBERX
UNDEFINED FUNCTION
(MEMBERX BROKEN)
1:> MEMBER X
Note that in the some cases the form containing the
offending atom will not be on the stack (notably,
after calls to APPLY) and in these cases the
function definition will not be changed. In most
cases, however, > will correct the function
definition.
1 . 9
USE x FOR y
Causes all occurrences of y in the form on the
stack at LASTPOS (for Error breaks, unless a F
command has been used, this form is the one in which
the error occurred.) to be replaced (RPLACA'ed) by
x. Note: This is a destructive change to the
s-expression involved and will, for example,
permanently change the definition of a function and
make a edit step unnecessary.
^
Calls ERR and aborts the break. 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.
^^
This returns control directly to the top level
of LISP.
ARGS
Prints the names and the current values of the
arguments of BRKFN. In most cases, these are the
arguments of the broken function.
1 . 10
Context Commands
All information pertaining to the evaluation of forms
in LISP is kept on the special push down stack. Whenever a
form is evaluated, that form is placed on the special push
down stack. Whenever a variable is bound, the old binding
is saved on the special push down stack. The context (the
bindings of free variables) of a function is determined by
its position in the stack. When a break occurs, it is often
useful to explore the contexts of other functions on the
stack. BREAK1 allows this by means of a context pointer,
LASTPOS, which is a pointer into the special push down
stack. BREAK1 contains commands to move the context pointer
and to evaluate atoms or expressions as of its position in
the stack. For the purposes of this document, when moving
through the stack, "backward" is considered to be toward the
top level or, equivalently, towards the older function calls
on the stack.
F [or &] arg1 arg2 ... argN
Resets the variable LASTPOS, which establishes
a context for the commands ?=, USE, EX and FROM?=,
and the backtrace commands described below. LASTPOS
is the position of a function call on the special
push down list. It is initialized to the function
just before the call to BREAK1.
F takes the rest of the teletype line as its
list of arguments. F first resets LASTPOS to the
function call just before the call to BREAK1, and
then for each atomic argument, F searches backward
for a call to that atom. The following atoms are
treated specially:
F
When used as the first argument
caused LASTPOS not to be reset to
above BREAK1 but continues searching
from the previous position of LASTPOS.
Numbers
If negative, move LASTPOS back
(i.e. towards the top level) that
number of calls, if positive, forward.
1 . 11
_
Search forward instead of
backward for the next atom
Example:
If the special 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
F FIE COND will set LASTPOS to to (7)
F & COND will then set LASTPOS to (5)
F FUM _ FIE will stop at (4)
F & 2 will then move LASTPOS to (6)
F will reset LASTPOS to (12)
If F cannot successfully complete a search,
for argN or if argN is a number and F cannot move
the number of functions asked, "argN?" is typed.
In either case, LASTPOS is restored to its value
before the F command was entered. Note: It is
possible to move past BRKEXP (i.e. into the break
package functions) when searching or moving
forwards.
When F finishes, it types the name of the
function at LASTPOS.
F can be used on BRKCOMS. In which case, the
remainder of the list is treated as the list of
arguments. (i.e. (F FOO FIE FOO)
1 . 12
EDIT arg1 arg2 ... argN
EDIT uses its arguments to reset LASTPOS in
the same manner as the F command. The form at
LASTPOS is then given to the LISP Editor. This
commands can often times save the user from the
trouble of calling EDITF and the finding the
expression that he needs to edit.
?= arg1 arg2 ... argN
This is a multi-purpose command. Its most
common use is to interrogate the value(s) of the
arguments of the broken function, (ARGS is also
useful for this purpose.) e.g. if FOO has three
arguments (X Y Z), then typing ?= to a break of
FOO, will produce:
n:?=
X = value of X
Y = value of Y
Z = value of Z
?= takes the rest of the teletype line as its
arguments. If the argument list to ?= is NIL, as
in the above case, it prints all of the arguments
of the function at LASTPOS. 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 into BREAK1 is that ?=
evaluates its inputs as of LASTPOS. This provides
a way of examining variables or forms as of a
particular point on the stack. For example,
F (FOO FOO)
?= X
will allow the user to examine the value of X in an
earlier call to FOO.
?= also recognizes numbers as referring to the
correspondingly numbered argument. Thus
:F FIE
:?= 2
1 . 13
will print the name and value of the second
argument of FIE (providing FIE is not compiled).
?= can also be used on BRKCOMS, in which case
the remainder of the list on BRKCOMS is treated as
the list of arguments. For example, if BRKCOMS is
((EVAL) (?= X (CAR Y)) GO)), BRKEXP will be
evaluated, the values of X and (CAR Y) printed, and
then the function exited with its value being
printed.
FROM?= [form]
FROM?= exits from the break by undoing the
special push down stack back to LASTPOS. If FORM
is NIL or missing, re-evaluation continues with the
form on the push down stack at LASTPOS. If FORM is
not NIL, the function call on the push down stack
at LASTPOS is replaced by FORM and evaluation
continues with FORM. FORM is evaluated in the
context of LASTPOS. There is no way of recovering
the break because the push down stack has been
undone. FROM?= allows the user to, among other
things, return a particular value as the value of
any function call on the stack. To return 1 as the
value of the previous call to FOO:
:F FOO
:FROM?= 1
Since form is evaluated after it is placed on the
stack, a value of NIL can be returned by using
(QUOTE NIL).
EX
EX exits from the break and re-evaluates the
form at LASTPOS. EX is equivalent to FROM?= NIL.
1 . 14
Backtrace Commands
The backtrace commands print information about
function calls on the special push down list. The
information is printed in the reverse order that the calls
were made. All backtraces start at LASTPOS.
BKF
BKF gives a backtrace of the names of
functions that are still pending.
BKE
BKE gives a backtrace of the expressions which
called functions still pending (i.e. It prints the
function calls themselves instead of only the names
as in BKF).
BK
BK gives a full backtrace of all expressions
still pending.
All of the backtrace commands may be suffixed by a 'V'
and/or followed by an integer. If the integer is included,
it specifies how many blocks are to be printed. The
limiting point of a block is a function call. This form is
useful when working on a Data Point. Using the integer
feature in conjunction with the F command, which moves
LASTPOS, the user can display any contiguous part of the
backtrace. If a 'V' is included, variable bindings are
printed along with the expressions in the backtrace.
Example:
BKFV would print the names and variable
bindings of the functions called before
LASTPOS.
BKV 5 would print everything (expressions and
variables) for 5 blocks before LASTPOS.
1 . 15
The output of the backtrace commands deserves some
explanation. Right circular lists are only printed up to
the point where they start repeating and are closed with
'...]' instead of a right parenthesis. Lists are only
printed to a depth of 2. /#/ Is a notation which
represents "the previous expression". For example, (SETQ
FIE (FOO)) would appear in a BK backtrace as
(FOO)
(SETQ FIE /#/)
1 . 16
Breakmacros
Whenever an atomic command is encountered by BREAK1
that it does not recognize, either via BRKCOMS or the
teletype, it searches (using ASSOC) the list BREAKMACROS to
see if the atom has been defined as a break macro. The
form of BREAKMACROS definitions is ( ... (atom ttyline1
ttyline2 ... ttylineN) ... ). ATOM is the command name.
ARGS is the argument(s) for the macro. The arguments of a
breakmacro are assigned values from the remainder of the
command line in which the macro is called. If ARGS is
atomic, it is assigned the remainder of the command line as
its value. If ARGS is a list, the elements of the rest of
the command line are assigned to the variables, in order.
If there are more variables in ARGS then items in the rest
of the command line, a value of NIL is filled in. Extra
items on the command line are ignored. The TTYLINEs are
the body of the breakmacro definition and are lists of
break commands or forms to be evaluated. If the atom is
defined as a macro, (i.e. is found on BREAKMACROS) BREAK1
assigns values to the variables in ARGS, substitutes these
values for all occurrences of the variables in TTYLINEs and
appends the TTYLINEs to the front of BRKCOMS. When BREAK1
is ready to accept another command, if BRKCOMS is non-NIL
it takes the first element of BRKCOMS and processes it
exactly as if it had been a line input from the teletype.
This means that a macro name can be defined to expand to
any arbitrary collection of expressions that the user could
type in. If the command is not contained in BREAKMACROS,
it is treated as a function or variable as before.
Example: a command PARGS to print the arguments of the
function at LASTPOS could be defined by evaluating:
(NCONC BREAKMACROS (QUOTE ((PARGS NIL (?=)))))
A command FP which finds a place on the SPD stack and
prints the form there can be defined by:
(NCONC BREAKMACROS (QUOTE (FP X (F . X) ((PRINT (SPDLRT
LASTPOS))))))
1 . 17
BREAK PACKAGE
How To Set A Break
The following functions are useful for setting and
unsetting breaks and traces.
Both BREAK and TRACE use a function BREAK0 to do the
actual modification of function definitions. When BREAK0
breaks a SUBR or an FSUBR, it prints a message of the form
(--- . ARGUMENT LIST?). The user should respond with a
list of arguments for the function being broken. (FSUBR's
take only one argument and BREAK0 checks for this.) The
arguments on this list are actually bound during the calls
to the broken function and care should be taken to insure
that they do not conflict with free variables. For
LSUBR's, the atom N? Is used as the argument. It is
possible to GRINDEF and edit functions that are traced or
broken. BROKENFNS is a list of the functions currently
broken. TRACEDFNS is a list of the functions currently
traced.
BREAK
BREAK is an FEXPR. For each atomic argument, it
breaks the function named each time it is called. For each
list in the form (fn1 IN fn2), it breaks only those
occurrences of FN1 which appear in FN2. This feature is
very useful for breaking 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. For each list not in this form, it assumes that
the CAR is a function to be broken; the CADR is the break
condition; (When the fuction is called, the break condition
is evaluated. If it returns a non-NIL value, the break
occurs. Otherwise, the computation continues without a
break.) and the CDDR is a list of command lines to be
performed before an interactive break is made (see BRWHEN
and BRKCOMS of BREAK1). For example,
(BREAK FOO1 (FOO2 (GREATERP N 5) (ARGS)))
will break all calls to FOO1 and all calls on FOO2 when N
is greater than 2 after first printing the arguments of
FOO2.
1 . 18
(BREAK ((FOO4 IN FOO5) (MINUSP X)))
will break all calls to FOO4 made from FOO5 when X is
negative.
Examples:
(BREAK FOO)
(BREAK ((GET IN FOO) T (GO)))
(BREAK (SETQ (EQ N 1) ((PRINT (QUOTE N=1)))(?= M)))
TRACE
TRACE is an FEXPR. For each atomic argument, it
traces the function named (see form on page 1.3) each time
it is called. For each list in the form (fn1 IN fn2), it
traces only those calls to FN1 that occur within FN2. For
each list argument not in this form, the CAR is the
function to be traced, and the CDR is a list of variables
(or forms) the user wishes to see in the trace.
For example, (TRACE (FOO1 Y) (SETQ IN FOO3)) will
cause both FOO1 and SETQ IN FOO3 to be traced. SETQ's
argument will be printed and the value of Y will be printed
for FOO1.
TRACE uses the global variable #%INDENT to keep its
position on the line. The printing of output by TRACE is
printed using %PRINFN (see page 1.9). TRACE can therefore
be pretty printed by:
(SETQ %PRINFN (QUOTE PRETPRIN))
(DE PRETPRIN (FORM)
(SPRINT FORM (*PLUS 10 #%INDENT)))
Examples:
(TRACE FOO)
(TRACE *TIMES (SELECTQ IN DOIT))
(TRACE (EVAL IN FOO))
(TRACE (TRY M N X (*PLUS N M)))
Note: The user can always call BREAK0 himself to
obtain combinations of options of BREAK1 not directly
available with BREAK and TRACE (see section on BREAK0
below). These functions merely provide convenient ways of
calling BREAK0, and will serve for most uses.
1 . 19
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 F)) after the last place X is set. Note that
(BEFORE TTY:), (AROUND TTY:) or (AFTER TTY:) permit the
user to type in commands to the editor, locate the correct
point, and verify it for himself using the P command, if he
desires. Upon exit from the editor with OK, the break is
inserted. (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).)
for BREAKIN BEFORE or AFTER, the break expression is
NIL, since the value of the break is usually not of
interest. For BREAKIN AROUND, the break expression will be
the indicated form. When in the break, the user can use
the EVAL command to evaluate that form, and see 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 fo 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), see its value and evaluate something
else if he wished.
The message typed for a BREAKIN break identifies the
location of the break as well as the function, e.g.,
1 . 20
((FOO (AFTER COND 2 1)) BROKEN).
BREAKIN is an FEXPR which has a maximum of four
arguments. The first argument is the function to be broken
in. The second argument is a list of editor commands,
preceded by BEFORE, AFTER, or AROUND, which specifies the
location inside the function at which to break. If there
is no second argument, a value of (BEFORE TTY:) is assumed.
(See earlier discussion.) The third and fourth arguments
are the break condition and the list of commands to be
performed before the interactive break occurs, (BRKWHEN and
BRKCOMS for BREAK1) respectively. If there is no third
argument, a value of T is assumed for BRKWHEN which causes
a break each time the BREAKIN break is executed. If the
fourth argument is missing, a value of NIL is assumed. For
example,
(BREAKIN FOO (AROUND COND))
inserts a break around the first call to COND in FOO.
It is possible to insert multiple break points, with a
single call to BREAKIN by using a list of the form ((BEFORE
...) ... (AROUND ...)) as the second argument. It is also
possible to BREAK or TRACE a function which has been
modified by BREAKIN, and conversely to BREAKIN a function
which is broken or traced. UNBREAK restores functions
which have been broken in. GRINDEF makes no attempt to
correct the modification of BREAKIN so functions should be
unbroken before they are stored on disk.
Examples:
(BREAKIN FOO (AROUND TTY:) T (?= M N) ((*PLUS X Y)))
(BREAKIN FOO2 (BEFORE SETQ) (EQ X Y))
UNBREAK
UNBREAK is an FEXPR. It takes a list of functions
modified by BREAK or BREAKIN and restores them to their
original state. It's value is the list of functions that
were "unbroken".
(UNBREAK T) will unbreak the function most recently
broken.
(UNBREAK) will unbreak all of the functions currently
1 . 21
broken (i.e. all those on BROKENFNS).
If one of the functions is not broken, UNBREAK has a
value of (fn NOT BROKEN) for that function and no changes
are made to fn.
Note: If a function is both traced and broken in,
either UNTRACE or UNBREAK will restore the original
function definition.
UNTRACE
UNTRACE is an FEXPR. It takes a list of functions
modified by TRACE and restores them to their original
state. It's value is the list of functions that were
"untraced".
(UNTRACE T) will unbreak the function most recently
traced.
(UNTRACE) will untrace all of the functions currently
traced (i.e. all those on TRACEDFNS).
If one of the functions is not traced, UNTRACE has a
value of (fn NOT BROKEN) for that function and no changes
are made to fn.
1 . 22
BREAK0 [FN WHEN COMS]
BREAK0 is an EXPR. It sets up a break on the function
FN by redefining FN as a call to BREAK1 with BRKEXP a form
equivalent to the definition of FN, and WHEN, FN and COMS
as BRKWHEN, BRKFN, and BRKCOMS, respectively (see BREAK1).
BREAK0 also adds FN to the front of the list BROKENFNS.
It's 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 is initially defined 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. This only works
in interpreted functions. If fn1 is not found in fn2,
BREAK0 returns the value (fn1 NOT FOUND IN fn2).
If FN is non-atomic and not of the above form, BREAK0
is called for each member of FN using the same values for
WHEN and COMS specified in this call to BREAK0. This
distributivity permits the user to specify complicated
break conditions without excessive retyping, e.g.,
(BREAK0 (QUOTE (FOO1 ((PRINT PRIN1)IN (FOO2 FOO3))))
(QUOTE (EQ X T))
(QUOTE ((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.
For example, BREAK0 can be used to trace the changing
of particular values by SETQ in the following manner:
*(SETQ VARLIST (QUOTE (X Y FOO)))
*(BREAK0 (QUOTE SETQ) (QUOTE (MEMQ (CAR XXXX) VARLIST))
* (QUOTE ((TRACE) (?=)(UNTRACE))))
(SETQ ARGMENTS?)*(XXXX)
SETQ will be traced whenever CAR of its argument (SETQ is
an FSUBR) is a member of VARLIST.
1 . 23
ERROR PACKAGE
Introduction
When an error occurs during the evaluation of a LISP
expression, control is turned over to the Error Package.
The I/O is forced to the TTY (channel NIL) but will be
restored to its previous channels if the user continues the
evaluation. The idea behind the error package is that it
may be possible to 'patch up' the form in which the error
occurred and continue. Or, at least, that you can find the
cause of the error more easily if you can examine the state
of the world at the time of the error. Basically, what the
Error Package does is call BREAK1 with BRKEXP set to the
form in which the error occurred. This puts the user 'in a
break' around the form in which the error occurred. BREAK1
acts just like the top level of the interpreter with some
added commands (see section on BREAK1). The main
difference when you are in the Error Package is that the
variable bindings that were in effect when the error
occurred are still in effect. Furthermore, the expressions
that were in the process of evaluation are still pending.
While in the Error Package, variables may be examined or
changed, and functions may be defined or edited just as if
you were at the top level. In addition, there are several
ways in which you can abort or continue from the point of
error. In particular, if you can patch up the error, you
can continue by typing OK. If you can't patch the error, ^
will get you out of the break. When you are in the error
package, the prompt character is ':' and is preceded by a
level number. Note: if you don't want the error package
invoked for some reason, it can be turned off by evaluating
(*RSET NIL). Similarly, (*RSET T) will turn the error
package back on.
Commands
There are several atoms which will cause special
actions when typed into BREAK1 (the error package). These
actions are useful for examining the push down stack (e.g.
backtraces), changing forms and exiting from the break in
various ways. Table I (on the next page) gives a summary
of the actions. For a complete description, see the
section on 'What You Can Do In A Break'.
1 . 24
Table I
Break Package Command Summary
(for complete description see pp. 1.8-1.16)
Command Action
GO Evaluates BRKEXP, prints its value,
and continues with this value
OK Same as GO but no print of value
EVAL Reevaluate BRKEXP and print its value.
Its value is bound to !VALUE
RETURN xx Evaluate xx and continue with its value
^ Escape one level of BREAK1
^^ Escape to the top level
> [->] expr After an error, use expr for the erring atom
FROM?= form Continues by re-evaluating form at LASTPOS
EX Same as FROM?= NIL
USE x FOR y Substitutes x for y in form at LASTPOS
(destructively)
F [&] a1..aN Resets LASTPOS (stack context)
EDIT A1..An Resets LASTPOS and gives the form at LASTPOS
to the LISP Editor
?= f1 ... fN Evaluates forms fI as of LASTPOS
ARGS Prints arguments of the broken function
BKF Backtrace Function Names
BKE Backtrace Function Calls
BK Backtrace Expressions
Note: All of the backtrace commands can be combined with a
'V' or followed by an integer. The 'V' will cause the
values of variables to be printed. The integer will limit
1 . 25
the trace to that number of blocks. For example, BK 3,
BKEV, BKFV 5 and BKEV are all legitimate commands.
1 . 26