Legend:
Library
Module
Module type
Parameter
Class
Class type
Write a trace in the Fuchsia Trace Format using a high level API. This allows you to convert your data to a trace and view it on an interactive timeline in Perfetto.
This module provides a wrapper around Tracing_zero.Writer.t which makes it easier to write traces with less steps and more normal OCaml types.
However, unlike Tracing_zero.Writer.t, this module allocates and doesn't pay close attention to its performance. It should still be fast enough for most offline converters though.
See app/tracing/bin/dominodb_converter.ml for an example of something using this API to visualize some performance data as a trace.
Viewing traces
You can view traces in the Perfetto web UI available at https://ui.perfetto.dev/
Use the "Open trace file" menu item on the left to select a trace file to view.
String interning
All strings in this API with the exception of Arg.String event arguments are interned in a lasting way such that every further use will just refer to the string by an identifier rather than putting it in the file again. This means long names and categories are fine and won't bloat the file too much if you create lots of events with them.
However, there's currently a limit of around 32k interned strings shared between names, categories and Arg.Interned arguments. If you exceed that limit the library will assert. We expect nearly every use case won't come close to this limit, but if you have a use case that does let us know and we can add logic to evict old strings from the interning slots for re-use.
Times: relative and absolute
Time instants are provided as a Time_ns.Span.t from the start of the trace. If you have absolute times you should subtract the time of the start of your trace or the start of the day the trace was taken (see dominodb_converter.ml in app/tracing for code). Then provide base_time to create so that tools which need absolute time can use it to for example merge traces while aligning time properly.
Avoid converting a Time_ns.t to a span since epoch, because although the format uses 64-bit nanoseconds, the Perfetto trace viewer visualizes using Javascript doubles so large timestamps like this will cause weird imprecision in where events are drawn due to rounding of large numbers.
type t
val create_for_file : base_time:Core.Time_ns.t option->filename:string ->t
Open a file to write trace events to in the Fuchsia Trace Format, suggested extension is .fxt.
If base_time is provided, a time initialization record will be written which records what absolute time corresponds to Time_ns.Span.zero.
Translate an absolute time to trace time. If base_time was provided it subtracts that, asserting if the time is before the base time. Otherwise it measures relative to unix epoch. If you already have times relative to a trace start time there's no need to use this.
The name shows up as the main label of the event in UIs, and the category is another string that can be used to classify the event, and is visible in Perfetto when an event is clicked on.
Same as write_instant except guaranteed to be visible in visualization UIs and usable with flow events.
Currently this uses write_duration_complete with an identical begin and end time, which shows up in Perfetto as chevron pointing at that time. In the future if we change Perfetto to be able to show instant events we may search for usages of this function which don't use flow events and replace them with write_instant.
A counters event uses its arguments to specify "counters" which may be represented by trace viewers as a chart over time. Its arguments must be Arg.Int or Arg.Float and there should be at least one.
The spec says that multiple arguments in the same counter event should be representable as a stacked area chart, and otherwise you should use multiple counter events. At the moment Perfetto doesn't implement this and instead ignores the counter ID and represents each argument as a separate line chart.
Counter events are grouped into a chart labeled "<event name>:<argument name>" per thread that name pair is used on.
Begin a duration slice which will be finished with a matching end event.
A "duration slice" starts and ends at different times but has a single name and category. It shows up as a bar between the start and end time in UIs.
Duration slices within a thread should be nested properly such that if a duration slice starts within another slice then it must end before that slice ends.
Flows are chains of duration slices which get connected by arrows when selected in a trace viewer UI. A flow is composed of "steps", each of which must be written at a time contained in a duration slice, and when one of those slices is selected in the UI it will display arrows connecting the slices of each step in order like this:
Flows have identity and can consist of multiple steps, which allows selecting one duration slice with an associated flow to show the arrows for all steps of that flow. Perfetto also allows the user to navigate through the steps of a flow with the square bracket keys.
Add a step to a flow, which will connect to the duration event enclosing the provided time on that thread. Specifically there needs to be a duration slice on the given thread with a start time less than or equal to time and an end time greater than or equal to time and the flow event will be associated with the latest-starting (i.e most deeply nested) such enclosing slice.
All flows must be "finished" before the end of the trace and after writing all steps.
The representation of trace events in the file format requires us to hold one flow step in memory until either the next flow step or we finish adding steps, in order to know what kind of event to write out. Finishing a flow writes out any last buffered step.
Begin an async slice with a given id which will have a corresponding end.
An "async slice" starts and ends at different times but has a single name, category, and id. It shows up as a bar between the start and end time in UIs.