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