Typical Technological Trickery

Tracing macros in Emacs

30 Mar 2018

I’ve been playing around in common lisp lately, and one feature I particularly like is the trace macro. trace is a macro which lets you easily log the arguments and result of all calls to a function. It’s particularly handy for visualising recursive function calls.

As you might expect Emacs has an equivalent macro. It’s called trace-function and is used like this:

(defun fact (n)
  (if (= n 0) 1
    (* n (fact (1- n)))))

(trace-function #'fact)

(fact 3)

which pops up a *trace-output* buffer containing

1 -> (fact 3)
| 2 -> (fact 2)
| | 3 -> (fact 1)
| | | 4 -> (fact 0)
| | | 4 <- fact: 1
| | 3 <- fact: 1
| 2 <- fact: 2
1 <- fact: 6

This is great if you’re working interactively, but I want to use it for debugging unit tests too. I run typically tests in batch mode using ert-runner, so the challenge is to get the trace macro to print to stdout instead of writing to a buffer.

Unfortunately Emacs doesn’t have a simple way of redirecting a buffer to stdout. My somewhat hacky solution is to add the following code to my ert-runner test-helper.el file:

(defun log-trace-buffer (&rest _)
  (when (get-buffer trace-buffer)
    (with-current-buffer trace-buffer
      (message (buffer-substring-no-properties (point-min) (point-max))))))
(add-to-list 'ert-runner-reporter-run-ended-functions #'log-trace-buffer)

which just dumps the entire contents of the trace output buffer to stdout when the tests finish. It’s not perfect but it’s enough for me to fix my tests!