This file is the documentation for the Lisp Trace package. It is admittedly out of date, but nonetheless better than nothing. Improve it! 08/01/72 The Lisp Trace package consists of three main functions, Trace, untrace, and remtrace, all of which are fexprs. A call to Trace has the following form: (Trace ) a "Trace spec" in turn is either an atom (the name of the function to be Traced) or a list of this form: ( ) where the options are as follows: Break causes a break after printing the entry Trace (if any) but before applying the Traced function to its arguments, if and only if evaluates to non-nil. Cond causes Trace information to be printed for function entry and/or exit if and only if evaluates to non-nil. Entrycond causes Trace information to be printed for function entry if and only if evaluates to non-nil. If both cond and entrycond are used, the specified for cond is evaluated first; and both 's must evaluate to non-nil for entry Trace information to be printed. If the for cond evaluates to nil, then the for entrycond will not be evaluated. Exitcond causes Trace information to be printed for function exit if and only if evaluates to non-nil. If both cond and exitcond are used, both must evaluate to non-nil for exit Trace information to be printed. Note that the for cond is not re-evaluated on exit; rather, its value on entry is saved. If this value is nil, then the for exitcond will not be evaluated. Wherein causes the function to be Traced only when called from the specified function. One can give several Trace specs to Trace, all specifying the same function but with different wherein options, so that the function is Traced in different ways when called from different functions. Note that if the function specified by the wherein option is already being Traced itself, the wherein option probably will not work as desired, probably. (Then again, it might.) Note that must be an interpreted function, since the wherein Trace involves altering the calling function as well as the called. Argpdl specifies an atom whose value Trace initially sets to nil. When the function is Traced, a list of the current recursion level for the function, the function's name, and a list of the arguments is consed onto the when the function is entered, and cdr'ed back off when the function is exited. (Actually, this consing/cdring is done by means of lambda-binding, so that if an error or other interrupt occurs the will be reset by unbinding.) The can be inspected from a breakpoint, for example, and used to determine the very recent history of the function. This option can be used with or without printed Trace output. Each function can be given its own pdl, or one pdl may serve several functions. Entry specifies a list of arbitrary s-expressions whose values are to be printed along with the usual entry Trace. The list of resultant values, when printed, is preceded by a \\ to separate them from the other information. Exit similar to entry, but specifies expressions whose values are printed with the exit Trace. Again, the list of values printed is preceded by \\. Arg specify that the function's arguments, value resultant value, both, or neither are to both be Traced. If not specified, the default nil is both. Any "options" following one of these four are assumed to be arbitrary s-expressions whose values are to be printed on both entry and exit to the function. However, if arg is specified, the values are printed only on entry, and if value, only on exit. Note that since arg, value, both, nil, swallow all following expressions for this purpose, whichever one is used should be the last option specified. Any such values printed will be preceded by a // and will follow any values specified by entry or exit options. If the variable arglist is used in any of the expressions given for the cond, break, entry, exit, or after-the-arg- value-both-or-nil options, when those expressions are evaluated the value of arglist will effectively be a list of the arguments given to the Traced function. Thus (Trace (foo break (null (car arglist)))) would cause a break in foo iff the first argument to foo is nil. Similarly, the variable fnvalue will effectively be the resulting value of the Traced function; since the value of the function is of course available only on exit from the function, this should only be used with the exit and exitcond options. Also, the variable recurlev, on both entry and exit, will effectively have as its value a number indicating the current recursion level (as would be printed by Trace). This allows conditional breaks and Traces based on the depth of recursion. The recursion level is incremented by means of lambda-binding; thus if an error or other interrupt occurs, the recursion level will be reset by unbinding. (Note that the word "effectively" is used in describing the values of the above "variables" since these variables are not actually used; Trace actually performs a sublis on the expressions for the options mentioned above, substituting certain non-interned atoms for any and all appearances of the special "variables" mentioned above. Therefore, do not try to use these variables for anything but obtaining their values when using them in expressions for the above options.) In addition to using the variable arglist, expressions for Trace options may refer to the actual names of the function's arguments if the function is interpreted rather than compiled. Furthermore, such argument variables may be examined from breakpoints. Trace uses the Prin1 current at the time of printing the trace for its printouts. Thus, in order to have your Trace output passed through Grind, you might perform (setq prin1 'Sprint); for debugging Macsyma internals, (Fasload Funpr Fasl Dsk Libmax) (setq prin1 'Funprint). You can also use the Grind option (Trace (foo Grind)), but of course this uses the system grind. Examples of calls to Trace: [1] to Trace function foo, printing both arguments on entry and result on exit: (Trace foo) or (Trace (foo)) or (Trace (foo both)). [2] to Trace function foo only when called from function bar, and then only if (cdr x) is nil: (Trace (foo wherein bar cond (null (cdr x)))) or (Trace (foo cond (null (cdr x)) wherein bar)). As this example shows, the order of the options makes no difference, except for arg, value, both, or nil, which must be last. [3] to Trace function quux, printing the resultant value on exiting but no arguments on entry, printing the value of (car x) on entry, of foo1, foo2, and (foo3 bar) on exit, and of zxcvbnm and (qwerty shrdlu) on both entry and exit: (Trace (quux entry ((car x)) exit (foo1 foo2 (foo3 bar)) value zxcvbnm (qwerty shrdlu))). [4] to Trace function foo only when called by functions bar and baz, printing args on entry and result on exit, printing the value of (quux barf barph) on exit from foo when called by baz only, and conditionally breaking when called by bar if a equals b: (Trace (foo wherein bar break (equal a b)) (foo wherein baz exit ((quux barf barph)))). [5] to Trace functions phoo and fu, never printing anything for either, but saving all arguments for both on a common pdl called foopdl, and breaking inside phoo if x is nil: (Trace (phoo argpdl foopdl break (null x) cond nil nil) (fu argpdl foopdl cond nil nil)). The "cond nil" prevents anything at all from being printed. The second nil in each specifies that no args or value are to be printed; although the cond nil would prevent the printout anyway, specifying this too prevents Trace from even setting up the mechanisms to do this (which it would, against the possibility that someday nil might not evaluate to nil.) [6] to Trace function foobar, printing args on entry and result on exit, plus the value of moby-expr on exit, and pretty-printing the output: (Trace (foobar grind exit (moby-expr))). [7] to Trace function ghoti, one of whose arguments is fish, printing all arguments only if fish is non-nil, and printing on exit only if the result is not a number: (Trace (ghoti entrycond fish exitcond (not (numberp fnvalue)))). [8] to Trace function ssehc, printing Trace output only if draob is non-atomic, and even then on entry only if pohsib is a negative number, breaking when the recursion level is either 3 or greater than 7: (Trace (ssehc cond draob entrycond (and (numberp pohsib) (minusp pohsib)) break (and neeuq (or (equal recurlev 3) (lessp 7 recurlev))))). Trace returns as its value a list of names of all functions set to Trace; for any functions Traced with the wherein option, say (Trace (foo wherein bar)), instead of returning just the name it returns a 3-list (foo wherein bar). If Trace finds a it doesn't like, instead of the function's name it returns a list whose car is ? and whose cdr indicates what Trace didn't like. (This should be slightly more helpful than the oold Trace, which simply returned nil.) A list of possible error indications: (? wherein foo) Trace couldn't find an expr, fexpr, or macro property for the function specified by the wherein option; or the function specified was not atomic. (? Argpdl foo) the item following the argpdl option was not a non-nil pname-type atom. (? Foo not function) indicates that the function specified to be Traced was non-atomic, or had no functional property. (Valid functional properties are expr, fexpr, subr, fsubr, lsubr, and macro.) (? Foo) foo is not a valid option. Thus a call to Trace such as (Trace (foo wherein (nil)) (bar argpdl nil)) would return, without setting up any Traces, ((? wherein (nil)) (? argpdl nil)). If you attempt to specify to Trace a function already being Traced, Trace calls untrace before setting up the new Trace. If an error occurs, causing (? ) to be returned, the function for which the error occurred may or may not have been untraced. Beware! it is possible to call Trace with no arguments. (Trace) returns as its value a list of all functions currently being Traced. Untrace is used to undo the effects of Trace and restore functions to their normal (?) untraced state. The argument to untrace for a given function should be essentially what Trace returned for it; i.e. If Trace returned foo, use (untrace foo); if Trace returned (foo wherein bar) use (untrace (foo wherein bar)). Untrace will take multiple untrace specifications, e.g. (Untrace foo quux (bar wherein baz) fuphoo). Calling untrace with no arguments will untrace all functions currently being Traced. Remtrace, oddly enough, expunges the entire Trace package. It takes no arguments.