-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor breakpoints system #405
Conversation
@vspinu Impressive work! @Malabarba please, take a look at the changes. |
@@ -253,6 +253,15 @@ | |||
;; Don't use `postwalk` because it destroys previous metadata. | |||
(walk-indexed #(tag-form %2 breakfunction) form)) | |||
|
|||
(defn print-form [form & [expand? meta?]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add some docstring here.
@@ -137,8 +135,7 @@ | |||
(let [{extras ::extras, | |||
[_ orig] ::original-form, | |||
bf ::breakfunction} (meta form)] | |||
(when verbose-debug | |||
(println "[DBG]" (not (not bf)) extras (or orig form))) | |||
;; (println "[DBG]" (not (not bf)) extras (or orig form)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't leave the commented out code here.
;; Top-level sexps are not debugged, just returned. | ||
(is (= (eval `(let [~'x 10] (d/breakpoint d/*locals* {:coor []} nil))) | ||
'{x 10}))))) | ||
;; (deftest breakpoint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving this commented out was probably not intended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Good catch.
Thank you so much for digging into this @vspinu! I hit this all the time, but I haven't had to time to do anything about it. |
One thing that I am not very clear about is why |
For the record. Two more factors contribute to the size of the method - original forms that are inlined into the final code and locals ( The original forms could be saved in a separate atom with a simple macro trick, but locals really need to be inlined. So I am "fixing" the "locals issue" by simply recompiling without locals when "method code too large!" is caught. You won't be able to use commands that depend on locals ( |
do you think it would be possible for you to put together a markdown document giving a general overview of how the debugger works? I'd imagine no one knows more about it that you right now. |
Thanks indeed @vspinu . I'll try to have a look as soon as possible. |
I am not currently in the position to do that as I don't have a good understanding of the emacs-debuger communication. Some design choices are still not clear to me (for instance why compulsory initialization?, why blocking debugger?). This PR mostly concerns with the breakpoint logic. Only @Malabarba could make such a write-up but I am not sure it would be worth it at this stage. The debugger is still a moving target and there are already quite a few documentation notes in the code. |
For what it's worth, I have been trying out this change and it works perfectly, as far as I can tell. I have tried out all the debug operations, and have not seen any regressions. I have also tried debugging functions that used to fail with "Method code too large", and they can be debugged now. Yay! 👍 |
8430601
to
9773df2
Compare
WRT a markdown guide: there's an issue on this repo (or maybe on the cider repo) about supporting cljs in the debugger. On that issue I made a very short guise on how the debugger works. |
[a b] | ||
(= (seq a) (seq b))) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace
(let [br (::breakfunction (meta f)) | ||
f1 (m/strip-meta f keys)] | ||
(if br | ||
(vary-meta f1 assoc :instrumented (name (:name (meta br)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use (name (:name (meta br)))
instead of just br
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we should use ::instrumented
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Final form has only :instrumented
string tag in it. Keeping full function around leads to huge clutter when you print the form with meta and generally makes it much harder to understand what is what. Each meta key also adds 5 extra bytecode ops to the final object size on every breakpoint invocation. After all it's just a tag and should be a string or a symbol, especially when you send it to emacs.
The non-qualified :instruented
is intentional. I am not bound to it but it looks to me that qualification is not warranted in this context. The:instrumented
tag is more generic than :cider.nrepl.middleware.util.instrument/instrumented
. Different middleware or different implementation of instrumentation should be able to use it without sending that sausage around. It also rather annoying in printouts as doesn't bear extra informational content there. On emacs side it feels like spillage of internals as well. See associated changes.
(->> (pr-very-short out#) | ||
(assoc d/*extras* :status :enlighten :debug-value) | ||
d/debugger-send)) | ||
(->> (assoc (:msg_ ~'META_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the _ in :msg_ intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo indeed.
Besides a few comments, I think it looks good. And it must have taken a lot of work. Thanks a lot for the effort. |
Also what's that META_ symbol you are using? Is that some syntax I never heard of? |
@vspinu I'll merge this once you address @Malabarba's comments. And you might also want to combine some of the commits together, but that's not particularly important to me. |
It's just an anaphoric local variable. It needs to be accessed from all breakpoint macros and as clojure autoqualifies symbols in macros you need to inhibit the auto-qualification by quoting with BTW, that cleaning of local variables will break if users use locals with |
Could be improved by removing symbols with |
Sounds good. Also add some explanations about this in the code itself. |
@vspinu ping :-) |
Yes. I will be back on it this weekend. Got a rather tight two weeks. |
OK. You'll also have to rebase this on top of the current |
I don't understand the changes, but the new expansion seems much more pleasing than the old 😄. |
- Common metadata is now let bound once at the top of the form. - Macros expand into parsimonious 'break' and `apply-instrumented-maybe` functions. This bring the size of the expanded code to one call per breakpoint. - Only `coor` is hooked in local `extras` metadata - Remove verbose-debug. It's much less useful now as there are more points of interests. It's easier to insert `print-form` whenever needed.
- save original forms within local map - recompile with no &env bindings when "code too large!" method detected
Reasons: - Each meta keywors shows 5 java ops in bytecode - Inspection of code with functions in metadata is a nightmare
71cdb34
to
650d8af
Compare
- Rename META_ into META__ and document - Only strip local variables with __ in their names - Use :cider/instrumented as instrumentation tag - Document defonces - Move *skip-breaks* reset to wrap-debug to make the intention clearer - Update enlighten to nested META_
- Rename resolve-special -> special-sym-meta - Don't check for special-symbol? explicitly but rely on special-sym-meta instead - Remove unnecessary duplicated computation in info-clj
I am done with addressing all comments and rebasing & squashing it. Added one more "side" commit relating to #410. One cell in travis matrix fails for non-sider reason. |
👍 |
This addresses clojure-emacs/cider#1699. The core of the change is to remove most of the global dynamic vars and rely on lexical binding on top of the main instrumented form for all the common metadata. Currently same metadata is replicated again and again for each sub-form. Another major change is to reduce the macros machinery to two basic function calls
break
andapply-instrumented-maybe
.This pretty much reaches theoretical minimum for the size of generated clojure code, one call per instrumented form. Pushing this further would require understanding lower level details of what and how eats into the java's 64KB method limit space.
The following code
now expands into:
instead of the current: