Source file flame_graph_panel.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
open! Core
open Bonsai_web
open Vdom_keyboard
open Memtrace_viewer_common
module Graph = struct
type t =
{ focus : Data.Fragment.t
; trie : Data.Fragment_trie.t
; call_sites : Data.Call_sites.t
; flame_tree : Data.Fragment.Oriented.t list
; icicle_tree : Data.Fragment.Oriented.t list
; focus_seq : Data.Fragment.Iterator.t option
}
module Node = struct
type t =
| Tree of Data.Fragment.Oriented.t
| Focus of Data.Fragment.Iterator.t
let location = function
| Tree tree -> Data.Fragment.Oriented.first tree
| Focus seq -> Data.Fragment.Iterator.location seq
;;
let type_ ~graph:_ t : Flame_graph_view.Node_type.t =
match location t with
| Allocation_site _ -> Allocation_site
| Function _ -> Function
| Allocator | Toplevel | Dummy -> assert false
;;
let label ~graph:_ t =
match location t with
| Allocation_site alloc_site -> Data.Allocation_site.short_name alloc_site
| loc -> Data.Location.defname loc
;;
let entry ~graph = function
| Tree tree -> Data.Fragment.entry (Data.Fragment.Oriented.fragment tree)
| Focus _ -> Data.Fragment.entry graph.focus
;;
let allocations ~graph t = Data.Entry.allocations (entry ~graph t)
let size ~graph t = allocations ~graph t |> Byte_units.bytes_float
let details ~graph t =
let loc = location t in
let entry = entry ~graph t in
let call_sites = Data.Call_sites.for_location graph.call_sites loc in
String.concat
[ Data.Location.full_name loc ~call_sites:(Some call_sites)
; ": "
; Data.Entry.allocations_string entry
; " ("
; Data.Entry.percentage_string entry
; ")"
]
;;
end
module Tree = struct
type t = Data.Fragment.Oriented.t
let node t = Node.Tree t
let children ~graph:_ t =
let children = Data.Fragment.Oriented.one_frame_extensions t in
List.filter_map children ~f:(fun (loc, fragment) ->
if Data.Location.is_special loc then None else Some fragment)
;;
let parent ~graph t =
let%bind.Option t = Data.Fragment.Oriented.retract t in
let fragment = Data.Fragment.Oriented.fragment t in
if Data.Fragment.same fragment graph.focus then None else Some t
;;
let same (t1 : t) (t2 : t) =
let fragment1 = Data.Fragment.Oriented.fragment t1 in
let fragment2 = Data.Fragment.Oriented.fragment t2 in
Data.Fragment.same fragment1 fragment2
;;
let allocations t =
let fragment = Data.Fragment.Oriented.fragment t in
let entry = Data.Fragment.entry fragment in
Data.Entry.allocations entry
;;
end
module Sequence = struct
type t = Data.Fragment.Iterator.t
let node t = Node.Focus t
let label ~graph t =
let fragment = Data.Fragment.Iterator.suffix t in
let allocs = Data.Entry.allocations (Data.Fragment.entry fragment) in
let total_allocs = Data.Fragment_trie.total_allocations graph.trie in
let percentage = Byte_units.(allocs // total_allocs) *. 100. in
[ allocs |> Byte_units.Short.to_string; sprintf "%.1f%%" percentage ]
;;
let next ~graph:_ seq =
let%bind.Option next = Data.Fragment.Iterator.next seq in
if Data.Location.is_special (Data.Fragment.Iterator.location next)
then None
else Some next
;;
let prev ~graph:_ seq =
let%bind.Option prev = Data.Fragment.Iterator.prev seq in
if Data.Location.is_special (Data.Fragment.Iterator.location prev)
then None
else Some prev
;;
let same t1 t2 =
let prefix1 = Data.Fragment.Iterator.prefix t1 in
let prefix2 = Data.Fragment.Iterator.prefix t2 in
Data.Fragment.same prefix1 prefix2
;;
end
let create ~trie ~call_sites ~focus =
let flame_tree =
if Data.Fragment.is_empty focus
then []
else (
let orient = Orientation.Callees in
let tree = Data.Fragment.oriented focus ~orient in
Tree.children ~graph:() tree)
in
let icicle_tree =
if Data.Fragment.is_empty focus
then []
else (
let orient = Orientation.Callers in
let tree = Data.Fragment.oriented focus ~orient in
Tree.children ~graph:() tree)
in
let focus_seq =
let%bind.Option seq = Data.Fragment.iterator_start focus in
let rec loop seq =
if Data.Location.is_special (Data.Fragment.Iterator.location seq)
then (
match Data.Fragment.Iterator.next seq with
| None -> None
| Some seq -> loop seq)
else Some seq
in
loop seq
in
{ trie; call_sites; focus; flame_tree; icicle_tree; focus_seq }
;;
let flame_tree { flame_tree; _ } = flame_tree
let icicle_tree { icicle_tree; _ } = icicle_tree
let focus { focus_seq; _ } = focus_seq
let size { flame_tree; icicle_tree; focus_seq; focus; _ } =
let allocations =
match focus_seq with
| Some _ ->
let entry = Data.Fragment.entry focus in
Data.Entry.allocations entry
| None ->
let flame_allocations =
List.fold_left
~init:Byte_units.zero
~f:(fun acc tree -> Byte_units.(acc + Tree.allocations tree))
flame_tree
in
let icicle_allocations =
List.fold_left
~init:Byte_units.zero
~f:(fun acc tree -> Byte_units.(acc + Tree.allocations tree))
icicle_tree
in
Byte_units.max flame_allocations icicle_allocations
in
Byte_units.bytes_float allocations
;;
end
module Flame_graph = Flame_graph_view.Make (Graph)
module Selection = struct
type t =
| Flame of { fragment : Data.Fragment.t }
| Icicle of { fragment : Data.Fragment.t }
| Focus of
{ callers_fragment : Data.Fragment.t
; callees_fragment : Data.Fragment.t
}
let of_selector (selector : Flame_graph.Selector.t) : t =
match selector with
| Flame tree ->
let fragment = Data.Fragment.Oriented.fragment tree in
Flame { fragment }
| Icicle tree ->
let fragment = Data.Fragment.Oriented.fragment tree in
Icicle { fragment }
| Focus seq ->
let callers_fragment = Data.Fragment.Iterator.suffix seq in
let callees_fragment = Data.Fragment.Iterator.prefix seq in
Focus { callers_fragment; callees_fragment }
;;
end
module Default_selection = App_state.Default_selection
module State = struct
type t =
| Flame of { backtrace : Data.Backtrace.t }
| Icicle of { backtrace : Data.Backtrace.Reversed.t }
| Focus of { trace : Data.Fragment.Iterator.Trace.t }
[@@deriving sexp, equal]
let to_selector ~trie : t -> Flame_graph.Selector.t option = function
| Flame { backtrace } ->
let%map.Option fragment = Data.Fragment_trie.find trie backtrace in
let orient = Orientation.Callees in
let tree = Data.Fragment.oriented fragment ~orient in
Flame_graph.Selector.Flame tree
| Icicle { backtrace } ->
let%map.Option fragment = Data.Fragment_trie.find_rev trie backtrace in
let orient = Orientation.Callers in
let tree = Data.Fragment.oriented fragment ~orient in
Flame_graph.Selector.Icicle tree
| Focus { trace } ->
let%map.Option seq = Data.Fragment_trie.find_iterator trie trace in
Flame_graph.Selector.Focus seq
;;
let of_selector : Flame_graph.Selector.t -> t = function
| Flame tree ->
let fragment = Data.Fragment.Oriented.fragment tree in
let backtrace = Data.Fragment.backtrace fragment in
Flame { backtrace }
| Icicle tree ->
let fragment = Data.Fragment.Oriented.fragment tree in
let backtrace = Data.Fragment.backtrace_rev fragment in
Icicle { backtrace }
| Focus seq ->
let trace = Data.Fragment.Iterator.trace seq in
Focus { trace }
;;
let default ~state ~default_selection ~focus =
match state with
| None -> None
| Some _ ->
(match default_selection with
| Default_selection.No_selection -> None
| Default_selection.First_caller ->
let%map.Option seq = Data.Fragment.iterator_start focus in
let trace = Data.Fragment.Iterator.trace seq in
Focus { trace }
| Default_selection.First_callee ->
let%map.Option seq = Data.Fragment.iterator_end focus in
let trace = Data.Fragment.Iterator.trace seq in
Focus { trace })
;;
end
let commands ~selection ~focus ~set_focus ~keep_selection_in_view
: Flame_graph_view.Commands.t
=
let module Command = Flame_graph_view.Command in
match selection with
| None -> Flame_graph_view.Commands.all_disabled
| Some selection ->
let set_focus fragment ~default_selection =
Effect.Many [ set_focus fragment ~default_selection; keep_selection_in_view ]
in
let extend_focus_to : Flame_graph_view.Command.t =
match (selection : Selection.t) with
| Flame { fragment } ->
Enabled
(set_focus fragment ~default_selection:App_state.Default_selection.First_callee)
| Icicle { fragment } ->
Enabled (set_focus fragment ~default_selection:First_caller)
| Focus _ -> Disabled
in
let retract_callers_from_focus : Command.t =
match selection with
| Focus { callers_fragment; _ } when not (Data.Fragment.same callers_fragment focus)
-> Enabled (set_focus callers_fragment ~default_selection:First_caller)
| Focus _ | Flame _ | Icicle _ -> Disabled
in
let retract_callees_from_focus : Command.t =
match selection with
| Focus { callees_fragment; _ } when not (Data.Fragment.same callees_fragment focus)
-> Enabled (set_focus callees_fragment ~default_selection:First_callee)
| Focus _ | Flame _ | Icicle _ -> Disabled
in
{ extend_focus_to; retract_callers_from_focus; retract_callees_from_focus }
;;
type t =
{ view : Vdom.Node.t
; key_handler : Keyboard_event_handler.t
; selection : Selection.t option
; reset_selection :
Data.Fragment.t -> default_selection:Default_selection.t -> unit Ui_effect.t
; scroll_focus_into_view : unit Ui_effect.t
}
let component ~trie ~call_sites ~focus ~set_focus ~activate =
let open Bonsai.Let_syntax in
let%sub state, set_state = Bonsai.state_opt () ~equal:[%equal: State.t] in
let%sub selection =
return
(let%map state = state
and trie = trie in
Option.bind ~f:(State.to_selector ~trie) state)
in
let%sub scroll_selection_into_view_after_display =
After_next_display.component (Value.return Flame_graph.scroll_selection_into_view)
in
let%sub scroll_focus_into_view_after_display =
After_next_display.component (Value.return Flame_graph.scroll_focus_into_view)
in
let%sub flame_graph =
let set_selection =
let%map set_state = set_state in
fun selector -> set_state (Some (State.of_selector selector))
in
let activate =
let%map activate = activate
and set_state = set_state in
fun selector ->
Ui_effect.Many
[ set_state (Some (State.of_selector selector))
; activate (Selection.of_selector selector)
]
in
let%sub graph : Graph.t Bonsai.Computation.t =
let%arr focus = focus
and trie = trie
and call_sites = call_sites in
Graph.create ~focus ~trie ~call_sites
in
let commands =
let%map selection = selection
and focus = focus
and set_focus = set_focus
and keep_selection_in_view = scroll_selection_into_view_after_display in
let selection = selection |> Option.map ~f:Selection.of_selector in
commands ~selection ~focus ~set_focus ~keep_selection_in_view
in
Flame_graph.component
graph
~selection
~select:set_selection
~navigate_to:set_selection
~activate
~commands
in
let selection =
let%map selection = selection in
Option.map ~f:Selection.of_selector selection
in
return
(let%map flame_graph = flame_graph
and state = state
and set_state = set_state
and selection = selection
and scroll_focus_into_view = scroll_focus_into_view_after_display in
let view = flame_graph.view in
let key_handler = flame_graph.key_handler in
let reset_selection focus ~default_selection =
let new_state = State.default ~state ~default_selection ~focus in
set_state new_state
in
{ view; key_handler; selection; reset_selection; scroll_focus_into_view })
;;