(** Program for testing magic-trace with threads and long running processes. *)
open! Core
let[@inline never] create_pair n =
let x = Some n in
x, n + 1
;;
let[@inline never] my_tiny_fn n =
match create_pair n with
| Some n', n'' when n' = n && n'' = n + 1 -> ()
| _, _ -> failwith "lolwut"
;;
let[@inline never] call_n_times n =
for _ = 1 to n do
my_tiny_fn n
done
;;
(* Can potentially help with debugging, by encoding a number as
control flow so that it can be read out of the processor trace. *)
let[@inline never] rec number_as_control_flow n =
match n > 0 with
| false -> ()
| true ->
call_n_times (n % 10);
number_as_control_flow (n / 10)
;;
type t =
{ limit : int option
; num_snaps : int
; snap_every : Time_ns.Span.t
; last_snap : Time_ns.t
; do_snap : bool
; sleeps : bool
}
let[@inline never] rec main_loop t n =
number_as_control_flow n;
let calibrator = force Time_stamp_counter.calibrator in
let now = Time_stamp_counter.now () |> Time_stamp_counter.to_time_ns ~calibrator in
let next_snap = Time_ns.add t.last_snap t.snap_every in
match Time_ns.( > ) now next_snap with
| true ->
if t.do_snap
then Magic_trace.take_snapshot_with_time_and_arg (Time_stamp_counter.now ()) n;
(match t.limit with
| Some limit when limit >= t.num_snaps -> ()
| _ -> main_loop { t with last_snap = now; num_snaps = t.num_snaps + 1 } (n + 1))
| false ->
if t.sleeps && n % 4000 = 0 then ignore (Core_unix.nanosleep 0.001 : float);
main_loop t (n + 1)
;;
let command =
Command.basic
~summary:"sample executable for tracing that continually uses CPU"
(let%map_open.Command limit = flag "-limit" (optional int) ~doc:"snapshot limit"
and snap_every =
flag
"-snap-every"
(optional_with_default (Time_ns.Span.of_int_ms 1_000) Time_ns_unix.Span.arg_type)
~doc:"Call Magic_trace.take_snapshot at this interval"
and multi_thread = flag "-multi-thread" no_arg ~doc:"Spawn a second thread"
and sleeps = flag "-sleeps" no_arg ~doc:"Periodically sleep" in
fun () ->
let t =
{ limit
; snap_every
; num_snaps = 0
; last_snap = Time_ns.now ()
; sleeps
; do_snap = true
}
in
let thread =
match multi_thread with
| false -> None
| true ->
(* Do this before multi-threading to avoid race *)
let _calibrator = force Time_stamp_counter.calibrator in
Core_thread.create
~on_uncaught_exn:`Kill_whole_process
(fun () ->
let t2 = { t with do_snap = false } in
main_loop t2 0)
()
|> Some
in
main_loop t 0;
Option.iter thread ~f:Core_thread.join)
;;
let () = Command_unix.run command