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