/clj-debugger

The missing tool

Primary LanguageClojureEclipse Public License 1.0EPL-1.0

Debugger

Build Status

Clojars Project

The missing tools in the Clojure ecosystem.

Usage

(use 'debugger.core)

(break (some-fn))

(break-catch (some-raising-fn))

Breakpoints

user=> (dotimes [n 5] (debugger.core-test/foo n))

Break from: /Users/razum2um/Code/debugger/src/debugger/core_test.clj:12 (type "(help)" for help)

   17:         e (fn [] nil)
   18:         x "world"
   19:         y '(8 9)
   20:         z (Object.)
=> 21:         ret (break (inc 42))]
   22:     (println "Exit foo with" ret)))

Interactive help

debugger.core-test/qux:31=> (h)

 (h)    (help)          prints this help
        (wtf)           prints short code of breakpointed function
        (wtf??)         prints full code of breakpointed function
 (l)    (locals)        prints locals
 (c)    (continue)      continues execution, preserves the result and will break here again
        (skip 3)        skips next 3 breakpoints in this place
 (q)    (quit)          or type Ctrl-D to exit break-repl, pass last result further, will never break here anymore

                        use (debugger.core/reset-skips!) if breaks are skipped
                        you can also access locals directly and build sexp with them
                        any last execution result (but `nil`) before exit will be passed further
                        if last result is `nil` execution will continue normally

nil

Locals access

debugger.core-test/foo:21=> (l)
{x "world",
 a [1 2],
 y (8 9),
 args nil,
 e #<core_test$foo$e__4735 debugger.core_test$foo$e__4735@6735cbba>,
 z #<Object java.lang.Object@2094643d>,
 h {:k "v"},
 b #{4 3},
 d nil}
nil
debugger.core-test/foo:21=> z
#<Object java.lang.Object@3dc76ae9>

Return value control

Use any non-nil value to fake inner result:

user=> (debugger.core-test/foo)

Break from: /Users/razum2um/Code/debugger/src/debugger/core_test.clj:12 (type "(help)" for help)

   17:         e (fn [] nil)
   18:         x "world"
   19:         y '(8 9)
   20:         z (Object.)
=> 21:         ret (break (inc 42))]
   22:     (println "Exit foo with" ret)))

debugger.core-test/foo:21=> 1                     ;; inner (inc 42) won't be called
1
debugger.core-test/foo:21=> Exit foo with 1       ;; used result from REPL
nil

Code expection

debugger.core-test/foo:21=> (whereami)

   12: (defn foo [& args]
   13:   (let [a [1 2]
   14:         b #{3 4}
   15:         h {:k "v"}
   16:         d nil
   17:         e (fn [] nil)
   18:         x "world"
   19:         y '(8 9)
   20:         z (Object.)
=> 21:         ret (break (inc 42))]
   22:     (println "Exit foo with" ret)))

nil

Stack expection

debugger.core-test/foo:21=> (wtf??)

  [0]                      core_test.clj:21     debugger.core-test/foo
  [1]                        RestFn.java:408    clojure.lang.RestFn
  [2]   form-init1204084863726891616.clj:1      user/eval3682
  [3]                      Compiler.java:6703   clojure.lang.Compiler
  [4]                      Compiler.java:6666   clojure.lang.Compiler
  [5]                           core.clj:2927   clojure.core/eval
  [6]                           main.clj:239    clojure.main/repl
  [7]                           main.clj:239    clojure.main/repl
  [8]                           main.clj:257    clojure.main/repl
  [9]                           main.clj:257    clojure.main/repl
 [10]                        RestFn.java:1523   clojure.lang.RestFn
 [11]             interruptible_eval.clj:67     clojure.tools.nrepl.middleware.interruptible-eval/evaluate
 [12]                           AFn.java:152    clojure.lang.AFn
 [13]                           AFn.java:144    clojure.lang.AFn
 [14]                           core.clj:624    clojure.core/apply
 [15]                           core.clj:1862   clojure.core/with-bindings*
 [16]                        RestFn.java:425    clojure.lang.RestFn
 [17]             interruptible_eval.clj:51     clojure.tools.nrepl.middleware.interruptible-eval/evaluate
 [18]             interruptible_eval.clj:183    clojure.tools.nrepl.middleware.interruptible-eval/interruptible-eval
 [19]             interruptible_eval.clj:152    clojure.tools.nrepl.middleware.interruptible-eval/run-next
 [20]                           AFn.java:22     clojure.lang.AFn
 [21]            ThreadPoolExecutor.java:1142   java.util.concurrent.ThreadPoolExecutor
 [22]            ThreadPoolExecutor.java:617    java.util.concurrent.ThreadPoolExecutor/Worker
 [23]                        Thread.java:745    java.lang.Thread

nil

Break on next (break) or skip it

debugger.core-test/foo:21=> (c)
43
Exit foo with #<Object java.lang.Object@3dc76ae9>

Break from: /Users/razum2um/Code/debugger/src/debugger/core_test.clj:12 (type "(help)" for help)

   17:         e (fn [] nil)
   18:         x "world"
   19:         y '(8 9)
   20:         z (Object.)
=> 21:         ret (break (inc 42))]
   22:     (println "Exit foo with" ret)))

debugger.core-test/foo:21=> (skip 3)
nil
Exit foo with 43
Exit foo with 43
Exit foo with 43

Break from: /Users/razum2um/Code/debugger/src/debugger/core_test.clj:12 (type "(help)" for help)

   17:         e (fn [] nil)
   18:         x "world"
   19:         y '(8 9)
   20:         z (Object.)
=> 21:         ret (break (inc 42))]
   22:     (println "Exit foo with" ret)))

debugger.core-test/foo:21=> (c)
43
Exit foo with 43
nil

Breakpoints in threads

user> (debugger.core-test/in-thread)
nil
Registered breakpoint: debugger.core_test$foo$breakpoint__20396__auto____20431@4c18a68e. 
Type (breakpoints) to see a list of registered breakpoint, and (connect) to connect to one.
user> (breakpoints)
Breakpoints:
0) debugger.core_test$foo$breakpoint__20396__auto____20431@4c18a68e
nil
user> (connect)

Break from: /Users/maxim/tmp/clj-debugger/src/debugger/core_test.clj:8 (type "(help)" for help)

   13:         e (fn [] nil)
   14:         x "world"
   15:         y '(8 9)
   16:         z (Object.)
=> 17:         ret (break (inc 42))]
   18:     (println "Exit foo with" ret))) 

debugger.core-test/foo:17=> a
[1 2]
debugger.core-test/foo:17=> (q)
Quitting debugger...
nil
nilExit foo with 
[1 2]
user> 

Configuration

The following dynamic vars are configurable in debugger.config

Var Default Value Use
*break-outside-repl* true Break in non-repl threads, since 0.2.0. Default value was false in 0.1.x.
*code-context-lines* 5 Number of lines to include when showing source.
*locals-print-length* 10 Print-length for locals inside the debugger.
*skip-repl-if-last-quit-ago* 2 Number of seconds to wait after a debugger is quit before starting another one.

TODO

  • (step), (up), (down) stack manipulation

Debug intermediate expressions

user=> (-> 2 (+ 3) (* 4) dbg (/ 5))
(* (+ 2 3) 4) -> 20
4

Debug incoming arguments

user=> (dbg-defn f1 [arg1 arg2] (+ arg1 arg2))
#'user/f1

user=> (f1 2 3)
{arg1 2, arg2 3}
5

Acknowledgements

YourKit

YourKit has given an open source license for their profiler, greatly simplifying the profiling of ClojureScript performance.

YourKit supports open source projects with its full-featured Java Profiler. YourKit, LLC is the creator of YourKit Java Profiler and YourKit .NET Profiler, innovative and intelligent tools for profiling Java and .NET applications.

License

Copyright © 2014-2018 Vlad Bokov

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.