The screenshot above shows an example report of running the profiler I wrote - Tracer - as viewed through Google Chrome's little known Trace Viewer, available under a simple URL: about://tracing . After profiling designated pieces of code, Tracer produces a JSON file that can be directly loaded into the Trace Viewer and explored in the browser.

Trace Viewer was around for quite a while now; the original idea of using it for profiling my own code came from a 2012 Gamasutra article. I've been toying with it for about four years now, but it took some actual hard-to-diagnose performance problems at work to actually built the tool I needed.

The implementation itself is quick and dirty. In the introduction, I mentioned Tracer is a tool for SBCL, not for Common Lisp in general. That's because it coopts the implementation of the TRACE facility in SBCL in a very violent way: it briefly redefines a few internal functions to make them record profiling data instead of reporting the execution trace to *TRACE-OUTPUT* . Doing it this way makes Tracer completely non-portable, but it let me avoid reinventing the already-existing TRACE infrastructure, and this way the user doesn't have to modify any of their code - all one needs to do is to specify which functions should be profiled, and Tracer will just run a modified TRACE over them.

There's a nice consequence to that approach: profiling becomes quick and easy. At my last job, whenever myself or my teammates were trying to investigate a performance issue, we could just wrap a call the suspected function from the REPL in (with-tracing ...) , and in a few moments we'd have a detailed report to explore. We could also wrap a call to sleep to just take a pulse of the application - Tracer records data across threads. With no need to modify or rebuild the actual code being profiled, exploring execution times became casual.

(A secondary benefit is that it allows for quick exploration of a large legacy codebase. If you're having trouble getting your head around who's calling who and why - as I often have in projects I'm new to - just set Tracer to trace every project package and explore the call graph visually!)