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