blob: 7ce7894f8539a1d1e573cce1a7d05584871b330d [file] [log] [blame] [view]
Primiano Tuccide23d682019-08-17 08:09:04 +02001# Synchronization of multiple clock domains
2
3As per [6756fb05][6756fb05] Perfetto allows to deal with events using different
4clock domains. On top of the default set of builtin clock domains, new clock
5domains can be dynamically created at trace-time.
6
7Clock domains are allowed to drift from each other.
8At import time, Perfetto's [Trace Processor](/docs/trace-processor.md) is able
9to rebuild the clock graph and use that to re-synchronize events on a global
10trace time, as long as [ClockSnapshot][clock_snapshot] packets are present in
11the trace.
12
13Problem statement
14-----------------
15In a complex multi-producer scenario, different data source can emit events
16using different clock domains.
17
18Some examples:
19
20* On Linux/Android, Ftrace events are emitted using the `CLOCK_BOOTTIME` clock,
21 but the Android event log uses `CLOCK_REALTIME`.
22 Some other data sources can use `CLOCK_MONOTONIC`.
23 These clocks can drift over time from each other due to suspend/resume.
24* Graphics-related events are typically timestamped by the GPU, which can use a
25 hardware clock source that drifts from the system clock.
26
27At trace-time, the data sources might not be able to use `CLOCK_BOOTTIME` (or
28even when possible, doing so might be prohibitively expensive).
29
30To solve this, we allow events to be recorded with different clock domains and
31re-synchronize them at import time using clock snapshots.
32
33Trace proto syntax
34------------------
35
36Clock synchronization is based on two elements of the trace:
37
38### 1. The [`timestamp_clock_id`][timestamp_clock_id] field of TracePacket
39
Primiano Tuccidfa48132020-04-20 20:17:16 +010040```protobuf
Primiano Tuccide23d682019-08-17 08:09:04 +020041message TracePacket {
42 optional uint64 timestamp = 8;
43
44 // Specifies the ID of the clock used for the TracePacket |timestamp|. Can be
45 // one of the built-in types from ClockSnapshot::BuiltinClocks, or a
46 // producer-defined clock id.
47 // If unspecified it defaults to BuiltinClocks::BOOTTIME.
48 optional uint32 timestamp_clock_id = 58;
49
50```
51
52This (optional) field determines the clock domain for the packet.
53If omitted it refers to the default clock domain of the trace
54(`CLOCK_BOOTTIME` for Linux/Android).
55It present, this field can be set to either:
56
57* One of the [builtin clocks defined in clock_snapshot.proto][builtin_clocks]
58 (e.g., `CLOCK_BOOTTIME`, `CLOCK_REALTIME`, `CLOCK_MONOTONIC`). These clocks
59 have an ID <= 63.
60* A custom sequence-scoped clock, with 64 <= ID < 128
61* A custom globally-scoped clock, with 128 <= ID < 2**32
62
63#### Builtin clocks
64Builtin clocks cover the most common case of data sources using one of the
65POSIX clocks (see `man clock_gettime`). These clocks are periodically
66snapshotted by the `traced` service. The producer doesn't need to do anything
67else other than setting the `timestamp_clock_id` field in order to emit events
68that are use these clocks.
69
70#### Sequence-scoped clocks
71Sequence-scoped clocks are application-defined clock domains that are valid only
72within the sequence of TracePacket(s) written by the same `TraceWriter`
73(i.e. TracePacket that have the same `trusted_packet_sequence_id` field).
74In most cases this really means *"events emitted by the same data source on
75the same thread"*.
76
77This covers the most common use case of a clock domain that is used only within
78a data source and not shared across different data sources.
79The main advantage of sequence-scoped clocks is that avoids the ID
80disambiguation problem and JustWorks&trade; for the most simple cases.
81
82In order to make use of a custom sequence-scoped clock domain a data source
83must:
84
85* Emit its packets with a `timestamp_clock_id` in the range [64, 127]
86* Emit at least once a [`ClockSnapshot`][clock_snapshot] packet.
87
88Such `ClockSnapshot`:
89
90* Must be emitted on the same sequence (i.e. by the same `TraceWriter`) that is
91 used to emit other `TracePacket`(s) that refer to such `timestamp_clock_id`.
92* Must be emitted before the custom clock is referred to by any `TracePacket`
93 written by the same `TraceWriter`.
94* Must contain a snapshot of: (i) the custom clock id [64, 127] and (ii) another
95 clock domain that can be resolved, at import time, against the default trace
96 clock domain (`CLOCK_BOOTTIME`) (see the [Operation section](#operation)
97 below).
98
99Collisions of `timestamp_clock_id` across two different `TraceWriter` sequences
100are okay. E.g., two data sources, unaware of each other, can both use clock ID
10164 to refer to two different clock domains.
102
103#### Globally-scoped clocks
104Globally-scoped clock domains work similarly to sequence-scoped clock domains,
105with the only difference that their scope is global and applies to all
106`TracePacket`(s) of the trace.
107
108The same `ClockSnapshot` rules as above apply. The only difference is that once
109a `ClockSnapshot` defines a clock domain with ID >= 128, that clock domain can
110be referred to by any `TracePacket` written by any `TraceWriter` sequence.
111
112Care must be taken to avoid collisions between global clock domains defined by
113different data sources unaware of each other.
114
115As such, it is **strongly discouraged** to just use the ID 128 (or any other
116arbitrarily chosen value). Instead the recommended pattern is:
117
118* Chose a fully qualified name for the clock domain
119 (e.g. `com.example.my_subsystem`)
120* Chose the clock ID as `(HASH("com.example.my_subsystem") + 128) & 0xFFFFFFF`
121 where `HASH(x)` is the FNV-1a hash of the fully qualified clock domain name.
122
123### 2. The [`ClockSnapshot`][clock_snapshot] trace packet
124
125The [`ClockSnapshot`][clock_snapshot] packet defines sync points between two or
126more clock domains. It conveys the notion *"at this point in time, the timestamp
127of the clock domains X,Y,Z was 1000, 2000, 3000."*.
128
129The trace importer ([Trace Processor](/docs/trace-processor.md)) uses this
130information to establish a mapping between these clock domain. For instance,
131to realize that 1042 on clock domain X == 3042 on clock domain Z.
132
133The `traced` service automatically emits `ClockSnapshot` packets for the builtin
134clock domains on a regular basis.
135
136A data source should emit `ClockSnapshot` packets only when using custom clock
137domains, either sequence-scoped or globally-scoped.
138
139It is *not* mandatory that the `ClockSnapshot` for a custom clock domain
140contains also a snapshot of `CLOCK_BOOTTIME` (although it is advisable to do
141so when possible). The Trace Processor can deal with multi-path clock domain
142resolution based on graph traversal (see the [Operation](#operation) section).
143
144## Operation
145
146At import time Trace Processor will attempt to convert the timestamp of each
147TracePacket down to the trace clock domain (`CLOCK_BOOTTIME`) using the
148`ClockSnapshot` packets seen until then using nearest neighbor approximation.
149
150For instance, assume that the trace contains `ClockSnapshot` for
151`CLOCK_BOOTTIME` and `CLOCK_MONOTONIC` as follows:
152
153```python
154CLOCK_MONOTONIC 1000 1100 1200 1900 ... 2000 2100
155CLOCK_BOOTTIME 2000 2100 2200 2900 ... 3500 3600
156```
157
158In this example `CLOCK_MONOTONIC` is 1000 ns ahead of `CLOCK_BOOTTIME` until
159T=2900. Then the two clocks go out of sync (e.g. the device is suspended) and,
160on the next snapshot, the two clocks are 1500 ns apart.
161
162If a `TracePacket` with `timestamp_clock_id=CLOCK_MONOTONIC` and
163`timestamp=1104` is seen, the clock sync logic will:
164
1651. Find the latest snapshot for `CLOCK_MONOTONIC` <= 1104 (in the example above
166 the 2nd one with `CLOCK_MONOTONIC=1100`)
1672. Compute the clock domain conversion to `CLOCK_BOOTTIME` by applying the
168 delta (1104 - 1100) to the corresponding `CLOCK_BOOTTIME` snapshot
169 (2100, so 2100 + (1104 - 1100) -> 2104).
170
171The example above is rather simple, because the source clock domain (i.e. the
172one specified by the `timestamp_clock_id` field) and the target clock domain
173(i.e. the trace time, `CLOCK_BOTTIME`) are snapshotted within the same
174`ClockSnapshot` packets.
175
176Clock domain conversion is possible also in more complex scenarios where the
177two domains are not directly connected, as long as a path exist between the two.
178
179In this sense `ClockSnapshot` packets define edges of an acyclic graph that is
180queried to perform clock domain conversions. All types of clock domains can be
181used in the graph search.
182
183In the more general case, the clock domain conversion logic operates as follows:
184
185* The shortest path between the source and target clock domains is identified,
186 using a breadth first search in the graph.
187* For each clock domain of the path identified, the timestamp is converted using
188 the aforementioned nearest neighbor resolution.
189
190This allows to deal with complex scenarios as follows:
191
192```python
193CUSTOM_CLOCK 1000 3000
194CLOCK_MONOTONIC 1100 1200 3200 4000
195CLOCK_BOOTTIME 5200 9000
196```
197
198In the example above, there is no snapshot that directly links `CUSTOM_CLOCK`
199and `CLOCK_BOOTTIME`. However there is an indirect path that allows a conversion
200via `CUSTOM_CLOCK -> CLOCK_MONOTONIC -> CLOCK_BOOTTIME`.
201
202This allows to synchronize a hypothetical `TracePacket` that has
203`timestamp_clock_id=CUSTOM_CLOCK` and `timestamp=3503` as follows:
204
205```python
206#Step 1
207CUSTOM_CLOCK = 3503
208Nearest snapshot: {CUSTOM_CLOCK:3000, CLOCK_MONOTONIC:3200}
209CLOCK_MONOTONIC = (3503 - 3000) + 3200 = 3703
210
211#Step 2
212CLOCK_MONOTONIC = 3703
213Nearest snapshot: {CLOCK_MONOTONIC:1200, CLOCK_BOOTTIME:5200}
214CLOCK_BOOTTIME = (3703 - 1200) + 5200 = 7703
215```
216
217Caveats
218-------
219Clock resolution between two domains (A,B) is allowed only as long as all the
220clock domains in the A -> B path are monotonic (or at least look so in the
221`ClockSnapshot` packets).
222If non-monotonicity is detected at import time, the clock domain is excluded as
223a source path in the graph search and is allowed only as a target path.
224
225For instance, imagine capturing a trace that has both `CLOCK_BOOTTIME`
226and `CLOCK_REALTIME` in the night when daylight saving is applied, when the
227real-time clock jumps back from 3AM to 2AM.
228
229Such a trace would contain several snapshots that break bijectivity between the
230two clock domains. In this case converting a `CLOCK_BOOTTIME` timestamp to
231`CLOCK_REALTIME` is always possible without ambiguities (eventually two distinct
232timestamps can be resolved against the same `CLOCK_REALTIME` timestamp).
233The opposite is not allowed, because `CLOCK_REALTIME` timestamps between 2AM
234and 3AM are ambiguous and could be resolved against two different
235`CLOCK_BOOTTIME` timestamps).
236
237[6756fb05]: https://android-review.googlesource.com/c/platform/external/perfetto/+/1101915/
238[clock_snapshot]: https://android.googlesource.com/platform/external/perfetto/+/refs/heads/master/protos/perfetto/trace/clock_snapshot.proto
239[timestamp_clock_id]: https://android.googlesource.com/platform/external/perfetto/+/3e7ca4f5893f7d762ec24a2eac9a47343b226c6c/protos/perfetto/trace/trace_packet.proto#68
240[builtin_clocks]: https://android.googlesource.com/platform/external/perfetto/+/3e7ca4f5893f7d762ec24a2eac9a47343b226c6c/protos/perfetto/trace/clock_snapshot.proto#25