This minitutorial discusses several topics:

Debugging without print statements and breaks

On your way to becoming a skilled Lisp "Hacker" you have the feeling that there must be a better way to debug you programs than by placing (break) and (print .. ) scattered about through your code. After all if MCL can provide all the functionality it does for ~$500.00 ( including trace ) , surely there should be some more functionality I can eek out of the trace facility in LCL ( ~$1500 ) . If not. I might as well use DTRACE from Touretzky's book, right? [ Or else maybe by a Mactinosh JUST to write Lisp programs ... don't laugh this has happened. ]

Alas the trace macro in LCL does have a bunch of "Whiz Bang" options. Shamelessly, extracting the Macro description from the LCL documentation. ( OIT should have the manual somewhere. I think it is OIT-66 )

trace

Purpose:  The macro trace allows you to trace one or more function and to
          perform certain actions at the time a function is called or at 
          the time it returns.  It returns as its value a list of 
          names of all the functions it has traced.

Syntax:   trace { trace-spec}*					[Macro ]

          trace-spec::= function-name | 
                        ( {function-name | ( {function-name}+)}
				{keyword form}* )
	   keyword::= 		:cond
		     	|	:entrycond
			|	:exitcond
			| 	:break
			| 	:exitbreak
			| 	:entry
			|	:exit
			|	:step


Remarks    You can specify the following keyword-values pairs to trace:

:cond form
If form has a non-nil value, this keyword argument displays the the trace output. This keyword argument overrrides the :exitcond and :entrycond arguments, if the value of this argument is nil. no trace information is displayed. The default value of form is non-nil.
:entrycond form
If from has a non-nil value, this keyword argument displayes tracing information on entry to the traced function. The default value of form is non-nil.
:exitcond form
If from has a non-nil value, this keyword argument displayes tracing information on exit from the traced function. The default value of form is non-nil.
:break form
If from has a non-nil value, this keyword argument calls the Debugger after the entry trace information his printed but before the traced function is applied to its arguments. The default value of form is nil.
:exitbreak form
If from has a non-nil value, this keyword argument calls the Debugger after the traced function has been executed and the exit trace information has been printed but before control returns. The default value of form is nil.
:entry ( {form}+ )
This keyword arguemtn dispalys the values of the forms in the list on entry to the traced function. The displayed values are preceded by two backslashes (\\).
:exit ( {form}+ )
This keyword arguemtn dispalys the values of the forms in the list on entry to the traced function. The displayed values are preceded by two backslashes (\\).
:step form
If form is non-nil, this keyword aruments calls the Stepper on the traced function. The Stepper allow you to interactively step through the evaluation of a function: it is described in the section "The Stepper" The default value of form is nil.
If a function is already being traced , trace calls untrace before starting the new trace. Calling untrace restores all functions to their normal state.

Tracing through an example function

First we'll need a function to trace. How about our old friend fact?
       > (defun fact (num )
              (declare (notinline fact))
              (if (= num 0 )
                  1
              (* num (fact (- num 1 )))))
       FACT
       > (trace fact)
       NIL
Now if you enter (fact 4 ) you'll see the familiar trace of fact.

Using the various tracing options

However we would like to try out some of the more fancy options. For instance:
      > (trace  (fact :exitcond nil  ))
      > (fact 4 )

or
      > (trace ( fact :break t ))
      > (fact 4 )

or
      > (trace ( fact  :entry (*trace-arglist*)))

or how about

      > (trace (fact :break (evenp (first *trace-arglist*))))
       
      > (fact 4 )

The variable *trace-arglist* always contains the arguments to the function being traced.

Using tracing and the stepper at the same time.

From the trace specification you can either turn stepping on full time. Or if the form provided to step is a predicate then the stepper will only turn on when the predicate is true.
	> (trace (fact :step (= 2 (first *trace-arglist*))))
Will only drop into the stepper once you reach (fact 2 ).

What else can I do?

You think of more examples of your own. If you haven't already you should try out the minitutorial on Stepping and the debugger.


Back to LCL and Tool Time

Last modified: by Lyman S. Taylor(lyman@cc.gatech.edu)
(c) copyright Lyman S. Taylor 1995, All rights reserved