Trace/Untrace Macro
The Trace/Untrace Macro-
Trace (and its counterpart Untrace) is a Common Lisp macro that, given one or many function-names of symbols, will cause the functions to be "traced". Whenever a traced
function is invoked, information about the call, about the arguments passed, and about any eventually returned values is printed to
trace output. If trace is used with no
function-names, no tracing action is performed; instead, a list of the
functions currently being traced is returned.
Example:
CL-USER 1 > (defun bar (x) (car x))
BAR
CL-USER 2 > (trace bar)
(BAR)
CL-USER 3 > (bar '(1 2 3 4 5 6 7 8))
0 BAR > ...
>> X : (1 2 3 4 5 6 7 8)
0 BAR < ...
<< VALUE-0 : 1
1
The idea-
The idea here was to use Java Dynamic Proxies to intercept, and temporarily replace
CommonLisp? functions so they may be traced. The Java Dynamic Proxy class uses the Reflection library to intercept method invocations, so we could inject output into the intercepted invocations. This was an experimental project, as we werent sure if Java's Proxy class could properly mimic all the different types of
CLForJava? functions. There are 3 different types of functions to worry about in the
CLForJava? system: (1) Java-defined functions, (2) Lisp-compiled functions, and (3) Lisp-on-the-fly functions.
The source-
The source for this macro is several locations:
/src/java/lisp/system/function/TraceHelper.java - The code that, given a single/list of function symbols, replaces their function with a created proxy function.
/src/java/lisp/system/function/TraceMap.java - Code that maintains a Hash Map of traced functions and helper functions.
/src/java/lisp/system/function/TraceProxy.java - Class that actually creates, and manipulates output of proxy functions.
/src/java/lisp/system/function/UntraceHelper.java - The code that, given a single/list of traced function symbols, replaces their proxy function with the original function.
/src/lisp/MacrosAndFunctions/Misc-Macros.lsp - Contains the macro declaration for Trace and Untrace
What works -
Trace and Untrace work on all 3 types of
ClForJava? functions (stated above) when simply traced and called. Trace and Untrace mimic
LispWorks? output exactily.
What doesn't work -
1. After a function has been traced, you can't use it when defining another function.
Example:
(trace car)
=> (CAR)
(defun foo (x) (car x))
=> explosion
We are not exactily sure why this is happening, but we have a hunch: When the defun expression is evaluated by Eval.java, the function munging field is not present for the proxied class (as it is for the original function class) and a FIELD_NOT_FOUND exception is thrown.
2. The Function Field in /src/java/lisp/extensions/type/CommonLispFunctions.java can't be replaced by a proxied functions. This can't be set because the proxied function is not actually and 'instanceof' the original function class, only its implemented interfaces. The solution to this is to change every function to an interface that contains a static inner class defining the function.
RyanMoore - 2010-12-14