-
Notifications
You must be signed in to change notification settings - Fork 46
Segfault on fac test with OCaml 4.12.0 #175
Description
I'm having problems with test_fac_issue59 from test/core/QCheck_expect_test.ml and ... issue #59.
Stripping away the expect tests I'm down to this:
open QCheck
let rec fac n = match n with
| 0 -> 1
| n -> n * fac (n - 1)
let test_fac =
Test.make
(make (Gen.return 600_000))
(fun n ->
(*Printf.printf "%i\n%!" n;*)
try
0 = fac n
with Stack_overflow -> false
)
let () = QCheck_base_runner.set_seed 1234
let _ = QCheck_base_runner.run_tests [test_fac]I've also cut away dune to reduce the number of moving parts:
$ ocamlbuild -clean && ocamlbuild -pkg qcheck fac_test.native
Finished, 0 targets (0 cached) in 00:00:00.
Finished, 4 targets (0 cached) in 00:00:00.
$ ./fac_test.native
random seed: 1234
Segmentation fault (core dumped)Compiling with debug information and running under gdb pinpoints the fac function and can show a long stack trace of fac frames. I have not been able to get the debug-executable to print a stack trace with OCAMLRUNPARAM=b (or variants).
The segfault disappears and the test behaves as expected if I
- comment in the
Printf.printfor - comment out the exception handler.
I get this behaviour with OCaml 4.12.0 across QCheck versions 0.18, 0.17, 0.15, 0.9 (I just tried a selection).
I cannot reproduce it with OCaml 4.11.2 on either of these QCheck versions.
I don't experience the problem if I instead compile and run with the bytecode backend.
I'd be grateful if others could confirm this behaviour to help understand if OS and hardware play in.
The above could indicate an OCaml issue - but it could affect QCheck when we include and trigger the test in our CI.
This is on a Linux machine, kernel 5.4.0-81, hardware is a Thinkpad with a 64-bit, dual-core Intel i5 CPU.
ulimit -s reports a stack limit of 8192.
I sometimes experience the bug as somewhat flaky (caveat: may be coffee/sleep underflow on my part... 😄)
A variant (probably increasing the required stack height) just repeats the test twice:
let _ = QCheck_base_runner.run_tests [test_fac; test_fac]I have sometimes experienced this variant to segfault when the first one stopped doing so.
(I'll tag @gasche as he has had his hands in both QCheck and the OCaml compiler...)