1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_ 18 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_ 19 20 #include <inttypes.h> 21 #include <stdint.h> 22 23 #include <array> 24 #include <map> 25 #include <random> 26 #include <set> 27 #include <vector> 28 29 #include "perfetto/base/logging.h" 30 #include "perfetto/ext/base/optional.h" 31 32 namespace perfetto { 33 namespace trace_processor { 34 35 class TraceProcessorContext; 36 37 // This class handles synchronization of timestamps across different clock 38 // domains. This includes multi-hop conversions from two clocks A and D, e.g. 39 // A->B -> B->C -> C->D, even if we never saw a snapshot that contains A and D 40 // at the same time. 41 // The API is fairly simple (but the inner operation is not): 42 // - AddSnapshot(map<clock_id, timestamp>): pushes a set of clocks that have 43 // been snapshotted at the same time (within technical limits). 44 // - Convert(src_clock_id, src_timestamp, target_clock_id): 45 // converts a timestamp between two clock domains. 46 // 47 // Concepts: 48 // - Snapshot hash: 49 // As new snapshots are pushed via AddSnapshot() we compute a snapshot hash. 50 // Such hash is the hash(clock_ids) (only IDs, not their timestamps) and is 51 // used to find other snapshots that involve the same clock domains. 52 // Two clock snapshots have the same hash iff they snapshot the same set of 53 // clocks (the order of clocks is irrelevant). 54 // This hash is used to efficiently go from the clock graph pathfinder to the 55 // time-series obtained by appending the various snapshots. 56 // - Snapshot id: 57 // A simple monotonic counter that is incremented on each AddSnapshot() call. 58 // 59 // Data structures: 60 // - For each clock domain: 61 // - For each snapshot hash: 62 // - A logic vector of (snapshot_id, timestamp) tuples (physically stored 63 // as two vectors of the same length instead of a vector of pairs). 64 // This allows to efficiently binary search timestamps within a clock domain 65 // that were obtained through a particular snapshot. 66 // 67 // - A graph of edges (source_clock, target_clock) -> snapshot hash. 68 // 69 // Operation: 70 // Upon each AddSnapshot() call, we incrementally build an unweighted, directed 71 // graph, which has clock domains as nodes. 72 // The graph is timestamp-oblivious. As long as we see one snapshot that 73 // connects two clocks, we assume we'll always be able to convert between them. 74 // This graph is queried by the Convert() function to figure out the shortest 75 // path between clock domain, possibly involving hopping through snapshots of 76 // different type (i.e. different hash). 77 // 78 // Example: 79 80 // We see a snapshot, with hash S1, for clocks (A,B,C). We build the edges in 81 // the graph: A->B, B->C, A->C (and the symmetrical ones). In other words we 82 // keep track of the fact that we can convert between any of them using S1. 83 // Later we get another snapshot containing (C,E), this snapshot will have a 84 // different hash (S2, because Hash(C,E) != Hash(A,B,C)) and will add the edges 85 // C->E, E->C [via S2] to the graph. 86 // At this point when we are asked to convert a timestamp from A to E, or 87 // viceversa, we use a simple BFS to figure out a conversion path that is: 88 // A->C [via S1] + C->E [via S2]. 89 // 90 // Visually: 91 // Assume we make the following calls: 92 // - AddSnapshot(A:10, B:100) 93 // - AddSnapshot(A:20, C:2000) 94 // - AddSnapshot(B:400, C:5000) 95 // - AddSnapshot(A:30, B:300) 96 97 // And assume Hash(A,B) = S1, H(A,C) = S2, H(B,C) = S3 98 // The vectors in the tracker will look as follows: 99 // Clock A: 100 // S1 {t:10, id:1} {t:30, id:4} 101 // S2 | {t:20, id:2} | 102 // | | | 103 // Clock B: | | | 104 // S1 {t:100, id:1} | {t:300, id:4} 105 // S3 | {t:400, id:3} 106 // | | 107 // Clock C: | | 108 // S2 {t: 2000, id: 2} | 109 // S3 {t:5000, id:3} 110 111 class ClockTracker { 112 public: 113 using ClockId = uint64_t; 114 115 // IDs in the range [64, 128) are reserved for sequence-scoped clock ids. 116 // They can't be passed directly in ClockTracker calls and must be resolved 117 // to 64-bit global clock ids by calling SeqScopedClockIdToGlobal(). IsReservedSeqScopedClockId(ClockId clock_id)118 static bool IsReservedSeqScopedClockId(ClockId clock_id) { 119 return clock_id >= 64 && clock_id < 128; 120 } 121 122 // Converts a sequence-scoped clock ids to a global clock id that can be 123 // passed as argument to ClockTracker functions. SeqScopedClockIdToGlobal(uint32_t seq_id,uint32_t clock_id)124 static ClockId SeqScopedClockIdToGlobal(uint32_t seq_id, uint32_t clock_id) { 125 PERFETTO_DCHECK(IsReservedSeqScopedClockId(clock_id)); 126 return (static_cast<uint64_t>(seq_id) << 32) | clock_id; 127 } 128 129 // Returns whether |global_clock_id| represents a sequence-scoped clock, i.e. 130 // a ClockId returned by SeqScopedClockIdToGlobal(). ClockIsSeqScoped(ClockId global_clock_id)131 static bool ClockIsSeqScoped(ClockId global_clock_id) { 132 // If the id is > 2**32, this is a sequence-scoped clock id translated into 133 // the global namespace. 134 return (global_clock_id >> 32) > 0; 135 } 136 137 explicit ClockTracker(TraceProcessorContext*); 138 virtual ~ClockTracker(); 139 140 // Clock description and its value in a snapshot. 141 struct ClockValue { ClockValueClockValue142 ClockValue(ClockId id, int64_t ts) : clock_id(id), absolute_timestamp(ts) {} ClockValueClockValue143 ClockValue(ClockId id, int64_t ts, int64_t unit, bool incremental) 144 : clock_id(id), 145 absolute_timestamp(ts), 146 unit_multiplier_ns(unit), 147 is_incremental(incremental) {} 148 149 ClockId clock_id; 150 int64_t absolute_timestamp; 151 int64_t unit_multiplier_ns = 1; 152 bool is_incremental = false; 153 }; 154 155 // Appends a new snapshot for the given clock domains. 156 // This is typically called by the code that reads the ClockSnapshot packet. 157 void AddSnapshot(const std::vector<ClockValue>&); 158 159 // Converts a timestamp between two clock domains. Tries to use the cache 160 // first (only for single-path resolutions), then falls back on path finding 161 // as described in the header. Convert(ClockId src_clock_id,int64_t src_timestamp,ClockId target_clock_id)162 base::Optional<int64_t> Convert(ClockId src_clock_id, 163 int64_t src_timestamp, 164 ClockId target_clock_id) { 165 if (PERFETTO_LIKELY(!cache_lookups_disabled_for_testing_)) { 166 for (const auto& ce : cache_) { 167 if (ce.src != src_clock_id || ce.target != target_clock_id) 168 continue; 169 int64_t ns = ce.src_domain->ToNs(src_timestamp); 170 if (ns >= ce.min_ts_ns && ns < ce.max_ts_ns) 171 return ns + ce.translation_ns; 172 } 173 } 174 return ConvertSlowpath(src_clock_id, src_timestamp, target_clock_id); 175 } 176 177 base::Optional<int64_t> ConvertSlowpath(ClockId src_clock_id, 178 int64_t src_timestamp, 179 ClockId target_clock_id); 180 ToTraceTime(ClockId clock_id,int64_t timestamp)181 base::Optional<int64_t> ToTraceTime(ClockId clock_id, int64_t timestamp) { 182 trace_time_clock_id_used_for_conversion_ = true; 183 if (clock_id == trace_time_clock_id_) 184 return timestamp; 185 return Convert(clock_id, timestamp, trace_time_clock_id_); 186 } 187 SetTraceTimeClock(ClockId clock_id)188 void SetTraceTimeClock(ClockId clock_id) { 189 PERFETTO_DCHECK(!IsReservedSeqScopedClockId(clock_id)); 190 if (trace_time_clock_id_used_for_conversion_) { 191 PERFETTO_ELOG("Not updating trace time clock from %" PRIu64 " to %" PRIu64 192 " because the old clock was already used for timestamp " 193 "conversion - ClockSnapshot too late in trace?", 194 trace_time_clock_id_, clock_id); 195 return; 196 } 197 trace_time_clock_id_ = clock_id; 198 } 199 set_cache_lookups_disabled_for_testing(bool v)200 void set_cache_lookups_disabled_for_testing(bool v) { 201 cache_lookups_disabled_for_testing_ = v; 202 } 203 204 private: 205 using SnapshotHash = uint32_t; 206 207 // 0th argument is the source clock, 1st argument is the target clock. 208 using ClockGraphEdge = std::tuple<ClockId, ClockId, SnapshotHash>; 209 210 // A value-type object that carries the information about the path between 211 // two clock domains. It's used by the BFS algorithm. 212 struct ClockPath { 213 static constexpr size_t kMaxLen = 4; 214 ClockPath() = default; 215 ClockPath(const ClockPath&) = default; 216 217 // Constructs an invalid path with just a source node. ClockPathClockPath218 explicit ClockPath(ClockId clock_id) : last(clock_id) {} 219 220 // Constructs a path by appending a node to |prefix|. 221 // If |prefix| = [A,B] and clock_id = C, then |this| = [A,B,C]. ClockPathClockPath222 ClockPath(const ClockPath& prefix, ClockId clock_id, SnapshotHash hash) { 223 PERFETTO_DCHECK(prefix.len < kMaxLen); 224 len = prefix.len + 1; 225 path = prefix.path; 226 path[prefix.len] = ClockGraphEdge{prefix.last, clock_id, hash}; 227 last = clock_id; 228 } 229 validClockPath230 bool valid() const { return len > 0; } atClockPath231 const ClockGraphEdge& at(uint32_t i) const { 232 PERFETTO_DCHECK(i < len); 233 return path[i]; 234 } 235 236 uint32_t len = 0; 237 ClockId last = 0; 238 std::array<ClockGraphEdge, kMaxLen> path; // Deliberately uninitialized. 239 }; 240 241 struct ClockSnapshots { 242 // Invariant: both vectors have the same length. 243 std::vector<uint32_t> snapshot_ids; 244 std::vector<int64_t> timestamps_ns; 245 }; 246 247 struct ClockDomain { 248 // One time-series for each hash. 249 std::map<SnapshotHash, ClockSnapshots> snapshots; 250 251 // Multiplier for timestamps given in this domain. 252 int64_t unit_multiplier_ns = 1; 253 254 // Whether this clock domain encodes timestamps as deltas. This is only 255 // supported on sequence-local domains. 256 bool is_incremental = false; 257 258 // If |is_incremental| is true, this stores the most recent absolute 259 // timestamp in nanoseconds. 260 int64_t last_timestamp_ns = 0; 261 262 // Treats |timestamp| as delta timestamp if the clock uses incremental 263 // encoding, and as absolute timestamp otherwise. ToNsClockDomain264 int64_t ToNs(int64_t timestamp) { 265 if (!is_incremental) 266 return timestamp * unit_multiplier_ns; 267 268 int64_t delta_ns = timestamp * unit_multiplier_ns; 269 last_timestamp_ns += delta_ns; 270 return last_timestamp_ns; 271 } 272 GetSnapshotClockDomain273 const ClockSnapshots& GetSnapshot(uint32_t hash) const { 274 auto it = snapshots.find(hash); 275 PERFETTO_DCHECK(it != snapshots.end()); 276 return it->second; 277 } 278 }; 279 280 // Holds data for cached entries. At the moment only single-path resolution 281 // are cached. 282 struct CachedClockPath { 283 ClockId src; 284 ClockId target; 285 ClockDomain* src_domain; 286 int64_t min_ts_ns; 287 int64_t max_ts_ns; 288 int64_t translation_ns; 289 }; 290 291 ClockTracker(const ClockTracker&) = delete; 292 ClockTracker& operator=(const ClockTracker&) = delete; 293 294 ClockPath FindPath(ClockId src, ClockId target); 295 GetClock(ClockId clock_id)296 ClockDomain* GetClock(ClockId clock_id) { 297 auto it = clocks_.find(clock_id); 298 PERFETTO_DCHECK(it != clocks_.end()); 299 return &it->second; 300 } 301 302 TraceProcessorContext* const context_; 303 ClockId trace_time_clock_id_ = 0; 304 std::map<ClockId, ClockDomain> clocks_; 305 std::set<ClockGraphEdge> graph_; 306 std::set<ClockId> non_monotonic_clocks_; 307 std::array<CachedClockPath, 2> cache_{}; 308 bool cache_lookups_disabled_for_testing_ = false; 309 std::minstd_rand rnd_; // For cache eviction. 310 uint32_t cur_snapshot_id_ = 0; 311 bool trace_time_clock_id_used_for_conversion_ = false; 312 }; 313 314 } // namespace trace_processor 315 } // namespace perfetto 316 317 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_CLOCK_TRACKER_H_ 318