Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Debuggers in Lispy Languages

Debuggers in Lispy Languages

This talk is about a unique way in which debuggers can be written in language which are lisp like.
With CIDER debug as an example, this talk takes you through how this approach works under the hood.
This talk was presented at Functional Conf 2019.

Suvrat Apte

November 16, 2019
Tweet

More Decks by Suvrat Apte

Other Decks in Programming

Transcript

  1. Debuggers in Lisps
    Suvrat Apte
    (with Clojure)
    (Helpshift Inc)

    View Slide

  2. Outline
    • Why we need debuggers
    • Demo: Clojure debugging
    • How GDB works
    • REPL middleware
    • How cider-debug works
    • Limitations
    • Questions

    View Slide

  3. Debuggers
    • Examine state of programs as they run
    • Halt execution
    • Step through execution
    • Modify state at runtime
    • Change flow of execution

    View Slide

  4. cider-nrepl
    Demo
    Clojure debugging

    View Slide

  5. 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

    View Slide

  6. GDB - GNU Debugger
    • Dependence on OS for process tracing
    • Dependence on OS for signals
    • Dependence on processors for interrupts
    GDB
    Target
    OS
    Processor

    View Slide

  7. 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?

    View Slide

  8. Clojure debugger
    f (x) = y
    Debugger (code) = debuggable code

    View Slide

  9. What is a REPL?
    Read-Eval-Print-Loop
    (defn repl
    []
    (let [input (read)
    result (eval input)]
    (println result)
    (recur)))

    View Slide

  10. Read Eval Print Loop (REPL)
    REPL Prompt:
    "=> (+ 1 2)
    "=> 3
    "=>
    REPL client REPL server
    Operation: “eval”
    Code: (+ 1 2)

    View Slide

  11. Read Eval Print Loop (REPL)
    REPL client REPL server
    Operation: “eval”
    Result: 3
    REPL Prompt:
    "=> (+ 1 2)
    "=> 3
    "=>

    View Slide

  12. nREPL Middleware
    nREPL
    CIDER
    nREPL
    CIDER
    Middleware

    View Slide

  13. cider-nrepl
    •CIDER uses cider-nrepl middleware
    •A collection of nREPL middleware
    • complete
    • info
    • debug
    • More. . .

    View Slide

  14. cider-nrepl
    Demo
    Client-server messaging

    View Slide

  15. Form based coords
    Coor: (3 1 1 1)
    (defn math-operations
    [n1 n2]
    (let [addition (+ n1 n2)
    subtraction (- n1 n2)
    exp (power n1 n2)]
    {:addition addition
    :subtraction subtraction
    :exp exp}))

    View Slide

  16. Coor: (3 1 1 1)
    (defn math-operations
    [n1 n2]
    (let [addition (+ n1 n2)
    subtraction (- n1 n2)
    exp (power n1 n2)]
    {:addition addition
    :subtraction subtraction
    :exp exp}))
    0 1
    2
    3
    Form based coords

    View Slide

  17. Coor: (3 1 1 1)
    (defn math-operations
    [n1 n2]
    (let [addition (+ n1 n2)
    subtraction (- n1 n2)
    exp (power n1 n2)]
    {:addition addition
    :subtraction subtraction
    :exp exp}))
    0
    1
    2
    Form based coords

    View Slide

  18. Coor: (3 1 1 1)
    (defn math-operations
    [n1 n2]
    (let [addition (+ n1 n2)
    subtraction (- n1 n2)
    exp (power n1 n2)]
    {:addition addition
    :subtraction subtraction
    :exp exp}))
    0 1
    2 3
    4 5
    Form based coords

    View Slide

  19. Coor: (3 1 1 1)
    (defn math-operations
    [n1 n2]
    (let [addition (+ n1 n2)
    subtraction (- n1 n2)
    exp (power n1 n2)]
    {:addition addition
    :subtraction subtraction
    :exp exp}))
    0 1 2
    Form based coords

    View Slide

  20. That’s why you should use Emacs!

    View Slide

  21. 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

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. How does it work?
    • Simple eval
    • Reader macro: #dbg
    • cider.nrepl.middleware.util.instrument/walk-indexed
    • Assigns coordinates to forms recursively

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. 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

    View Slide

  30. Clojure debugger
    f (x) = y
    Debugger (code) = debuggable code

    View Slide

  31. Clojure debugger
    f (x) = y
    Debugger (code) = debuggable code
    Clojure Clojure

    View Slide

  32. GDB - GNU Debugger
    f (p, q, r) = y
    Debugger (code, os, processor) = debuggable code
    C Assembly

    View Slide

  33. Limitations
    • Cannot instrument bytecode
    • Cannot go back in time
    • Cannot replay

    View Slide

  34. cider-debug
    • Debug.clj: 655 lines
    • Instrument.clj: 372 lines
    • The code is very readable!

    View Slide

  35. cider-debug
    • Bozhidar Batsov
    • Artur Malabara
    • Vitalie Spinu
    • Chris Perkins
    • Neil Okamoto
    • Michael Griffiths
    • Lars Anderson
    • Dominic Monroe
    Thank you!

    View Slide

  36. Lisp is similar to Chess.
    It will take only a day to learn the rules.
    But years to master !
    - Harold Abelson

    View Slide