+(setq rcs-trace-
+ "$Header: /usr/lib/lisp/RCS/trace.l,v 1.2 83/08/15 22:30:36 jkf Exp $")
+
+;---- The Joseph Lister Trace Package, v1
+; John Foderaro, Sept 1979
+;------------------------------------------------------------------;
+; Copyright (c) 1979 The Regents of the University of California ;
+; All rights reserved. ;
+;------------------------------------------------------------------;
+(eval-when (eval)
+ (setq old-read-table-trace readtable)
+ (setq readtable (makereadtable t))
+ (setq old-uctolc-value (status uctolc))
+ (sstatus uctolc nil) ; turn off case conversion
+ (load 'charmac)
+ (setsyntax '\; 'macro 'zapline)
+ )
+
+
+
+;----
+; trace uses these properties on the property list:
+; trace-orig-fcn: original occupant of the function cell
+; trace-trace-fcn: the value trace puts in the function cell
+; (used to check if the trace function has be overwritten).
+; trace-trace-args: the arguments when function was traced.
+; trace-printargs: function to print argument to function
+; trace-printres: function to print result of function
+
+(declare (nlambda T-status T-sstatus)
+ (special piport
+ if ifnot evalin evalout
+ printargs printres evfcn
+ traceenter traceexit
+ prinlevel prinlength
+ $$traced-functions$$ ; all functions being traced
+ $$functions-in-trace$$ ; active functions
+ $$funcargs-in-trace$$ ; arguments to active functions.
+ $tracemute ; if t, then enters and exits
+ ; are quiet, but info is still
+ ; kept so (tracedump) will work
+ trace-prinlevel ; default values
+ trace-prinlength
+ trace-printer ; function trace uses to print
+ ))
+
+
+
+(cond ((null (boundp '$$traced-functions$$)) (setq $$traced-functions$$ nil)))
+(cond ((null (boundp '$$functions-in-trace$$)) (setq $$functions-in-trace$$ nil)))
+(cond ((null (boundp '$$funcargs-in-trace$$)) (setq $$funcargs-in-trace$$ nil)))
+(cond ((null (boundp '$tracemute)) (setq $tracemute nil)))
+(cond ((null (boundp 'trace-prinlevel)) (setq trace-prinlevel 4)))
+(cond ((null (boundp 'trace-prinlength)) (setq trace-prinlength 5)))
+(cond ((null (boundp 'trace-printer)) (setq trace-printer 'Trace-print)))
+
+;----> It is important that the trace package not use traced functions
+; thus we give the functions the trace package uses different
+; names and make them equivalent at this time to their
+; traceable counterparts.
+(defun trace-startup-func nil
+ (do ((i '( (add1 T-add1)(append T-append)
+ (and T-and) (apply T-apply)
+ (cond T-cond) (cons T-cons) (delq T-delq)
+ (def T-def) (do T-do) (drain T-drain)
+ (dtpr T-dtpr) (eval T-eval)(funcall T-funcall)
+ (get T-get) (getd T-getd)(getdisc T-getdisc)
+ (greaterp T-greaterp)(lessp T-lessp)
+ (mapc T-mapc) (not T-not)(nreverse T-nreverse)
+ (patom T-patom) (print T-print) (prog T-prog)
+ (patom T-patom)(putd T-putd)
+ (putprop T-putprop)
+ (read T-read)(remprop T-remprop) (reverse T-reverse)
+ (return T-return)
+ (set T-set) (setq T-setq)
+ (status T-status) (sstatus T-sstatus)
+ (sub1 T-sub1) (terpr T-terpr)
+ (zerop T-zerop))
+ (cdr i)))
+ ((null i))
+ (putd (cadar i) (getd (caar i)))
+ (putprop (cadar i) t 'Untraceable)))
+
+(trace-startup-func)
+
+
+(putprop 'quote t 'Untraceable) ; this prevents the common error
+ ; of (trace 'foo) from causing big
+ ; problems.
+
+;--- trace - arg1,arg2, ... names of functions to trace
+; This is the main user callable trace routine.
+; work in progress, documentation incomplete since im not sure exactly
+; where this is going.
+;
+(def trace
+ (nlambda (argl)
+ (prog (if ifnot evalin evalout funnm typ
+ funcd did break printargs printres evfcn traceenter traceexit
+ traceargs)
+
+ ; turn off transfer table linkages if they are on
+ (cond ((T-status translink) (T-sstatus translink nil)))
+
+ ; process each argument
+
+ (do ((ll argl (cdr ll))
+ (funnm)
+ (funcd))
+ ((null ll))
+ (setq funnm (car ll)
+ if t
+ break nil
+ ifnot nil
+ evalin nil
+ evalout nil
+ printargs nil
+ printres nil
+ evfcn nil
+ traceenter 'T-traceenter
+ traceexit 'T-traceexit
+ traceargs nil)
+
+ ; a list as an argument means that the user is specifying
+ ; conditions on the trace
+ (cond ((not (atom funnm))
+ (cond ((not (atom (setq funnm (car funnm))))
+ (T-print (car funnm))
+ (T-patom '" is non an function name")
+ (go botloop)))
+ ; remember the arguments in case a retrace is requested
+ (setq traceargs (cdar ll))
+ ; scan the arguments
+ (do ((rr (cdar ll) (cdr rr)))
+ ((null rr))
+ (cond ((memq (car rr) '(if ifnot evalin evalout
+ printargs printres evfcn
+ traceenter traceexit))
+ (T-set (car rr) (cadr rr))
+ (setq rr (cdr rr)))
+ ((eq (car rr) 'evalinout)
+ (setq evalin (setq evalout (cadr rr))
+ rr (cdr rr)))
+ ((eq (car rr) 'break)
+ (setq break t))
+ ((eq (car rr) 'lprint)
+ (setq printargs 'T-levprint
+ printres 'T-levprint))
+ (t (T-patom '"bad request: ")
+ (T-print (car rr))
+ (T-terpr)))))
+ (t (setq traceargs nil) ;no args given
+ ))
+
+ ; if function is untraceable, print error message and skip
+ (cond ((get funnm 'Untraceable)
+ (setq did (cons `(,funnm untraceable) did))
+ (go botloop)))
+
+
+ ; Untrace before tracing
+ (let ((res (funcall 'untrace (list funnm))))
+ (cond (res (setq did (cons `(,funnm untraced) did)))))
+
+ ; store the names of the arg printing routines if they are
+ ; different than print
+
+ (cond (printargs (T-putprop funnm printargs 'trace-printargs)))
+ (cond (printres (T-putprop funnm printres 'trace-printres)))
+ (T-putprop funnm traceargs 'trace-trace-args)
+
+ ; we must determine the type of function being traced
+ ; in order to create the correct replacement function
+
+ (cond ((setq funcd (T-getd funnm))
+ (cond ((bcdp funcd) ; machine code
+ (cond ((or (eq 'lambda (T-getdisc funcd))
+ (eq 'nlambda (T-getdisc funcd))
+ (eq 'macro (T-getdisc funcd)))
+ (setq typ (T-getdisc funcd)))
+ ((stringp (T-getdisc funcd)) ; foreign func
+ (setq typ 'lambda)) ; close enough
+ (t (T-patom '"Unknown type of compiled function")
+ (T-print funnm)
+ (setq typ nil))))
+
+ ((dtpr funcd) ; lisp coded
+ (cond ((or (eq 'lambda (car funcd))
+ (eq 'lexpr (car funcd)))
+ (setq typ 'lambda))
+ ((or (eq 'nlambda (car funcd))
+ (eq 'macro (car funcd)))
+ (setq typ (car funcd)))
+ (t (T-patom '"Bad function definition: ")
+ (T-print funnm)
+ (setq typ nil))))
+ ((arrayp funcd) ; array
+ (setq typ 'lambda))
+ (t (T-patom '"Bad function defintion: ")
+ (T-print funnm)))
+
+ ; now that the arguments have been examined for this
+ ; function, do the tracing stuff.
+ ; First save the old function on the property list
+
+ (T-putprop funnm funcd 'trace-orig-fcn)
+
+ ; now build a replacement
+
+ (cond
+ ((eq typ 'lambda)
+ (T-eval
+ `(T-def
+ ,funnm
+ (lexpr (T-nargs)
+ ((lambda (T-arglst T-res T-rslt
+ $$functions-in-trace$$
+ $$funcargs-in-trace$$)
+ (T-do ((i T-nargs (T-sub1 i)))
+ ((T-zerop i))
+ (T-setq T-arglst
+ (T-cons (arg i) T-arglst)))
+ (T-setq $$funcargs-in-trace$$
+ (T-cons T-arglst
+ $$funcargs-in-trace$$))
+ (T-cond ((T-setq T-res
+ (T-and ,if
+ (T-not ,ifnot)))
+ (,traceenter ',funnm T-arglst)
+ ,@(cond (evalin
+ `((T-patom ,'":in: ")
+ ,evalin
+ (T-terpr))))
+ (T-cond (,break
+ (trace-break)))))
+ (T-setq T-rslt
+ ,(cond
+ (evfcn)
+ (t `(T-apply
+ ',funcd
+ T-arglst))))
+ (T-cond (T-res
+ ,@(cond (evalout
+ `((T-patom ,'":out: ")
+ ,evalout
+ (T-terpr))))
+ (,traceexit ',funnm T-rslt)))
+ T-rslt)
+ nil nil nil
+ (T-cons ',funnm $$functions-in-trace$$)
+ $$funcargs-in-trace$$))))
+ (T-putprop funnm (T-getd funnm) 'trace-trace-fcn)
+ (setq did (cons funnm did)
+ $$traced-functions$$ (cons funnm
+ $$traced-functions$$)))
+
+ ((or (eq typ 'nlambda)
+ (eq typ 'macro))
+ (T-eval
+ `(T-def ,funnm
+ (,typ (T-arglst)
+ ((lambda (T-res T-rslt
+ $$functions-in-trace$$
+ $$funcargs-in-trace$$)
+ (T-setq $$funcargs-in-trace$$
+ (T-cons
+ T-arglst
+ $$funcargs-in-trace$$))
+ (T-cond ((T-setq
+ T-res
+ (T-and ,if
+ (not ,ifnot)))
+ (,traceenter
+ ',funnm
+ T-arglst)
+ ,evalin
+ (T-cond (,break
+ (trace-break)))))
+ (T-setq T-rslt
+ ,(cond
+ (evfcn `(,evfcn
+ ',funcd
+ T-arglst))
+ (t `(T-apply ',funcd
+ T-arglst))))
+ (T-cond (T-res
+ ,evalout
+ (,traceexit ',funnm T-rslt)))
+ T-rslt)
+ nil nil
+ (cons ',funnm $$functions-in-trace$$)
+ $$funcargs-in-trace$$))))
+ (T-putprop funnm (T-getd funnm) 'trace-trace-fcn)
+ (setq did (cons funnm did)
+ $$traced-functions$$ (cons funnm
+ $$traced-functions$$)))
+
+ (t (T-patom '"No such function as: ")
+ (T-print funnm)
+ (T-terpr)))))
+ botloop )
+ ; if given no args, just return the function currently being traced
+ (return (cond ((null argl) $$traced-functions$$)
+ (t (T-nreverse did)))))))
+
+;--- untrace
+; (untrace foo bar baz)
+; untraces foo, bar and baz.
+; (untrace)
+; untraces all functions being traced.
+;
+
+(def untrace
+ (nlambda (argl)
+ (cond ((null argl) (setq argl $$traced-functions$$)))
+
+ (do ((i argl (cdr i))
+ (tmp)
+ (curf)
+ (res))
+ ((null i)
+ (cond ((null $$traced-functions$$)
+ (setq $$functions-in-trace$$ nil)
+ (setq $$funcargs-in-trace$$ nil)))
+ res)
+ (cond ((and (T-getd (setq curf (car i)))
+ (eq (T-getd (car i))
+ (get (car i) 'trace-trace-fcn)))
+ ; we only want to restore the original definition
+ ; if this function has not been redefined!
+ ; we test this by checking to be sure that the
+ ; trace-trace-property is the same as the function
+ ; definition.
+ (T-putd curf (get curf 'trace-orig-fcn))
+ (T-remprop curf 'trace-orig-fcn)
+ (T-remprop curf 'trace-trace-fcn)
+ (T-remprop curf 'trace-trace-args)
+ (T-remprop curf 'entercount)
+ (setq $$traced-functions$$
+ (T-delq curf $$traced-functions$$))
+ (setq res (cons curf res)))))))
+
+
+;--- retrace :: trace again all function thought to be traced.
+;
+(def retrace
+ (nlambda (args)
+ (cond ((null args) (setq args $$traced-functions$$)))
+ (mapcan '(lambda (fcn)
+ (cond ((and (symbolp fcn)
+ (not (eq (T-getd fcn)
+ (get fcn 'trace-trace-fcn))))
+
+ (funcall 'trace
+ `((,fcn ,@(get fcn 'trace-trace-args)))))))
+ args)))
+
+;--- tracedump :: dump the currently active trace frames
+;
+(def tracedump
+ (lambda nil
+ (let (($tracemute nil))
+ (T-tracedump-recursive $$functions-in-trace$$
+ $$funcargs-in-trace$$))))
+
+
+;--- traceargs :: return list of args to currently entered traced functions
+; call is:
+; (traceargs foo) returns first call to foo starting at most current
+; (traceargs foo 3) returns args to third call to foo, starting at
+; most current
+;
+(def traceargs
+ (nlambda (args)
+ (cond ((and args $$functions-in-trace$$)
+ (let ((name (car args))
+ (amt (cond ((numberp (cadr args)) (cadr args))
+ (t 1))))
+ (do ((fit $$functions-in-trace$$ (cdr fit))
+ (fat $$funcargs-in-trace$$ (cdr fat)))
+ ((null fit))
+ (cond ((eq name (car fit))
+ (cond ((zerop (setq amt (1- amt)))
+ (return (car fat))))))))))))
+
+;--- T-tracedump-recursive
+; since the lists of functions being traced and arguments are in the reverse
+; of the order we want to print them, we recurse down the lists and on the
+; way back we print the information.
+;
+(def T-tracedump-recursive
+ (lambda ($$functions-in-trace$$ $$funcargs-in-trace$$)
+ (cond ((null $$functions-in-trace$$))
+ (t (T-tracedump-recursive (cdr $$functions-in-trace$$)
+ (cdr $$funcargs-in-trace$$))
+ (T-traceenter (car $$functions-in-trace$$)
+ (car $$funcargs-in-trace$$))))))
+
+
+
+;--- T-traceenter - funnm : name of function just entered
+; - count : count to print out
+; This routine is called to print the entry banner for a
+; traced function.
+;
+(def T-traceenter
+ (lambda (name args)
+ (prog (count indent)
+ (cond ((not $tracemute)
+ (setq count 0 indent 0)
+ (do ((ll $$functions-in-trace$$ (cdr ll)))
+ ((null ll))
+ (cond ((eq (car ll) name) (setq count (1+ count))))
+ (setq indent (1+ indent)))
+
+ (T-traceindent indent)
+ (T-print count)
+ (T-patom '" <Enter> ")
+ (T-print name)
+ (T-patom '" ")
+ (cond ((setq count (T-get name 'trace-printargs))
+ (funcall count args))
+ (t (funcall trace-printer args)))
+ (T-terpr))))))
+
+(def T-traceexit
+ (lambda (name res)
+ (prog (count indent)
+ (cond ((not $tracemute)
+ (setq count 0 indent 0)
+ (do ((ll $$functions-in-trace$$ (cdr ll)))
+ ((null ll))
+ (cond ((eq (car ll) name) (setq count (1+ count))))
+ (setq indent (1+ indent)))
+
+
+ (T-traceindent indent)
+ (T-print count)
+ (T-patom " <EXIT> ")
+ (T-print name)
+ (T-patom " ")
+
+ (cond ((setq count (T-get name 'trace-printres))
+ (funcall count res))
+ (t (funcall trace-printer res)))
+
+ (T-terpr))))))
+
+
+;--- Trace-printer
+; this is the default value of trace-printer. It prints a form after
+; binding prinlevel and prinlength.
+;
+(def Trace-print
+ (lambda (form)
+ (let ((prinlevel trace-prinlevel)
+ (prinlength trace-prinlength))
+ (T-print form))))
+
+; T-traceindent
+; - n : indent to column n
+
+(def T-traceindent
+ (lambda (col)
+ (do ((i col (1- i))
+ (char '| |))
+ ((< i 2))
+ (T-patom (cond ((eq char '| |) (setq char '\|))
+ (t (setq char '| |)))))))
+; from toplevel.l:
+;
+;--- read and print functions are user-selectable by just
+; assigning another value to top-level-print and top-level-read
+;
+(declare (special top-level-read top-level-print))
+
+(defmacro top-print (&rest args)
+ `(cond (top-level-print (funcall top-level-print ,@args))
+ (t (T-print ,@args))))
+
+(defmacro top-read (&rest args)
+ `(cond ((and top-level-read
+ (T-getd top-level-read))
+ (funcall top-level-read ,@args))
+ (t (T-read ,@args))))
+
+
+; trace-break - this is the trace break loop
+(def trace-break
+ (lambda nil
+ (prog (tracevalread piport)
+ (T-terpr) (T-patom '"[tracebreak]")
+ loop (T-terpr)
+ (T-patom '"T>")
+ (T-drain)
+ (cond ((or (eq nil (setq tracevalread
+ (car
+ (errset (top-read nil nil)))))
+ (and (dtpr tracevalread)
+ (eq 'tracereturn (car tracevalread))))
+ (T-terpr)
+ (return nil)))
+ (top-print (car (errset (T-eval tracevalread))))
+ (go loop))))
+
+
+(def T-levprint
+ (lambda (x)
+ ((lambda (prinlevel prinlength)
+ (T-print x))
+ 3 10)))
+
+
+(eval-when (eval)
+ (apply 'sstatus `(uctolc ,old-uctolc-value))
+ (setq readtable old-read-table-trace)
+ )