Logging, runtime tracing, and the obs performance profiler for OCaml
0

Configure Feed

Select the types of activity you want to include in your feed.

obs: extract memtrace report's derived predicates

merlint now measures functions inside functor bodies, and Model.report
lives in one. Its cyclomatic complexity (12) was driven by the boolean
operators of two derived flags, no_runtime_timing and no_evidence, computed
inline. Lift each to a named predicate above report. They are pure boolean
tests, which merlint does not count as control-flow complexity, and the
report body drops well under the threshold. No behaviour change.

+20 -16
+20 -16
lib/obs/memtrace_eio.ml
··· 1441 1441 1442 1442 (* A finding below [min_frac] of wall is not worth acting on (the "stop when 1443 1443 the payoff is trivial" rule), so it is dropped before ranking. *) 1444 + (* A trace with ocaml-probe spans but no GC-phase or Eio scheduler events: 1445 + waiting and gc are structurally zero and cpu = wall by default, so the 1446 + budget cannot be split and "CPU-bound" is never earned. Spans present with 1447 + none of that timing is the signature; a runtime_events capture of an Eio or 1448 + allocating program always carries fibers or GC slices. A bare allocating 1449 + loop has no spans, so it keeps the residual CPU verdict. *) 1450 + let no_runtime_timing t ~gc_time ~n_fibers ~n_ccs ~spans = 1451 + gc_time = 0. && n_fibers = 0 && n_ccs = 0 1452 + && Hashtbl.length t.suspend_total = 0 1453 + && spans <> [] 1454 + 1455 + (* The meta file recorded a wall window but the runtime_events stream was 1456 + empty and no memtrace samples arrived (mono_first never set): there is no 1457 + event of any kind to attribute, so the leftover wall is unknown, not 1458 + on-CPU. Distinct from no_runtime_timing, which still has spans. *) 1459 + let no_evidence t ~wall_time = 1460 + t.mono_first = None && t.total_alloc_words = 0 && wall_time > 0. 1461 + 1444 1462 let report ?(min_frac = 0.1) t : Report.t = 1445 1463 let now_ns = t.mono_last in 1446 1464 flush_open_state t ~now_ns; ··· 1623 1641 List.sort (fun (a, _) (b, _) -> compare b a) !raw_findings 1624 1642 |> List.mapi (fun i (_, f) -> { f with Report.rank = i + 1 }) 1625 1643 in 1626 - (* A trace with ocaml-probe spans but no GC-phase or Eio scheduler events: 1627 - waiting and gc are structurally zero and cpu = wall by default, so the 1628 - budget cannot be split and "CPU-bound" is never earned. Spans present with 1629 - none of that timing is the signature; a runtime_events capture of an Eio 1630 - or allocating program always carries fibers or GC slices. A bare allocating 1631 - loop has no spans, so it keeps the residual CPU verdict. *) 1632 1644 let no_runtime_timing = 1633 - gc_time = 0. && n_fibers = 0 && n_ccs = 0 1634 - && Hashtbl.length t.suspend_total = 0 1635 - && spans <> [] 1645 + no_runtime_timing t ~gc_time ~n_fibers ~n_ccs ~spans 1636 1646 in 1637 - (* The meta file recorded a wall window but the runtime_events stream was 1638 - empty and no memtrace samples arrived (mono_first never set): there is no 1639 - event of any kind to attribute, so the leftover wall is unknown, not 1640 - on-CPU. Distinct from no_runtime_timing, which still has spans. *) 1641 - let no_evidence = 1642 - t.mono_first = None && t.total_alloc_words = 0 && wall_time > 0. 1643 - in 1647 + let no_evidence = no_evidence t ~wall_time in 1644 1648 let verdict = 1645 1649 compute_verdict ~allocation_only ~no_runtime_timing ~no_evidence ~findings 1646 1650 ~frac ~waiting ~gc_frac ~cpu