Source file memprof_tracer.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
type t =
{ mutable locked : bool;
mutable locked_ext : bool;
mutable failed : bool;
mutable stopped : bool;
report_exn : exn -> unit;
trace : Trace.Writer.t;
ext_sampler : Geometric_sampler.t; }
let curr_active_tracer : t option ref = ref None
let active_tracer () = !curr_active_tracer
let bytes_before_ext_sample = ref max_int
let draw_sampler_bytes t =
Geometric_sampler.draw t.ext_sampler * (Sys.word_size / 8)
let[@inline never] rec lock_tracer s =
if s.locked then
if s.locked_ext then false
else (Thread.yield (); lock_tracer s)
else if s.failed then
false
else
(s.locked <- true; true)
let[@inline never] rec lock_tracer_ext s =
if s.locked then
(Thread.yield (); lock_tracer_ext s)
else if s.failed then
false
else
(s.locked <- true; s.locked_ext <- true; true)
let[@inline never] unlock_tracer s =
assert (s.locked && not s.locked_ext && not s.failed);
s.locked <- false
let[@inline never] unlock_tracer_ext s =
assert (s.locked && s.locked_ext && not s.failed);
s.locked_ext <- false;
s.locked <- false
let[@inline never] mark_failed s e =
assert (s.locked && not s.failed);
s.failed <- true;
s.locked <- false;
s.locked_ext <- false;
s.report_exn e
let default_report_exn e =
match e with
| Trace.Writer.Pid_changed ->
()
| e ->
let msg = Printf.sprintf "Memtrace failure: %s\n" (Printexc.to_string e) in
output_string stderr msg;
Printexc.print_backtrace stderr;
flush stderr
let start ?(report_exn=default_report_exn) ~sampling_rate trace =
let ext_sampler = Geometric_sampler.make ~sampling_rate () in
let s = { trace; locked = false; locked_ext = false; stopped = false; failed = false;
report_exn; ext_sampler } in
let tracker : (_,_) Gc.Memprof.tracker = {
alloc_minor = (fun info ->
if lock_tracer s then begin
match Trace.Writer.put_alloc_with_raw_backtrace trace (Trace.Timestamp.now ())
~length:info.size
~nsamples:info.n_samples
~source:Minor
~callstack:info.callstack
with
| r -> unlock_tracer s; Some r
| exception e -> mark_failed s e; None
end else None);
alloc_major = (fun info ->
if lock_tracer s then begin
match Trace.Writer.put_alloc_with_raw_backtrace trace (Trace.Timestamp.now ())
~length:info.size
~nsamples:info.n_samples
~source:Major
~callstack:info.callstack
with
| r -> unlock_tracer s; Some r
| exception e -> mark_failed s e; None
end else None);
promote = (fun id ->
if lock_tracer s then
match Trace.Writer.put_promote trace (Trace.Timestamp.now ()) id with
| () -> unlock_tracer s; Some id
| exception e -> mark_failed s e; None
else None);
dealloc_minor = (fun id ->
if lock_tracer s then
match Trace.Writer.put_collect trace (Trace.Timestamp.now ()) id with
| () -> unlock_tracer s
| exception e -> mark_failed s e);
dealloc_major = (fun id ->
if lock_tracer s then
match Trace.Writer.put_collect trace (Trace.Timestamp.now ()) id with
| () -> unlock_tracer s
| exception e -> mark_failed s e) } in
curr_active_tracer := Some s;
bytes_before_ext_sample := draw_sampler_bytes s;
Gc.Memprof.start
~sampling_rate
~callstack_size:max_int
tracker;
s
let stop s =
if not s.stopped then begin
s.stopped <- true;
Gc.Memprof.stop ();
if lock_tracer s then begin
try Trace.Writer.close s.trace with e -> mark_failed s e
end;
curr_active_tracer := None
end
let[@inline never] ext_alloc_slowpath ~bytes =
match !curr_active_tracer with
| None -> bytes_before_ext_sample := max_int; None
| Some s ->
if lock_tracer_ext s then begin
match
let bytes_per_word = Sys.word_size / 8 in
let size_words = (bytes + bytes_per_word - 1) / bytes_per_word in
let samples = ref 0 in
while !bytes_before_ext_sample <= 0 do
bytes_before_ext_sample :=
!bytes_before_ext_sample + draw_sampler_bytes s;
incr samples
done;
assert (!samples > 0);
let callstack = Printexc.get_callstack max_int in
Some (Trace.Writer.put_alloc_with_raw_backtrace s.trace
(Trace.Timestamp.now ())
~length:size_words
~nsamples:!samples
~source:External
~callstack)
with
| r -> unlock_tracer_ext s; r
| exception e -> mark_failed s e; None
end else None
type ext_token = Trace.Obj_id.t
let ext_alloc ~bytes =
let n = !bytes_before_ext_sample - bytes in
bytes_before_ext_sample := n;
if n <= 0 then ext_alloc_slowpath ~bytes else None
let ext_free id =
match !curr_active_tracer with
| None -> ()
| Some s ->
if lock_tracer_ext s then begin
match
Trace.Writer.put_collect s.trace (Trace.Timestamp.now ()) id
with
| () -> unlock_tracer_ext s; ()
| exception e -> mark_failed s e; ()
end