package trace-fuchsia

  1. Overview
  2. Docs

Source file trace_fuchsia_write.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
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
(** Write fuchsia events into buffers.

Reference: https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format *)

module Util = Util
module Buf = Buf
module Output = Output
module Buf_pool = Buf_pool

open struct
  let spf = Printf.sprintf
end

open Util

type user_data = Trace_core.user_data

module I64 = struct
  include Int64

  let ( + ) = add
  let ( - ) = sub
  let ( = ) = equal
  let ( land ) = logand
  let ( lor ) = logor
  let lnot = lognot
  let ( lsl ) = shift_left
  let ( lsr ) = shift_right_logical
  let ( asr ) = shift_right
end

open struct
  (** maximum length as specified in the
        {{: https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format} spec} *)
  let max_str_len = 32000

  (** Length of string, in words *)
  let[@inline] str_len_word (s : string) =
    let len = String.length s in
    assert (len <= max_str_len);
    round_to_word len lsr 3

  let str_len_word_maybe_too_big s =
    let len = min max_str_len (String.length s) in
    round_to_word len lsr 3
end

module Str_ref = struct
  type t = int
  (** 16 bits *)

  let[@inline never] inline_fail_ () =
    invalid_arg
      (Printf.sprintf "fuchsia: max length of strings is %d" max_str_len)

  let inline (size : int) : t =
    if size > max_str_len then
      inline_fail_ ()
    else if size = 0 then
      0
    else
      (1 lsl 15) lor size
end

(** [truncate_string s] truncates [s] to the maximum length allowed for
    strings. If [s] is already short enough, no allocation is done. *)
let[@inline] truncate_string s : string =
  if String.length s <= max_str_len then
    s
  else
    String.sub s 0 max_str_len

module Thread_ref = struct
  type t =
    | Ref of int
    | Inline of {
        pid: int;
        tid: int;
      }

  let inline ~pid ~tid : t = Inline { pid; tid }

  let ref x : t =
    if x = 0 || x > 255 then
      invalid_arg "fuchsia: thread inline ref must be >0 < 256";
    Ref x

  let size_word (self : t) : int =
    match self with
    | Ref _ -> 0
    | Inline _ -> 2

  (** 8-bit int for the reference *)
  let as_i8 (self : t) : int =
    match self with
    | Ref i -> i
    | Inline _ -> 0
end

(** record type = 0 *)
module Metadata = struct
  (** First record in the trace *)
  module Magic_record = struct
    let value = 0x0016547846040010L
    let size_word = 1

    let encode (out : Output.t) =
      let buf = Output.get_buf out ~available_word:size_word in
      Buf.add_i64 buf value
  end

  module Initialization_record = struct
    let size_word = 2

    (** Default: 1 tick = 1 ns *)
    let default_ticks_per_sec = 1_000_000_000L

    let encode (out : Output.t) ~ticks_per_secs () : unit =
      let buf = Output.get_buf out ~available_word:size_word in
      let hd = I64.(1L lor (of_int size_word lsl 4)) in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf ticks_per_secs
  end

  module Provider_info = struct
    let size_word ~name () = 1 + str_len_word name

    let encode (out : Output.t) ~(id : int) ~name () : unit =
      let name = truncate_string name in
      let size = size_word ~name () in
      let buf = Output.get_buf out ~available_word:size in
      let hd =
        I64.(
          (of_int size lsl 4)
          lor (1L lsl 16)
          lor (of_int id lsl 20)
          lor (of_int (Str_ref.inline (str_len_word name)) lsl 52))
      in
      Buf.add_i64 buf hd;
      Buf.add_string buf name
  end

  module Provider_section = struct end
  module Trace_info = struct end
end

module Argument = struct
  type 'a t = string * ([< user_data | `Kid of int ] as 'a)

  let check_valid_ : _ t -> unit = function
    | _, `String s -> assert (String.length s < max_str_len)
    | _ -> ()

  let[@inline] is_i32_ (i : int) : bool = Int32.(to_int (of_int i) = i)

  let size_word (self : _ t) =
    let name, data = self in
    match data with
    | `None | `Bool _ -> 1 + str_len_word name
    | `Int i when is_i32_ i -> 1 + str_len_word name
    | `Int _ -> (* int64 *) 2 + str_len_word name
    | `Float _ -> 2 + str_len_word name
    | `String s -> 1 + str_len_word_maybe_too_big s + str_len_word name
    | `Kid _ -> 2 + str_len_word name

  open struct
    external int_of_bool : bool -> int = "%identity"
  end

  let encode (buf : Buf.t) (self : _ t) : unit =
    let name, data = self in
    let name = truncate_string name in
    let size = size_word self in

    (* part of header with argument name + size *)
    let hd_arg_size =
      I64.(
        (of_int size lsl 4)
        lor (of_int (Str_ref.inline (String.length name)) lsl 16))
    in

    match data with
    | `None ->
      let hd = hd_arg_size in
      Buf.add_i64 buf hd;
      Buf.add_string buf name
    | `Int i when is_i32_ i ->
      let hd = I64.(1L lor hd_arg_size lor (of_int i lsl 32)) in
      Buf.add_i64 buf hd;
      Buf.add_string buf name
    | `Int i ->
      (* int64 *)
      let hd = I64.(3L lor hd_arg_size) in
      Buf.add_i64 buf hd;
      Buf.add_string buf name;
      Buf.add_i64 buf (I64.of_int i)
    | `Float f ->
      let hd = I64.(5L lor hd_arg_size) in
      Buf.add_i64 buf hd;
      Buf.add_string buf name;
      Buf.add_i64 buf (I64.bits_of_float f)
    | `String s ->
      let s = truncate_string s in
      let hd =
        I64.(
          6L lor hd_arg_size
          lor (of_int (Str_ref.inline (String.length s)) lsl 32))
      in
      Buf.add_i64 buf hd;
      Buf.add_string buf name;
      Buf.add_string buf s
    | `Bool b ->
      let hd = I64.(9L lor hd_arg_size lor (of_int (int_of_bool b) lsl 16)) in
      Buf.add_i64 buf hd;
      Buf.add_string buf name
    | `Kid kid ->
      (* int64 *)
      let hd = I64.(8L lor hd_arg_size) in
      Buf.add_i64 buf hd;
      Buf.add_string buf name;
      Buf.add_i64 buf (I64.of_int kid)
end

module Arguments = struct
  type 'a t = 'a Argument.t list

  let[@inline] len (self : _ t) : int =
    match self with
    | [] -> 0
    | [ _ ] -> 1
    | _ :: _ :: tl -> 2 + List.length tl

  let check_valid (self : _ t) =
    let len = len self in
    if len > 15 then
      invalid_arg (spf "fuchsia: can have at most 15 args, got %d" len);
    List.iter Argument.check_valid_ self;
    ()

  let[@inline] size_word (self : _ t) =
    match self with
    | [] -> 0
    | [ a ] -> Argument.size_word a
    | a :: b :: tl ->
      List.fold_left
        (fun n arg -> n + Argument.size_word arg)
        (Argument.size_word a + Argument.size_word b)
        tl

  let[@inline] encode (buf : Buf.t) (self : _ t) =
    let rec aux buf l =
      match l with
      | [] -> ()
      | x :: tl ->
        Argument.encode buf x;
        aux buf tl
    in

    match self with
    | [] -> ()
    | [ x ] -> Argument.encode buf x
    | x :: tl ->
      Argument.encode buf x;
      aux buf tl
end

(** record type = 3 *)
module Thread_record = struct
  let size_word : int = 3

  (** Record that [Thread_ref.ref as_ref] represents the pair [pid, tid] *)
  let encode (out : Output.t) ~as_ref ~pid ~tid () : unit =
    if as_ref <= 0 || as_ref > 255 then
      invalid_arg "fuchsia: thread_record: invalid ref";

    let buf = Output.get_buf out ~available_word:size_word in

    let hd = I64.(3L lor (of_int size_word lsl 4) lor (of_int as_ref lsl 16)) in
    Buf.add_i64 buf hd;
    Buf.add_i64 buf (I64.of_int pid);
    Buf.add_i64 buf (I64.of_int tid)
end

(** record type = 4 *)
module Event = struct
  (** type=0 *)
  module Instant = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns ~args ()
        : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      (* set category = 0 *)
      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      ()
  end

  (** type=1 *)
  module Counter = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args + 1 (* counter id *)

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns ~args ()
        : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (1L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      (* just use 0 as counter id *)
      Buf.add_i64 buf 0L;
      ()
  end

  (** type=2 *)
  module Duration_begin = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns ~args ()
        : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (2L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      ()
  end

  (** type=3 *)
  module Duration_end = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns ~args ()
        : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (3L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      ()
  end

  (** type=4 *)
  module Duration_complete = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args + 1 (* end timestamp *)

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns
        ~end_time_ns ~args () : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      (* set category = 0 *)
      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (4L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      Buf.add_i64 buf end_time_ns;
      ()
  end

  (** type=5 *)
  module Async_begin = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args + 1 (* async id *)

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns
        ~(async_id : int) ~args () : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (5L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      Buf.add_i64 buf (I64.of_int async_id);
      ()
  end

  (** type=7 *)
  module Async_end = struct
    let size_word ~name ~t_ref ~args () : int =
      1 + Thread_ref.size_word t_ref + 1 (* timestamp *) + str_len_word name
      + Arguments.size_word args + 1 (* async id *)

    let encode (out : Output.t) ~name ~(t_ref : Thread_ref.t) ~time_ns
        ~(async_id : int) ~args () : unit =
      let name = truncate_string name in
      let size = size_word ~name ~t_ref ~args () in
      let buf = Output.get_buf out ~available_word:size in

      let hd =
        I64.(
          4L
          lor (of_int size lsl 4)
          lor (7L lsl 16)
          lor (of_int (Arguments.len args) lsl 20)
          lor (of_int (Thread_ref.as_i8 t_ref) lsl 24)
          lor (of_int (Str_ref.inline (String.length name)) lsl 48))
      in
      Buf.add_i64 buf hd;
      Buf.add_i64 buf time_ns;

      (match t_ref with
      | Thread_ref.Inline { pid; tid } ->
        Buf.add_i64 buf (I64.of_int pid);
        Buf.add_i64 buf (I64.of_int tid)
      | Thread_ref.Ref _ -> ());

      Buf.add_string buf name;
      Arguments.encode buf args;
      Buf.add_i64 buf (I64.of_int async_id);
      ()
  end
end

(** record type = 7 *)
module Kernel_object = struct
  let size_word ~name ~args () : int =
    1 + 1 + str_len_word name + Arguments.size_word args

  (* see:
     https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/public/zircon/types.h;l=441?q=ZX_OBJ_TYPE&ss=fuchsia%2Ffuchsia
  *)

  type ty = int

  let ty_process : ty = 1
  let ty_thread : ty = 2

  let encode (out : Output.t) ~name ~(ty : ty) ~(kid : int) ~args () : unit =
    let name = truncate_string name in
    let size = size_word ~name ~args () in
    let buf = Output.get_buf out ~available_word:size in

    let hd =
      I64.(
        7L
        lor (of_int size lsl 4)
        lor (of_int ty lsl 16)
        lor (of_int (Arguments.len args) lsl 40)
        lor (of_int (Str_ref.inline (String.length name)) lsl 24))
    in
    Buf.add_i64 buf hd;
    Buf.add_i64 buf (I64.of_int kid);
    Buf.add_string buf name;
    Arguments.encode buf args;
    ()
end
OCaml

Innovation. Community. Security.