(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))
Run Code Online (Sandbox Code Playgroud)
那是因为trace-fn-call
,dotrace
用于包装要跟踪的函数的东西,用于str
产生不错的TRACE foo => val
输出.
该dotrace
宏做它的魔力通过安装一个线程每个瓦尔抱着要跟踪的功能结合; 在这种情况下,有一个这样的Var , clojure.core/str
. 替换看起来大致如此:
(let [f @#'str]
(fn [& args]
(trace-fn-call 'str f args)))
Run Code Online (Sandbox Code Playgroud)
的trace-fn-call
,引用文档字符串它,"跟踪与ARGS函数f一个电话." 在这样做时,它调用跟踪函数,记录返回值,打印出表单的信息良好的消息,TRACE foo => val
并返回从跟踪函数获得的值,以便可以继续定期执行.
如上所述,此TRACE foo => val
消息是使用的str
; 然而,在手头的情况下,这实际上是被跟踪的函数,所以对它的调用导致另一个调用trace-fn-call
,这使得它自己尝试使用生成跟踪输出字符串str
,这导致另一个调用trace-fn-call
...最终导致堆栈爆炸.
即使存在核心Vars的奇怪绑定,以下修改版本dotrace
也trace-fn-call
应该可以正常工作(请注意,期货可能不会及时安排;如果这是一个问题,请参见下文):
(defn my-trace-fn-call
"Traces a single call to a function f with args. 'name' is the
symbol name of the function."
[name f args]
(let [id (gensym "t")]
@(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
(let [value (binding [*trace-depth* (inc *trace-depth*)]
(apply f args))]
@(future (tracer id (str (trace-indent) "=> " (pr-str value))))
value)))
(defmacro my-dotrace
"Given a sequence of function identifiers, evaluate the body
expressions in an environment in which the identifiers are bound to
the traced functions. Does not work on inlined functions,
such as clojure.core/+"
[fnames & exprs]
`(binding [~@(interleave fnames
(for [fname fnames]
`(let [f# @(var ~fname)]
(fn [& args#]
(my-trace-fn-call '~fname f# args#)))))]
~@exprs))
Run Code Online (Sandbox Code Playgroud)
(重新绑定trace-fn-call
常规dotrace
显然不起作用;我的猜测是因为clojure.*
Var调用仍然被编译器硬连接,但这是一个单独的问题.无论如何,上面的方法都可行.)
另一种方法是将上述my-dotrace
宏与my-trace-fn-call
不使用期货的函数一起使用,但修改为clojure.contrib.trace
使用以下代码调用函数的自定义替换str
:
(defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))
Run Code Online (Sandbox Code Playgroud)
替换是直截了当的,乏味的,我从答案中省略了它们.