Huh. I just solved it. The debug exception handler has to do something a
bit fancier, that's all:

1. Dig up the bytecode of the method executing the throw and copy it
somewhere.
2. Unwind by one call.
3. Take the bytecode in 1, alter it to wrap the throw site in
redl.core/break's macro expansion that captures locals, or something
similar, and replacing the offending throw with a direct punt to the debug
REPL.
4. Load a class with that as its only method.
5. Invoke that method, with the same arguments as the ...

Ah, crap. The arguments might have been locals-cleared by then, in the
caller, and by the time of the throw in the callee. Damn damn damn. It
would have to recurse farther up the stack and who knows where it would
stop? So even with pure functions this enhanced scheme still would only
sometimes work -- and with side-effecting ones you have side effects
happening twice.

I think locals clearing is simply incompatible with ever having full debug
info at an error site without anticipation of an error at that site, and
with anticipation you can put debug prints, logging, watchers, and suchlike
at the site.

The lesson for clojure developers being, strive for reproducibility! You
should already be using lots of pure functions and few side-effecting ones,
and your IDE should keep a REPL history that can be used to reproduce, and
perhaps to distill down to a small failing test, any kabooms you get while
experimenting in the REPL. And if the domain inherently is going to produce
hard-to-reproduce circumstances, invest proactively in aggressive and
detailed logging.



On Tue, May 28, 2013 at 12:24 PM, Cedric Greevey <cgree...@gmail.com> wrote:

> On Tue, May 28, 2013 at 9:36 AM, Lee Spector <lspec...@hampshire.edu>wrote:
>
>>
>> On May 28, 2013, at 8:43 AM, dgrnbrg wrote:
>> > There's not much you can do to retrieve the locals around an exception
>> with adding a Java Agent. REDL is able to get the locals around uses of
>> redl.core/break, since it's a macro and that's an ability of macros.
>>
>> In Common Lisp exceptions are handled in the contexts in which they
>> occur, so if you handle the exception by stopping execution and presenting
>> a REPL (which is the default) then the locals don't have to be "retrieved"
>> -- they're just still where they were, and where the REPL is too. And there
>> are functions that let you look at locals throughout the stack (graphically
>> browsable in IDEs, but REPL-accessible in any event).
>>
>> I gather that one can't handle exceptions "in context" like this in
>> Java... but I'm approaching this from the Lisp side and don't really
>> know... I see some related discussions (e.g. in
>> https://news.ycombinator.com/item?id=438125) but it's not clear to me
>> what the bottom line is.
>>
>> If it *is* possible to get this kind of behavior in Clojure than that
>> would be a beautiful thing.
>>
>
> Eclipse allows setting a breakpoint that triggers on an exception throw,
> rather than a particular line of code. This implies that the JVM has a
> debugging hook that allows effectively wrapping every throw with a phantom
> try...catch to trap that exception and handle it a certain way. So it
> *should* be possible (with the IDE and debugged code running in separate
> JVMs) to make a Clojure IDE that can pop you into a REPL at the point of an
> exception throw (and even to set it to do this only on specific classes of
> exception). I doubt you could just patch-and-continue like in Common Lisp,
> though, and it would need further work to be able to examine local
> variables (and unwind the stack a notch at a time and repeat). I have a
> general notion of how you might fake some of Common Lisp's functionality:
> the exception handler creates or recreates a "debug" namespace, uses more
> of the JVM's debugging capabilities (which, again, Eclipse can use to let
> you examine locals when a breakpoint trips) to copy locals into vars of the
> same names, and binds some var to a function that will set a
> debug-REPL-triggering breakpoint in the caller one line after the callee
> returns and then resume, dropping you immediately into the debug REPL again
> but now in the callee. So you'd end up with something like
>
> #<java.lang.NullPointerException>
>     in foo.core$bar.invoke (core.clj:962)
>     in foo.core$quux.invoke (core.clj:1701)
>     ...
> In foo.core$bar.invoke (core.clj:962)
> debug=> a
> 3
> debug=> b
> nil
> ; Aha! Someone called bar with nil. In fact, quux called bar with nil.
> debug=> (unwind)
> In foo.core$quux.invoke (core.clj:1701)
> debug=> x
> 0
> ; Aha! Should be at least 1.
>
> Of course, locals clearing would sometimes mean that information you
> needed would have been nulled out already. If x had no further use in quux
> after being passed to bar, for example, it would probably be nil instead of
> 0, and you'd be limited in what conclusions you could draw from a nil
> value, unless the local was used farther on. I doubt that's surmountable
> without reproducing the bug first and then adding a dummy use of the local
> later than the point of the error so as to keep it from being
> locals-cleared, but then you might as well just put a println instead of a
> dummy use there and call it a night.
>
> TL;DR: if anything seriously impedes development of a locals-examining
> debugger capability in a Clojure IDE, it will be locals clearing rather
> than a lack of capabilities for JVM instrumentation.
>

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to