Use reraise_raw_backtrace primitive in the compiler#374
Conversation
|
I like the general work very much and I would be in favor of merging it for 4.03. The thing that needs discussing is the I have three reservations: the API of Api of
|
|
Here would be one attempt to de-spaghettify the let try_finally ?(always=(fun () -> ())) ?(exceptionally=(fun _exn -> ())) work =
match work () with
| result ->
begin match always () with
| () -> result
| exception always_exn ->
let always_bt = Printexc.get_raw_backtrace () in
exceptionally always_exn;
Printexc.reraise_raw_backtrace always_exn always_bt
end
| exception work_exn ->
let work_bt = Printexc.get_raw_backtrace () in
begin match always () with
| () ->
exceptionally work_exn;
Printexc.reraise_raw_backtrace work_exn work_bt
| exception always_exn ->
let always_bt = Printexc.get_raw_backtrace () in
exceptionally always_exn;
(* why are we not raising work_exn, work_bt in this case? *)
Printexc.reraise_raw_backtrace always_exn always_bt
end |
|
For the addition of the exception I'm less convinced, it was not useful in the cases I translated and since the caller doesn't know if the exception comes from If we always raise For the style of callsites, my only goal was to have something reasonable that doesn't change the indentation (using ocp-indent) so that the commit is simple to review. Now that it is done, I can use your third style if you prefer and re-indent. |
Ok.
After thinking about the issue more, I understand it less. In particular, why is your implementation not catching exceptions thrown by If this is correct, to correctly preserve backtraces a I do see your point about errors being lost. Maybe we should warn explicitly about them (have a runtime warning when an exception is ignored in I still suspect, from the examples in the compiler callsites, that the
That's a good point. Another thing I was thinking is that sometimes placing the |
The root of the problems is not exceptions that are raised outside |
|
After thinking more about this issue, I wonder if a much simpler specification wouldn't be better for - close_out outchan
- with x ->
- close_out outchan;
- remove_file lib_name;
- remove_file archive_name;
- raise x
+ end
+ ~always:(fun () -> close_out outchan)
+ ~exceptionally:(fun () ->
+ remove_file lib_name;
+ remove_file archive_name)Note that in the original code, if Also in the following case: Misc.try_finally begin fun () ->
comp (Pparse.parse_implementation ~tool_name ppf sourcefile)
end
~always:(fun () -> Stypes.dump (Some (outputprefix ^ ".annot")))
~exceptionally:(fun () -> remove_file objfile; remove_file cmxfile)removing object files and cmx files if the production of the Finally, in let read_ast magic fn =
let ic = open_in_bin fn in
Misc.try_finally begin fun () ->
let buffer = really_input_string ic (String.length magic) in
assert(buffer = magic); (* already checked by apply_rewriter *)
Location.input_name := input_value ic;
input_value ic
end
~always:(fun () -> close_in ic; Misc.remove_file fn)there is no I would therefore propose the following, much simplified form: (** [try_finally work ~always ~exceptionally] is designed to run code
in [work] that may fail with an exception, and has two kind of
cleanup routines: [always], that must be run after any execution
of the function (typically, freeing system resources), and
[exceptionally], that should be run only if [work] failed with an
exception (typically, undoing user-visible state changes that
would only make sense if the function completes correctly). For
example:
{|
let objfile = outputprefix ^ ".cmo" in
let oc = open_out_bin objfile in
Misc.try_finally
(fun () ->
bytecode
++ Timings.(accumulate_time (Generate sourcefile))
(Emitcode.to_file oc modulename objfile);
Warnings.check_fatal ())
~always:(fun () -> close_out oc)
~exceptionally:(fun _exn -> remove_file objfile);
|}
If [always] or [exceptionally] fail with an exception, it is
propagated as usual. In particular, if [always] fails with an
exception, then [exceptionally] is *not* called.
On the other hand, if they use exceptions internally for
control-flow but do not raise, then [try_finally] is careful to
preserve any exception backtrace coming from [work] for easier
debugging.
*)
let try_finally ?(always=(fun () -> ())) ?(exceptionally=(fun _exn -> ())) work =
match work () with
| result -> always (); result
| exception exn ->
let bt = Printexc.get_raw_backtrace () in
always ();
exceptionally exn;
Printexc.reraise_raw_backtrace exn btWith this specification, @bobot , it seems to me that your PR is in two parts, the primitive implementation and related |
Indeed, I had misunderstood this previously, and only got it when looking at it again this morning. This supports my intuition that maybe not trying to catch failures in |
|
OK I'm going to split the PR as you proposed. Your last |
|
I don't think the split would necessarily delay the PR, on the contrary it may be a way to converge more quickly. The |
3540eb2 to
94844f5
Compare
|
Split done (#378). In the end I disagree with - close_out outchan
- with x ->
- close_out outchan;
- remove_file lib_name;
- remove_file archive_name;
- raise x
+ end
+ ~always:(fun () -> close_out outchan)
+ ~exceptionally:(fun () ->
+ remove_file lib_name;
+ remove_file archive_name)
|
b0de4a1 to
aec9983
Compare
|
@gasche I rebased and replaced my version of |
aec9983 to
767263d
Compare
|
Going through pull requests that have been quiet for a while, I find this one which looks pretty good to me. Any remaining problem? The 4.04 and 4.05 deadlines are passed, but it could go in master and then in 4.06 if there is agreement. |
|
Indeed, thanks! We should check that it rebases as expected (the primitive itself was only merged recently due to my own slowness), and I think it would be fine in trunk. @bobot, would you have time to work on the rebase? |
|
I regret that we dropped the ball on this one, and I hope that someone (not me right now) will be able to move it forward. Having useful backtraces in the compiler sounds like a nice property to gain. |
|
@gasche I rebased this PR on Apart from the mechanical adaptations, I removed the bootstrap commit and added a commit replacing @bobot I could force push into your branch if you agree. |
|
@nojb you can. Thank you for the effort. |
767263d to
0defbbe
Compare
|
Thanks, done! As mentioned, for reviewing add |
Changes
Outdated
| has changed by using optional arguments. Cleanup in exceptional case | ||
| have been added (`~exceptionally`). It uses | ||
| `Printexc.raise_with_backtrace` for preserving backtraces. | ||
| (François Bobot and Gabriel Scherer) |
There was a problem hiding this comment.
This entry should be moved up, and @nojb's contribution added to the credit line.
| then raise(Error(Assembler_error asm_filename)); | ||
| if create_asm && not keep_asm then remove_file asm_filename | ||
| ) | ||
| ~exceptionally:(fun () -> remove_file obj_filename) |
There was a problem hiding this comment.
The code of this whole block could be simplified.
- the inner
exceptionallyblock is code duplicated with the end of the outer action, you should remove the duplication by moving the code to an outeralwaysblock. - the outer
exceptionallycode is much shorter than the outer action, I would rather have it come first in this case.
There was a problem hiding this comment.
In the case where the call to Proc.assemble_file fails, then the file is not removed, so we can't just move the inner exceptionally block to the outer scope.
asmcomp/asmlibrarian.ml
Outdated
| then raise(Error(Archiver_error archive_name)); | ||
| ) | ||
| ~always:(fun () -> close_out outchan) | ||
| ~exceptionally:(fun () -> |
There was a problem hiding this comment.
Here as well, always and exceptionally are short, could they be placed before the action? As a side-effect, they would be closer to the resource allocation code, which makes the whole thing easier to read.
| output_binary_int outchan pos_toc; | ||
| ) | ||
| ~always:(fun () -> close_out outchan) | ||
| ~exceptionally:(fun () -> remove_file lib_name) |
There was a problem hiding this comment.
The short named arguments could be passed first.
| Bytesections.write_toc_and_trailer outchan; | ||
| ) | ||
| ~always:(fun () -> close_out outchan) | ||
| ~exceptionally:(fun () -> remove_file exec_name) |
| in | ||
| cmi, cmt | ||
| ) | ||
| ~always:(fun () -> close_in ic) |
typing/ctype.ml
Outdated
| umode := old_unification_mode; | ||
| generate_equations := old_gen; | ||
| assume_injective := old_inj | ||
| ) |
| try let res = f t1 t2 in univar_pairs := old_univars; res | ||
| with exn -> univar_pairs := old_univars; raise exn | ||
| Misc.try_finally (fun () -> f t1 t2) | ||
| ~always:(fun () -> univar_pairs := old_univars) |
There was a problem hiding this comment.
Not sure about this one. A priori, the code between the definition of old_univars and the call to f may be mutating univar_pairs, in which the case using Misc.protect_refs would not be correct.
There was a problem hiding this comment.
That's a fair point. Two remarks:
- We could use
univar_paris := old_univarsand then callMisc.protect_refs, but that would be evil. - I don't know the type-checker better than I do, so I can't say whether the code above can reach this point with
!univar_paris <> old_univars, but I can say with confidence that this code would be extremely fishy if it could, and probably wrong. So maybe it's worth asking someone that does do type-checker stuff to confirm here. (@trefis?)
There was a problem hiding this comment.
(I won't let this question delay the PR; it's fine to merge if this point remains unresolved.)
| Misc.try_finally (fun () -> | ||
| eqtype_list rename type_pairs subst env tl1 tl2 | ||
| ) | ||
| ~always:(fun () -> backtrack snap) |
typing/typecore.ml
Outdated
| gadt_equations_level := None; | ||
| r | ||
| ) | ||
| ~always:(fun () -> gadt_equations_level := None) |
|
Thanks for the review @gasche! I think I addressed all the suggestions (except for the |
gasche
left a comment
There was a problem hiding this comment.
I believe this is good to go if CI passes, but see minor Changes remark and small possibly-fixup-inspiring question.
| arguments. Cleanup in exceptional case have been added (`~exceptionally`). It | ||
| uses `Printexc.raise_with_backtrace` for preserving backtraces. | ||
| (François Bobot, Gabriel Scherer, and Nicolás Ojeda Bär, review by Gabriel | ||
| Scherer) |
There was a problem hiding this comment.
Reading this change entry again, I believe that it doesn't reflect the content of the PR anymore. Here would be a suggestion for a replacement:
- GPR#374: use Misc.try_finally for resource cleanup in the compiler codebase. This should
fix the problem of catch-and-reraise `try .. with` blocks destroying backtrace information.
(credits)
(Semi-related question: does the use of protect_refs drops backtraces? Its implementation is not using try_finally itself, it probably should, but it may be that the "detect raises that are re-raises" logic suffice in this particular case, as the exception-cleanup work does not involved raising exceptions.)
There was a problem hiding this comment.
Thanks, amended as suggested. And yes, I believe Misc.protect_refs preserves backtraces.
e910cfc to
685d898
Compare
|
(Personally I would squash |
|
Yes, will do that before merging. |
And add labels ~always for previous cleanup function and
~exceptionally for new cleanup function in exceptional case
685d898 to
40bab2d
Compare
|
Squashed & rebased. Will merge once CI passes to avoid new conflicts cropping up. |
…llection Force major slice on minor collection
23a7f73 flambda-backend: Fix some Debuginfo.t scopes in the frontend (ocaml#248) 33a04a6 flambda-backend: Attempt to shrink the heap before calling the assembler (ocaml#429) 8a36a16 flambda-backend: Fix to allow stage 2 builds in Flambda 2 -Oclassic mode (ocaml#442) d828db6 flambda-backend: Rename -no-extensions flag to -disable-all-extensions (ocaml#425) 68c39d5 flambda-backend: Fix mistake with extension records (ocaml#423) 423f312 flambda-backend: Refactor -extension and -standard flags (ocaml#398) 585e023 flambda-backend: Improved simplification of array operations (ocaml#384) faec6b1 flambda-backend: Typos (ocaml#407) 8914940 flambda-backend: Ensure allocations are initialised, even dead ones (ocaml#405) 6b58001 flambda-backend: Move compiler flag -dcfg out of ocaml/ subdirectory (ocaml#400) 4fd57cf flambda-backend: Use ghost loc for extension to avoid expressions with overlapping locations (ocaml#399) 8d993c5 flambda-backend: Let's fix instead of reverting flambda_backend_args (ocaml#396) d29b133 flambda-backend: Revert "Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382)" (ocaml#395) d0cda93 flambda-backend: Revert ocaml#373 (ocaml#393) 1c6eee1 flambda-backend: Fix "make check_all_arches" in ocaml/ subdirectory (ocaml#388) a7960dd flambda-backend: Move flambda-backend specific flags out of ocaml/ subdirectory (ocaml#382) bf7b1a8 flambda-backend: List and Array Comprehensions (ocaml#147) f2547de flambda-backend: Compile more stdlib files with -O3 (ocaml#380) 3620c58 flambda-backend: Four small inliner fixes (ocaml#379) 2d165d2 flambda-backend: Regenerate ocaml/configure 3838b56 flambda-backend: Bump Menhir to version 20210419 (ocaml#362) 43c14d6 flambda-backend: Re-enable -flambda2-join-points (ocaml#374) 5cd2520 flambda-backend: Disable inlining of recursive functions by default (ocaml#372) e98b277 flambda-backend: Import ocaml#10736 (stack limit increases) (ocaml#373) 82c8086 flambda-backend: Use hooks for type tree and parse tree (ocaml#363) 33bbc93 flambda-backend: Fix parsecmm.mly in ocaml subdirectory (ocaml#357) 9650034 flambda-backend: Right-to-left evaluation of arguments of String.get and friends (ocaml#354) f7d3775 flambda-backend: Revert "Magic numbers" (ocaml#360) 0bd2fa6 flambda-backend: Add [@inline ready] attribute and remove [@inline hint] (not [@inlined hint]) (ocaml#351) cee74af flambda-backend: Ensure that functions are evaluated after their arguments (ocaml#353) 954be59 flambda-backend: Bootstrap dd5c299 flambda-backend: Change prefix of all magic numbers to avoid clashes with upstream. c2b1355 flambda-backend: Fix wrong shift generation in Cmm_helpers (ocaml#347) 739243b flambda-backend: Add flambda_oclassic attribute (ocaml#348) dc9b7fd flambda-backend: Only speculate during inlining if argument types have useful information (ocaml#343) aa190ec flambda-backend: Backport fix from PR#10719 (ocaml#342) c53a574 flambda-backend: Reduce max inlining depths at -O2 and -O3 (ocaml#334) a2493dc flambda-backend: Tweak error messages in Compenv. 1c7b580 flambda-backend: Change Name_abstraction to use a parameterized type (ocaml#326) 07e0918 flambda-backend: Save cfg to file (ocaml#257) 9427a8d flambda-backend: Make inlining parameters more aggressive (ocaml#332) fe0610f flambda-backend: Do not cache young_limit in a processor register (upstream PR 9876) (ocaml#315) 56f28b8 flambda-backend: Fix an overflow bug in major GC work computation (ocaml#310) 8e43a49 flambda-backend: Cmm invariants (port upstream PR 1400) (ocaml#258) e901f16 flambda-backend: Add attributes effects and coeffects (#18) aaa1cdb flambda-backend: Expose Flambda 2 flags via OCAMLPARAM (ocaml#304) 62db54f flambda-backend: Fix freshening substitutions 57231d2 flambda-backend: Evaluate signature substitutions lazily (upstream PR 10599) (ocaml#280) a1a07de flambda-backend: Keep Sys.opaque_identity in Cmm and Mach (port upstream PR 9412) (ocaml#238) faaf149 flambda-backend: Rename Un_cps -> To_cmm (ocaml#261) ecb0201 flambda-backend: Add "-dcfg" flag to ocamlopt (ocaml#254) 32ec58a flambda-backend: Bypass Simplify (ocaml#162) bd4ce4a flambda-backend: Revert "Semaphore without probes: dummy notes (ocaml#142)" (ocaml#242) c98530f flambda-backend: Semaphore without probes: dummy notes (ocaml#142) c9b6a04 flambda-backend: Remove hack for .depend from runtime/dune (ocaml#170) 6e5d4cf flambda-backend: Build and install Semaphore (ocaml#183) 924eb60 flambda-backend: Special constructor for %sys_argv primitive (ocaml#166) 2ac6334 flambda-backend: Build ocamldoc (ocaml#157) c6f7267 flambda-backend: Add -mbranches-within-32B to major_gc.c compilation (where supported) a99fdee flambda-backend: Merge pull request ocaml#10195 from stedolan/mark-prefetching bd72dcb flambda-backend: Prefetching optimisations for sweeping (ocaml#9934) 27fed7e flambda-backend: Add missing index param for Obj.field (ocaml#145) cd48b2f flambda-backend: Fix camlinternalOO at -O3 with Flambda 2 (ocaml#132) 9d85430 flambda-backend: Fix testsuite execution (ocaml#125) ac964ca flambda-backend: Comment out `[@inlined]` annotation. (ocaml#136) ad4afce flambda-backend: Fix magic numbers (test suite) (ocaml#135) 9b033c7 flambda-backend: Disable the comparison of bytecode programs (`ocamltest`) (ocaml#128) e650abd flambda-backend: Import flambda2 changes (`Asmpackager`) (ocaml#127) 14dcc38 flambda-backend: Fix error with Record_unboxed (bug in block kind patch) (ocaml#119) 2d35761 flambda-backend: Resurrect [@inline never] annotations in camlinternalMod (ocaml#121) f5985ad flambda-backend: Magic numbers for cmx and cmxa files (ocaml#118) 0e8b9f0 flambda-backend: Extend conditions to include flambda2 (ocaml#115) 99870c8 flambda-backend: Fix Translobj assertions for Flambda 2 (ocaml#112) 5106317 flambda-backend: Minor fix for "lazy" compilation in Matching with Flambda 2 (ocaml#110) dba922b flambda-backend: Oclassic/O2/O3 etc (ocaml#104) f88af3e flambda-backend: Wire in the remaining Flambda 2 flags (ocaml#103) 678d647 flambda-backend: Wire in the Flambda 2 inlining flags (ocaml#100) 1a8febb flambda-backend: Formatting of help text for some Flambda 2 options (ocaml#101) 9ae1c7a flambda-backend: First set of command-line flags for Flambda 2 (ocaml#98) bc0bc5e flambda-backend: Add config variables flambda_backend, flambda2 and probes (ocaml#99) efb8304 flambda-backend: Build our own ocamlobjinfo from tools/objinfo/ at the root (ocaml#95) d2cfaca flambda-backend: Add mutability annotations to Pfield etc. (ocaml#88) 5532555 flambda-backend: Lambda block kinds (ocaml#86) 0c597ba flambda-backend: Revert VERSION, etc. back to 4.12.0 (mostly reverts 822d0a0 from upstream 4.12) (ocaml#93) 037c3d0 flambda-backend: Float blocks 7a9d190 flambda-backend: Allow --enable-middle-end=flambda2 etc (ocaml#89) 9057474 flambda-backend: Root scanning fixes for Flambda 2 (ocaml#87) 08e02a3 flambda-backend: Ensure that Lifthenelse has a boolean-valued condition (ocaml#63) 77214b7 flambda-backend: Obj changes for Flambda 2 (ocaml#71) ecfdd72 flambda-backend: Cherry-pick 9432cfdadb043a191b414a2caece3e4f9bbc68b7 (ocaml#84) d1a4396 flambda-backend: Add a `returns` field to `Cmm.Cextcall` (ocaml#74) 575dff5 flambda-backend: CMM traps (ocaml#72) 8a87272 flambda-backend: Remove Obj.set_tag and Obj.truncate (ocaml#73) d9017ae flambda-backend: Merge pull request ocaml#80 from mshinwell/fb-backport-pr10205 3a4824e flambda-backend: Backport PR#10205 from upstream: Avoid overwriting closures while initialising recursive modules f31890e flambda-backend: Install missing headers of ocaml/runtime/caml (ocaml#77) 83516f8 flambda-backend: Apply node created for probe should not be annotated as tailcall (ocaml#76) bc430cb flambda-backend: Add Clflags.is_flambda2 (ocaml#62) ed87247 flambda-backend: Preallocation of blocks in Translmod for value let rec w/ flambda2 (ocaml#59) a4b04d5 flambda-backend: inline never on Gc.create_alarm (ocaml#56) cef0bb6 flambda-backend: Config.flambda2 (ocaml#58) ff0e4f7 flambda-backend: Pun labelled arguments with type constraint in function applications (ocaml#53) d72c5fb flambda-backend: Remove Cmm.memory_chunk.Double_u (ocaml#42) 9d34d99 flambda-backend: Install missing artifacts 10146f2 flambda-backend: Add ocamlcfg (ocaml#34) 819d38a flambda-backend: Use OC_CFLAGS, OC_CPPFLAGS, and SHAREDLIB_CFLAGS for foreign libs (#30) f98b564 flambda-backend: Pass -function-sections iff supported. (#29) e0eef5e flambda-backend: Bootstrap (#11 part 2) 17374b4 flambda-backend: Add [@@Builtin] attribute to Primitives (#11 part 1) 85127ad flambda-backend: Add builtin, effects and coeffects fields to Cextcall (#12) b670bcf flambda-backend: Replace tuple with record in Cextcall (#10) db451b5 flambda-backend: Speedups in Asmlink (#8) 2fe489d flambda-backend: Cherry-pick upstream PR#10184 from upstream, dynlink invariant removal (rev 3dc3cd7 upstream) d364bfa flambda-backend: Local patch against upstream: enable function sections in the Dune build 886b800 flambda-backend: Local patch against upstream: remove Raw_spacetime_lib (does not build with -m32) 1a7db7c flambda-backend: Local patch against upstream: make dune ignore ocamldoc/ directory e411dd3 flambda-backend: Local patch against upstream: remove ocaml/testsuite/tests/tool-caml-tex/ 1016d03 flambda-backend: Local patch against upstream: remove ocaml/dune-project and ocaml/ocaml-variants.opam 93785e3 flambda-backend: To upstream: export-dynamic for otherlibs/dynlink/ via the natdynlinkops files (still needs .gitignore + way of generating these files) 63db8c1 flambda-backend: To upstream: stop using -O3 in otherlibs/Makefile.otherlibs.common eb2f1ed flambda-backend: To upstream: stop using -O3 for dynlink/ 6682f8d flambda-backend: To upstream: use flambda_o3 attribute instead of -O3 in the Makefile for systhreads/ de197df flambda-backend: To upstream: renamed ocamltest_unix.xxx files for dune bf3773d flambda-backend: To upstream: dune build fixes (depends on previous to-upstream patches) 6fbc80e flambda-backend: To upstream: refactor otherlibs/dynlink/, removing byte/ and native/ 71a03ef flambda-backend: To upstream: fix to Ocaml_modifiers in ocamltest 686d6e3 flambda-backend: To upstream: fix dependency problem with Instruct c311155 flambda-backend: To upstream: remove threadUnix 52e6e78 flambda-backend: To upstream: stabilise filenames used in backtraces: stdlib/, otherlibs/systhreads/, toplevel/toploop.ml 7d08e0e flambda-backend: To upstream: use flambda_o3 attribute in stdlib 403b82e flambda-backend: To upstream: flambda_o3 attribute support (includes bootstrap) 65032b1 flambda-backend: To upstream: use nolabels attribute instead of -nolabels for otherlibs/unix/ f533fad flambda-backend: To upstream: remove Compflags, add attributes, etc. 49fc1b5 flambda-backend: To upstream: Add attributes and bootstrap compiler a4b9e0d flambda-backend: Already upstreamed: stdlib capitalisation patch 4c1c259 flambda-backend: ocaml#9748 from xclerc/share-ev_defname (cherry-pick 3e937fc) 00027c4 flambda-backend: permanent/default-to-best-fit (cherry-pick 64240fd) 2561dd9 flambda-backend: permanent/reraise-by-default (cherry-pick 50e9490) c0aa4f4 flambda-backend: permanent/gc-tuning (cherry-pick e9d6d2f) git-subtree-dir: ocaml git-subtree-split: 23a7f73
Signed-off-by: Christine Rose <[email protected]>
(splitted after creation to #378)
The function
Misc.try_finallyusereraise_raw_backtracefunction for keeping correct backtrace even if the cleaning function use exceptions.This PR also add a function
Misc.try_finally'that superseedMisc.try_finally, and it uses it for fixing backtrace in the compiler. So it should superseed half of #358 by usingMisc.try_finally'whenMisc.remove_fileis used for cleaning.This PR goes a little further and convert many other
try withtoMisc.try_finally', even in case where theraisetoreraiseheuristic gives correct backtrace, since that reduce code duplication, and that is clearer,EDIT: remove what is in #378