CIDER ships with a powerful interactive Clojure debugger inspired by Emacs's own Edebug. You're going to love it!

CIDER Debugger

Debugging

The debugger can be invoked in several ways, the simplest one is to type C-u C-M-x. This will take the current top-level form, place as many breakpoints inside it as possible (instrument it), and then evaluate it as normal. Whenever a breakpoint is reached, you'll be shown the value and asked for input (see below). Note that if the current form is a defn, it will stay instrumented, so the debugger will be triggered every time the function is called. To uninstrument defn (or similar forms), you just have to evaluate it again as you'd normally do (e.g. with C-M-x).

Another way to trigger the debugger is by placing breakpoints yourself. Just write #break before a form, and the debugger will popup every time that form is evaluated. For instance, if you hit C-M-x on the following, a breakpoint is triggered every time (inspector msg) is evaluated.

(defn eval-msg [{:keys [inspect] :as msg}]
  (if inspect
    #break (inspector msg)
    msg))

Instead of #break you can also write #dbg before a form, this will not only breakpoint the form but also everything inside it. In the example above, this places a breakpoint around (inspector msg) and another around msg. If you've been paying attention, you may have noticed that the first option (C-u C-M-x) is a quick way of evaluating the current top-level form with #dbg in front.

At any point, you can bring up a list of all currently instrumented defs with the command M-x cider-browse-instrumented-defs. Protocols and types can be instrumented as well, but they will not be listed by this command.

Keys

cider-debug tries to be consistent with Edebug, although there are some differences. It makes available the following bindings while stepping through code.

Keyboard shortcut Description
n Next step
i Step in to a function
o Step out of the current sexp (like up-list)
O Force-step out of the current sexp
h Skip all sexps up to “here” (current position). Move the cursor before doing this.
H Force-step to “here”
c Continue without stopping
e Eval code in current context
p Inspect a value
l Inspect local variables
j Inject a value into running code
s Show the current stack
t Trace. Continue, printing expressions and their values.
q Quit execution

In addition, all the usual evaluation commands (such as C-x C-e or C-c M-:) will use the current lexical context (local variables) while the debugger is active.

Command Details

Here are some more details about what each of the above commands does.

Stepping Commands

These commands continue execution until reaching a breakpoint.

In the cider debugger, the term "breakpoint" refers to a place where the debugger can halt execution and display the value of an expression. You can set a single breakpoint with #break, or set breakpoints throughout a form with #dbg (or by evaluating with C-u C-M-x). Not every form is wrapped in a breakpoint; the debugger tries to avoid setting breakpoints on expressions that would not be interesting to stop at, such as constants. For example, there would not be much point in stopping execution at a literal number 23 in your code and showing that its value is 23 - you already know that.

Other Commands

Conditional Breakpoints

Breakpoints can be conditional, such that the debugger will only stop when the condition is true.

Conditions are specified using :break/when metadata attached to a form.

(dotimes [i 10]
  #dbg ^{:break/when (= i 7)}
  (prn i))

Evaluating the above with C-M-x, the debugger will stop only once, when i is 7.

You can also have cider insert the break-condition into your code for you. Place the point where you want the condition to go and evaluate with C-u C-u C-M-x or C-u C-u C-c C-c.

Internal Details

This section explains a bit of the inner workings of the debugger. It is intended mostly to help those who are interested in contributing, and doesn't teach anything about the debugger's usage.

The CIDER debugger works in several steps:

  1. First it walks through the user's code, adding metadata to forms and symbols that identify their position (coordinate) in the code.
  2. Then it macroexpands everything to get rid of macros.
  3. Then it walks through the code again, instrumenting it. That involves a few things.

    • It understands all existing special forms, and takes care not to instrument where it's not supposed to. For instance, the arglist of a fn* or the left-side of a let-binding.
    • Wherever it finds the previously-injected metadata (if that place is valid for instrumentation) it wraps the form/symbol in a macro called breakpoint-if-interesting.
  4. When the resulting code actually gets evaluated by the Clojure compiler, the breakpoint-if-interesting macro will be expanded. This macro decides whether the return value of the form/symbol in question is actually something the user wants to see (see below). If it is, the form/symbol gets wrapped in the breakpoint macro, otherwise it's returned as is.

  5. The breakpoint macro takes that coordinate information that was provided in step 1. and sends it over to Emacs (the front-end). It also sends the return value of the form and a prompt of available commands. Emacs then uses this information to show the value of actual code forms and prompt for the next action.

A few example of forms that don't have interesting return values (and so are not wrapped in a breakpoint):