(* TEST flags = "-g"; { reference = "${test_source_directory}/comballoc.byte.reference"; bytecode; }{ reference = "${test_source_directory}/comballoc.opt.reference"; native; } *) (* Tests that combined allocations are counted correctly by statmemprof *) module MP = Gc.Memprof (* A single 5-word allocation - header plus 4 content words *) let[@inline never] f5 n = (n,n,n,n) (* A combined 12-word allocation: 5 words, 4 words, and 3 words *) let[@inline never] f12 n = (n, (n, n, f5 n)) let test sampling_rate = let allocs = Array.make 257 0 in (* block size -> allocated samples *) let deallocs = Array.make 257 0 in (* block size -> minor-dealloc samples *) let promotes = Array.make 257 0 in (* block size -> promoted samples *) let callstacks = Array.make 257 None in (* block size -> callstack option *) let _:MP.t = MP.start ~callstack_size:10 ~sampling_rate { MP.null_tracker with (* checks all allocations with a given block size have the same callstack *) alloc_minor = (fun info -> allocs.(info.size) <- allocs.(info.size) + info.n_samples; begin match callstacks.(info.size) with | None -> callstacks.(info.size) <- Some info.callstack | Some s -> assert (s = info.callstack) end; Some (info.size, info.n_samples)); dealloc_minor = (fun (sz,n) -> deallocs.(sz) <- deallocs.(sz) + n); promote = (fun (sz,n) -> promotes.(sz) <- promotes.(sz) + n; None); } in let iter = 100_000 in let arr = Array.make iter (0,0,0,0) in for i = 0 to Array.length arr - 1 do (* extract the 5-word alloc from a 12-word comballoc *) let (_, (_, _, x)) = Sys.opaque_identity f12 i in arr.(i) <- x; done; Gc.minor (); MP.stop (); (* use arr, so it's still alive here and is not collected *) ignore (Sys.opaque_identity arr); for i = 0 to 256 do assert (deallocs.(i) + promotes.(i) = allocs.(i)); if allocs.(i) > 0 then begin let total = (i + 1) * iter in (* allocs.(i) / total is Binomial(total, rate) / total which is approx. Normal(total * rate, total * rate*(1-rate)) / total which is Normal(1, rate*(1-rate) / total) which has stddev sqrt(rate*(1-rate)/total) which is less than 10^-3 for the values here. So, an error of 0.005 (enough to make %.2f print differently) is a 5-sigma event, with probability less than 3*10^-7 *) Printf.printf "%d: %.2f %b\n" i (float_of_int allocs.(i) /. float_of_int total) (promotes.(i) > 1000); (match callstacks.(i) with | Some s -> Printexc.print_raw_backtrace stdout s | None -> assert false) end done let () = test 0.42; test 0.01; test 0.83 let no_callback_after_stop trigger = let stopped = ref false in let cnt = ref 0 in let _:MP.t = MP.start ~callstack_size:0 ~sampling_rate:1. { MP.null_tracker with alloc_minor = (fun info -> assert(not !stopped); incr cnt; if !cnt > trigger then begin MP.stop (); stopped := true end; None); } in for i = 0 to 1000 do ignore (Sys.opaque_identity f12 i) done; assert !stopped let () = for i = 0 to 1000 do no_callback_after_stop i done; Printf.printf "OK\n"