
                         THE REDUCE DEBUGGING PACKAGE

                                 A. C. Norman
                                D. F. Morrison

                        Last updated 19 February 1981.

                                   ABSTRACT

A  library  of  routines  useful  for  program  development  and  debugging  in
Reduce/Rlisp is described.

                               Table of Contents
1. Introduction                                                               1
     1.1. Use                                                                 1
     1.2. Functions which depend on redefining user functions                 1
     1.3. Special considerations for compiled functions                       1
     1.4. A few known deficiencies                                            1
2. Tracing function execution                                                 1
     2.1. Saving trace output                                                 1
     2.2. Making tracing more selective                                       2
     2.3. Turning off tracing                                                 2
     2.4. Automatic tracing of newly defined functions                        2
3. A heavy handed backtrace facility                                          2
4. Embeded Functions                                                          2
5. Counting function invocations                                              2
6. Stubs                                                                      3
7. Functions for printing useful information                                  3
8. Printing circular and shared structures                                    3
9. Safe List Access Functions                                                 3
10. Library of Useful Functions                                               3
11. Internals and cusomization                                                3
     11.1. User Hooks                                                         3
     11.2. Functions used for printing/reading                                3
     11.3. Flags                                                              3
APPENDIX A:  Example                                                          4

1. Introduction
     The REDUCE debugging package contains a selection of functions that can be
used  to  aid  program  development  and  to  investigate  faulty programs.  It
contains the following facilities.

   - A trace package.  This allows the user to see the arguments passed to
     and the values returned by selected functions.  It is  also  possible
     to  have  traced interpreted functions print all the assignments they
     make with SETQ (see section 2).

   - A backtrace facility.  This allows one to  see  which  of  a  set  of
     selected functions were active when an error occurred (section 3).

   - Embedded  functions  make it possible to do everything that the trace
     package can do, and much more besides (section 4).

   - Some primitive statistics gathering (section 5).

   - Generation of simple stubs.   When  invoked,  procedures  defined  as
     stubs simply print their argument and read a value to return (section
     6).

   - Some  functions  for  printing  useful  information, such as property
     lists, in an intelligible format (section 7).

   - PRINTX is a function that can print circular  and  re-entrant  lists,
     and  so  can sometimes allow debugging to proceed even in the face of
     severe damage caused by the wild use of RPLACA  and  RPLACD  (section
     8).

   - A   set  of  functions  !:CAR,...,!:CDDDDR,  !:RPLACA,  !:RPLACD  and
     !:RPLACW that behave exactly as the corresponding functions with  the
     !:  removed, except that they explicitly check that they are not used
     improperly on atomic arguments (section 9).

   - A collection of utility  functions,  not  specifically  intended  for
     examining or debugging code, but often useful (section 10).



1.1. Use
     To use load <REDUCE.UTAH>DEBUG.FAP 

    FLOAD <REDUCE.UTAH>DEBUG.FAP;



1.2. Functions which depend on redefining user functions
     A  number  of  facilities in Debug depend on redefining user functions, so
that they may log or print behavior when called.  The Debug  package  tries  to
redefine  user  functions  once and for all, and then keep specific information
about what is required at run time  in  a  table.    This  allows  considerable
flexibility,   and  is  used  for  a  number  different  facilities,  including
trace/traceset (section 2), a backtrace facility (section 3),  some  statistics
gathering (section 5)and EMB functions (section 4).

     Some,  like trace and EMB, only take effect if further action is requested
on specific user functions.  Others, like backtrace and  statistics  are  of  a
more  global nature.  Once one of these global facilities is enabled it applies
to all functions which have been made "known" to Debug.    To  undo  this,  use
RESTR (section 2.3).



1.3. Special considerations for compiled functions
     All functions in Debug which depend on redefining user functions must make
some  assumptions  about the number of arguments.  The Debug package is able to
find the correct names for the arguments of interpreted functions, and also for
functions loaded from FAP files and generated with an argument  naming  option.
This option is enabled by setting the switch 

    ON ARGNAMES; % for full names of all arguments

or 

    ON ARGCOUNT; % args will be printed with names A1,A2,...

before  compiling the relevant functions.  If Debug can not find out for itself
how many arguments a function has, it will interactively  ask  for  assistance.
In reply to the question 

    HOW MANY ARGUMENTS DOES xxxx HAVE?

it is possible to reply one of:

?               ask for assistance

UNKNOWN         give up

<number>        specify the number of arguments

(name ...)      give the names of arguments.

     If  you give an incorrect answer to the question, the system may misbehave
in an arbitrary manner. There can be problems if the answer  UNKNOWN  is  given
and  subsequently  functions  get  redefined or recompiled - if at all possible
find out how many arguments are taken by the function that you wish to trace.

     It is possible to suppress the argument number query with 

    ON TRUNKNOWN

This is equivalent to always answering "UNKNOWN".



1.4. A few known deficiencies

   - An attempt to trace certain system functions  (e.g.CONS)  will  cause
     the  trace package to overwrite itself.  Given the names of functions
     that cause this sort of trouble it is fairly easy to change the trace
     package to deal gracefully with them - so report trouble to a  system
     expert.

   - Once  fast  links are established trace can not work.  Fast links are
     turned off when Debug is loaded, and even if they are  restored  they
     are  turned  off  each  time  TR or a related function is called.  In
     Standard Lisp 1.6 on the PDP10/20 the statement 

         ON NOUUO;

     will also suppress fast links.  Thus either load Debug or do ON NOUUO
     prior to any attempt to execute code that will need to be traced.

   - The portable Lisp compiler uses  information  about  which  registers
     certain  system  functions destroy.  Tracing these functions may make
     the optimizations based thereon invalid.  The correct way of handling
     this problem is currently under consideration.  In the mean time  you
     should  avoid  tracing any functions with the ONEREG or TWOREG flags.
     On the PDP10/20 these currently include
      UPBV        FLOATP      FLOAT       NUMVAL      LPOSN       NCONS
      POSN        FIXP        GET         EXAMINE     SCANSET     SETPCHAR
      EJECT       TYO         BINI        BIGP        PRINC       ABS
      CODEP       LINELENGTH  STRINGP     MINUS       PAIRP       RECLAIM
      TERPRI      XCONS       UNTYI       *BOX        CONS        MKVECT
      GETD        ATSOC       CLOSE       GCTIME      MKCODE      REVERSE
      ASCII       BINO        LENGTH      FILEP       PUTV        SPEAK
      DELIMITER   PAGELENGTH  RDSLSH      TIME        REMD        FIX
      CONSTANTP   INUMP       ATOM        VECTORP     GETV        IDP
      REMPROP     EXCISE      NUMBERP     PUT         LETTER

   - The current implementation does not handle MACROs correctly.   It  is
     not  possible  to  expand  a  MACRO  and  not  evaluate the resulting
     expansion.  This deficiency will be remedied shortly.   In  the  mean
     time do not use any traced MACROs under the influence of ON DEFN.

2. Tracing function execution
     To  see  when  a function gets called, what arguments it is given and what
value it returns, do 

    TR functionname;

or if several functions are of interest, 

    TR name1,name2,...;

If the specified functions are defined (as EXPR,  FEXPR  or  MACRO),  and  fast
links  to  them  have  not  yet  been  established  (section  1.4), this REDUCE
statement modifies the function definition to  include  print  statements.  The
following example shows the style of output produced by this sort of tracing:

     The input...

    SYMBOLIC PROCEDURE XCDR A;
      CDR A; % A very simple function;
    TR XCDR;
    XCDR '(P Q R);

gives output...

    XCDR entered
       A: (P Q R)
    XCDR = (Q R)

Interpreted functions can also be traced at a deeper level.  

    TRST name1,name2...;

causes  the  body  of  an  interpreted  function  to  be  redefined so that all
assignments (made with SETQ) in its body  are  printed.    Calling  TRST  on  a
function  automatically  has the effect of doing a TR on it too, and the use of
UNTR automatically does an UNTRST if necessary (section 2.3), so that it is not
possible to have a function subject to TRST but not TR.

     Trace output will often appear mixed up with output from the program being
studied, and to avoid too much confusion TR arranges to preserve the column  in
which  printing  was taking place across any output that it generates. If trace
output is produced when part of a line has been printed, the trace data will be
enclosed in markers '<' and '>', and these symbols will be placed on  the  line
so  as  to  mark  out the amount of printing that had occurred before trace was
entered.



2.1. Saving trace output
     The trace facility makes it possible to discover  in  some  detail  how  a
function  is  used,  but  in  certain  cases  its direct use will result in the
generation of vast amounts of (mostly useless) print-out.   There  are  several
options.    One  is  to  make tracing more selective (section 2.2).  The other,
discussed here, is to either print only the most recent information, or dump it
all to a file to be perused at leisure.

     Debug  has  a  ring  buffer in which it saves information to reproduce the
most recent information printed by the trace facility (both TR and TRST).    To
see the contents of this buffer use TR without any arguments 

    TR;

To set the number of entries retained to n use 

    NEWTRBUFF(n);

It is initially set to 5.

     Turning off the TRACE flag 

    OFF TRACE;

will  suppress the printing of any trace information at run time; it will still
be saved in the ring buffer.    Thus  a  useful  technique  for  isolating  the
function  in  which  an  error  occurs  is to trace a large number of candidate
functions, do OFF TRACE  and  after  the  failure  look  at  the  latest  trace
information by calling TR with no arguments.

     Normally trace information is directed to the standard output, rather than
the currently selected output.  To send it elsewhere use the statement 

    TROUT filename;

The statement 

    STDTRACE;

Will  close  that file and cause future trace output to be sent to the standard
output.  Note that output saved in the ring buffer is  sent  to  the  currently
selected output, not that selected by TROUT.



2.2. Making tracing more selective
     The function TRACECOUNT(n) can be used to switch off trace output. If n is
a  positive  number,  after  a  call to TRACECOUNT(n) the next n items of trace
output that are generated will not be printed.  TRACECOUNT(n) with  n  negative
or zero switches all trace output back on. TRACECOUNT(NIL) returns the residual
count, i.e. the number of additional trace entries that will be suppressed.

     Thus  to  get detailed tracing in the stages of a calculation that lead up
to an error, try

    TRACECOUNT 1000000; % or some other suitable large number
    TR ....; % as required
    % run the failing problem
    TRACECOUNT NIL;

It is now possible to calculate how many  trace  entries  occurred  before  the
error,  and so the problem can now be re-run with TRACECOUNT set to some number
slightly less than that.

     An alternative to the direct of TRACECOUNT is TRIN. To use TRIN, establish
tracing for a collection of functions, using TR in the normal way. Then do TRIN
on some small collection of other functions. The effect  is  just  as  for  TR,
except  that  trace output will be inhibited except when control is dynamically
within the TRIN functions. This makes it possible to use  TR  on  a  number  of
heavily  used  general  purpose  functions, and then only see the calls to them
that occur within some specific sub-part of your entire program.   UNTR  undoes
the effect of TRIN (section 2.3).

     The  global variables TRACEMINLEVEL!* and TRACEMAXLEVEL!* (which should be
non-negative integers) are the minimum and maximum depths of recursion at which
to print trace information.  Thus if you only want to see top level calls of  a
highly recursive function (like a simple-minded version of LENGTH) simply do 

    TRACEMAXLEVEL!* := 1;



2.3. Turning off tracing
     When a particular function no longer needs tracing, do 

    UNTR functionname;

or 

    UNTR name1,name2...;

This  merely suppresses generation of trace output.  Other information, such as
invocation counts,  bactrace  information,  and  the  number  of  arguments  is
retained.    Thus  UNTR followed later by TR will not have to enquire about the
number of arguments.

     To completely destroy information about a function use 

    RESTR name1,name2...;

This returns the function to it's original state.

     To suppress traceset output without suppressing normal trace output use 

    UNTRST name1,name2...;

UNTRing a TRSTed function also UNTRST's it.

     TRIN (section 2.2) is undone by UNTR (but not by UNTRST).



2.4. Automatic tracing of newly defined functions
     Under the influence of 

    ON TRACEALL;

any functions successfully defined by PUTD will be traced.  Note that  if  PUTD
fails (as might happen under the influence of the LOSE flag) no attempt will be
made to trace the function.

     To  enable  those facilities (such as BTR (section 3) and TRCOUNT (section
5)) which require redefinition, but without tracing, use 

    ON INSTALL;

     Thus, a common scenario might look like

    ON INSTALL;
    IN MYFNS.RED$
    OFF INSTALL;

which would enable the backtrace and statistics routines to work with  all  the
functions defined in MYFNS.RED.

     Warning:  if  you  intend to use ON TRACEALL or ON INSTALL, make sure that
fast links are suppressed before you define ANY functions, even those you  will
never trace (section 1.4).

3. A heavy handed backtrace facility

    BTR f1,f2,...;

     arranges  that  a  stack  of functions entered but not left is kept - this
stack records the names of functions and the arguments that  they  were  called
with.  If  a  function  returns  normally  the stack is unwound. If however the
function fails, the stack is left alone  by  the  normal  LISP  error  recovery
processes.

     To print this information call BTR without any arguments 

    BTR;

Calling  BTR  on  new  functions  resets  the  stack.  This may also be done by
explicitly calling RESBTR 

    RESBTR;

     The disposition of information about  functions  which  failed  within  an
ERRORSET  is  controlled by the BTRSAVE.  ON BTRSAVE will cause them to be save
separately, and printed when the stack is printed; OFF BTRSAVE will cause  them
to be thrown away.

     OFF BTR will suppress saving of any BTR information.  Note that any traced
function will have its invocations pushed and popped by the BTR maechanism.

4. Embeded Functions
     EMBEDDING  means  redefining  a  function  in terms of its old definition,
usually with the intent that the new version will do some  tests  or  printing,
use the old one, do some more printing and then return.  If ff is a function of
two arguments, it can be embedded using a statement of the form:

    SYMBOLIC EMB PROCEDURE ff(A1,A2);
      << PRINT A1;
         PRINT A2;
         PRINT ff(A1,A2) >>;

The  effect of this particular use of embed is broadly similar to a call TR ff,
and arranges that whenever ff is called it prints both its  arguments  and  its
result.    After a function has been embedded, the embedding can be temporarily
removed by the use of 

    UNEMBED ff;

and it can be reinstated by 

    EMBED ff;

5. Counting function invocations
     Whenever the flag TRCOUNT is ON the number of times user  functions  known
to Debug are entered is counted.  The statement 

    ON TRCOUNT;

also resets that count to zero.  The statement 

    OFF TRCOUNT;

causes a simple histogram of function invocations to be printed.  To make Debug
aware of a function use 

    TRCNT name1,name2,...;

See also section 2.4.

6. Stubs
     The statement 

    STUB FOO(U,V);

defines  an  EXPR, FOO, of two arguments.  When executed such a stub will print
its arguments and read a value to return.  FSTUB is  used  to  define  FEXPR's.
This is often useful when developing programs in a top down fashion.

     At  present  the currently (i.e. when the stub is executed) selected input
and output are used.  This may  be  changed  in  the  future.    Algebraic  and
possibly MACRO stubs may be implemented in the future.

7. Functions for printing useful information

    PLIST id1,id2,...;

     prints the property lists of the specified id's.  

    PPF fn1,fn2,...;

prints  the  definitions  and  other  useful  information  about  the specified
functions.

8. Printing circular and shared structures
     Some LISP programs rely on parts of their datastructures being shared,  so
that  an  EQ  test  can be used rather than the more expensive EQUAL one. Other
programs (either deliberately or by accident) construct circular lists  through
the use of RPLACA or RPLACD. Such lists can be displayed by use of the function
PRINTX.  If  given  a  normal list the behaviour of this function is similar to
that of PRINT - if it is given a looped or re-entrant datastructure  it  prints
it  in  a  special  format.    The representation used by PRINTX for re-entrant
structures is based on the idea of labels for those nodes in the structure that
are referenced more than once. Consider the list created by the operations:

    A:=NIL . NIL; % make a node
    RPLACA(A,A); RPLACD(A,A); % point it at itself

If PRINTX is called on the list A it will discover that the node is  referenced
repeatedly,  and  will invent the label %L1 for it.  The structure will then be
printed as 

    %L1: (%L1 . %L1)

where %L1: sets the label, and the other instances of %L1  refer  back  to  it.
Labelled  sublists can appear anywhere within the list being printed.  Thus the
list B := 'X . A; could be printed as 

    (X . %L1: (%L1 . %L1))

This use of dotted  pair  representation  is  often  clumsy,  and  so  it  gets
contracted to 

    (X %L1, %L1 . %L1)

where  a  label set with a comma (rather than a colon) is a label for part of a
list, not for the sublist.

9. Safe List Access Functions
     The functions !:CAR, ... !:CDDDDR, !:RPLACA,  !:RPLACD  and  !:RPLACW  all
contain  explicit  checks to ensure that they are not used improperly on atomic
arguments.

     The user can either edit source files  systematically  changing  CAR  into
!:CAR  etc  and  recompile  everything  to  use  these, or use !:REDEFINE.  The
function !:REDEFINE (of no arguments) redefines CAR, CDR,  etc.  to  be  !:CAR,
etc.    It  leaves  the  original, "dangerous" definitions under !%CAR, etc.  A
second call on !:REDEFINE undoes the process.  Warning:  the  second  technique
will  not  normally  work  with  compiled functions, as CAR, CDR, etc are often
compiled inline.

10. Library of Useful Functions
     Debug contains a library of utility functions which may be useful to those
debugging code.  The collection is as yet very small.  Suggestions for  further
functions to be in corporated are definitely solicited.

     Those currently available:

REDEFINE(nam,old,new)
                redefines the function named <nam> to be the same as that named
                <new>.    If  <old> is non-nil, the former definition is stored
                under the name <old>.  For example, 

                    REDEFINE('EVAL,'!%EVAL,'MYEVAL)

                saves the definition of EVAL as %EVAL, and redfines  it  to  be
                MYEVAL.

COPY U          returns  a  freshly cons'd together copy of U, often usefull in
                debugging functions which use RPLACA/RPLACD.

VCOPY U         Like COPY, but copies vectors, non-unique numbers, and strings,
                too.

11. Internals and cusomization
     This section describes some internal details of the  Debug  package  which
may be useful in customizing it for specific applications.

     The reader is urged to consult the source (section <REDUCE.UTAH>DEBUG.RED)
for further details.



11.1. User Hooks
     These  are  all  global variables whose value is normally NIL.  If non-nil
they should be exprs taking the number of  variables  specified,  and  will  be
called as specified.

PUTDHOOK!*      takes  one argument, the function name.  It is called after the
                function has been defined, and any tracing under the  influence
                of  TRACEALL  or  INSTALL has taken place.  It is not called if
                the function cannot be  defined  (as  might  happen  under  the
                influence of the LOSE flag).

TRACENTRYHOOK!* takes two arguments, the function name and a list of the actual
                arguments.  It is called by the trace package whenever a traced
                function  is entered, but before it is executed.  The execution
                of a surrounding EMB function takes place after TRACENTRYHOOK!*
                is called.  This is useful when you need to call special  user-
                provided print routines to display critical data structures, as
                are TRACEXITHOOK!* and TRACEXPANDHOOK!*.

TRACEXITHOOK!*  takes  two  arguments,  the function name and the value.  It is
                called after the function has been evaluated.

TRACEXPANDHOOK!*
                takes two arguments, the function name and the macro expansion.
                It is only called for macros, and is called after the macro  is
                expanded, but before the expansion has been evaluated.

TRINSTALLHOOK!* takes  one  argument, a function name.  It is called whenever a
                function is redefined by the Debug package, as for example when
                it is first traced.  It is called before the redefinition takes
                place.



11.2. Functions used for printing/reading
     These should all contain EXPRS taking the specified number  of  arguments.
The initial values are given in square brackets.

PPFPRINTER!* [RPRINT]
                takes  one argument.  It is used by PPF to print the body of an
                interpreted function.

PROPERTYPRINTER!* [PRETTYPRINT]
                takes one argument.  It is used by PLIST to print the values of
                properties.

STUBPRINTER!* [PRINTX]
                takes one argument.  Stubs defined with STUB/FSTUB  use  it  to
                print their arguments.

STUBREADER!* [XREAD(NIL)]
                takes  no  arguments.   Stubs defined with STUB/FSTUB use it to
                read their return value.

TREXPRINTER!* [RPRINT]
                takes one argument.  It is used  to  print  the  expansions  of
                traced macros.

TRPRINTER!* [PRINTX]
                takes  one  argument.    It  is used to print the arguments and
                values of traced functions.



11.3. Flags
     These are all  flags  which  can  be  set  with  the  Reduce/Rlisp  ON/OFF
statements.  Their initial setting is given in square brackets.  Many have been
described above, but are collected here for reference.

BTR [on]        enables  backtracing  of  functions which the Debug package has
                been told about.

BTRSAVE [on]    causes backtrace information leading up to an error  within  an
                errorset to be saved.

INSTALL [off]   causes all Debug to know about all functions defined with PUTD.

SAVENAMES [off] causes names assigned to substructures by PRINTX to be retained
                from  one  use  to  the  next.    Thus  substurctures common to
                different items will be show as the same.

TRACE [on]      enables runtime printing of  trace  information  for  functions
                which have been traced.

TRACEALL [off]  causes all functions defined with PUTD to be traced.

TRUNKNOWN [off] instead  of  querying the user for the number of arguments to a
                compiled EXPR, just assumes the user will say "UNKNOWN".

TRCOUNT [on]    enables counting invocations of functions known to Debug.  Note
                that ON TRCOUNT resets the count,  and  OFF  TRCOUNT  prints  a
                simple histogram of the available counts.

APPENDIX A:  Example
     This contrived example demonstrates many of the available features.  It is
a transcript of an actual Reduce session.
REDUCE 2 (Dec-1-80) ...
FOR HELP, TYPE HELP<ESCAPE>

1: CORE 80;

2: FLOAD <MORRISON>NUDBUG.FAP;

3: SYMBOLIC PROCEDURE FOO N;
3: BEGIN SCALAR A;
3:   IF REMAINDER(N,2) NEQ 0 AND N < 0 THEN
3:     A := !:CAR N; % Should err out if N is a number
3:   IF N = 0 THEN
3:     RETURN 'BOTTOM;
3:   N := N-2;
3:   A := BAR N;
3:   N := N-2;
3:   RETURN LIST(A,BAR N,A)
3: END FOO;

FOO

4: SYMBOLIC PROCEDURE FOOBAR N;
4: << FOO N; NIL>>;

FOOBAR

5: SYMBOLIC OPERATOR FOOBAR;

NIL

6: TR FOO,FOOBAR;

(FOO FOOBAR)

7: PPF FOOBAR,FOO;


EXPR procedure FOOBAR(N) [Traced;Invoked 0 times;Flagged: OPFN]:
<<FOO N; NIL>>;

EXPR procedure FOO(N) [Traced;Invoked 0 times]:
BEGIN SCALAR A;
   IF NOT REMAINDER(N,2)=0 AND N<0 THEN A := !:CAR N;
   IF N=0 THEN RETURN 'BOTTOM;
   N := N - 2;
   A := BAR N;
   N := N - 2;
   RETURN LIST(A,BAR N,A)
 END;

FOOBAR(FOO)

8: ON COMP;

9: SYMBOLIC PROCEDURE BAR N;
9: IF REMAINDER(N,2)=0 THEN FOO(2*(N/4)) ELSE FOO(2*(N/4)-1);

*** BAR 164896 BASE 20 WORDS 63946 LEFT

BAR

10: OFF COMP;

11: FOOBAR 8;
FOOBAR being entered
   N:   8
  FOO being entered
     N: 8
    FOO (level 2) being entered
       N:       2
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
    FOO (level 2) being entered
       N:       2
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
  FOO = (%L1: (BOTTOM BOTTOM BOTTOM) (BOTTOM BOTTOM BOTTOM)
%L1)
FOOBAR = NIL

0

12: % Notice how in the above PRINTX printed the return values
12: % to show shared structure
12: TRST FOO;

(FOO)

13: FOOBAR 8;
FOOBAR being entered
   N:   8
  FOO being entered
     N: 8
  N := 6
    FOO (level 2) being entered
       N:       2
    N := 0
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    A := BOTTOM
    N := -2
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
  A := (BOTTOM BOTTOM BOTTOM)
  N := 4
    FOO (level 2) being entered
       N:       2
    N := 0
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    A := BOTTOM
    N := -2
      FOO (level 3) being entered
         N:     0
      FOO (level 3) = BOTTOM
    FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
  FOO = (%L1: (BOTTOM BOTTOM BOTTOM) (BOTTOM BOTTOM BOTTOM)
%L1)
FOOBAR = NIL

0

14: TR BAR;

*** How many arguments does BAR take ?  1

(BAR)

15: FOOBAR 8;
FOOBAR being entered
   N:   8
  FOO being entered
     N: 8
  N := 6
    BAR being entered
       A1:      6
      FOO (level 2) being entered
         N:     2
      N := 0
        BAR (level 2) being entered
           A1:  0
          FOO (level 3) being entered
             N: 0
          FOO (level 3) = BOTTOM
        BAR (level 2) = BOTTOM
      A := BOTTOM
      N := -2
        BAR (level 2) being entered
           A1:  -2
          FOO (level 3) being entered
             N: 0
          FOO (level 3) = BOTTOM
        BAR (level 2) = BOTTOM
      FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
    BAR = (BOTTOM BOTTOM BOTTOM)
  A := (BOTTOM BOTTOM BOTTOM)
  N := 4
    BAR being entered
       A1:      4
      FOO (level 2) being entered
         N:     2
      N := 0
        BAR (level 2) being entered
           A1:  0
          FOO (level 3) being entered
             N: 0
          FOO (level 3) = BOTTOM
        BAR (level 2) = BOTTOM
      A := BOTTOM
      N := -2
        BAR (level 2) being entered
           A1:  -2
          FOO (level 3) being entered
             N: 0
          FOO (level 3) = BOTTOM
        BAR (level 2) = BOTTOM
      FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
    BAR = (BOTTOM BOTTOM BOTTOM)
  FOO = (%L1: (BOTTOM BOTTOM BOTTOM) (BOTTOM BOTTOM BOTTOM)
%L1)
FOOBAR = NIL
0

16: OFF TRACE;

17: FOOBAR 8;

0

18: TR;
*** Start of saved trace information ***
        BAR (level 2) = BOTTOM
      FOO (level 2) = (BOTTOM BOTTOM BOTTOM)
    BAR = (BOTTOM BOTTOM BOTTOM)
  FOO = (%L1: (BOTTOM BOTTOM BOTTOM) (BOTTOM BOTTOM BOTTOM)
%L1)
FOOBAR = NIL
*** End of saved trace information ***

19: FOOBAR 13;

***** -1 illegal CAR

20: TR;
*** Start of saved trace information ***
    BAR being entered
       A1:      11
      FOO (level 2) being entered
         N:     3
      N := 1
        BAR (level 2) being entered
           A1:  1
          FOO (level 3) being entered
             N: -1
*** End of saved trace information ***

21: BTR;
*** Backtrace: ***
These functions were left abnormally:
  FOO
     N: -1
  BAR
     A1:        1
  FOO
     N: 3
  BAR
     A1:        11
  FOO
     N: 13
  FOOBAR
     N: 13
*** End of backtrace ***

22: SYMBOLIC EMB PROCEDURE FOO N;
22: IF N < 0 THEN <<
22:   LPRIM "FOO would have failed";
22:   NIL >>
22: ELSE
22:   FOO N;

FOO

23: RESBTR;

24: FOOBAR 13;

*** FOO WOULD HAVE FAILED

*** FOO WOULD HAVE FAILED

*** FOO WOULD HAVE FAILED

*** FOO WOULD HAVE FAILED

0

25: TR;
*** Start of saved trace information ***
        BAR (level 2) = NIL
      FOO (level 2) = (NIL NIL NIL)
    BAR = (NIL NIL NIL)
  FOO = (%L1: (NIL NIL NIL) (NIL NIL NIL) %L1)
FOOBAR = NIL
*** End of saved trace information ***

26: BTR;
*** No traced functions were left abnormally ***

27: UNEMBED FOO;

(FOO)

28: FOOBAR 13;

***** -1 illegal CAR

29: STUB FOO N;

*** FOO REDEFINED

30: FOOBAR 13;
 Stub FOO called

N: 13
Return? :
30: BAR(N-2);
 Stub FOO called

N: 3
Return? :
30: BAR(N-2);
 Stub FOO called

N: -1
Return? :
30: 'ERROR;

0

31: TR;
*** Start of saved trace information ***
  BAR being entered
     A1:        11
    BAR (level 2) being entered
       A1:      1
    BAR (level 2) = ERROR
  BAR = ERROR
FOOBAR = NIL
*** End of saved trace information ***

32: OFF TRCOUNT;


FOOBAR(8)           ****************
BAR(24)             ************************************************



33: QUIT;
