Tracein is a facility for very low level tracing/stepping, i.e. to show you everything that takes place within a particular piece of code. ***IMPORTANT*** You should load the compiled version of tracein, not the interpreted version. The interpreted version does not do the right thing for RETURNs. The problem is that the lisp interpreter does not care which return goes with which prog. The stepper contains a PROG in which your code is EVAL'd. If that code is a RETURN, it returns to the prog in the stepper. The compiled version does not have this problem. (TRACEIN fn {T} loc1 loc2 ...) [NLAMBDA NOSPREAD] TRACEIN is like BREAKIN except that it arranges to single step the code rather than just breaking when it gets there. The optional T causes an automatic trace, otherwise a prompting trace is started. The first argument is the name of the function in which the code is to be found. It can also be a list of the form (fn test) where test will be evaluated to decide whether to actually trace. The default condition is T. (Note: the condition NIL is also treated as T - if you want never to break try (NOT T). This is a feature inherited from BREAKIN.) The other arguments are editor location specifications. (TRACEIN FN) is like (BREAKIN FN) - it allows you to find the desired location in the editor. When you type OK the current expression is the answer. (TRACEIN fn loc1) does a (BREAKIN fn (AROUND loc1) ). Since TRACEIN works by calling BREAKIN, it can be undone by calling UNBREAK. (WATCH form stepaction nohook) [LAMBDA SPREAD] Watch traces the evaluation of form. If STEPACTION is T an automatic trace is done, if NIL a prompting trace. If nohook is T, the WATCH mode will always be used (rather than EVALHOOK mode as described below). BREAKMACROS: These break macros will work at entry breakpoints for interpreted functions, and also at BREAKIN breakpoints where AROUND was the break type. TRACEALL starts an automatic trace of the current form, so you can use BREAKIN and decide to trace it from the break. STEP starts a prompting trace. General Description TRACEIN uses BREAKIN to set up a breakpoint, and works just like BREAKIN except that BREAK1 is called with a command to start tracing the form. If there is no second arg of T, then you are prompted at each trace step, and given several continuation options. If the second arg is T, then the tracing proceeds automatically, showing the full trace of what goes on within the form being traced. Note that it only makes sense to tracein expressions that will be evaluated. You will get into trouble if you attempt to trace a clause of a COND, e.g. ((ATOM X) NIL), because it will be interpreted as call to the function (ATOM X). TRACEIN prompts with "->" if the expression has yet to be evaluated and "<-" if it has already been evaluated. The prompting is done by ASKUSER, so ? will print the full list of options to respond to the prompt. The options are: S continue stepping (into the form from ->, on to the next one from <-) E evaluate (or re-evaluate) the form silently, prompt again with the result F finish the evaluation without further printing T trace the form, showing every evaluation without prompting P prettyprint the form V prettyprint the value (if the prompt is -> this is NOBIND) R retry - go back to -> mode X set the result to be returned on exit to a value you supply B Break - type RETURN to get back to the stepper escape to eval one expression There are two slightly different implementations of TRACEIN, the "watch" implementation and the "evalhook" implementation. Currently, the "evalhook" is only available on Interlisp-VAX. The "watch" implementation works in any Interlisp. The "evalhook" mode is standard for the vax. WATCH mode. This is in use when the variable WATCH-EVALHOOK is NIL. In this mode, the tracing is done by laboriously generating a translation of the traced expression. There are several constraints on this process; first, the tracer has to guess correctly which forms will actually be evaluated. There is a mechanism (detailed later) to advise the translation process about any nonstandard special forms you are tracing. Also, it is the DWIMIFIED version of your code that is traced, so you never see iteration forms, macros, or changetran expressions. For example, you might see a PROG or a MAPCAR in place of a FOR. You might prefer this mode to EVALHOOK mode in some circumstances, since only code lexically scoped by the initial BREAKIN is traced: other EXPRS encountered in the process of evaluating the traced form are not automatically traced. Sometimes, this is just what you want. The WATCHified form (the translation of the form to be traced is the result of embedding each subexpression to be evaluated in a call to WATCH-EVAL) is computed the first time it is needed, and is stored in CLISPARRAY. Thus the WATCHified version will automatically go away (and have to be recomputed) if you ever change the expression. (Detail: before WATCHification, the expression is DWIMified. WATCHification of an expression which already has a CLISP translation stores the WATCHified version of the CLISP translation as the CLISP translation of the CLISP translation of the original form. Thus, when the break condition is false, the original CLISP translation is used. When the expression is changed, the CLISP translation goes away, and so the WATCHification (as well as the DWIMification) is redone.) EVALHOOK mode This is modeled after the EVALHOOK feature of the MACLSP family. Basically, there is a hook that allows any call to EVAL to be interrupted and some other function called instead. In EVALHOOK mode this feature is used to trap to the stepper. The main difference between this and WATCH mode is that ANY form that is EVAL'd is trapped, not just the lexical code you use BREAKIN on, once the stepper is invoked. This means that you trace all levels of interpreted code below a TRACEIN. Also, because your program does not have to be analyzed in advance, you see the original Iteration, changetran, macros and so on, as well as their translations. Unfortunately, you also occasionslly see a random EVAL done in the process of generating the translation. You can disable EVALHOOK mode by setting WATCH-EVALHOOK to NIL. Advising WATCH mode about nonstandard special forms. Most users can ignore this. If you are tracing NLAMBDAs of your own construction, this may be important. TRACEIN traces every subexpression that it EXPECTS to eventually be evaluated. It doesn't expect the arguments of NLAMBDAs to be evaluated, although sometimes they are. Users can tell TRACEIN what will be evaluated by adding an EVL-FIX property to functions that TRACEIN does not understand. (Maybe some masterscope expert can figure out how to use the masterscope templates to get most of the effect of EVL-FIX properties described below.) The system functions are supposed to be already understood, but if that understanding is wrong (or the user wants to change it for any other reason) the same tactic will work. The EVL-FIX property is a pattern to be applied to the tail of a function call. A pattern element of T means that the corresponding argument will be evaluated (and thus ought to be traced). NIL means that it will not. Thus the pattern for SETQ is (NIL T). If a pattern element is itself a list, then that argument is not to be traced, but it is expected to be a list which will in turn be matched with the pattern element to determine whether its elements will be traced. In addition, the special pattern element TAIL allows the pattern element after it to be applied to as many arguments as necessary to exhaust the list. For example, the pattern for COND is (TAIL (TAIL T)). The pattern for SELECTQ is (T TAIL (NIL TAIL T) T). If the CAR of a pattern is TEST, the next element is an expression which is evaluated. If the result is not NIL it will be traced. Also, as a special case, if a pattern element is an atom other than T or NIL, it is expected to be a function which will be applied to the corresponding argument, and if the result is not NIL then the argument will be traced. If the CAR of a pattern is EVAL, the next element is evaluated, and what it returns is used as the pattern. In such code, the free variable EXP is the function call that the resulting pattern is supposed to describe. In addition, the variable NOEMBED may be set to T to indicate that the function call itself is not to be traced. This feature is not normally needed, but LAMBDA is an example where it is. Controlling the Printing and PRINTUPTO The printing is done by (APPLY* FORMPRINTER
) and (APPLY* VALUEPRINTER ) so you can control the printing. The initial values of these call PRINTUPTO to print as much as will fit on the line after the indentation, leaving space for the response to ASKUSER (but never cutting off the printing in less than 20 characters). If anyone can use such a function, it is (PRINTUPTO x limit UsePrin2 Ignore File), which prints up to limit characters of x. If UsePrin2 is not NIL then it prints with PRIN2 (otherwise PRIN1). Ignore is used to skip calls to WATCH-EVAL. (The WATCHified form is passed to the formprinter.) When the CAR of a list is in Ignore, only the CADR is printed. If you want to replace FORMPRINTER, the function UNWATCH will remove the WATCHes from the form. Similar applications and EVL-FIX The WATCHifier might be of use to people who are interested in similar applications. For example one could keep track of how many times each subexpression is executed by embedding each expression to be evaluated in (Count 0 ) where Count is a function that increments the counter and returns (EVAL ). (EVL-FIX form prefix) returns the result of appending prefix to the front every expression to be evaluated in form. (EVL-FIX '(ADD1 (COND (FOO BAR))) '(Count 0)) would be (Count 0 (ADD1 (Count 0 (COND ((Count 0 FOO)(Count 0 BAR)))))). DonC (Don Cohen) @ ISIF ddyer (Dave Dyer) @ ISIB