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 has this insanely great Stepper and Inspector and Documentation tool, surely there should be some more functionality I can eek out of the trace facility. If not. I might as well use DTRACE from Touretzky's book, right?

Alas the trace macro in MCL does have a bunch of "Whiz Bang" options. Shamelessly, extracting the Macro description from the MCL documentation.

 trace															                                          [Macro ]

Syntax    trace { spec | spec { option modifier } ) }

Description    
   The trace macro encapsulates the function or method specified by each spec,
   causing the actions specified by the options. When no options are specified,
   the default actions print arguments on entry and values on exit.

    Invoking (trace) without arguments returns a list of the functions currently
    being traced. If no fucntions are currently being traced, (trace) returns
    NIL.

Arguments    
    spec         The specification of the function to be traced. This is either
                 a symbol that is the name of the a function or generic
                 function, or an expression of the form (setf symbol) , or an
                 specific metho of a generic function in the form
	                ( :method symbol { qualifiers } (specializer {specializer}*)).

     option      An option that specifies an action to be performed.
                 The follwoing options and their modifiers are supported:
          :before    Specifies the action to be taken before the traced function
                     is called.  The :before keyword must be followed by a 
                     modifier:
               :print   Prints the name and arguments to the function before
                        the function is called.
                :break  Pints the name and arguments to the function and enters
                        a break loop before the function is called.
	                       You can examine the Stack Backtrace, perform operations
                        in the Listener, and continue if desired.

                 lisp-function  If the :before option is a function, it is called
                                before the traced function is called. The 
                                arguments to lisp-function are the name of the
                                trace function and the arguments passed to the
                                traced function.

           :after      Specifies the action to be taken after the traced function
                       returns. This keyword must be followed by a modifer, which
                       should be one of the following:
                :print   Prints the name and the returned values.
                :break   Prints the name of the function and the returned values,
                         and enters a break loop.  You can examine the Stack 
                         Backtrace, perform operations in the Listener, and 
                         continue if desired.
                 lisp-function   If the :after option is a function, it is called
                                 after the trace function returns.  The arguments
                                 are the name of the traced function and the
                                 values returned by the traced function.

            :step       Specifies whether the traced function shoud be stepped
                        when it is run. .... [ stuff about *saved-definitions*..]

                        The :step keyword must be followed by a modifier, which
                        is either t or a function whose arguments are the
                        name of the trace function and the arguments passed to
                        to the traced function.  If it is t or if the 
                        function returns non-NIL, then the traced function is
                        stepped; otherwise it is run without stepping.
                 
Well let's try some of this "Wizardary" out...

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 :before :print ))
      ? (fact 4 )

or
      ? (trace ( fact :before :break
                      :after  :print ))
      ? (fact 4 )

or how about

       ? (defun only-on-odd (func &rest args)
                   "only break if number is odd"
                   (if (evenp (car args ))
                     (format t "~&Calling ~s~%"
                             (cons func args))
                     (break "on calling ~s"
                            (cons func args))))
        ONLY-ON-ODD
       ? (trace (fact :before only-on-odd ))
       NIL
       ? (fact 4 )

Note in the last example the lisp function that :before is using is not preceeded by #' . The trace macro will append that on for you. However, for some strange reason I could not get the facility to accpet a lambda function as the argument ( even though the documentation contains an example using a lambda function it didn't work... ). Well I guess you can't have everything.

Using tracing and the stepper at the same time.

From the trace specification you can either turn stepping on full time. Or supply a function that will be used as a predicate as to whether stepping should be turn on or off. So you should be able to trace only on the odd arguments by using the evenp S-Expression that appears in only-on-odd above.

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 MCL and Tool Time

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