Trailing-Edge - PDP-10 Archives - decuslib10-04 - 43,50322/break.doc
There are no other files named break.doc in the archive.

                              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 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))))))

          *(TRACE FACTORIAL)

          *(FACTORIAL 4)
          !     N = 4
          !  ENTER FACTORIAL:
          !  !     N = 3
          !  !  ENTER FACTORIAL:
          !  !  !     N = 2
          !  !  !  ENTER FACTORIAL:
          !  !  !  !     N = 1
          !  !  !  !  ENTER FACTORIAL:
          !  !  !  !  !     N = 0

          (L BROKEN)


          1:> 1

          !  !  !  !  FACTORIAL = 1
          !  !  !  FACTORIAL = 1
          !  !  FACTORIAL = 2
          !  FACTORIAL = 6
          FACTORIAL = 30


          *(FACTORIAL 4)


                                      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))))))

          *(BREAK (ACK (EQ N M) (ARGS)))

          *(ACK 2 1)
             M = 1
             N = 1

          (ACK BROKEN)

              M = 1
              N = 1
              M = 2
              N = 0
              M = 2
              N = 1



             M = 1
             N = 1

          (ACK BROKEN)

          *(UNBREAK T)


                                      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

          5.   Control-Z:  This  returns the  user to the top-level of
          LISP,  (i.e.  either the READ-EVAL-PRINT loop or the current

          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


               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


          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

                                      1 . 7


          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

               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.

                       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

                       Same as GO except that the  value of  BRKEXP is
                  not printed.

                       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

          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.,

                       UNDEFINED FUNCTION
                       (FOO1 BROKEN)
                       1:> FOO

                  changes FOO1 to FOO and  continues  the computation.
                  Expr need not be atomic, e.g.,

                       UNBOUND ATOM
                       (FOO BROKEN)
                       1:> (QUOTE FOO)

                  For UNDEFINED FUNCTION  breaks, the user can specify
                  a function and its first argument, e.g.,

                       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

                                      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.

                       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:

                                     When used  as  the first argument
                                caused  LASTPOS  not  to  be  reset to
                                above  BREAK1 but  continues searching
                                from the previous position of LASTPOS.

                                     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


                   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)

                        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

                        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:

                             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

           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 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   gives  a  backtrace   of  the  names  of
                   functions that are still pending.

                        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 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.


                     BKFV      would  print  the  names  and  variable
                               bindings of the functions called before

                     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

                    (SETQ FIE /#/)

                                      1 . 16


                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:


           A command FP  which  finds a place  on  the  SPD  stack and
           prints the form there can be defined by:


                                      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


                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

                                      1 . 18

           (BREAK ((FOO4 IN FOO5) (MINUSP X)))

           will break all  calls to  FOO4  made  from FOO5  when  X is

                (BREAK FOO)
                (BREAK ((GET IN FOO) T (GO)))
                (BREAK (SETQ (EQ N 1) ((PRINT (QUOTE N=1)))(?= M)))


                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)))

                (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  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


           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.

                (BREAKIN FOO (AROUND TTY:) T (?= M N) ((*PLUS X Y)))
                (BREAKIN FOO2 (BEFORE SETQ) (EQ X Y))


                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

                (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  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

                (UNTRACE  T)  will  unbreak the function most recently

                (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


                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.


                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

           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