package core_profiler

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file interest.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
open Core
open Poly
open Core_profiler
open Core_profiler_disabled

module Raw = struct
  type 'a t =
    | Single of 'a
    | Group_point of 'a * 'a
    | Group_path of 'a * 'a Path.t
  [@@deriving sexp, compare]

  let is_path = function
    | Single _
    | Group_point _ -> false
    | Group_path _ -> true

  module I = struct
    type id_raw_interest = Probe_id.t t [@@deriving sexp, compare]
    module T = struct
      type t = id_raw_interest    [@@deriving sexp, compare]

      let hash = function
        | Single id -> Probe_id.to_int_exn id
        | Group_point (_, id) -> Probe_id.to_int_exn id
        | Group_path (_, path) -> Path.I.hash path
    end
    include T
    include Comparable.Make(T)
    include Hashable.Make_and_derive_hash_fold_t(T)
  end
end

module Interval_subject = struct
  type t =
    | Value
    | Delta
    | Time_delta
  [@@deriving sexp, compare]

  let of_string = function
    | "v"  -> Value
    | "dv" -> Delta
    | "dt" -> Time_delta
    | _ -> failwith "Bad interval subject, expected one of v, dv or dt"

  let to_string = function
    | Value      -> "v"
    | Delta      -> "dv"
    | Time_delta -> "dt"

  let to_int = function
    | Value -> 1
    | Delta -> 2
    | Time_delta -> 3

end

type 'a t =
  | All of 'a Raw.t
  | In_interval of 'a Raw.t * Interval_subject.t * Profiler_units.t * Interval.Int.t
[@@deriving sexp]

let interval_compare a b =
  match (Interval.Int.bounds a, Interval.Int.bounds b) with
  | (None, None) -> 0
  | (Some _, None) -> 1
  | (None, Some _) -> -1
  | (Some (la, ua), Some (lb, ub)) ->
    let c = Int.compare la lb in
    if c <> 0 then c else Int.compare ua ub

let compare a_compare x y =
  match (x, y) with
  | (All rx, All ry) -> Raw.compare a_compare rx ry
  | (In_interval (rx, sx, ux, ivx), In_interval(ry, sy, uy, ivy)) ->
    let c = Raw.compare a_compare rx ry in
    if c <> 0 then c else
    let c = Interval_subject.compare sx sy in
    if c <> 0 then c else
    let c = Profiler_units.compare ux uy in
    if c <> 0 then c else
    interval_compare ivx ivy

  | (All _, In_interval _) -> -1
  | (In_interval _, All _) -> 1

let raw = function
  | All r -> r
  | In_interval (r, _, _, _) -> r

let string_t_of_sexp = t_of_sexp String.t_of_sexp

let sexp_of_string_t = sexp_of_t String.sexp_of_t

let parse_filter =
  let regex = Or_error.ok_exn (Re2.create "(v|dv|dt)\\[(.+)\\,(.+)\\]") in
  (fun str ->
     let subs = Re2.find_submatches_exn regex str in
     let subject_str = Option.value_exn subs.(1) ~message:"missing subject" in
     let left_str = Option.value_exn subs.(2) ~message:"missing left limit" in
     let right_str = Option.value_exn subs.(3) ~message:"missing right limit" in
     let subject = Interval_subject.of_string subject_str in
     (subject, Util.int_units_of_string left_str, Util.int_units_of_string right_str))

let string_t_of_string str =
  let (str, filter_part) =
    match String.rsplit2 str ~on:'~' with
    | Some (l, r) -> (l, Some r)
    | None -> (str, None)
  in
  let raw =
    let open Raw in
    match String.lsplit2 str ~on:':' with
    | None -> Single str
    | Some (group, group_interest) ->
      begin
        match Path.string_t_of_string group_interest with
        | None -> Group_point (group, group_interest)
        | Some path -> Group_path (group, path)
      end
  in
  match filter_part with
  | Some filter_part ->
    let (subject, (left, left_units), (right, right_units)) = parse_filter filter_part in
    let units = Util.choose_units left_units right_units in
    let left  = Util.coerce_units left  ~current:left_units  ~desired:units in
    let right = Util.coerce_units right ~current:right_units ~desired:units in
    let interval = Interval.Int.create left right in
    In_interval (raw, subject, units, interval)

  | None -> All raw

let string_t_to_string interest =
  let raw_to_string r =
    let open Raw in
    match r with
    | Single a -> a
    | Group_point (g, pt) -> g ^ ":" ^ pt
    | Group_path (g, pth) -> g ^ ":" ^ Path.string_t_to_string pth
  in
  match interest with
  | All raw -> raw_to_string raw
  | In_interval (raw, sub, units, interval) ->
    let sub = Interval_subject.to_string sub in
    let (l, r) = Interval.Int.bounds_exn interval in
    sprintf "%s~%s[%s,%s]"
      (raw_to_string raw)
      sub
      (Profiler_units.format_int units l)
      (Profiler_units.format_int units r)

let lookup_ids' t (name_map : Util.Name_map.t) =
  let lookup_raw r =
    let open Raw in
    match r with
    | Single name ->
      Single (Map.find_exn name_map.singles name)

    | Group_point (name, point) ->
      let group = Map.find_exn name_map.groups name in
      Group_point (group.id, Map.find_exn group.children point)

    | Group_path (name, path) ->
      let group = Map.find_exn name_map.groups name in
      Group_path (group.id, Path.lookup_ids path group)
  in
  match t with
  | All raw -> All (lookup_raw raw)
  | In_interval (raw, subject, units, interval) ->
    In_interval (lookup_raw raw, subject, units, interval)

let lookup_ids t name_map =
  try
    lookup_ids' t name_map
  with
  | Not_found_s _ | Caml.Not_found as ex ->
    Exn.reraisef ex
      "Invalid interest %s: name lookup in header failed"
      (string_t_to_string t) ()

let lookup_names t id_map =
  let get_name x = Reader.Header.get_name_exn ?with_group:None id_map x in
  let lookup_raw r =
    let open Raw in
    match r with
    | Single id -> Single (get_name id)
    | Group_point (group, point) ->
      Group_point (get_name group, get_name point)
    | Group_path (group, path) ->
      Group_path (get_name group, Path.lookup_names path id_map)
  in
  match t with
  | All raw -> All (lookup_raw raw)
  | In_interval (raw, subject, units, interval) ->
    In_interval (lookup_raw raw, subject, units, interval)

let id_t_to_string t id_map = string_t_to_string (lookup_names t id_map)

let spec interest id_map =
  match (raw interest : Probe_id.t Raw.t) with
  | Single id
  | Group_point (id, _)
  | Group_path (id, _) ->
    Reader.Header.get_spec_exn id_map id

let is_path = function
  | All raw -> Raw.is_path raw
  | In_interval (raw, _, _, _) -> Raw.is_path raw

let coerce_interval_units t id_map =
  match t with
  | All _ -> t
  | In_interval (raw, iv_subject, iv_units, interval) ->
    let desired =
      match iv_subject with
      | Value | Delta ->
        Probe_type.units (spec t id_map)
        |> Option.value_exn ~message:"Can't filter the value / delta of a Timer"
      | Time_delta ->
        Profiler_units.Nanoseconds
    in
    let interval =
      Interval.Int.map
        interval
        ~f:(Util.coerce_units ~current:iv_units ~desired)
    in
    In_interval (raw, iv_subject, desired, interval)

let examples =
  let open Raw in
  [ All (Single "probe_or_timer_name")
  ; All (Group_point ("group1", "single_point_name"))
  ; In_interval
      ( Single "some_probe"
      , Value
      , Profiler_units.Words
      , Interval.Int.create 20_000 20_100
      )
  ; In_interval
      ( Group_path ("some_group", List.nth_exn Path.examples 0)
      , Delta
      , Profiler_units.Int
      , Interval.Int.create 5 12
      )
  ; In_interval
      ( Group_path ("some_other_group", List.nth_exn Path.examples 1)
      , Time_delta
      , Profiler_units.Nanoseconds
      , Interval.Int.create 20 10_000
      )
  ]

let%test "of_to_string" =
  List.for_all examples ~f:(fun ex ->
    let ex2 = ex |> string_t_to_string |> string_t_of_string in
    ex = ex2
  )

let readme = lazy (
  [ `S "An interest is a string that specifies some subset of the core-profiler file \
        that you are interested in / wish to inspect.\n\n"
  ; `S "You can ask for an individual timer or probe by specifying its name:\n\n"
  ; `S "    "; `E (All (Single "probe_or_timer_name")); `S "\n\n"
  ; `S "And inspect the values (no deltas) of a point of a probe group like so:\n\n"
  ; `S "    "; `E (All (Group_point ("group1", "single_point_name"))); `S "\n\n"
  ; `S "To get deltas from a group, you need to describe the path you're interested in.\n"
  ; `S (Lazy.force Path.readme); `S "\n\n"
  ; `S "You may also filter the data by demanding that the value, delta or time_delta be \
        in some interval. \
        The syntax for filtering is as follows: \n\n"
  ; `S "    interest~subject[lower,upper]\n\n"
  ; `S "Where interest is a raw interest as described above, subject is one of \
        't', 'd' or 'v', and [lower, upper] specify the interval. The interval may be \
        specified using units appropriate to the probe filtered, e.g., '3ms' or \
        '1kw'.\n\n"
  ; `S "Some examples:\n\n"
  ; `S "    "; `E (List.nth_exn examples 2); `S "\n"
  ; `S "    "; `E (List.nth_exn examples 3); `S "\n"
  ; `S "    "; `E (List.nth_exn examples 4)
  ]
  |> List.map ~f:(function
    | `S s -> s
    | `E e -> string_t_to_string e
  )
  |> String.concat
)

let arg_type = Command.Spec.Arg_type.create string_t_of_string
let list_arg =
  Command.Spec.(
    anon (sequence ("interest" %: arg_type))
  )

module I = struct
  type id_interest = Probe_id.t t [@@deriving sexp, compare]
  module T = struct
    type t = id_interest    [@@deriving sexp, compare]

    let hash_in_interval subj interval =
      let subj = Interval_subject.to_int subj in
      let l = Option.value ~default:1 (Interval.Int.lbound interval) in
      let h = Option.value ~default:2 (Interval.Int.ubound interval) in
      (1 lsl 17 - 1) + subj + l + h

    let hash = function
      | All raw -> Raw.I.hash raw
      | In_interval (raw, subj, _units, interval) ->
        Int.hash (Raw.I.hash raw * hash_in_interval subj interval)
  end
  include T
  include Comparable.Make(T)
  include Hashable.Make_and_derive_hash_fold_t(T)
end

let default_interests id_map =
  let get_points group_id =
    Reader.Header.((find_group_exn id_map group_id).Item.children)
  in

  let get_points_sources group_id =
    get_points group_id
    |> List.map ~f:(fun child_id ->
      let sources = Reader.Header.((find_group_point_exn id_map child_id).Item.sources) in
      (child_id, sources)
    )
  in

  let product_fold outer ~init ~get_inner ~f =
    List.fold outer ~init ~f:(fun acc b ->
      List.fold (get_inner b) ~init:acc ~f:(fun acc a ->
        f acc a b
      )
    )
  in

  let add_group_default_interests accum group_id =
    let group_points = get_points_sources group_id in
    let some_defaults_specified =
      List.exists
        group_points
        ~f:(fun (_id, sources) -> not (List.is_empty sources))
    in

    if some_defaults_specified
    then
      product_fold
        group_points
        ~get_inner:(fun (_, sources) -> sources)
        ~init:accum
        ~f:(fun acc a (b, _) ->
          let path = Path.({ last = b; first = Point a; rest_rev = [] }) in
          All (Group_path (group_id, path)) :: acc
        )
    else
      product_fold
        group_points
        ~get_inner:(fun _ -> group_points)
        ~init:accum
        ~f:(fun acc (a, _) (b, _) ->
          let path = Path.({ last = b; first = Direct_point a; rest_rev = [] }) in
          All (Group_path (group_id, path)) :: acc
        )
  in

  Id_table.fold
    id_map
    ~init:[]
    ~f:(fun acc id header_item ->
      match header_item with
      | Single _ -> All (Single id) :: acc
      | Group _ -> add_group_default_interests acc id
      | Group_point { parent; _ } -> All (Group_point (parent, id)) :: acc
    )
OCaml

Innovation. Community. Security.