This minitutorial discusses several topics:
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 HLW ( ~$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 HLW does have a bunch of "Whiz Bang" options.
Shamelessly, extracting the Macro description from the HLW documentation.
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::= :after
| :before
| :break
| :break-on-exit
| :entrycond
| :exitcond
| :process
| :trace-output
| :eval-before
| :eval-after
| :allocation
| :step
Remarks You can specify the following keyword-values pairs to trace:
- :after (form)*
-
Allows the user to supply a list of forms to be evaluated upon
returning from the function. The forms are evaluated after printing out the
results of the functions call, and there values are also printed out by the
tracer.
- :before (form)*
-
Allows the user to supply a list of forms to be evaluated upon
entering from the function. The forms are evaluated after printing out the
arguments of the function, and there values are also printed out by the
tracer.
- :break form
-
Allows the user to enter the debugger directly from the tracing
facility. It takes a form which is evaluated after printing the standard
information caused by entering the function, and after excuting any :before
forms; if it returns
nil then tracing continues normally,
otherwise break is called.
- :break-on-exit form
-
Allows the user to enter the debugger directly from the tracing
facility as above, except that the form is evaluated after printing the
standard information caused by returning from the function,
and before excuting any :after forms ( so that the debugger is entered after
the function call, rather than before ).
- :entrycond form
- Controls whether or not the standard entry message ( including the
function's arguments) is printed. It takes a form and displays the entry
message if and only if on entry to the function the form evaluates to a
non-nil value.
- :exitcond form
- Controls whether or not the standard exit message ( including the
functions's results) is printed, in an analogous fashion to the above.
- :process
- may be used to restrict the tracing of a funcdtion to a particular
process.
- :trace-output stream
- May be used to direct all output from the tracer to a specified stream.
By using this the user can arrange to dispatch traced output from different
functions to different places.
- :eval-before (forms*)
- Allows the user to supply a list of forms for evaluation upon
entering the function. The form are evaluated after printing out the
arguments to the function, but unlike :before no output is given.
- :eval-after (forms*)
- Allows the user to supply a list of forms for evaluation upon
leaving the function. The form are evaluated after printing out the
results of the function, but unlike :after there is no output.
- :allocation
- Prints out the amount of memory allocated to the funcdtion, allowing for
such things as the amount of consing involed in the function.
- :step
- causes the function to enter the stepper, thus allowing the user to step
through interpreted or compiled code one step at a time.
If a function is already being traced , trace calls untrace before starting
the new trace. Calling untrace restores all functions to their normal state.
First we'll need a function to trace. How about our old friend fact?
CL-USER 3 > (defun fact (num )
(declare (notinline fact))
(if (= num 0 )
1
(* num (fact (- num 1 )))))
FACT
CL-USER 4 > (trace fact)
(FACT)
Now if you enter (fact 4 ) you'll see the familiar trace of
fact.
However we would like to try out some of the more fancy options.
For instance:
CL-USER 5 > (trace (fact :exitcond nil ))
CL-USER 6 > (fact 4 )
or
CL-USER 7 > (trace ( fact :break t ))
CL-USER 8 > (fact 4 )
or
CL-USER 9 > (trace ( fact :before (*traced-arglist*)))
or how about
CL-USER 10 > (trace (fact :break (evenp (first *traced-arglist*))))
CL-USER 11 > (fact 3 )
The variable *traced-arglist* always contains the arguments to the function
being traced.
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.
CL-USER 12 > (trace (fact :step (= 2 (first *traced-arglist*))))
Will only drop into the stepper once you reach (fact 2 ).
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 HLW and Tool Time
Last modified: by Lyman S. Taylor(lyman@cc.gatech.edu)