package eio-trace

  1. Overview
  2. Docs

Source file read.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
module R = Eio.Buf_read

type thread = { pid : int64; tid : int64 }

type t = {
  strings : (int, string) Hashtbl.t;
  threads : (int, thread) Hashtbl.t;
}

let pp_thread f { pid; tid } = Fmt.pf f "%Ld:%Ld" pid tid

let bad_char = function
  | ' ' | '"' | '\'' | '\\' -> true
  | c ->
    let c = Char.code c in
    c <= 32 || c >= 127

let pp_string f x =
  if x = "" || String.exists bad_char x then Fmt.pf f "%S" x
  else Fmt.string f x

let ( >>> ) = Int64.shift_right_logical
let ( &&& ) = Int64.logand
let i64 = Int64.to_int

let parse_string t id body =
  if id = 0 then (
    "", body
  ) else if id land 0x8000 = 0 then (
    let s = Hashtbl.find_opt t.strings id |> Option.value ~default:"(missing string)" in
    s, body
  ) else (
    let len = id land 0x7fff in
    let s = Cstruct.to_string body ~len in
    let len = ((len + 7) lsr 3) lsl 3 in
    s, Cstruct.shift body len
  )

module Args = struct
  type value = [
    | `Int64 of int64
    | `Uint64 of int64
    | `String of string
    | `Pointer of int64
    | `Koid of int64
    | `Unknown of int
  ]

  type t = (string * value) list

  let parse t cs : (string * value) * Cstruct.t =
    let header = Cstruct.LE.get_uint64 cs 0 in
    let ty = i64 header land 0xf in
    let size = i64 (header >>> 4) land 0xfff in
    let rest = Cstruct.shift cs (size * 8) in
    let cs = Cstruct.shift cs 8 in
    let name = i64 (header >>> 16) land 0xffff in
    let name, cs = parse_string t name cs in
    let value =
      match ty with
      | 3 -> `Int64 (Cstruct.LE.get_uint64 cs 0)
      | 4 -> `Uint64 (Cstruct.LE.get_uint64 cs 0)
      | 6 ->
        let v = i64 (header >>> 32) land 0xffff in
        let v, _cs = parse_string t v cs in
        `String v
      | 7 -> `Pointer (Cstruct.LE.get_uint64 cs 0)
      | 8 -> `Koid (Cstruct.LE.get_uint64 cs 0)
      | x -> `Unknown x
    in
    (name, value), rest

  let pp_value f = function
    | `Int64 x -> Fmt.pf f "%Ld" x
    | `Uint64 x -> Fmt.pf f "%Lu" x
    | `String x -> pp_string f x
    | `Koid x -> Fmt.pf f "%Lu" x
    | `Pointer x -> Fmt.pf f "%Lu" x
    | `Unknown x -> Fmt.pf f "(unknown type %d)" x

  let pp f =
    List.iter (fun (k, v) -> Fmt.pf f "@ %a=%a" pp_string k pp_value v)
end

module Event = struct
  type ty =
    | Instant
    | Counter
    | Duration_begin
    | Duration_end
    | Duration_complete
    | Async_begin
    | Async_instant
    | Async_end
    | Flow_begin
    | Flow_step
    | Flow_end
    | Unknown of int

  type t = {
    ty : ty;
    timestamp : Int64.t;
    thread : thread;
    category : string;
    name : string;
    args : Args.t;
  }

  let ty = function
    | 0 -> Instant
    | 1 -> Counter
    | 2 -> Duration_begin
    | 3 -> Duration_end
    | 4 -> Duration_complete
    | 5 -> Async_begin
    | 6 -> Async_instant
    | 7 -> Async_end
    | 8 -> Flow_begin
    | 9 -> Flow_step
    | 10 -> Flow_end
    | x -> Unknown x

  let pp_ty f ty =
    Fmt.string f @@ match ty with
    | Instant -> "instant"
    | Counter -> "counter"
    | Duration_begin -> "duration begin"
    | Duration_end -> "duration end"
    | Duration_complete -> "duration complete"
    | Async_begin -> "async begin"
    | Async_instant -> "async instant"
    | Async_end -> "async end"
    | Flow_begin -> "flow begin"
    | Flow_step  -> "flow step"
    | Flow_end  -> "flow end"
    | Unknown _ -> "UNKNOWN"

  let pp f { ty; timestamp; args; thread; category; name } =
    Fmt.pf f "ts=%Ld %a cat=%a %a %a%a"
      timestamp
      pp_thread thread
      pp_string category
      pp_ty ty
      pp_string name
      Args.pp args
end

module Scheduling = struct
  type t =
    | Thread_wakeup of { cpu : int; timestamp : int64; id : int64; args : Args.t }
    | Unknown of int

  let pp f = function
    | Thread_wakeup { cpu; timestamp; id; args } ->
      Fmt.pf f "thread_wakeup ts=%Ld cpu=%d %Ld%a" timestamp cpu id Args.pp args
    | Unknown x -> Fmt.pf f "unknown %d" x
end

module User = struct
  type t = {
    id : int64;
    name : string;
    thread : thread;
    args : Args.t;
  }

  let pp f { id; name; thread; args } =
    Fmt.pf f "%Ld name=%a thread=%a%a" id pp_string name pp_thread thread Args.pp args
end

module Kernel = struct
  type t = {
    ty : int;
    koid : int64;
    name : string;
    args : Args.t;
  }

  let pp f { ty; koid; name; args } =
    Fmt.pf f "%Ld ty=%d name=%a%a" koid ty pp_string name Args.pp args
end

type record =
  | Metadata
  | Event of Event.t
  | User of User.t
  | Kernel of Kernel.t
  | Scheduling of Scheduling.t
  | Unknown of int

let rec parse_args t n_args body =
  if n_args = 0 then ([], body)
  else (
    let x, body = Args.parse t body in
    let xs, body = parse_args t (n_args - 1) body in
    (x :: xs), body
  )

let scheduling_record ~ty ~value t body =
  match ty with
  | 2 ->
    let n_args = i64 value land 0xf in
    let cpu = i64 (value >>> 4) land 0xffff in
    let timestamp = Cstruct.LE.get_uint64 body 0 in
    let id = Cstruct.LE.get_uint64 body 8 in
    let args, _body = parse_args t n_args (Cstruct.shift body 16) in
    Scheduling.Thread_wakeup { timestamp; cpu; id; args }
  | x -> Unknown x

let unknown_thread = { pid = -1L; tid = -1L }

let parse_thread t thread body =
  if thread = 0 then (
    let pid = Cstruct.LE.get_uint64 body 0 in
    let tid = Cstruct.LE.get_uint64 body 8 in
    { pid; tid }, Cstruct.shift body 16
  ) else (
    let thread = Hashtbl.find_opt t.threads thread |> Option.value ~default:unknown_thread in
    thread, body
  )

let parse_record t ty head body =
  match ty with
  | 0 -> Some Metadata
  | 2 ->
    let idx = i64 head land 0xffff in
    let len = i64 (head >>> 16) land 0xffff in
    let data = Cstruct.to_string body ~len in
    Hashtbl.replace t.strings idx data;
    None
  | 3 ->
    let idx = i64 head land 0xff in
    let pid = Cstruct.LE.get_uint64 body 0 in
    let tid = Cstruct.LE.get_uint64 body 8 in
    Hashtbl.replace t.threads idx { pid; tid };
    None
  | 4 ->
    let ty = i64 head land 0xf in
    let n_args = i64 (head >>> 4) land 0xf in
    let thread = i64 (head >>> 8) land 0xff in
    let category = i64 (head >>> 16) land 0xffff in
    let name = i64 (head >>> 32) land 0xffff in
    let timestamp = Cstruct.LE.get_uint64 body 0 in
    let body = Cstruct.shift body 8 in
    let thread, body = parse_thread t thread body in
    let category, body = parse_string t category body in
    let name, body = parse_string t name body in
    let args, _body = parse_args t n_args body in
    let ty = Event.ty ty in
    Some (Event { ty; timestamp; thread; name; category; args })
  | 6 ->
    let thread = i64 head land 0xff in
    let name = i64 (head >>> 8) land 0xffff in
    let n_args = i64 (head >>> 24) land 0xf in
    let id = Cstruct.LE.get_uint64 body 0 in
    let thread, body = parse_thread t thread body in
    let name, body = parse_string t name (Cstruct.shift body 8) in
    let args, _body = parse_args t n_args body in
    Some (User { id; name; thread; args })
  | 7 ->
    let ty = i64 head land 0xf in
    let name = i64 (head >>> 8) land 0xffff in
    let n_args = i64 (head >>> 24) land 0xf in
    let koid = Cstruct.LE.get_uint64 body 0 in
    let name, body = parse_string t name (Cstruct.shift body 8) in
    let args, _body = parse_args t n_args body in
    Some (Kernel { ty; koid; name; args })
  | 8 ->
    let ty = i64 (head >>> 44) in
    let value = (head &&& 0xfffffffffffL) in
    Some (Scheduling (scheduling_record ~ty ~value t body))
  | x -> Some (Unknown x)

let record t r =
  let header = R.LE.uint64 r in
  let low = (Int64.to_int header land 0xffff) in
  let ty = low land 0xf in
  let payload_size = ((low lsr 4) - 1) * 8 in
  R.ensure r payload_size;
  let x = parse_record t ty (header >>> 16) (Cstruct.sub (R.peek r) 0 payload_size) in
  R.consume r payload_size;
  x

let records r =
  let t = { strings = Hashtbl.create 100; threads = Hashtbl.create 100 } in
  let rec seq () =
    if R.at_end_of_input r then Seq.Nil
    else (
      match record t r with
      | None -> seq ()
      | Some x -> Cons (x, seq)
      | exception End_of_file -> Seq.Nil        (* File may be being written at the same time *)
    )
  in
  seq

let pp_record f = function
  | Metadata -> Fmt.string f "metadata"
  | Event e -> Fmt.pf f "@[<hv2>event %a@]"Event.pp e
  | User x -> Fmt.pf f "@[<hv2>user %a@]" User.pp x
  | Kernel x -> Fmt.pf f "@[<hv2>kernel %a@]" Kernel.pp x
  | Scheduling x -> Fmt.pf f "@[<hv2>scheduling %a@]" Scheduling.pp x
  | Unknown x -> Fmt.pf f "unknown(%d)" x
OCaml

Innovation. Community. Security.