I’m working on a library to capture exceptions and upload them to Sentry, and I’m running into a lot of trouble consistently getting backtrace information, especially if an
exn is returned from a function (as
('a, exn) Result.t or one of the variants of
Error.t for example), or in the case of Async where the Monitor knows the real backtrace but I can’t seem to get it out of the
It also makes it really annoying to write a try/finally construct containing a callback correctly (which is only possible at all since 4.05.0:
try f () with e -> let backtrace = Caml.Printexc.get_raw_backtrace () in do_something () >>| fun _ -> Caml.Printexc.raise_with_backtrace e backtrace
All of this seems to be because exn’s share a global backtrace, but I’m wondering – why? It seems to make exceptions substantially less useful, and I’m not sure how bad the performance impact would be. I’m guessing if there’s a big performance hit for doing this, the compiler could continue secretly using global backtraces, and then copy them into the exception if it detects that the exception can leave the current scope (returned or used in a closure) – if you’re returning an exception then you presumably want the info in it for some reason, and you can remove the backtrace if you want. Maybe it would make the compiler more complicated, but it seems like it would substantially improve Async/Lwt exceptions, and make it harder to mess up try/finally constructs.