philoskim / debux Goto Github PK
View Code? Open in Web Editor NEWA trace-based debugging library for Clojure and ClojureScript.
A trace-based debugging library for Clojure and ClojureScript.
More info:
When debugging, I frequently want to know the evaluation of the individual components of a boolean expression. It would be cool if Debux could support and/or as special macros.
Version: [philoskim/debux "0.4.11"]
Here's the simplest example:
(dbgn
(defn glug []
))
(defmacro bar [data]
`(do
~(get data ::key glug)))
(defn -main []
(bar {}))
The error:
Exception in thread "main" java.lang.ExceptionInInitializerError, compiling:(core.clj:13:1)
at clojure.lang.Compiler$DefExpr.eval(Compiler.java:470)
at clojure.lang.Compiler.eval(Compiler.java:7067)
at clojure.lang.Compiler.load(Compiler.java:7514)
at clojure.lang.RT.loadResourceScript(RT.java:379)
at clojure.lang.RT.loadResourceScript(RT.java:370)
at clojure.lang.RT.load(RT.java:460)
at clojure.lang.RT.load(RT.java:426)
at clojure.core$load$fn__6548.invoke(core.clj:6046)
at clojure.core$load.invokeStatic(core.clj:6045)
at clojure.core$load.doInvoke(core.clj:6029)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5848)
at clojure.core$load_one.invoke(core.clj:5843)
at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
at clojure.core$load_lib.invokeStatic(core.clj:5887)
at clojure.core$load_lib.doInvoke(core.clj:5868)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5925)
at clojure.core$load_libs.doInvoke(core.clj:5909)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5947)
at clojure.core$require.doInvoke(core.clj:5947)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval3111$fn__3115.invoke(form-init1748171861957467243.clj:1)
at user$eval3111.invokeStatic(form-init1748171861957467243.clj:1)
at user$eval3111.invoke(form-init1748171861957467243.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:7062)
at clojure.lang.Compiler.eval(Compiler.java:7052)
at clojure.lang.Compiler.load(Compiler.java:7514)
at clojure.lang.Compiler.loadFile(Compiler.java:7452)
at clojure.main$load_script.invokeStatic(main.clj:278)
at clojure.main$init_opt.invokeStatic(main.clj:280)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invokeStatic(main.clj:311)
at clojure.main$null_opt.invokeStatic(main.clj:345)
at clojure.main$null_opt.invoke(main.clj:342)
at clojure.main$main.invokeStatic(main.clj:424)
at clojure.main$main.doInvoke(main.clj:387)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:702)
at clojure.main.main(main.java:37)
Caused by: java.lang.ExceptionInInitializerError
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at clojure.lang.Compiler$ObjExpr.eval(Compiler.java:4989)
at clojure.lang.Compiler$DefExpr.eval(Compiler.java:457)
... 42 more
Caused by: java.lang.IllegalArgumentException: No matching ctor found for class debux_meta.core$eval6680$glug__6681
at clojure.lang.Reflector.invokeConstructor(Reflector.java:163)
at clojure.lang.LispReader$EvalReader.invoke(LispReader.java:1303)
at clojure.lang.LispReader$DispatchReader.invoke(LispReader.java:843)
at clojure.lang.LispReader.read(LispReader.java:275)
at clojure.lang.LispReader.read(LispReader.java:206)
at clojure.lang.LispReader.read(LispReader.java:195)
at clojure.lang.RT.readString(RT.java:1871)
at clojure.lang.RT.readString(RT.java:1866)
at debux_meta.core$_main.<clinit>(core.clj:13)
... 49 more
I tried following things in an plain vanilla project for verification. Have I missed anything?
Try and exceptions
$ lein repl
user=> (use 'debux.core)
CompilerException java.lang.Exception: No namespace: cljs.core found, compiling:(debux/cs/macro_types.cljc:5:1)
Environments and dependencies
Leiningen: 2.8.1
JVM: 1.8, 64bit
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.854"]
[philoskim/debux "0.4.6"]]
Version [philoskim/debux "0.4.10"]
Oddly, it appears to be this particular combination which really upsets debux. The fn doesn't need to be called at all; an exception is thrown when compiling it:
(dbgn
(defn bar []
(letfn [(foo []
(try
nil
(catch Exception e
nil)))]
(loop [i 0]
(recur (inc i))))))
The exception:
Exception in thread "main" java.lang.NullPointerException, compiling:(debux_meta/core.clj:5:1)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:7010)
at clojure.lang.Compiler.analyze(Compiler.java:6773)
at clojure.lang.Compiler.analyze(Compiler.java:6729)
at clojure.lang.Compiler$IfExpr$Parser.parse(Compiler.java:2822)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:7003)
at clojure.lang.Compiler.analyze(Compiler.java:6773)
at clojure.lang.Compiler.analyze(Compiler.java:6729)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6100)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5460)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4022)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:7001)
at clojure.lang.Compiler.analyze(Compiler.java:6773)
at clojure.lang.Compiler.eval(Compiler.java:7059)
at clojure.lang.Compiler.load(Compiler.java:7514)
at clojure.lang.RT.loadResourceScript(RT.java:379)
at clojure.lang.RT.loadResourceScript(RT.java:370)
at clojure.lang.RT.load(RT.java:460)
at clojure.lang.RT.load(RT.java:426)
at clojure.core$load$fn__6548.invoke(core.clj:6046)
at clojure.core$load.invokeStatic(core.clj:6045)
at clojure.core$load.doInvoke(core.clj:6029)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5848)
at clojure.core$load_one.invoke(core.clj:5843)
at clojure.core$load_lib$fn__6493.invoke(core.clj:5888)
at clojure.core$load_lib.invokeStatic(core.clj:5887)
at clojure.core$load_lib.doInvoke(core.clj:5868)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5925)
at clojure.core$load_libs.doInvoke(core.clj:5909)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5947)
at clojure.core$require.doInvoke(core.clj:5947)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval3111$fn__3115.invoke(form-init8000152408779894085.clj:1)
at user$eval3111.invokeStatic(form-init8000152408779894085.clj:1)
at user$eval3111.invoke(form-init8000152408779894085.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:7062)
at clojure.lang.Compiler.eval(Compiler.java:7052)
at clojure.lang.Compiler.load(Compiler.java:7514)
at clojure.lang.Compiler.loadFile(Compiler.java:7452)
at clojure.main$load_script.invokeStatic(main.clj:278)
at clojure.main$init_opt.invokeStatic(main.clj:280)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invokeStatic(main.clj:311)
at clojure.main$null_opt.invokeStatic(main.clj:345)
at clojure.main$null_opt.invoke(main.clj:342)
at clojure.main$main.invokeStatic(main.clj:424)
at clojure.main$main.doInvoke(main.clj:387)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:702)
at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
at clojure.lang.Namespace.find(Namespace.java:188)
at clojure.core$find_ns.invokeStatic(core.clj:4096)
at clojure.core$the_ns.invokeStatic(core.clj:4126)
at clojure.core$ns_name.invokeStatic(core.clj:4130)
at clojure.core$ns_name.invoke(core.clj:4130)
at debux.common.util$var__GT_symbol.invokeStatic(util.cljc:112)
at debux.common.util$var__GT_symbol.invoke(util.cljc:110)
at debux.common.util$ns_symbol_for_clj.invokeStatic(util.cljc:119)
at debux.common.util$ns_symbol_for_clj.invoke(util.cljc:117)
at debux.common.util$ns_symbol.invokeStatic(util.cljc:140)
at debux.common.util$ns_symbol.doInvoke(util.cljc:136)
at clojure.lang.RestFn.invoke(RestFn.java:423)
at debux.common.skip$insert_o_skip_for_recur.invokeStatic(skip.cljc:190)
at debux.common.skip$insert_o_skip_for_recur.doInvoke(skip.cljc:180)
at clojure.lang.RestFn.invoke(RestFn.java:423)
at debux.dbgn$dbgn.invokeStatic(dbgn.clj:298)
at debux.dbgn$dbgn.doInvoke(dbgn.clj:286)
at clojure.lang.RestFn.applyTo(RestFn.java:146)
at clojure.lang.Var.applyTo(Var.java:702)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6912)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6989)
... 53 more
Hey there! As per the docs, defn
allows for attribute maps before the arities, as well as after. debux ends up wrapping these guys in debux.dbgn/d
, which causes Clojure to choke. Here's the simplest test case showing the issue:
(dbgn
(defn identity'
{:private true}
[x]
x))
When trying to hook dbgn
into an existing macro, which is taking advantage of these attribute maps, I've hit a wall. Hopefully this is something you're willing to support!
I have a plain clojure project (no clojurescript), when I use debux and require core namespace in REPL i get exception
CompilerException java.io.FileNotFoundException: Could not locate cljs/analyzer/api__init.class or cljs/analyzer/api.clj on classpath., compiling:(debux/common/util.cljc)
Probably this caused by init of [cljs.analyzer.api :as ana]
which is not in classpath.
Is there a way to disable clojurescript and make it work for clojure only?
P.S. Adding clojurescript deps looks like short term fix, but would be good to avoid unnecessary deps
Hello.
It is seems that debux will not run on JVM 10.
I want to use debux 0.5.0 and my deps.edn look like this:
:xb {:jvm-opts ["--add-modules" "java.xml.bind"]}
:debug-tools {:extra-deps {vvvvalvalval/scope-capture {:mvn/version "0.3.1"}
philoskim/debux {:mvn/version "0.5.0"}
spyscope {:mvn/version "0.1.7-SNAPSHOT"}
com.bhauman/spell-spec {:mvn/version "0.1.1"}}
I run REPL:
clojure -R:debug-tools:xb -r
nREPL 0.4.5
Clojure 1.10.0-alpha7
Java HotSpot(TM) 64-Bit Server VM 10.0.2+13
user=> (use 'debux.core)
Syntax error compiling at (cljs/util.cljc:1:1).
Cause: javax.xml.bind.DatatypeConverter
user=> *e
#error {
:cause "javax.xml.bind.DatatypeConverter"
:via
[{:type clojure.lang.Compiler$CompilerException
:message "Syntax error compiling at (cljs/util.cljc:1:1)."
:data #:clojure.error{:line 1, :column 1, :phase :compile, :source "cljs/util.cljc"}
:at [clojure.lang.Compiler load "Compiler.java" 7624]}
{:type java.lang.ClassNotFoundException
:message "javax.xml.bind.DatatypeConverter"
:at [java.net.URLClassLoader findClass "URLClassLoader.java" 466]}]
:trace
[[java.net.URLClassLoader findClass "URLClassLoader.java" 466]
[clojure.lang.DynamicClassLoader findClass "DynamicClassLoader.java" 69]
[java.lang.ClassLoader loadClass "ClassLoader.java" 566]
[clojure.lang.DynamicClassLoader loadClass "DynamicClassLoader.java" 77]
[java.lang.ClassLoader loadClass "ClassLoader.java" 499]
[java.lang.Class forName0 "Class.java" -2]
[java.lang.Class forName "Class.java" 374]
[clojure.lang.RT classForName "RT.java" 2204]
[clojure.lang.RT classForNameNonLoading "RT.java" 2217]
[cljs.util$eval182$loading__6539__auto____183 invoke "util.cljc" 9]
[cljs.util$eval182 invokeStatic "util.cljc" 9]
[cljs.util$eval182 invoke "util.cljc" 9]
[clojure.lang.Compiler eval "Compiler.java" 7160]
[clojure.lang.Compiler eval "Compiler.java" 7149]
[clojure.lang.Compiler load "Compiler.java" 7612]
[clojure.lang.RT loadResourceScript "RT.java" 379]
[clojure.lang.RT loadResourceScript "RT.java" 370]
[clojure.lang.RT load "RT.java" 460]
[clojure.lang.RT load "RT.java" 426]
[clojure.core$load$fn__6653 invoke "core.clj" 6071]
[clojure.core$load invokeStatic "core.clj" 6070]
[clojure.core$load doInvoke "core.clj" 6054]
[clojure.lang.RestFn invoke "RestFn.java" 408]
[clojure.core$load_one invokeStatic "core.clj" 5873]
[clojure.core$load_one invoke "core.clj" 5868]
[clojure.core$load_lib$fn__6598 invoke "core.clj" 5913]
[clojure.core$load_lib invokeStatic "core.clj" 5912]
[clojure.core$load_lib doInvoke "core.clj" 5893]
[clojure.lang.RestFn applyTo "RestFn.java" 142]
[clojure.core$apply invokeStatic "core.clj" 659]
[clojure.core$load_libs invokeStatic "core.clj" 5950]
[clojure.core$load_libs doInvoke "core.clj" 5934]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.core$apply invokeStatic "core.clj" 659]
[clojure.core$require invokeStatic "core.clj" 5972]
[clojure.core$require doInvoke "core.clj" 5972]
[clojure.lang.RestFn invoke "RestFn.java" 436]
....skipped.......
Version: [philoskim/debux "0.4.9"]
Here's the simplest test case:
(dbgn
(defn foo []
(if-let [user-json nil]
::then
::else)))
The error:
Exception in thread "main" clojure.lang.ExceptionInfo: Call to clojure.core/if-let did not conform to spec:
In: [3] val: (:debux-meta.core/else) fails at: [:args] predicate: (cat :bindings (and vector? :clojure.core.specs.alpha/binding) :then any? :else (? any?)), Extra input
#:clojure.spec.alpha{:problems [{:path [:args], :reason "Extra input", :pred (clojure.spec.alpha/cat :bindings (clojure.spec.alpha/and clojure.core/vector? :clojure.core.specs.alpha/binding) :then clojure.core/any? :else (clojure.spec.alpha/? clojure.core/any?)), :val (:debux-meta.core/else), :via [], :in [3]}], :spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x56554365 "clojure.spec.alpha$regex_spec_impl$reify__2436@56554365"], :value ([user-json nil] (debux.common.util/insert-blank-line) :debux-meta.core/then :debux-meta.core/else), :args ([user-json nil] (debux.common.util/insert-blank-line) :debux-meta.core/then :debux-meta.core/else)}, compiling:(debux_meta/core.clj:5:1)
EDIT: Looks like if-some
fails as well.
Hi there, firstly thanks heaps for creating this library, it is very useful. I've been looking at the output of dbg
and dbgn
and wondered if it would be possible for dbgn
to use the special-casing approach in dbg
where it wraps ->
, ->>
, comp
and let
?
I've taken a look through the code and I get the general approach, but wondered if there were some subtleties which meant that the approaches of dbg
and dbgn
wouldn't work together?
This looks fantastic, thanks!
But, could you add some tips (to the examples or readme) of best practices to pull in this code only in dev builds?
As previously communicated, we have now put out that release of re-frame-10x
which owes much to debux. Many Thanks.
See: https://groups.google.com/forum/#!topic/clojurescript/Cw0nXgRuPB4
I'll close this now. Just wanted to let you know.
First, thank you for this wonderful library. I use it constantly.
I have some records for which I've defined custom printing functions, and I'd like to be able to see this version in my debux output. I'd like the ability to prevent certain objects from being turned into maps by the debux printer.
I should add that I'd like this to work even when the objects are nested inside other objects. I tried the :print
option, and while I think I could eventually get that to work, it would require a tree traversal to find all of the objects I want to print using my custom printer.
Hi, is there any way to avoid having the output include internal debux forms like wrapping (clojure.core/binding [debux.common.util/*indent-level* (clojure.core/inc debux.common.util/*indent-level*)] (clojure.core/reset! (:evals +debux-dbg-opts+) {})
(or extra do
blocks) on to submitted sub-forms, etc - and just returning the orignals? Thanks!
Hi,
Thanks for your super userful library!
Is it expected behavior that the ... :if nil ...
modifier will always evaluate to true, thereby causing the logging/breaking to effectively occur? I would expect that it should have similar behaviour as ... :if false ...
When compiling with shadow-cljs I get the following warning from the Google Closure Compiler:
------ WARNING #1 - :undeclared-var --------------------------------------------
Resource: debux/common/util.cljc:77:14
--------------------------------------------------------------------------------
74 | (boolean (:ns env)))
75 |
76 | (defn lazy-seq? [coll]
77 | (instance? clojure.lang.IPending coll))
--------------------^-----------------------------------------------------------
Use of undeclared Var debux.common.util/clojure
--------------------------------------------------------------------------------
78 |
79 | (defn replace-& [v]
80 | (walk/postwalk-replace {'& ''&} v))
81 |
--------------------------------------------------------------------------------
I'm not 100% sure, but the right approach would probably be to check against this, right? https://github.com/clojure/clojurescript/blob/75e4e528f84a38b7bb9741a498f153457182a057/src/main/cljs/cljs/core.cljs#L3412
I found a useful function in the Postmortem that provides the local variables of a function. It's really useful for trying to understand the local context of the function. Would you consider adding a feature to debux?
For example, something like this:
(let [i 10]
(* 2 (dbg (+ i 5) :locals ))
{:ns user, :line 1}
dbg: (+ 10 5) :locals {:i 10} =>
| 15
30
(dbgn :locals (defn stats [fname [height weight] ]
(let [i 10]
(println fname "is" height))))
(stats "Bob" [178 68])
| fname =>
| "Bob"
| height =>
| 178
Bob is 178
| (println fname "is" height) =>
| nil
| (let [i 10] (debux.common.util/insert-blank-line) (println fname "is" ... =>
| nil
nil
| :locals {name "Bob", height 178, weight 68, i 10}
A new option called :locals
provides any local vars.
In Postmortem there's a specific function called debug, see the link for an example. Hash-meta has played with some interesting ideas and its implementation does this:
{:locals {}, :result 23, :form (+ 3 (* 4 5))}
The only way I could figure out how to do this with debux at the moment was to specifically call the vars in a dbg such as (dbg [fname height weight i])
(count (range 200))
=> 200
(count (debux.dbgn/dbgn (range 200)))
=> 100
I think this is because of this line:
https://github.com/philoskim/debux/blob/master/src/debux/dbgn.clj#L218-L219
The limited result#
should probably be renamed to a different name so that the unchanged result can be passed through safely.
I've been thinking that it would be great to be able to get the debug results so that it is possible to show them in different UI's, similar to re-frame-debux, but without the re-frame dependency. Other use cases would be logging or piping into REBL, reveal or portal.
I've already started a prototype to play around with designs for the interface. If you are interested in the feature I'll fork and push my changes.
This is a very trivial request because it's solely about how the debug output looks. It may be solely a question of personal preference.
I have been trying out a few debugging tools like Hashp, Spyscope and Tupelo
What I notice with the output of debux, is that it's taking a lot more lines in the REPL.
user=> (take 20 (repeat (spyx :dbug (+ 1 2 3))))
:dbg (+ 1 2 3) => 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
In Debux:
user=> (take 20 (repeat (dbg (+ 1 2 3))))
{:ns user, :line 1}
dbg: (+ 1 2 3) =>
| 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
The value of the form (+ 1 2 3) is put on a new line and a | put ahead of it. I find this visually a bit confusing, I expect the output to be after the =>.
When you do it with debugn on something longer it becomes very spread out. This time using hashp (I had problems with Spyscope):
user=> (inc #p (* 2 #p (+ 3 #p (* 4 5))))
#p[user/eval9305:1] (* 4 5) => 20
#p[user/eval9305:1] (+ 3 (* 4 5)) => 23
#p[user/eval9305:1] (* 2 (+ 3 (* 4 5))) => 46
47
Then with debux:
;; Example 2: we want the value of each step
(dbgn (inc (* 2 (+ 3 (* 4 5)))))
{:ns user, :line 1}
dbgn: (inc (* 2 (+ 3 (* 4 5)))) =>
| (* 4 5) =>
| 20
| (+ 3 (* 4 5)) =>
| 23
| (* 2 (+ 3 (* 4 5))) =>
| 46
| (inc (* 2 (+ 3 (* 4 5)))) =>
| 47
47
I would like dbg and dbgn to put the value of the form after the arrow (=>) on the same line. So this would become:
(dbgn (inc (* 2 (+ 3 (* 4 5)))))
{:ns user, :line 1}
dbgn: (inc (* 2 (+ 3 (* 4 5)))) =>
| (* 4 5) => 20
| (+ 3 (* 4 5)) => 23
| (* 2 (+ 3 (* 4 5))) => 46
| (inc (* 2 (+ 3 (* 4 5)))) => 47
47
I find this view easier to understand the steps and see the output clearly.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.