upstream: https://github.com/janestreet/memtrace
at main 142 lines 4.7 kB view raw
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