Simpler heuristic to determine reraises#246
Conversation
|
We should get @alainfrisch 's opinion, but I personally think that this behavior is nicer than the current one. (The code is also a bit simpler.) It would be nice to have some test coverage for reraise in the testsuite, but I'm not sure how to write good tests for this behavior. |
|
Isn't it still the case that the backtrace mechanism discards the current segment when the current exception is physically different from the previous one? It used to be like that, which breaked the "reraise after wrapping" scenario anyway. The reason I restricted the reraise heuristics was to be closer to the existing behavior when I changed the representation of constant exception constructors (which has the effect of making all instances of e.g. "Exit" physically equal, and thus breaking the runtime decision based on physical equality of exceptions and common cases). The risk here is to get completely bogus stack traces, not corresponding to any actual control flow. |
|
Yes in its current state the backtrace mechanism still check for equality. Equality of exception values is too restrictive and not really meaningful (especially with parameter less exceptions). My opinion would be to drop the mechanism and rely only on raise vs reraise (that is done in my branch storing multiple exception values, I should probably also have included this patch here :)). The rewriting done ensures backtraces are correct (I have no formal proof, but it's turning a raise to a reraise only occurs inside the handler of a try, not going through closures to avoid 'capturing and reraising in a different context'). |
|
Ah, I had not realized that the backtrace would still be cut above the now-reraise point because of the runtime. The "nicer behavior" I had in mind would include the trace of both the raised and the re-raised exceptions in the backtrace. |
|
This latest commit makes backtrace reset only happen when using raise primitive. In bytecode, it is determined by reraise boolean flag. |
|
Small brain dump -- sorry. Here is one scenario where maybe the new approach is not as good as the previous one. Consider a backtracking search implemented using I think both "yes" and "no" would be reasonable answers to this question. It is clear that it does not make much sense the failure trace of Which comes to the question: I suppose that the re-raise decision is currently is made before inlining (so in particular raises that would be inlined inside a handler would not be turned into re-raises). Would there be any reason to change this? How should we think of backtrace to evaluate whether a particular behavior is a feature or "completely bogus"? What does it mean for a trace to "correspond to any control flow" in the relaxed setting proposed by @def-lkb ? One proposal would be causality: it is good to have to raise traces S1 and S2 concatenated in the returned trace if the raise S2 was caused by the raise S1. (This all means we should have tests in the testsuite, to at least specify some partial behavior.) |
It is done during translation, so before inlining yes. I like it this way, it is a simple syntactic criterion.
I just realised that a function called inside the handler, raising and catching an exception (not letting it escape), and returning to the handler which finally reraise will produce a "bogus" backtrace: try ...
with exn ->
f (); (* f messes with backtrace buffer *)
raise exnThis can already be done by using constant exceptions or capturing a closure that raises inside a static handler, not checking for equality just make it happen more often. |
|
Well we could rewrite this handler in a more invasive way that just turning the |
|
Neither am I. |
|
We could also keep a sequence of traces instead of just the last one, as long as the total memory usage does not grow above the |
|
Yes, the example 4 posts above this one (try...with exn -> f(); raise exn) is typical of the situations we don't handle well, neither with the current OCaml solution (the backtrace is too short, claiming that the exception is raised for the first time at the reraise point) nor with the proposed change (the backtrace claims that the exception was first raised inside f(), right?). I don't have anything better to suggest at this point. |
|
@gasche Your idea of keeping at the start of the handler some timestamp(
|
|
I can partially reply to my own question. If the invariant of the datastructure needs the participation of the raise or reraise, it will not work when a code compiled with movq %r14, %rsp
popq %r14
retIt is also true for code compiled with PS: external raise_notrace: exn -> 'a = "%raise_notrace"
let f () =
try raise Not_found
with _ -> raise_notrace Exit |
|
One remark on this: @chambart pointed out that GPR#270, with flambda, may mean that we get worse backtraces (I think since arguments to [raise] will be more likely to be lifted out as constants and shared, which means such things will be physically equal to each other more often). It is suspected that the work of @def-lkb referred to above (removing the phys-equal check) would solve this problem. |
|
The latest comment for this PR goes back to Nov 2015, so I feel safe in closing it. Reopen if anyone has new things to contribute. |
|
Maybe we would need a specific label "Frozen Nice Stuff that Frédéric may have time to look at again someday." |
* Update books * Add new books * Add language selector to books
This patch simplify the heuristic to determine when a raise is a reraise.
A raise starts a fresh backtrace whereas a reraise appends to the existing backtrace. But when coding, one almost only uses the 'raise' primitive. Letting the compiler determine when a raise is a reraise significantly improve quality of backtraces.
The basic idea is to consider that a raise statically occuring inside the handler of a try statement is a re-raise. However the current heuristic feels a bit too conservative: it tries to determine that the reraised exception is the same as the original one by tracking its name.
The patch just considers that any exception raised inside the handler is a reraise.
While being simpler, it fits better the pattern of wrapping an exception with more information before reraising it.
This work will be followed by one extending the backtrace API to observe the multiple raised exceptions.