Debuggers in Lisps
Suvrat Apte
(with Clojure)
(Helpshift Inc)
Slide 2
Slide 2 text
Outline
• Why we need debuggers
• Demo: Clojure debugging
• How GDB works
• REPL middleware
• How cider-debug works
• Limitations
• Questions
Slide 3
Slide 3 text
Debuggers
• Examine state of programs as they run
• Halt execution
• Step through execution
• Modify state at runtime
• Change flow of execution
Slide 4
Slide 4 text
cider-nrepl
Demo
Clojure debugging
Slide 5
Slide 5 text
GDB - GNU Debugger
• Debugger runs as a separate process
• Spawns the target program as a child
• Examines state with process tracing calls
• Implementing breakpoints is a trick!
• Puts invalid instructions on breakpoints
• Processor traps OS
• OS signals the process which caused it
• It passes the signal to its parent (GDB)
• GDB checks IP and breaks
• Some processors support breakpoints
GDB
Target
OS
Processor
Slide 6
Slide 6 text
GDB - GNU Debugger
• Dependence on OS for process tracing
• Dependence on OS for signals
• Dependence on processors for interrupts
GDB
Target
OS
Processor
Slide 7
Slide 7 text
Lisps
• Lisp has minimal syntax
• Code is data
• Code itself is a valid AST
• Debuggers can use code as data
(map sq-root (filter even? numbers))
map
sq-root filter
numbers
even?
Slide 8
Slide 8 text
Clojure debugger
f (x) = y
Debugger (code) = debuggable code
Slide 9
Slide 9 text
What is a REPL?
Read-Eval-Print-Loop
(defn repl
[]
(let [input (read)
result (eval input)]
(println result)
(recur)))
How does it work?
1. Walk through code
1. Mark forms/symbols with coordinates
2. Expand macros
3. Walk through code
1. Wrap forms/symbols with pre-breakpoint macro
4. Pre-breakpoint macro expansion
1. Put a breakpoint if necessary
When a function is marked for debugging
Slide 22
Slide 22 text
How does it work?
1. Necessary information is sent to client
1. Coordinates
2. Return value at current position
3. Values of local vars
4. Possible actions
2. Waits for response from client. . .
When a breakpoint is hit
Slide 23
Slide 23 text
How does it work?
1. Walk through code
1. Mark forms/symbols with coordinates
2. Expand macros
3. Walk through code
1. Wrap forms/symbols with pre-breakpoint macro
4. Pre-breakpoint macro expansion
1. Put a breakpoint if necessary
When a function is marked for debugging
Slide 24
Slide 24 text
How does it work?
• Simple eval
• Reader macro: #dbg
• cider.nrepl.middleware.util.instrument/walk-indexed
• Assigns coordinates to forms recursively
Slide 25
Slide 25 text
How does it work?
1. Walk through code
1. Mark forms/symbols with coordinates
2. Expand macros
3. Walk through code
1. Wrap forms/symbols with pre-breakpoint macro
4. Pre-breakpoint macro expansion
1. Put a breakpoint if necessary
When a function is marked for debugging
Slide 26
Slide 26 text
How does it work?
1. Walk through code
1. Mark forms/symbols with coordinates
2. Expand macros
3. Walk through code
1. Wrap forms/symbols with pre-breakpoint macro
4. Pre-breakpoint macro expansion
1. Put a breakpoint if necessary
When a function is marked for debugging
Slide 27
Slide 27 text
How does it work?
1. Walk through code
1. Mark forms/symbols with coordinates
2. Expand macros
3. Walk through code
1. Wrap forms/symbols with pre-breakpoint macro
4. Pre-breakpoint macro expansion
1. Put a breakpoint if necessary
When a function is marked for debugging
Slide 28
Slide 28 text
How does it work?
1. Necessary information is sent to client
1. Coordinates
2. Return value at current position
3. Values of local vars
4. Possible actions
2. Waits for response from client. . .
When a breakpoint is hit
Slide 29
Slide 29 text
How does it work?
1. Necessary information is sent to client
1. Coordinates
2. Return value at current position
3. Values of local vars
4. Possible actions
2. Waits for response from client. . .
When a breakpoint is hit
Slide 30
Slide 30 text
Clojure debugger
f (x) = y
Debugger (code) = debuggable code
Slide 31
Slide 31 text
Clojure debugger
f (x) = y
Debugger (code) = debuggable code
Clojure Clojure
Slide 32
Slide 32 text
GDB - GNU Debugger
f (p, q, r) = y
Debugger (code, os, processor) = debuggable code
C Assembly
Slide 33
Slide 33 text
Limitations
• Cannot instrument bytecode
• Cannot go back in time
• Cannot replay
Slide 34
Slide 34 text
cider-debug
• Debug.clj: 655 lines
• Instrument.clj: 372 lines
• The code is very readable!
Slide 35
Slide 35 text
cider-debug
• Bozhidar Batsov
• Artur Malabara
• Vitalie Spinu
• Chris Perkins
• Neil Okamoto
• Michael Griffiths
• Lars Anderson
• Dominic Monroe
Thank you!
Slide 36
Slide 36 text
Lisp is similar to Chess.
It will take only a day to learn the rules.
But years to master !
- Harold Abelson