File: exception_handler.ml

package info (click to toggle)
ocaml 5.4.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 44,384 kB
  • sloc: ml: 370,196; ansic: 52,820; sh: 27,419; asm: 5,462; makefile: 3,684; python: 974; awk: 278; javascript: 273; perl: 59; fortran: 21; cs: 9
file content (66 lines) | stat: -rw-r--r-- 1,804 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
(* TEST
 frame_pointers;
 readonly_files = "fp_backtrace.c";
 all_modules = "${readonly_files} exception_handler.ml";
 native;
*)

(* https://github.com/ocaml/ocaml/pull/11031 *)
external fp_backtrace : string -> unit = "fp_backtrace" [@@noalloc]

exception Exn1
exception Exn2

(* We want to be sure to use some stack space so that frame pointer is shifted,
 * preventing inlining seems enough *)
let[@inline never] raiser i =
  match i with
  | 1 -> raise Exn1
  | 2 -> raise Exn2
  | _ -> 42 (* shouldn't happen *)

let[@inline never][@local never] f x = x

(* This give us a chance to overwrite the memory address pointed by frame
 * pointer if it is still within 'raiser' stack frame.
 * Technically we don't need to overwrite it but by doing so we avoid some
 * infinite loop while walking the stack. *)
let[@inline never] handler () =
  (* Force spilling of x0, x1, x2 *)
  let x0 = Sys.opaque_identity 0x6f56df77 (* 0xdeadbeef *) in
  let x1 = Sys.opaque_identity 0x6f56df77 (* 0xdeadbeef *) in
  let x2 = Sys.opaque_identity 0x6f56df77 (* 0xdeadbeef *) in
  let _ = f x0 in
  let _ = f x1 in
  let _ = f x2 in
  let _ = Sys.opaque_identity x0 in
  let _ = Sys.opaque_identity x1 in
  let _ = Sys.opaque_identity x2 in
  fp_backtrace Sys.argv.(0)

let[@inline never] nested i =
  begin
    try
      try ignore (raiser i) with Exn1 -> handler ()
    with
    | Exn2 -> handler ()
  end;
  i

(* Check that we haven't broken anything by raising directly from this
 * function, it doesn't require the frame pointer to be adjusted. *)
let[@inline never] bare i =
  begin
    try
      try (if i == 1 then raise Exn1 else raise Exn2) with
      | Exn1 -> handler ()
    with
    | Exn2 -> handler ()
  end;
  i

let () =
  ignore (bare 1);
  ignore (bare 2);
  ignore (nested 1);
  ignore (nested 2)