• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #include "src/trace_processor/importers/proto/track_event_tracker.h"
18 
19 #include <algorithm>
20 #include <array>
21 #include <cinttypes>
22 #include <cstddef>
23 #include <cstdint>
24 #include <memory>
25 #include <optional>
26 #include <unordered_set>
27 
28 #include "perfetto/base/logging.h"
29 #include "src/trace_processor/importers/common/args_tracker.h"
30 #include "src/trace_processor/importers/common/process_track_translation_table.h"
31 #include "src/trace_processor/importers/common/process_tracker.h"
32 #include "src/trace_processor/importers/common/track_tracker.h"
33 #include "src/trace_processor/importers/common/tracks.h"
34 #include "src/trace_processor/importers/common/tracks_common.h"
35 #include "src/trace_processor/importers/common/tracks_internal.h"
36 #include "src/trace_processor/storage/stats.h"
37 #include "src/trace_processor/storage/trace_storage.h"
38 #include "src/trace_processor/types/trace_processor_context.h"
39 #include "src/trace_processor/types/variadic.h"
40 
41 namespace perfetto::trace_processor {
42 
43 namespace {
44 
45 constexpr auto kThreadCounterTrackBlueprint = tracks::CounterBlueprint(
46     "thread_counter_track_event",
47     tracks::DynamicUnitBlueprint(),
48     tracks::DimensionBlueprints(tracks::kThreadDimensionBlueprint,
49                                 tracks::LongDimensionBlueprint("track_uuid")),
50     tracks::DynamicNameBlueprint());
51 
52 constexpr auto kProcessCounterTrackBlueprint = tracks::CounterBlueprint(
53     "process_counter_track_event",
54     tracks::DynamicUnitBlueprint(),
55     tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint,
56                                 tracks::LongDimensionBlueprint("track_uuid")),
57     tracks::DynamicNameBlueprint());
58 
59 constexpr auto kGlobalCounterTrackBlueprint = tracks::CounterBlueprint(
60     "global_counter_track_event",
61     tracks::DynamicUnitBlueprint(),
62     tracks::DimensionBlueprints(tracks::LongDimensionBlueprint("track_uuid")),
63     tracks::DynamicNameBlueprint());
64 
65 constexpr auto kThreadTrackBlueprint = tracks::SliceBlueprint(
66     "thread_track_event",
67     tracks::DimensionBlueprints(tracks::kThreadDimensionBlueprint,
68                                 tracks::LongDimensionBlueprint("track_uuid")),
69     tracks::DynamicNameBlueprint());
70 
71 constexpr auto kProcessTrackBlueprint = tracks::SliceBlueprint(
72     "process_track_event",
73     tracks::DimensionBlueprints(tracks::kProcessDimensionBlueprint,
74                                 tracks::LongDimensionBlueprint("track_uuid")),
75     tracks::DynamicNameBlueprint());
76 
77 constexpr auto kGlobalTrackBlueprint = tracks::SliceBlueprint(
78     "global_track_event",
79     tracks::DimensionBlueprints(tracks::LongDimensionBlueprint("track_uuid")),
80     tracks::DynamicNameBlueprint());
81 
82 }  // namespace
83 
TrackEventTracker(TraceProcessorContext * context)84 TrackEventTracker::TrackEventTracker(TraceProcessorContext* context)
85     : source_key_(context->storage->InternString("source")),
86       source_id_key_(context->storage->InternString("trace_id")),
87       is_root_in_scope_key_(context->storage->InternString("is_root_in_scope")),
88       category_key_(context->storage->InternString("category")),
89       builtin_counter_type_key_(
90           context->storage->InternString("builtin_counter_type")),
91       has_first_packet_on_sequence_key_id_(
92           context->storage->InternString("has_first_packet_on_sequence")),
93       child_ordering_key_(context->storage->InternString("child_ordering")),
94       explicit_id_(context->storage->InternString("explicit")),
95       lexicographic_id_(context->storage->InternString("lexicographic")),
96       chronological_id_(context->storage->InternString("chronological")),
97       sibling_order_rank_key_(
98           context->storage->InternString("sibling_order_rank")),
99       descriptor_source_(context->storage->InternString("descriptor")),
100       default_descriptor_track_name_(
101           context->storage->InternString("Default Track")),
102       context_(context) {}
103 
ReserveDescriptorTrack(uint64_t uuid,const DescriptorTrackReservation & reservation)104 void TrackEventTracker::ReserveDescriptorTrack(
105     uint64_t uuid,
106     const DescriptorTrackReservation& reservation) {
107   if (uuid == kDefaultDescriptorTrackUuid && reservation.parent_uuid) {
108     PERFETTO_DLOG(
109         "Default track (uuid 0) cannot have a parent uui specified. Ignoring "
110         "the descriptor.");
111     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
112     return;
113   }
114 
115   auto [it, inserted] = reserved_descriptor_tracks_.Insert(uuid, reservation);
116   if (inserted) {
117     return;
118   }
119 
120   if (!it->IsForSameTrack(reservation)) {
121     PERFETTO_DLOG("New track reservation for track with uuid %" PRIu64
122                   " doesn't match earlier one",
123                   uuid);
124     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
125     return;
126   }
127   it->min_timestamp = std::min(it->min_timestamp, reservation.min_timestamp);
128 }
129 
130 std::optional<TrackEventTracker::ResolvedDescriptorTrack>
GetDescriptorTrackImpl(uint64_t uuid,StringId event_name,std::optional<uint32_t> packet_sequence_id)131 TrackEventTracker::GetDescriptorTrackImpl(
132     uint64_t uuid,
133     StringId event_name,
134     std::optional<uint32_t> packet_sequence_id) {
135   auto* resolved_ptr = resolved_descriptor_tracks_.Find(uuid);
136   if (resolved_ptr) {
137     if (event_name.is_null()) {
138       return *resolved_ptr;
139     }
140 
141     // Update the name to match the first non-null and valid event name. We need
142     // this because TrackEventParser calls |GetDescriptorTrack| with
143     // kNullStringId which means we cannot just have the code below for updating
144     // the name
145     DescriptorTrackReservation* reservation_ptr =
146         reserved_descriptor_tracks_.Find(uuid);
147     PERFETTO_CHECK(reservation_ptr);
148     auto* tracks = context_->storage->mutable_track_table();
149     auto rr = *tracks->FindById(resolved_ptr->track_id());
150     bool is_root_thread_process_or_counter = reservation_ptr->pid ||
151                                              reservation_ptr->tid ||
152                                              reservation_ptr->is_counter;
153     if (rr.name().is_null() && !is_root_thread_process_or_counter) {
154       if (resolved_ptr->scope() == ResolvedDescriptorTrack::Scope::kProcess) {
155         rr.set_name(context_->process_track_translation_table->TranslateName(
156             event_name));
157       } else {
158         rr.set_name(event_name);
159       }
160     }
161     return *resolved_ptr;
162   }
163 
164   DescriptorTrackReservation* reservation_ptr =
165       reserved_descriptor_tracks_.Find(uuid);
166   if (!reservation_ptr) {
167     if (uuid != kDefaultDescriptorTrackUuid) {
168       return std::nullopt;
169     }
170 
171     // For the default track, if there's no reservation, forcefully create it
172     // as it's always allowed to emit events on it, even without emitting a
173     // descriptor.
174     DescriptorTrackReservation r;
175     r.parent_uuid = 0;
176     r.name = default_descriptor_track_name_;
177     ReserveDescriptorTrack(kDefaultDescriptorTrackUuid, r);
178 
179     reservation_ptr = reserved_descriptor_tracks_.Find(uuid);
180     PERFETTO_CHECK(reservation_ptr);
181   }
182 
183   // Before trying to resolve anything, ensure that the hierarchy of tracks is
184   // well defined.
185   if (!IsTrackHierarchyValid(uuid)) {
186     return std::nullopt;
187   }
188 
189   // Resolve process and thread id for tracks produced from within a pid
190   // namespace.
191   //
192   // Get the root-level trusted_pid for the process that produces the track
193   // event.
194   std::optional<uint32_t> trusted_pid =
195       context_->process_tracker->GetTrustedPid(uuid);
196   DescriptorTrackReservation& reservation = *reservation_ptr;
197 
198   // Try to resolve to root-level pid and tid if the process is pid-namespaced.
199   if (trusted_pid && reservation.tid) {
200     std::optional<uint32_t> resolved_tid =
201         context_->process_tracker->ResolveNamespacedTid(*trusted_pid,
202                                                         *reservation.tid);
203     if (resolved_tid) {
204       reservation.tid = *resolved_tid;
205     }
206   }
207   if (trusted_pid && reservation.pid) {
208     std::optional<uint32_t> resolved_pid =
209         context_->process_tracker->ResolveNamespacedTid(*trusted_pid,
210                                                         *reservation.pid);
211     if (resolved_pid) {
212       reservation.pid = *resolved_pid;
213     }
214   }
215 
216   bool is_root_thread_process_or_counter = reservation_ptr->pid ||
217                                            reservation_ptr->tid ||
218                                            reservation_ptr->is_counter;
219   if (reservation.name.is_null() && !is_root_thread_process_or_counter) {
220     reservation.name = event_name;
221   }
222 
223   // If the reservation does not have a name specified, name it the same
224   // as the first event on the track. Note this only applies for non-root and
225   // non-counter tracks.
226   auto [it, inserted] = resolved_descriptor_tracks_.Insert(
227       uuid, ResolveDescriptorTrack(uuid, reservation, packet_sequence_id));
228   PERFETTO_CHECK(inserted);
229   return *it;
230 }
231 
232 TrackEventTracker::ResolvedDescriptorTrack
ResolveDescriptorTrack(uint64_t uuid,const DescriptorTrackReservation & reservation,std::optional<uint32_t> packet_sequence_id)233 TrackEventTracker::ResolveDescriptorTrack(
234     uint64_t uuid,
235     const DescriptorTrackReservation& reservation,
236     std::optional<uint32_t> packet_sequence_id) {
237   TrackTracker::SetArgsCallback args_fn_root =
238       [&, this](ArgsTracker::BoundInserter& inserter) {
239         AddTrackArgs(uuid, packet_sequence_id, reservation, true /* is_root*/,
240                      inserter);
241       };
242   TrackTracker::SetArgsCallback args_fn_non_root =
243       [&, this](ArgsTracker::BoundInserter& inserter) {
244         AddTrackArgs(uuid, packet_sequence_id, reservation, false /* is_root*/,
245                      inserter);
246       };
247 
248   // Try to resolve any parent tracks recursively, too.
249   std::optional<ResolvedDescriptorTrack> parent_resolved_track;
250   if (reservation.parent_uuid != kDefaultDescriptorTrackUuid) {
251     parent_resolved_track = GetDescriptorTrackImpl(
252         reservation.parent_uuid, kNullStringId, packet_sequence_id);
253   }
254 
255   if (reservation.tid) {
256     UniqueTid utid = context_->process_tracker->UpdateThread(*reservation.tid,
257                                                              *reservation.pid);
258     auto [it, inserted] = descriptor_uuids_by_utid_.Insert(utid, uuid);
259     if (!inserted) {
260       // We already saw a another track with a different uuid for this thread.
261       // Since there should only be one descriptor track for each thread, we
262       // assume that its tid was reused. So, start a new thread.
263       uint64_t old_uuid = *it;
264       PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
265       *it = uuid;
266 
267       PERFETTO_DLOG("Detected tid reuse (pid: %" PRIu32 " tid: %" PRIu32
268                     ") from track descriptors (old uuid: %" PRIu64
269                     " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
270                     *reservation.pid, *reservation.tid, old_uuid, uuid,
271                     reservation.min_timestamp);
272 
273       // Associate the new thread with its process.
274       utid = context_->process_tracker->StartNewThread(std::nullopt,
275                                                        *reservation.tid);
276       UniqueTid updated_utid = context_->process_tracker->UpdateThread(
277           *reservation.tid, *reservation.pid);
278       PERFETTO_CHECK(updated_utid == utid);
279     }
280 
281     TrackId id;
282     if (reservation.is_counter) {
283       id = context_->track_tracker->InternTrack(
284           kThreadCounterTrackBlueprint,
285           tracks::Dimensions(utid, static_cast<int64_t>(uuid)),
286           tracks::DynamicName(reservation.name), args_fn_root,
287           tracks::DynamicUnit(reservation.counter_details->unit));
288     } else if (reservation.use_separate_track) {
289       id = context_->track_tracker->InternTrack(
290           kThreadTrackBlueprint,
291           tracks::Dimensions(utid, static_cast<int64_t>(uuid)),
292           tracks::DynamicName(reservation.name), args_fn_root);
293     } else {
294       id = context_->track_tracker->InternThreadTrack(utid);
295     }
296     return ResolvedDescriptorTrack::Thread(id, utid, reservation.is_counter,
297                                            true /* is_root*/);
298   }
299 
300   if (reservation.pid) {
301     UniquePid upid =
302         context_->process_tracker->GetOrCreateProcess(*reservation.pid);
303     auto [it, inserted] = descriptor_uuids_by_upid_.Insert(upid, uuid);
304     if (!inserted) {
305       // We already saw a another track with a different uuid for this process.
306       // Since there should only be one descriptor track for each process,
307       // we assume that its pid was reused. So, start a new process.
308       uint64_t old_uuid = *it;
309       PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
310       *it = uuid;
311 
312       PERFETTO_DLOG("Detected pid reuse (pid: %" PRIu32
313                     ") from track descriptors (old uuid: %" PRIu64
314                     " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
315                     *reservation.pid, old_uuid, uuid,
316                     reservation.min_timestamp);
317 
318       upid = context_->process_tracker->StartNewProcess(
319           std::nullopt, std::nullopt, *reservation.pid, kNullStringId,
320           ThreadNamePriority::kTrackDescriptor);
321     }
322     StringId translated_name =
323         context_->process_track_translation_table->TranslateName(
324             reservation.name);
325     TrackId id;
326     if (reservation.is_counter) {
327       id = context_->track_tracker->InternTrack(
328           kProcessCounterTrackBlueprint,
329           tracks::Dimensions(upid, static_cast<int64_t>(uuid)),
330           tracks::DynamicName(translated_name), args_fn_root,
331           tracks::DynamicUnit(reservation.counter_details->unit));
332     } else {
333       id = context_->track_tracker->InternTrack(
334           kProcessTrackBlueprint,
335           tracks::Dimensions(upid, static_cast<int64_t>(uuid)),
336           tracks::DynamicName(translated_name), args_fn_root);
337     }
338     return ResolvedDescriptorTrack::Process(id, upid, reservation.is_counter,
339                                             true /* is_root*/);
340   }
341 
342   auto set_parent_id = [&](TrackId id) {
343     if (parent_resolved_track) {
344       auto rr = context_->storage->mutable_track_table()->FindById(id);
345       PERFETTO_CHECK(rr);
346       rr->set_parent_id(parent_resolved_track->track_id());
347     }
348   };
349 
350   if (parent_resolved_track) {
351     switch (parent_resolved_track->scope()) {
352       case ResolvedDescriptorTrack::Scope::kThread: {
353         // If parent is a thread track, create another thread-associated track.
354         TrackId id;
355         if (reservation.is_counter) {
356           id = context_->track_tracker->InternTrack(
357               kThreadCounterTrackBlueprint,
358               tracks::Dimensions(parent_resolved_track->utid(),
359                                  static_cast<int64_t>(uuid)),
360               tracks::DynamicName(reservation.name), args_fn_non_root,
361               tracks::DynamicUnit(reservation.counter_details->unit));
362         } else {
363           id = context_->track_tracker->InternTrack(
364               kThreadTrackBlueprint,
365               tracks::Dimensions(parent_resolved_track->utid(),
366                                  static_cast<int64_t>(uuid)),
367               tracks::DynamicName(reservation.name), args_fn_non_root);
368         }
369         // If the parent has a process descriptor set, promote this track
370         // to also be a root thread level track. This is necessary for
371         // backcompat reasons: see the comment on parent_uuid in
372         // TrackDescriptor.
373         if (!parent_resolved_track->is_root()) {
374           set_parent_id(id);
375         }
376         return ResolvedDescriptorTrack::Thread(
377             id, parent_resolved_track->utid(), reservation.is_counter,
378             false /* is_root*/);
379       }
380       case ResolvedDescriptorTrack::Scope::kProcess: {
381         // If parent is a process track, create another process-associated
382         // track.
383         StringId translated_name =
384             context_->process_track_translation_table->TranslateName(
385                 reservation.name);
386         TrackId id;
387         if (reservation.is_counter) {
388           id = context_->track_tracker->InternTrack(
389               kProcessCounterTrackBlueprint,
390               tracks::Dimensions(parent_resolved_track->upid(),
391                                  static_cast<int64_t>(uuid)),
392               tracks::DynamicName(translated_name), args_fn_non_root,
393               tracks::DynamicUnit(reservation.counter_details->unit));
394         } else {
395           id = context_->track_tracker->InternTrack(
396               kProcessTrackBlueprint,
397               tracks::Dimensions(parent_resolved_track->upid(),
398                                  static_cast<int64_t>(uuid)),
399               tracks::DynamicName(translated_name), args_fn_non_root);
400         }
401         // If the parent has a thread descriptor set, promote this track
402         // to also be a root thread level track. This is necessary for
403         // backcompat reasons: see the comment on parent_uuid in
404         // TrackDescriptor.
405         if (!parent_resolved_track->is_root()) {
406           set_parent_id(id);
407         }
408         return ResolvedDescriptorTrack::Process(
409             id, parent_resolved_track->upid(), reservation.is_counter,
410             false /* is_root*/);
411       }
412       case ResolvedDescriptorTrack::Scope::kGlobal:
413         break;
414     }
415   }
416 
417   // root_in_scope only matters for legacy JSON export. This is somewhat related
418   // but intentionally distinct from our handling of parent_id relationships.
419   bool is_root_in_scope = uuid == kDefaultDescriptorTrackUuid;
420   TrackId id;
421   if (reservation.is_counter) {
422     id = context_->track_tracker->InternTrack(
423         kGlobalCounterTrackBlueprint,
424         tracks::Dimensions(static_cast<int64_t>(uuid)),
425         tracks::DynamicName(reservation.name),
426         is_root_in_scope ? args_fn_root : args_fn_non_root,
427         tracks::DynamicUnit(reservation.counter_details->unit));
428   } else {
429     id = context_->track_tracker->InternTrack(
430         kGlobalTrackBlueprint, tracks::Dimensions(static_cast<int64_t>(uuid)),
431         tracks::DynamicName(reservation.name),
432         is_root_in_scope ? args_fn_root : args_fn_non_root);
433   }
434   set_parent_id(id);
435   return ResolvedDescriptorTrack::Global(id, reservation.is_counter);
436 }
437 
ConvertToAbsoluteCounterValue(uint64_t counter_track_uuid,uint32_t packet_sequence_id,double value)438 std::optional<double> TrackEventTracker::ConvertToAbsoluteCounterValue(
439     uint64_t counter_track_uuid,
440     uint32_t packet_sequence_id,
441     double value) {
442   auto* reservation_ptr = reserved_descriptor_tracks_.Find(counter_track_uuid);
443   if (!reservation_ptr) {
444     PERFETTO_DLOG("Unknown counter track with uuid %" PRIu64,
445                   counter_track_uuid);
446     return std::nullopt;
447   }
448 
449   DescriptorTrackReservation& reservation = *reservation_ptr;
450   if (!reservation.is_counter) {
451     PERFETTO_DLOG("Track with uuid %" PRIu64 " is not a counter track",
452                   counter_track_uuid);
453     return std::nullopt;
454   }
455   if (!reservation.counter_details) {
456     PERFETTO_FATAL("Counter tracks require `counter_details`.");
457   }
458   DescriptorTrackReservation::CounterDetails& c_details =
459       *reservation.counter_details;
460 
461   if (c_details.unit_multiplier > 0)
462     value *= static_cast<double>(c_details.unit_multiplier);
463 
464   if (c_details.is_incremental) {
465     if (c_details.packet_sequence_id != packet_sequence_id) {
466       PERFETTO_DLOG(
467           "Incremental counter track with uuid %" PRIu64
468           " was updated from the wrong packet sequence (expected: %" PRIu32
469           " got:%" PRIu32 ")",
470           counter_track_uuid, c_details.packet_sequence_id, packet_sequence_id);
471       return std::nullopt;
472     }
473 
474     c_details.latest_value += value;
475     value = c_details.latest_value;
476   }
477   return value;
478 }
479 
OnIncrementalStateCleared(uint32_t packet_sequence_id)480 void TrackEventTracker::OnIncrementalStateCleared(uint32_t packet_sequence_id) {
481   // TODO(eseckler): Improve on the runtime complexity of this. At O(hundreds)
482   // of packet sequences, incremental state clearing at O(trace second), and
483   // total number of tracks in O(thousands), a linear scan through all tracks
484   // here might not be fast enough.
485   for (auto it = reserved_descriptor_tracks_.GetIterator(); it; ++it) {
486     DescriptorTrackReservation& reservation = it.value();
487     // Only consider incremental counter tracks for current sequence.
488     if (!reservation.is_counter || !reservation.counter_details ||
489         !reservation.counter_details->is_incremental ||
490         reservation.counter_details->packet_sequence_id != packet_sequence_id) {
491       continue;
492     }
493     // Reset their value to 0, see CounterDescriptor's |is_incremental|.
494     reservation.counter_details->latest_value = 0;
495   }
496 }
497 
OnFirstPacketOnSequence(uint32_t packet_sequence_id)498 void TrackEventTracker::OnFirstPacketOnSequence(uint32_t packet_sequence_id) {
499   sequences_with_first_packet_.insert(packet_sequence_id);
500 }
501 
AddTrackArgs(uint64_t uuid,std::optional<uint32_t> packet_sequence_id,const DescriptorTrackReservation & reservation,bool is_root_in_scope,ArgsTracker::BoundInserter & args)502 void TrackEventTracker::AddTrackArgs(
503     uint64_t uuid,
504     std::optional<uint32_t> packet_sequence_id,
505     const DescriptorTrackReservation& reservation,
506     bool is_root_in_scope,
507     ArgsTracker::BoundInserter& args) {
508   args.AddArg(source_key_, Variadic::String(descriptor_source_))
509       .AddArg(source_id_key_, Variadic::Integer(static_cast<int64_t>(uuid)))
510       .AddArg(is_root_in_scope_key_, Variadic::Boolean(is_root_in_scope));
511   if (reservation.counter_details) {
512     if (!reservation.counter_details->category.is_null()) {
513       args.AddArg(category_key_,
514                   Variadic::String(reservation.counter_details->category));
515     }
516     if (!reservation.counter_details->builtin_type_str.is_null()) {
517       args.AddArg(
518           builtin_counter_type_key_,
519           Variadic::String(reservation.counter_details->builtin_type_str));
520     }
521   }
522   if (packet_sequence_id &&
523       sequences_with_first_packet_.find(*packet_sequence_id) !=
524           sequences_with_first_packet_.end()) {
525     args.AddArg(has_first_packet_on_sequence_key_id_, Variadic::Boolean(true));
526   }
527 
528   switch (reservation.ordering) {
529     case DescriptorTrackReservation::ChildTracksOrdering::kLexicographic:
530       args.AddArg(child_ordering_key_, Variadic::String(lexicographic_id_));
531       break;
532     case DescriptorTrackReservation::ChildTracksOrdering::kChronological:
533       args.AddArg(child_ordering_key_, Variadic::String(chronological_id_));
534       break;
535     case DescriptorTrackReservation::ChildTracksOrdering::kExplicit:
536       args.AddArg(child_ordering_key_, Variadic::String(explicit_id_));
537       break;
538     case DescriptorTrackReservation::ChildTracksOrdering::kUnknown:
539       break;
540   }
541 
542   if (reservation.sibling_order_rank) {
543     args.AddArg(sibling_order_rank_key_,
544                 Variadic::Integer(*reservation.sibling_order_rank));
545   }
546 }
547 
IsTrackHierarchyValid(uint64_t uuid)548 bool TrackEventTracker::IsTrackHierarchyValid(uint64_t uuid) {
549   // Do a basic tree walking algorithm to find if there are duplicate ids or
550   // the path to the root is longer than kMaxAncestors.
551   static constexpr size_t kMaxAncestors = 100;
552   std::array<uint64_t, kMaxAncestors> seen;
553   uint64_t current_uuid = uuid;
554   for (uint32_t i = 0; i < kMaxAncestors; ++i) {
555     if (current_uuid == 0) {
556       return true;
557     }
558     for (uint32_t j = 0; j < i; ++j) {
559       if (current_uuid == seen[j]) {
560         PERFETTO_ELOG("Loop detected in hierarchy for track %" PRIu64, uuid);
561         return false;
562       }
563     }
564     auto* reservation_ptr = reserved_descriptor_tracks_.Find(current_uuid);
565     if (!reservation_ptr) {
566       PERFETTO_ELOG("Missing uuid in hierarchy for track %" PRIu64, uuid);
567       return false;
568     }
569     seen[i] = current_uuid;
570     current_uuid = reservation_ptr->parent_uuid;
571   }
572   PERFETTO_ELOG("Too many ancestors in hierarchy for track %" PRIu64, uuid);
573   return false;
574 }
575 
576 TrackEventTracker::ResolvedDescriptorTrack
Process(TrackId track_id,UniquePid upid,bool is_counter,bool is_root)577 TrackEventTracker::ResolvedDescriptorTrack::Process(TrackId track_id,
578                                                     UniquePid upid,
579                                                     bool is_counter,
580                                                     bool is_root) {
581   ResolvedDescriptorTrack track;
582   track.track_id_ = track_id;
583   track.scope_ = Scope::kProcess;
584   track.is_counter_ = is_counter;
585   track.upid_ = upid;
586   track.is_root_ = is_root;
587   return track;
588 }
589 
590 TrackEventTracker::ResolvedDescriptorTrack
Thread(TrackId track_id,UniqueTid utid,bool is_counter,bool is_root)591 TrackEventTracker::ResolvedDescriptorTrack::Thread(TrackId track_id,
592                                                    UniqueTid utid,
593                                                    bool is_counter,
594                                                    bool is_root) {
595   ResolvedDescriptorTrack track;
596   track.track_id_ = track_id;
597   track.scope_ = Scope::kThread;
598   track.is_counter_ = is_counter;
599   track.utid_ = utid;
600   track.is_root_ = is_root;
601   return track;
602 }
603 
604 TrackEventTracker::ResolvedDescriptorTrack
Global(TrackId track_id,bool is_counter)605 TrackEventTracker::ResolvedDescriptorTrack::Global(TrackId track_id,
606                                                    bool is_counter) {
607   ResolvedDescriptorTrack track;
608   track.track_id_ = track_id;
609   track.scope_ = Scope::kGlobal;
610   track.is_counter_ = is_counter;
611   track.is_root_ = false;
612   return track;
613 }
614 
615 }  // namespace perfetto::trace_processor
616