Trace Event Format summary CLI
0

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-catapult / lib / catapult.mli
13 kB 332 lines
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2026 Thomas Gazagnaire. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** Trace Event Format (Catapult) JSON codecs. 7 8 The 9 {{:https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU} 10 Trace Event Format} is the JSON trace format consumed by 11 [chrome://tracing], {{:https://ui.perfetto.dev}Perfetto} and the Catapult 12 tools. This library provides typed values and {!Json.codec}s for it: 13 14 - {!Event}, one trace event (a {!Phase.t} plus the common members); 15 - {!type-t}, a whole trace in either container format: the JSON array format 16 (a plain list of events, allowing an unterminated tail) or the JSON object 17 format ([traceEvents] plus stack frames, samples and metadata); 18 - {!Writer}, a streaming array-format emitter for trace producers. 19 20 Produce and consume a trace: 21 22 {[ 23 let trace = 24 Catapult.v 25 [ 26 Catapult.Event.process_name ~pid:1 "compiler"; 27 Catapult.Event.complete ~pid:1 ~tid:1 ~name:"parse" ~ts:10. ~dur:200. 28 (); 29 ] 30 31 let s = Catapult.to_string trace 32 let trace' = Catapult.of_string_exn s 33 let () = assert (Catapult.equal trace trace') 34 ]} *) 35 36(** {1:events Events} *) 37 38module Phase = Phase 39(** Event phases ([ph] member). *) 40 41module Event = Event 42(** Trace events. *) 43 44(** {1:traces Traces} *) 45 46module Frame = Trace.Frame 47(** Stack frames ([stackFrames] dictionary values). *) 48 49module Sample = Trace.Sample 50(** Sampling profiler samples, the elements of the {!field-samples} array. *) 51 52type time_unit = Trace.time_unit 53(** Display time units for the trace viewer ([displayTimeUnit] member). *) 54 55type t = Trace.t = { 56 events : Event.t list; (** The [traceEvents] member. *) 57 display_time_unit : time_unit option; 58 system_trace_events : string option; 59 (** Linux ftrace or Windows ETW data, raw. *) 60 power_trace_as_string : string option; (** BattOr power data, raw. *) 61 controller_trace_data_key : string option; 62 (** Metadata key identifying the controller tracing agent's data. *) 63 stack_frames : (string * Frame.t) list; 64 (** The [stackFrames] dictionary, in source order. *) 65 samples : Sample.t list; (** The [samples] array. *) 66 metadata : (string * Json.t) list; 67 (** Any other object-format member, in source order. *) 68} 69(** The type for traces. *) 70 71val v : 72 ?display_time_unit:time_unit -> 73 ?system_trace_events:string -> 74 ?power_trace_as_string:string -> 75 ?controller_trace_data_key:string -> 76 ?stack_frames:(string * Frame.t) list -> 77 ?samples:Sample.t list -> 78 ?metadata:(string * Json.t) list -> 79 Event.t list -> 80 t 81(** [v events] is a trace of [events]. All extras default to absent or empty. *) 82 83val events_only : t -> bool 84(** [events_only t] is [true] iff [t] carries nothing but events, i.e. it can be 85 encoded in the array format without loss. *) 86 87val equal : t -> t -> bool 88(** [equal a b] compares the JSON encodings of [a] and [b]. *) 89 90val pp : t Fmt.t 91(** [pp] prints the JSON encoding of the trace. *) 92 93(** {1:rewrite Trace composition and rewriting} *) 94 95val map_events : (Event.t -> Event.t) -> t -> t 96(** [map_events f t] applies [f] to every event in [t]. *) 97 98val map_samples : (Sample.t -> Sample.t) -> t -> t 99(** [map_samples f t] applies [f] to every sample in [t]. *) 100 101val shift_time : float -> t -> t 102(** [shift_time dt t] adds [dt] microseconds to every event timestamp, thread 103 timestamp and sample timestamp in [t]. Durations are unchanged. *) 104 105val rewrite_pid : (int -> int) -> t -> t 106(** [rewrite_pid f t] rewrites every event [pid] in [t] with [f]. Events without 107 a [pid] are left unchanged. *) 108 109val rewrite_tid : (int -> int) -> t -> t 110(** [rewrite_tid f t] rewrites every event and sample [tid] in [t] with [f]. 111 Values without a [tid] are left unchanged. *) 112 113val set_pid : int -> t -> t 114(** [set_pid pid t] sets every event with a [pid] to [pid]. Events without a 115 [pid] are left unchanged. *) 116 117val set_tid : int -> t -> t 118(** [set_tid tid t] sets every event and sample with a [tid] to [tid]. Values 119 without a [tid] are left unchanged. *) 120 121val add_process_metadata : pid:int -> name:string -> t -> t 122(** [add_process_metadata ~pid ~name t] prepends a Catapult [process_name] 123 metadata event for [pid]. *) 124 125val add_thread_metadata : pid:int -> tid:int -> name:string -> t -> t 126(** [add_thread_metadata ~pid ~tid ~name t] prepends a Catapult [thread_name] 127 metadata event for [tid] in [pid]. *) 128 129val merge : ?time_offset:float -> t list -> t 130(** [merge ?time_offset traces] is one Catapult trace containing the events and 131 object-format extras of [traces], in order. 132 133 When [time_offset] is provided it is added to event and sample timestamps in 134 every input before concatenation. This is intentionally a plain offset; use 135 {!shift_time} on individual traces first when each input needs a different 136 alignment. 137 138 Stack-frame dictionaries are namespaced by input index and sample [sf] 139 references are rewritten to the new keys, so sampling data from different 140 producers cannot collide. Event-level [sf]/[stack] members live in 141 {!Event.t.extra} and are not rewritten; merge traces using them only when 142 their frame ids already agree. Colliding [pid]s are not disambiguated 143 either: apply {!rewrite_pid} per input first. Display time unit and 144 controller data keep the first value present. Raw system/power trace strings 145 are concatenated with a newline separator, and free-form metadata members 146 are appended in input order. *) 147 148(** {1:codecs Codecs} 149 150 Pass these to the {!Json} entry points for uses not covered by the functions 151 below, e.g. decoding with source locations or embedding a trace inside a 152 larger JSON document. *) 153 154val codec : t Json.codec 155(** JSON codec for traces. Decodes both container formats; encodes the array 156 format when {!events_only} holds and the object format otherwise. *) 157 158val array_codec : t Json.codec 159(** [array_codec] is jSON codec for the array container format. Encoding raises 160 [Invalid_argument] when {!events_only} is [false]. *) 161 162val object_codec : t Json.codec 163(** JSON codec for the object container format. *) 164 165(** {1:slices Slices} *) 166 167module Slice = Slice 168(** Events paired into time intervals, the rows a trace viewer draws. *) 169 170(** {1:errors Errors} *) 171 172module Error = Json.Error 173(** Decode errors, shared with {!Json}. *) 174 175exception Error of Error.t 176(** Alias for the {!Json} error exception, raised by [_exn] functions. *) 177 178(** {1:io Byte-stream decoding and encoding} *) 179 180val of_string : 181 ?meta:Json.meta -> ?file:Json.fpath -> string -> (t, Error.t) result 182(** [of_string s] decodes a trace from [s] in either container format. An 183 array-format input may lack the closing bracket (and have a trailing comma), 184 as written by producers that crashed mid-trace: the tail is repaired and 185 parsing retried. A complete trailing event is required; a partially written 186 final event is an error. *) 187 188val of_string_exn : ?meta:Json.meta -> ?file:Json.fpath -> string -> t 189(** [of_string_exn] is like {!of_string} but raises {!exception-Error}. *) 190 191val of_reader : 192 ?meta:Json.meta -> 193 ?file:Json.fpath -> 194 Bytesrw.Bytes.Reader.t -> 195 (t, Error.t) result 196(** [of_reader r] decodes a trace from [r]. Unlike {!of_string} the input is not 197 buffered, so the array-format tail repair is not available: the input must 198 be well-formed JSON. *) 199 200val of_reader_exn : 201 ?meta:Json.meta -> ?file:Json.fpath -> Bytesrw.Bytes.Reader.t -> t 202(** [of_reader_exn] is like {!of_reader} but raises {!exception-Error}. *) 203 204val fold_array_events : 205 ?meta:Json.meta -> 206 ?file:Json.fpath -> 207 init:'a -> 208 f:('a -> Event.t -> 'a) -> 209 Bytesrw.Bytes.Reader.t -> 210 ('a, Error.t) result 211(** [fold_array_events ~init ~f r] folds [f] over the events of an array-format 212 trace read from [r], one event at a time. [init] is the accumulator for an 213 empty trace and [f acc e] combines the running accumulator with the next 214 event [e]. 215 216 Only the array container format is read. Events are streamed straight from 217 [r] through {!Event.codec} and never collected, so a trace far larger than 218 memory can be consumed in a single pass by an [f] that retains only what it 219 needs; folding into a list reconstructs {!of_reader}'s events. 220 221 A trace truncated by a crashed or stopped producer (an unclosed array, a 222 trailing comma, or a half-written final event) is folded up to the 223 truncation: decoding stops at the first incomplete event and the accumulator 224 over the complete events before it is returned. An input whose root is not a 225 JSON array, or that yields no complete event, is an error. *) 226 227val fold_array_events_exn : 228 ?meta:Json.meta -> 229 ?file:Json.fpath -> 230 init:'a -> 231 f:('a -> Event.t -> 'a) -> 232 Bytesrw.Bytes.Reader.t -> 233 'a 234(** [fold_array_events_exn] is like {!fold_array_events} but raises 235 {!exception-Error}. *) 236 237val to_string : ?indent:int -> ?format:[ `Array | `Object ] -> t -> string 238(** [to_string t] encodes [t]. [indent] is the standard pretty-printing knob: 239 omitted means compact, [~indent:n] indents nested structures by [n] spaces 240 per level. [format] forces a container format; by default the array format 241 is used when {!events_only} holds and the object format otherwise. Forcing 242 [`Array] raises [Invalid_argument] when the trace carries non-event data. *) 243 244val to_writer : 245 ?indent:int -> 246 ?format:[ `Array | `Object ] -> 247 t -> 248 eod:bool -> 249 Bytesrw.Bytes.Writer.t -> 250 unit 251(** [to_writer t ~eod w] is like {!to_string} but writes on [w]. [eod] indicates 252 whether the end of data slice is written on [w]. *) 253 254(** {1:protobuf Protocol Buffers} 255 256 A binary encoding of the same {!type-t}: the typed event members are native 257 protobuf fields and the open-ended [args], [extra] and [metadata] values are 258 carried as their JSON text. It round-trips a trace unchanged and decodes 259 without the JSON tokenizer, so it is markedly cheaper to read and write than 260 the JSON forms above. *) 261 262val pb_codec : t Protobuf.t 263(** Protobuf codec for traces, for uses not covered by the functions below. *) 264 265val to_protobuf : t -> string 266(** [to_protobuf t] encodes [t] to its protobuf wire form. *) 267 268val to_protobuf_writer : t -> Bytesrw.Bytes.Writer.t -> unit 269(** [to_protobuf_writer t w] is like {!to_protobuf} but writes on [w]. *) 270 271val of_protobuf : string -> (t, Protobuf.Error.t) result 272(** [of_protobuf s] decodes a trace from its protobuf wire form. *) 273 274val of_protobuf_exn : string -> t 275(** [of_protobuf_exn] is like {!of_protobuf} but raises on a decode error. *) 276 277val of_protobuf_reader : Bytesrw.Bytes.Reader.t -> (t, Protobuf.Error.t) result 278(** [of_protobuf_reader r] decodes a trace from the protobuf bytes of [r]. *) 279 280val of_protobuf_reader_exn : Bytesrw.Bytes.Reader.t -> t 281(** [of_protobuf_reader_exn] is like {!of_protobuf_reader} but raises. *) 282 283val fold_protobuf_events : 284 init:'a -> f:('a -> Event.t -> 'a) -> string -> ('a, Protobuf.Error.t) result 285(** [fold_protobuf_events ~init ~f s] folds [f] over the events of the 286 protobuf-encoded trace [s], one event at a time, without materialising the 287 event list -- the binary counterpart to {!fold_array_events}. A trace 288 truncated by a crashed producer is folded up to the truncation. *) 289 290val fold_protobuf_events_reader : 291 init:'a -> 292 f:('a -> Event.t -> 'a) -> 293 Bytesrw.Bytes.Reader.t -> 294 ('a, Protobuf.Error.t) result 295(** [fold_protobuf_events_reader] is {!fold_protobuf_events} reading the 296 protobuf bytes from a reader. *) 297 298(** Streaming protobuf emitter for trace producers, the binary counterpart to 299 {!Writer}. Each {!event} is written as it arrives, so a producer never holds 300 the whole trace. *) 301module Pb_writer : sig 302 type t 303 (** A streaming protobuf trace writer. *) 304 305 val of_writer : Bytesrw.Bytes.Writer.t -> t 306 (** [of_writer w] is a writer emitting onto [w]. *) 307 308 val event : t -> Event.t -> unit 309 (** [event t e] appends [e] to the trace. Raises [Invalid_argument] once 310 {!close} has been called. *) 311 312 val close : ?eod:bool -> t -> unit 313 (** [close t] finishes the trace. [eod] (default [true]) writes the end of 314 data slice on the underlying writer. Idempotent. *) 315end 316 317(** {1:runtime Runtime over {!Json.t}} *) 318 319val decode : Json.t -> (t, Error.t) result 320(** [decode j] decodes a trace from a generic JSON value. *) 321 322val decode_exn : Json.t -> t 323(** [decode_exn] is like {!decode} but raises {!exception-Error}. *) 324 325val encode : ?format:[ `Array | `Object ] -> t -> Json.t 326(** [encode t] is [t] as a generic JSON value. [format] behaves as in 327 {!to_string}. *) 328 329(** {1:writer Streaming production} *) 330 331module Writer = Writer 332(** Streaming array-format emitter for trace producers. *)