summaryrefslogtreecommitdiffstats
path: root/stdlib/printexc.ml
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/printexc.ml')
-rw-r--r--stdlib/printexc.ml161
1 files changed, 135 insertions, 26 deletions
diff --git a/stdlib/printexc.ml b/stdlib/printexc.ml
index 9f20c7b46..4ebb84cea 100644
--- a/stdlib/printexc.ml
+++ b/stdlib/printexc.ml
@@ -82,12 +82,13 @@ let catch fct arg =
eprintf "Uncaught exception: %s\n" (to_string x);
exit 2
-type raw_backtrace
+type raw_backtrace_slot
+type raw_backtrace = raw_backtrace_slot array
external get_raw_backtrace:
unit -> raw_backtrace = "caml_get_exception_raw_backtrace"
-type loc_info =
+type backtrace_slot =
| Known_location of bool (* is_raise *)
* string (* filename *)
* int (* line number *)
@@ -98,29 +99,27 @@ type loc_info =
(* to avoid warning *)
let _ = [Known_location (false, "", 0, 0, 0); Unknown_location false]
-type backtrace = loc_info array
+external convert_raw_backtrace_slot:
+ raw_backtrace_slot -> backtrace_slot = "caml_convert_raw_backtrace_slot"
-external convert_raw_backtrace:
- raw_backtrace -> backtrace option = "caml_convert_raw_backtrace"
+let convert_raw_backtrace rbckt =
+ try Some (Array.map convert_raw_backtrace_slot rbckt)
+ with Failure _ -> None
-let format_loc_info pos li =
- let is_raise =
- match li with
- | Known_location(is_raise, _, _, _, _) -> is_raise
- | Unknown_location(is_raise) -> is_raise in
- let info =
+let format_backtrace_slot pos slot =
+ let info is_raise =
if is_raise then
if pos = 0 then "Raised at" else "Re-raised at"
else
if pos = 0 then "Raised by primitive operation at" else "Called from"
in
- match li with
+ match slot with
+ | Unknown_location true -> (* compiler-inserted re-raise, skipped *) None
+ | Unknown_location false ->
+ Some (sprintf "%s unknown location" (info false))
| Known_location(is_raise, filename, lineno, startchar, endchar) ->
- sprintf "%s file \"%s\", line %d, characters %d-%d"
- info filename lineno startchar endchar
- | Unknown_location(is_raise) ->
- sprintf "%s unknown location"
- info
+ Some (sprintf "%s file \"%s\", line %d, characters %d-%d"
+ (info is_raise) filename lineno startchar endchar)
let print_exception_backtrace outchan backtrace =
match backtrace with
@@ -129,8 +128,9 @@ let print_exception_backtrace outchan backtrace =
"(Program not linked with -g, cannot print stack backtrace)\n"
| Some a ->
for i = 0 to Array.length a - 1 do
- if a.(i) <> Unknown_location true then
- fprintf outchan "%s\n" (format_loc_info i a.(i))
+ match format_backtrace_slot i a.(i) with
+ | None -> ()
+ | Some str -> fprintf outchan "%s\n" str
done
let print_raw_backtrace outchan raw_backtrace =
@@ -147,20 +147,70 @@ let backtrace_to_string backtrace =
| Some a ->
let b = Buffer.create 1024 in
for i = 0 to Array.length a - 1 do
- if a.(i) <> Unknown_location true then
- bprintf b "%s\n" (format_loc_info i a.(i))
+ match format_backtrace_slot i a.(i) with
+ | None -> ()
+ | Some str -> bprintf b "%s\n" str
done;
Buffer.contents b
let raw_backtrace_to_string raw_backtrace =
backtrace_to_string (convert_raw_backtrace raw_backtrace)
+let backtrace_slot_is_raise = function
+ | Known_location(is_raise, _, _, _, _) -> is_raise
+ | Unknown_location(is_raise) -> is_raise
+
+type location = {
+ filename : string;
+ line_number : int;
+ start_char : int;
+ end_char : int;
+}
+
+let backtrace_slot_location = function
+ | Unknown_location _ -> None
+ | Known_location(_is_raise, filename, line_number,
+ start_char, end_char) ->
+ Some {
+ filename;
+ line_number;
+ start_char;
+ end_char;
+ }
+
+let backtrace_slots raw_backtrace =
+ (* The documentation of this function guarantees that Some is
+ returned only if a part of the trace is usable. This gives us
+ a bit more work than just convert_raw_backtrace, but it makes the
+ API more user-friendly -- otherwise most users would have to
+ reimplement the "Program not linked with -g, sorry" logic
+ themselves. *)
+ match convert_raw_backtrace raw_backtrace with
+ | None -> None
+ | Some backtrace ->
+ let usable_slot = function
+ | Unknown_location _ -> false
+ | Known_location _ -> true in
+ let rec exists_usable = function
+ | (-1) -> false
+ | i -> usable_slot backtrace.(i) || exists_usable (i - 1) in
+ if exists_usable (Array.length backtrace - 1)
+ then Some backtrace
+ else None
+
+module Slot = struct
+ type t = backtrace_slot
+ let format = format_backtrace_slot
+ let is_raise = backtrace_slot_is_raise
+ let location = backtrace_slot_location
+end
+
+let raw_backtrace_length bckt = Array.length bckt
+let get_raw_backtrace_slot bckt i = Array.get bckt i
+
(* confusingly named:
returns the *string* corresponding to the global current backtrace *)
let get_backtrace () =
- (* we could use the caml_get_exception_backtrace primitive here, but
- we hope to deprecate it so it's better to just compose the
- raw stuff *)
backtrace_to_string (convert_raw_backtrace (get_raw_backtrace ()))
external record_backtrace: bool -> unit = "caml_record_backtrace"
@@ -169,10 +219,8 @@ external backtrace_status: unit -> bool = "caml_backtrace_status"
let register_printer fn =
printers := fn :: !printers
-
external get_callstack: int -> raw_backtrace = "caml_get_current_callstack"
-
let exn_slot x =
let x = Obj.repr x in
if Obj.tag x = 0 then Obj.field x 0 else x
@@ -184,3 +232,64 @@ let exn_slot_id x =
let exn_slot_name x =
let slot = exn_slot x in
(Obj.obj (Obj.field slot 0) : string)
+
+
+let uncaught_exception_handler = ref None
+
+let set_uncaught_exception_handler fn = uncaught_exception_handler := Some fn
+
+let empty_backtrace : raw_backtrace = Obj.obj (Obj.new_block Obj.abstract_tag 0)
+
+let try_get_raw_backtrace () =
+ try
+ get_raw_backtrace ()
+ with _ (* Out_of_memory? *) ->
+ empty_backtrace
+
+let handle_uncaught_exception' exn debugger_in_use =
+ try
+ (* Get the backtrace now, in case one of the [at_exit] function
+ destroys it. *)
+ let raw_backtrace =
+ if debugger_in_use (* Same test as in [byterun/printexc.c] *) then
+ empty_backtrace
+ else
+ try_get_raw_backtrace ()
+ in
+ (try Pervasives.do_at_exit () with _ -> ());
+ match !uncaught_exception_handler with
+ | None ->
+ eprintf "Fatal error: exception %s\n" (to_string exn);
+ print_raw_backtrace stderr raw_backtrace;
+ flush stderr
+ | Some handler ->
+ try
+ handler exn raw_backtrace
+ with exn' ->
+ let raw_backtrace' = try_get_raw_backtrace () in
+ eprintf "Fatal error: exception %s\n" (to_string exn);
+ print_raw_backtrace stderr raw_backtrace;
+ eprintf "Fatal error in uncaught exception handler: exception %s\n"
+ (to_string exn');
+ print_raw_backtrace stderr raw_backtrace';
+ flush stderr
+ with
+ | Out_of_memory ->
+ prerr_endline
+ "Fatal error: out of memory in uncaught exception handler"
+
+(* This function is called by [caml_fatal_uncaught_exception] in
+ [byterun/printexc.c] which expects no exception is raised. *)
+let handle_uncaught_exception exn debugger_in_use =
+ try
+ handle_uncaught_exception' exn debugger_in_use
+ with _ ->
+ (* There is not much we can do at this point *)
+ ()
+
+external register_named_value : string -> 'a -> unit
+ = "caml_register_named_value"
+
+let () =
+ register_named_value "Printexc.handle_uncaught_exception"
+ handle_uncaught_exception