upstream: https://github.com/janestreet/memtrace
1type tracer = Memprof_tracer.t
2
3let getpid64 () = Int64.of_int (Unix.getpid ())
4
5let start_tracing ~if_started ~context ~sampling_rate ~filename =
6 match Memprof_tracer.active_tracer () with
7 | Some tracer -> (
8 match if_started with
9 | `Fail -> failwith "Only one Memtrace instance may be active at a time"
10 | `Ignore -> tracer)
11 | None ->
12 let fd =
13 try Unix.openfile filename Unix.[ O_CREAT; O_WRONLY ] 0o600
14 with Unix.Unix_error (err, _, _) ->
15 raise
16 (Invalid_argument
17 ("Cannot open memtrace file " ^ filename ^ ": "
18 ^ Unix.error_message err))
19 in
20 (try Unix.lockf fd F_TLOCK 0
21 with Unix.Unix_error ((EAGAIN | EACCES), _, _) ->
22 Unix.close fd;
23 raise
24 (Invalid_argument
25 ("Cannot lock memtrace file " ^ filename
26 ^ ": is another process using it?")));
27 (try Unix.ftruncate fd 0
28 with Unix.Unix_error _ ->
29 (* On special files (e.g. /dev/null), ftruncate fails. Ignoring errors
30 here gives us the truncate-if-a-regular-file behaviour of O_TRUNC. *)
31 ());
32 let info : Trace.Info.t =
33 {
34 sample_rate = sampling_rate;
35 word_size = Sys.word_size;
36 executable_name = Sys.executable_name;
37 host_name = Unix.gethostname ();
38 ocaml_runtime_params = Sys.runtime_parameters ();
39 pid = getpid64 ();
40 initial_domain = Trace.Domain_id.main_domain;
41 start_time = Trace.Timestamp.now ();
42 context;
43 }
44 in
45 let trace = Trace.Writer.create fd ~getpid:getpid64 info in
46 Memprof_tracer.start ~sampling_rate trace
47
48let stop_tracing t = Memprof_tracer.stop t
49
50let () =
51 at_exit (fun () ->
52 match Memprof_tracer.active_tracer () with
53 | Some s -> stop_tracing s
54 | None -> ())
55
56let currently_tracing () =
57 match Memprof_tracer.active_tracer () with Some _ -> true | None -> false
58
59let default_sampling_rate = 1e-5
60
61let trace_if_requested_env ?(if_started = `Ignore) ?context ?sampling_rate () =
62 match Sys.getenv_opt "MEMTRACE" with
63 | None | Some "" -> ()
64 | Some filename ->
65 (* Prevent spawned OCaml programs from being traced *)
66 (Unix.putenv [@ocaml.alert "-unsafe_multidomain"]) "MEMTRACE" "";
67 let check_rate = function
68 | Some rate when 0. < rate && rate <= 1. -> rate
69 | _ ->
70 raise
71 (Invalid_argument
72 ("Memtrace.trace_if_requested: "
73 ^ "sampling_rate must be between 0 and 1"))
74 in
75 let sampling_rate =
76 match sampling_rate with
77 | Some _ -> check_rate sampling_rate
78 | None -> (
79 match Sys.getenv_opt "MEMTRACE_RATE" with
80 | None | Some "" -> default_sampling_rate
81 | Some rate -> check_rate (float_of_string_opt rate))
82 in
83 let _s = start_tracing ~if_started ~context ~sampling_rate ~filename in
84 ()
85
86let term =
87 let open Cmdliner in
88 let memtrace =
89 let doc = "Write a memory allocation trace to $(docv)." in
90 Arg.(value & opt (some string) None & info [ "memtrace" ] ~docv:"FILE" ~doc)
91 in
92 let memtrace_rate =
93 let doc =
94 "Sampling rate for memtrace (proportion of words to sample, default \
95 1e-5)."
96 in
97 Arg.(
98 value & opt (some float) None & info [ "memtrace-rate" ] ~docv:"RATE" ~doc)
99 in
100 let memtrace_context =
101 let doc = "Context string to identify this trace." in
102 Arg.(
103 value
104 & opt (some string) None
105 & info [ "memtrace-context" ] ~docv:"CTX" ~doc)
106 in
107 let start file rate context =
108 match file with
109 | None -> ()
110 | Some filename ->
111 let sampling_rate =
112 match rate with Some r -> r | None -> default_sampling_rate
113 in
114 let _t =
115 start_tracing ~if_started:`Ignore ~context ~sampling_rate ~filename
116 in
117 ()
118 in
119 Term.(const start $ memtrace $ memtrace_rate $ memtrace_context)
120
121let trace_if_requested ?if_started ?context ?sampling_rate () =
122 ignore (Cmdliner.Cmd.eval_peek_opts term : unit option * _);
123 trace_if_requested_env ?if_started ?context ?sampling_rate ()
124
125let enabled ?argv () =
126 ignore (Cmdliner.Cmd.eval_peek_opts ?argv term : unit option * _)
127
128let start_tracing ~context ~sampling_rate ~filename =
129 (* Default of `Fail for backward compatibility *)
130 start_tracing ~if_started:`Fail ~context ~sampling_rate ~filename
131
132module Trace = Trace
133module Memprof_tracer = Memprof_tracer
134
135module External = struct
136 type token = Memprof_tracer.ext_token
137
138 let alloc = Memprof_tracer.ext_alloc
139 let free = Memprof_tracer.ext_free
140end
141
142module Geometric_sampler = Geometric_sampler