How to disable inlining, for real?

From time to time it’s nice to have a backtrace that matches exactly the literal code flow of a program.
Then, one want to disable inlining, but that’s not easy. Even -Oclassic -inline 0 does not, according to documentation, prevent all inlining. Resorting to the flambda options appears not that trivial either.

Here is a simple example:

% cat test.ml
let[@inline never] foo x = if x = 2 then raise Not_found else x + 1
let[@inline never] bar x = foo (x + 1)
let[@inline never] baz x = bar (x + 1)
let () =
  let x = baz 0 in
  exit x
% ocamlfind ocamlopt -version
4.07.1
% ocamlfind ocamlopt -inline-max-depth 0 -inline-max-unroll 0 -inline-toplevel 0 -g -linkpkg test.ml -o test
% ./test
Fatal error: exception Not_found
Raised at file "test.ml", line 1, characters 41-56
Called from file "test.ml", line 5, characters 10-15
% ocamlfind ocamlc -g -linkpkg test.ml -o test.byte
% ./test.byte
Fatal error: exception Not_found
Raised at file "test.ml", line 1, characters 47-56
Called from file "test.ml", line 5, characters 10-15

How could one obtain a stack frame that mention foo, bar and baz?

1 Like

In this particular case, I believe the problem is not inlining but instead tail-calls. Indeed your baz and bar function end with a tail-call, and since in a tail-call, the callee stack frame replaces the caller stack frame, well the backtrace shown is actually correct, i.e. it faithfully represents the state of the stack at the moment the exception is raised (which, as you point out, is different from what one might instinctively expect).

One possible workaround is to define an extra identity function, prevent its inlining, and wrap the results of tail-calling functions with it:

let[@inline never] id x = x
let[@inline never] foo x = if x = 2 then raise Not_found else x + 1
let[@inline never] bar x = id (foo (x + 1))
let[@inline never] baz x = id (bar (x + 1))
let () =
  let x = baz 0 in
  exit x

(Also, if you use a Flambda compiler and remove the [@inline] annotations, you’ll get a decent backtrace as inlining actually preserves the backtraces)

Looks like you are all correct, thank you for the answers!