At Nubank we are investigating why devs complain about cider/calva debugger not working during their debugging, after some research and tests, me and @arthurfucher the major issue we found for us was that cider-nrepl seems to override/lose #dbg meta on functions when using load-file .
Not sure if that's a design decision but people (including us) heavily rely on load-file when coding, avoiding to eval specific functions, just the file and things will just work™, but with that limitation (or not having a option to allow that), makes debugging experience really unfeasible most of the cases for complex codebases, WDYT? Do you see any way to improve/workaround that?
When I use the CIDER debugger and I'm seeing an inline value printed, can I get it to work together with cider-pprint?
as in, cider-pprint-eval-last-sexpr or so?
o wait, it even says it at the bottom: cider-inspect-last-result
which doesn't work for me. it just takes a very long time and nothing happens
timed out it says
Let's move this discussion to #cider please, shall we? We are far away from the original topic now.
yep.
@bozhidar @alexyakushev just created https://github.com/clojure-emacs/cider-nrepl/issues/946
The issue seams to be in how cider.nrepl.middleware.debug/maybe-debug works. It reads the code with a fake reader to determine if there is a #dbg tag.
In the single form eval, the code is #dbg (defn some-code [...]...) which makes has-debug? true. But in the load file case the code is :
(try (clojure.lang.Compiler/load
(java.io.StringReader.
((clojure.core/deref (clojure.core/deref (var nrepl.middleware.load-file/file-contents)))
(quote ["... CODE_FILE_PATH ..." G__23022]))) "... CODE_FILE_PATH ..." "... CODE_FILE_PATH ...")
(finally (clojure.core/swap! (clojure.core/deref (var nrepl.middleware.load-file/file-contents)) clojure.core/dissoc (quote ["... CODE_FILE_PATH ..." G__23022]))))
which makes has-debug? false because that code doesn't have a #dbg tagthose FlowStorm screen captures should make it more clear
not sure about a clean way to fix this, but this fake-reader trick needs to be applied to the file code reading somewhere before eval happens so we can add the :eval cider.nrepl.middleware.debug/instrument-and-eval to the message
Oh that looks like a really good explanation, thank you a lot @jpmonettas that should help maintainers
I just went and recorded what I did in case someone finds it useful https://www.youtube.com/watch?v=sHGnb70xosk
I've only watched the first minute and thank you for showing how frigging easy it is to use the CIDER debugger
(going to watch the rest now)
haha thanks @borkdude. If you learn to use the cider-debugger, you learn for free to use the elisp debugger. They are almost the same, with the same keybindings. Just C-u C-M-x to "eval instrumented", and then when the function run it should show the commands (n for next, etc). Then re-eval it to get rid of the instrumentation.
Although now I think I showed something different on that video, which is to write the #dbg key. I never used it like that tbh
yes, I find the #dbg much easier since I always forget this instrument keybinding
It's still good to know about #dbg because it doesn't have to be written at top level, but anywhere in the form.
I'll be using #dbg . last time I used the cider debugger was in 2018 and having to look up a keybinding is enough friction for me to just not do it, but I can remember #dbg. It's stupid how these things work ;)
@jpmonettas This is yet another very impressive demo. I really must overcome the inertia to get into Flowstorm.
yes, I agree. impressive!
> I’ll be using #dbg . last time I used the cider debugger was in 2018 and having to look up a keybinding is enough friction for me to just not do it, but I can remember #dbg. It’s stupid how these things work 😉
For me it was easy to remember the keybinding as it’s the same in edebug and SLIME. In general most core keybindings were lifted from elisp-mode and SLIME, but I guess for people who don’t use them much they might seem pretty random.
@jpmonettas Can you add your findings to a GH ticket so we can discuss our options for a solution there? It’s nice to see someone willing to help with the maintenance of the debugger!
Is it when functions are instrumented in the repl, or does literal #dbg meta in the file also get nuked?
I believe it's the instrumentation indeed
So then it’s not specifically about load-file, but rather that the functions get evaluated at all, I am thinking.
I mean, when using load-file the function is reevaluated and it's like it had no dbg inside it, it just ignores it
If I add a println to the function and use load-file the function is reevaluated and the println starts to work, so I wonder why #dbg doesn't work
@pez is related to the behavior that is described also in the Calva docs (https://calva.io/debugger/#loading-the-file-and-eval-on-save)
So, then you have #dbg in the function definition and gets removed?
That doesn’t seem right to me.
gets removed == unset, the code is there yet, it was just ignored
yeah, we thought the same @pez but maybe it was a design decision, but sounds really weird to the point people think that debugger doesn't work hehe
eval and load-file use separate code paths. The latter just feeds the file contents to Compiler/load , so it has no special treatment for #dbg and similar reader macros.
Ah, that makes sense.
This has been a design decision from early nREPL days (`load-file` existed in nREPL from day one, I believe)
I see @alexyakushev, thanks for explaining, wouldn't makes sense to make that code be instrumented as well?
Then it should work re-evaluating the whole file instead of using load-file.
A fix would have to introduce an alternative cider/load-file that feeds forms into the evaluator one by one, and teaching CIDER and others to use this op when it is present. It is feasible, just needs a bit of work
[side remark] damn, I should start using the debugger some time again. I'm sure it'll beat my manual println approach 😆
In fact, there's nothing that Compiler/load does that we can't do ourselves on form-by-form basis.
@borkdude exactly, the debugger is so powerful IMO, but thousands of devs at nubank still use #nu/tap (a colored println facepalm), that's what we trying to improve
It sometimes is better than inline defs, @borkdude, but most often not.
Let me take a look at it, I'm annoyed by C-c C-k ignoring dbgs too
@borkdude especially when you wanna navigate stacktrace, see complex values in nested stuff and lots more
btw if you use load-file or load-string it seems data readers do still work.
(set! *data-readers* {'foo (fn [x] [::foo x])})
user=> #foo 1
[:user/foo 1]
user=> (load-string "#foo 1")
[:user/foo 1]
user=> (load-file "/tmp/foo.clj")
[:user/foo 1]
I just don't want to change nREPL's original load-file behavior just in case, but a new custom cider-nrepl op is a fair game.
oh yes, inline defs I use a lot.
and for users like me, that has feature like "load file on save" is really annoying 😅
what about manually using load-file though instead of the cider OP?
> btw if you use load-file or load-string it seems data readers do still work.
#dbg data reader doesn't do anything by itself, it is a special eval "mode" that does the instrumentation, #dbg is only a tag for it
oh right C-c C-k, I use that a lot
one last side remark question: have you guys tried flowstorm and how does it compare to cider debugger?
(I have only briefly tried it but not enough to get it working)
flowstorm is awesome. Works in ClojureScript too.
But I tend to just use inline defs, often with the help of Snitch.
I tested flowstorm, it's awesome indeed, but I see as a different extra tool, would be really nice if things work out of the box for editors too
I haven't tried it, but I'm always apprehensive about using such tools that unpredictably increase computational and memory footprint of runtime stuff. At least, with CIDER debugger, I know it won't blow up my stuff (and I still know better not to instrument hot multithread-called functions)
ok thanks, I'll give the cider debugger another shot
If you like it enough, maybe Babashka nrepl gets support for it.. 😃
CIDER debugger is good for one-off debugging, but I still use inline defs too.
I just tried the "tutorial" of FlowStorm, and seems that it is more to record a flow and understand things.. than for a debugger that I'm really evaluating the lines and going with the debugger
I actually want to go another direction with babashka.nrep. I want to make stuff more compatible with the CIDER nREPL stuff so it just works from source
Sweet. I didn’t wish for any particular direction to take it, but that sounds super awesomest.
> If you like it enough, maybe Babashka nrepl gets support for it.. 😃 I don't doubt Michiel's capabilities one second, but CIDER debugger is really quite a complex thing. Nobody but @malabarba ever understood it. Don't know if it's even worth the complexity of trying to reproduce it.
Of course, I want it to work in Joyride too, so then it will be some special thing, I guess.
@alexyakushev yeah, that's something interesting to think about: most experienced devs know about those inline defs hacks and things like that, but IMO they are just workarounds of a debugger experience. Most nubank engs that come from other languages feel the need of a debugger like java, so I believe offer the same capabilities/xp would be awesome
I'm not surprised that it's a complex thing, but what I would expect is that it's "just" a tools.analyzer like analyzer + pass + emit
To me the debugger is a workaround when inline defs get a bit involved with conditional logic.
It doesn't use tools.analyzer, but you can probably implement it using it and that could simplify things
Stopping the program to debug it is a bit uncivilized.
at this point we're ok going off topic a bit? @alexyakushev does the CIDER debugger support 1.12?
I mean the method value syntax etc
@ericdallo Nah, I definitely agree the OT debugger bug should be fixed and we will do it. Just saying that even as a CIDER debugger ambassador, I still use defs quite often.
for clerk I migrated away from tools.analyzer in favor of our own analyzer that does support new features. tools.analyzer lags a bit behind
Yes, like I said, it doesn't rely on tools.analyzer, so it works with all clojure versions
@alexyakushev got it, thank you so much!
I realize now that most people in this thread are coming to the conj. Are you coming too @alexyakushev?
Hah, I would love to, but I'm stuck in this little thing called "war" and "military" for the past few years 😅
I could have guessed. Hope it will be over soon. Are you serving?
Yeah, since 2022
Thank you for protecting your country but also Europe!
> I don’t doubt Michiel’s capabilities one second, but CIDER debugger is really quite a complex thing. Nobody but @malabarba ever understood it. Don’t know if it’s even worth the complexity of trying to reproduce it. And Artur became a Ruby dev in the end, like me. 😅 We used to work in the same company for quite a while.
@bozhidar weren't you already doing Ruby before Clojure?
Yep. In a way I’ve always been doing Ruby in the past 15 years, at least as far as jobs go. (I’m mostly a manager these day on my job, though). Clojure was always a passion that I didn’t manage to convert into a career.
> This has been a design decision from early nREPL days (`load-file` existed in nREPL from day one, I believe) Indeed. I believe the reasoning behind this was a combination of efficiency and easy of getting the source metadata when evaluating an entire file. I wasn’t part of the original design discussions, but I discovered many things while working on CIDER and afterwards when I took over the maintenance of nREPL.
Btw, I’m not sure how many people know this, but the debugger in its current version is heavily inspired by edebug itself. The first version of the debugger used the Java debugger APIs, but Artur realized the approach taken by edebug was a lot simpler and almost independent from the underlying platform.
That's really interesting to know, especially that used java debugger API
@bozhidar care to explain more - what does edebug do?
It's a Emacs/elisp debugger @borkdude, really nice
There was a nice talk on the subject from the last Clojure event I ever attended. I planned to do an expanded version later in 2020, but it wasn’t meant to be. 😅 I’ll dig up the talk in a bit.
nice!
Here it is https://drive.google.com/file/d/1YhnPBJOXbUzXfVOEojICFTwhCt_3u6Ss/view It’s from a contributor to the debugger, but I helped a bit with the content and I think it’s probably the deepest dive into the debugger’s internals we ever did.
cool, where was this presented?
It was either at IN/Clojure 2020 or a Clojure meetup that took place in Pune right after the conference.
The debugger hasn’t changed much since then, at least as far as main design decisions go.
right
nice presentation!
> I haven't tried it, but I'm always apprehensive about using such tools that unpredictably increase computational and memory footprint of runtime stuff just to clarify, it shouldn't be unpredictable, you can control instrumentation and recording with a couple of buttons and you can also establish heap limits for auto stop recording
> Nobody but @malabarba ever understood it. Don't know if it's even worth the complexity of trying to reproduce it. I can probably help with cider-debugger stuff, since vanilla FlowStorm is basically the same code as cider-debugger but recording instead of stopping and waiting for a "next command"