Skip to content

Commit deb6b67

Browse files
authored
Merge pull request #931 from goblint/backtrace-marking
Add custom location marks to exception backtraces
2 parents 4828e7b + fbca8bc commit deb6b67

File tree

7 files changed

+133
-4
lines changed

7 files changed

+133
-4
lines changed

src/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
(name goblint_lib)
88
(public_name goblint.lib)
99
(modules :standard \ goblint mainspec privPrecCompare apronPrecCompare messagesCompare)
10-
(libraries goblint.sites goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult
10+
(libraries goblint.sites goblint-cil.all-features batteries.unthreaded qcheck-core.runner sha json-data-encoding jsonrpc cpu arg-complete fpath yaml yaml.unix uuidm goblint_timing catapult goblint_backtrace
1111
; Conditionally compile based on whether apron optional dependency is installed or not.
1212
; Alternative dependencies seem like the only way to optionally depend on optional dependencies.
1313
; See: https://dune.readthedocs.io/en/stable/concepts.html#alternative-dependencies.

src/framework/constraints.ml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,12 +687,20 @@ struct
687687
| Skip -> tf_skip var edge prev_node
688688
end getl sidel getg sideg d
689689

690+
type Goblint_backtrace.mark += TfLocation of location
691+
692+
let () = Goblint_backtrace.register_mark_printer (function
693+
| TfLocation loc ->
694+
Some ("transfer function at " ^ CilType.Location.show loc)
695+
| _ -> None (* for other marks *)
696+
)
697+
690698
let tf var getl sidel getg sideg prev_node (_,edge) d (f,t) =
691699
let old_loc = !Tracing.current_loc in
692700
let old_loc2 = !Tracing.next_loc in
693701
Tracing.current_loc := f;
694702
Tracing.next_loc := t;
695-
Fun.protect ~finally:(fun () ->
703+
Goblint_backtrace.protect ~mark:(fun () -> TfLocation f) ~finally:(fun () ->
696704
Tracing.current_loc := old_loc;
697705
Tracing.next_loc := old_loc2
698706
) (fun () ->

src/framework/control.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,9 @@ struct
299299
in
300300
let with_externs = do_extern_inits ctx file in
301301
(*if (get_bool "dbg.verbose") then Printf.printf "Number of init. edges : %d\nWorking:" (List.length edges); *)
302+
let old_loc = !Tracing.current_loc in
302303
let result : Spec.D.t = List.fold_left transfer_func with_externs edges in
304+
Tracing.current_loc := old_loc;
303305
if M.tracing then M.trace "global_inits" "startstate: %a\n" Spec.D.pretty result;
304306
result, !funs
305307
in

src/maingoblint.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,11 @@ let do_analyze change_info merged_AST =
461461
with e ->
462462
let backtrace = Printexc.get_raw_backtrace () in (* capture backtrace immediately, otherwise the following loses it (internal exception usage without raise_notrace?) *)
463463
Goblintutil.should_warn := true; (* such that the `about to crash` message gets printed *)
464-
let loc = !Tracing.current_loc in
465-
Messages.error ~category:Analyzer ~loc:(Messages.Location.CilLocation loc) "About to crash!";
464+
let pretty_mark () = match Goblint_backtrace.find_marks e with
465+
| m :: _ -> Pretty.dprintf " at mark %s" (Goblint_backtrace.mark_to_string m)
466+
| [] -> Pretty.nil
467+
in
468+
Messages.error ~category:Analyzer "About to crash%t!" pretty_mark;
466469
(* trigger Generic.SolverStats...print_stats *)
467470
Goblintutil.(self_signal (signal_of_string (get_string "dbg.solver-signal")));
468471
do_stats ();

src/util/backtrace/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(include_subdirs no)
2+
3+
(library
4+
(name goblint_backtrace)
5+
(public_name goblint.backtrace))
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
type mark = ..
2+
3+
module Exn =
4+
struct
5+
type t = exn
6+
let equal = (==)
7+
let hash = Hashtbl.hash
8+
end
9+
10+
module EWH = Ephemeron.K1.Make (Exn)
11+
12+
let marks: mark EWH.t = EWH.create 10
13+
14+
let add_mark e m =
15+
EWH.add marks e m
16+
17+
18+
(* Copied & modified from Fun. *)
19+
let protect ~(mark: unit -> mark) ~(finally: unit -> unit) work =
20+
let finally_no_exn () =
21+
try
22+
finally ()
23+
with e ->
24+
let bt = Printexc.get_raw_backtrace () in
25+
let finally_exn = Fun.Finally_raised e in
26+
add_mark finally_exn (mark ());
27+
Printexc.raise_with_backtrace finally_exn bt
28+
in
29+
match work () with
30+
| result ->
31+
finally_no_exn ();
32+
result
33+
| exception work_exn ->
34+
let work_bt = Printexc.get_raw_backtrace () in
35+
finally_no_exn ();
36+
add_mark work_exn (mark ());
37+
Printexc.raise_with_backtrace work_exn work_bt
38+
39+
40+
let mark_printers: (mark -> string option) list ref = ref []
41+
42+
let register_mark_printer f =
43+
mark_printers := f :: !mark_printers
44+
45+
let apply_mark_printers m =
46+
List.find_map (fun f ->
47+
match f m with
48+
| Some s -> Some s
49+
| None
50+
| exception _ -> None
51+
) !mark_printers
52+
53+
let mark_to_string_default m =
54+
Obj.Extension_constructor.(name (of_val m))
55+
56+
let mark_to_string m =
57+
match apply_mark_printers m with
58+
| Some s -> s
59+
| None -> mark_to_string_default m
60+
61+
let find_marks e =
62+
List.rev (EWH.find_all marks e)
63+
64+
let print_marktrace oc e =
65+
let ms = find_marks e in
66+
List.iter (fun m ->
67+
Printf.fprintf oc "Marked with %s\n" (mark_to_string m)
68+
) ms
69+
70+
let () =
71+
Printexc.set_uncaught_exception_handler (fun e bt ->
72+
(* Copied & modified from Printexc.default_uncaught_exception_handler. *)
73+
Printf.eprintf "Fatal error: exception %s\n" (Printexc.to_string e);
74+
if Printexc.backtrace_status () then
75+
print_marktrace stderr e;
76+
Printexc.print_raw_backtrace stderr bt;
77+
flush stderr
78+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
(** Backtraces with custom marks. *)
2+
3+
(** {2 Mark definition} *)
4+
5+
type mark = ..
6+
(** Extensible type of marks. *)
7+
8+
val register_mark_printer: (mark -> string option) -> unit
9+
(** Register printing function for custom mark.
10+
The function should return [None] for other marks. *)
11+
12+
val mark_to_string: mark -> string
13+
(** Convert mark to string using registered printers, or {!mark_to_string_default} if unhandled. *)
14+
15+
val mark_to_string_default: mark -> string
16+
(** Convert mark to default string. *)
17+
18+
19+
(** {2 Mark usage} *)
20+
21+
val add_mark: exn -> mark -> unit
22+
(** Add mark to exception. *)
23+
24+
val protect: mark:(unit -> mark) -> finally:(unit -> unit) -> (unit -> 'a) -> 'a
25+
(** {!Fun.protect} with additional [~mark] addition to all exceptions. *)
26+
27+
val print_marktrace: out_channel -> exn -> unit
28+
(** Print trace of marks of an exception.
29+
30+
Used by default for uncaught exceptions. *)
31+
32+
val find_marks: exn -> mark list
33+
(** Find all marks of an exception. *)

0 commit comments

Comments
 (0)