More precise and efficient handling of backtraces#293
More precise and efficient handling of backtraces#293bobot wants to merge 7 commits intoocaml:trunkfrom
Conversation
The reference given are the current results not the ideal results.
The tests show cases where the backtrace we want to use during
reraise have been lost by intermediary raise.
Previously this raise was transformed into a reraise although the backtrace used can't be the one of the first try. Remove this transformation by simplifying the code. ```ocaml try e1 with exn -> try e2 with _ -> raise exn ```
It reraises an exception just after copying the given backtrace to the backtrace buffer. The primitive only does the copying part, the compiler adds the reraise.
The heuristic that transform raise into reraise automatically save the raw_backtrace using caml_get_exception_raw_backtrace at the start of the handler and use caml_reraise_raw_backtrace just before the reraise. WARNING: If an exception is used inside a guard of a try_with the backtrace is still wrong.
Add the construction [rec id] in exception handler that should bind
the backtrace of the point of programm where the exception have been
raised. The type of the backtrace is `Printexc.raw_backtrace`.
Currently it is implemented by adding a `let id =
Printexc.get_raw_backtrace in` so if a raise is done during a
previous when clause the backtrace is still the wrong one.
```ocaml
try
exp
with exn when exp rec id -> exp
```
In a very inefficient way. Save the backtrace before guards of `try with` using `get_raw_backtrace` and restore it using `reraise_raw_backtrace` (that doesn't reraise by itself).
Add a warning for use of these functions outside exception handler:
- print_backtrace
- get_backtrace
- get_raw_backtrace
It is not the application that is warned, but just the use of the
identifier.
|
We discussed it at the developer meeting today. The various advanced aspect of the proposal are too fresh to be included in the next release, but your proposal of a |
| with Error "a" -> print_string "a"; print_newline(); 0 | ||
| | Error "b" as exn -> print_string "b"; print_newline(); raise exn | ||
| | Error "c" -> raise (Error "c") | ||
| (** [Error "d"] not catched *) |
|
Is it worth the pain of executing the Executing pattern matching only should be enough to catch reasonable use cases. This of course is a premature concern. I mention it because putting the |
|
The current proposal specializes the grammar rules for try-cases. |
|
The A proper representation is probably preferable :). |
There is two opposites objectives:
Executing on top of the stack allows to have both, and it could be more efficient than currently since instead of computing backtrace at raise it could be done only when needed. However other tradeoff can be found. For example we could keep on the side the
Funnily I preferred to put it after so that it is clear that the backtrace variable is not binded in the
@lpw25 proposed the syntax |
|
Yes I like I also agree with @let-def that supporting (I might even be tempted to just ban |
This is too much overloading for pair syntax, especially if you start adding funky restrictions on parentheses. What about |
|
Here are some details from a Mantis issue (#7262) that I think can be reasonably incorporated into this GPR. In the example below the second and subsequent exception raises are treated as re-raises, which leads to the second and subsequent backtraces containing stale backtraces. together with the C stub: Fixing |
|
Three years later, where are we with these issues and proposed improvements to backtraces? |
Simplifies addition of new operations and safe with existing code.
|
We should turn this PR into an RFC to discuss the design we want. I agree that this area is in dire need of well-designed improvements. Do we have a volunteer to write an RFC? Otherwise, should we just close? |
|
Archiving this PR, but an RFC is still welcome. |
This merge-request doesn't propose yet any codes for integration. The goal is to understand if the proposed directions are worth investigating.
Keeping correct backtraces
The merge-request in #246 shows that there are cases were the backtraces used for reraise or just
Printexc.print_backtracehave been lost by intermediaryraise:whenguard.The tests for these cases are added in the first commit with the buggy backtraces as reference.
Thanks to the
Printexc.raw_backtracetype, it is possible to fix these backtraces, in a very inefficient way , by usingPrintexc.get_raw_backtraceand adding a functioncaml_reraise_raw_backtracethat make the given backtrace and exception the current ones (it setscaml_backtrace_bufferandcaml_backtrace_last_exn):whenguards and restore it at the end.The third, fourth and sixth commits implement these inefficient fix.
The references of the tests are fixed
Do we agree that there is no other case where the backtrace is wrong, and that the backtraces returned with these fixes are the correct one?
Keeping the backtrace creation local
Currently the current backtrace is a unique global state without precise semantics, which makes its optimization hard. Since the only points where the current backtrace is clear are at the start of
exception handlers we propose to restrict the creation of backtraces (
Printexc.print_backtrace,get_backtrace,get_raw_backtrace) inside exception handler and that they refer to these backtrace. The only exception I found is inCorewhere a function factorized code and which is used only in exception handler.reclike record backtrace).The function
Printexc.print_backtrace,get_backtrace,get_raw_backtracehave not yet been made to refer to the backtrace at the start of the exception handler but it is easy to do so once thewarning becomes a real error.
Compute backtrace as late as possible
This part have not been implemented and is the hardest to implement. We are looking for feedback.
Since the backtrace is computed always at
raisesite when-gis specified and the recording of backtrace activated, it is often computed without behind ever used. That's whyraise_notracehavebeen added for not computing backtrace. However it is more natural to specify if you want the backtrace at the exception handler site. We propose that if the backtrace is not binded in an exception handler (no
rec bt,Printexc.print_backtrace,get_backtrace,get_raw_backtrace) the backtrace is not computed.For doing that we think that the way the stack is handled during exceptions must be modified in a way similar but a lot simpler and less powerful than resumable exception and algebraic effect:
raisedoesn't pop the stack, but executes the matching of the exception and executes guards of exception handlers directly on the current stack,Stack_overflowmust be handled specially by computing directly the backtrace and poping the stack.The advantages are:
raise_notracebecause if you don't use the backtrace it is not computed.-gand with backtrace recording activated are a lot more efficient, if the code doesn't bind any backtrace it is as fast as if the backtraces are not recorded.Drawbacks:
-gmust be also modifiedI discussed this proposition with @def-lkb and @chambart at the last OUPS. We are convinced that it is possible and interesting to implement. Does the core developers agree, have some remarks or oppositions? Does OCaml developers bind backtraces outside exception handlers?