• 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   auto resolved_track = ResolveDescriptorTrackImpl(uuid, reservation_it->second,
295                                                    descendent_uuids);
296   resolved_descriptor_tracks_[uuid] = resolved_track;
297   return resolved_track;
298 }
299 
300 TrackEventTracker::ResolvedDescriptorTrack
ResolveDescriptorTrackImpl(uint64_t uuid,const DescriptorTrackReservation & reservation,std::vector<uint64_t> * descendent_uuids)301 TrackEventTracker::ResolveDescriptorTrackImpl(
302     uint64_t uuid,
303     const DescriptorTrackReservation& reservation,
304     std::vector<uint64_t>* descendent_uuids) {
305   static constexpr size_t kMaxAncestors = 10;
306 
307   // Try to resolve any parent tracks recursively, too.
308   base::Optional<ResolvedDescriptorTrack> parent_resolved_track;
309   if (reservation.parent_uuid) {
310     // Input data may contain loops or extremely long ancestor track chains. To
311     // avoid stack overflow in these situations, we keep track of the ancestors
312     // seen in the recursion.
313     std::unique_ptr<std::vector<uint64_t>> owned_descendent_uuids;
314     if (!descendent_uuids) {
315       owned_descendent_uuids.reset(new std::vector<uint64_t>());
316       descendent_uuids = owned_descendent_uuids.get();
317     }
318     descendent_uuids->push_back(uuid);
319 
320     if (descendent_uuids->size() > kMaxAncestors) {
321       PERFETTO_ELOG(
322           "Too many ancestors in parent_track_uuid hierarchy at track %" PRIu64
323           " with parent %" PRIu64,
324           uuid, reservation.parent_uuid);
325     } else if (std::find(descendent_uuids->begin(), descendent_uuids->end(),
326                          reservation.parent_uuid) != descendent_uuids->end()) {
327       PERFETTO_ELOG(
328           "Loop detected in parent_track_uuid hierarchy at track %" PRIu64
329           " with parent %" PRIu64,
330           uuid, reservation.parent_uuid);
331     } else {
332       parent_resolved_track =
333           ResolveDescriptorTrack(reservation.parent_uuid, descendent_uuids);
334       if (!parent_resolved_track) {
335         PERFETTO_ELOG("Unknown parent track %" PRIu64 " for track %" PRIu64,
336                       reservation.parent_uuid, uuid);
337       }
338     }
339 
340     descendent_uuids->pop_back();
341     if (owned_descendent_uuids)
342       descendent_uuids = nullptr;
343   }
344 
345   if (reservation.tid) {
346     UniqueTid utid = context_->process_tracker->UpdateThread(*reservation.tid,
347                                                              *reservation.pid);
348     auto it_and_inserted =
349         descriptor_uuids_by_utid_.insert(std::make_pair<>(utid, uuid));
350     if (!it_and_inserted.second) {
351       // We already saw a another track with a different uuid for this thread.
352       // Since there should only be one descriptor track for each thread, we
353       // assume that its tid was reused. So, start a new thread.
354       uint64_t old_uuid = it_and_inserted.first->second;
355       PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
356 
357       PERFETTO_DLOG("Detected tid reuse (pid: %" PRIu32 " tid: %" PRIu32
358                     ") from track descriptors (old uuid: %" PRIu64
359                     " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
360                     *reservation.pid, *reservation.tid, old_uuid, uuid,
361                     reservation.min_timestamp);
362 
363       utid = context_->process_tracker->StartNewThread(base::nullopt,
364                                                        *reservation.tid);
365 
366       // Associate the new thread with its process.
367       PERFETTO_CHECK(context_->process_tracker->UpdateThread(
368                          *reservation.tid, *reservation.pid) == utid);
369 
370       descriptor_uuids_by_utid_[utid] = uuid;
371     }
372     return ResolvedDescriptorTrack::Thread(utid, false /* is_counter */,
373                                            true /* is_root*/);
374   }
375 
376   if (reservation.pid) {
377     UniquePid upid =
378         context_->process_tracker->GetOrCreateProcess(*reservation.pid);
379     auto it_and_inserted =
380         descriptor_uuids_by_upid_.insert(std::make_pair<>(upid, uuid));
381     if (!it_and_inserted.second) {
382       // We already saw a another track with a different uuid for this process.
383       // Since there should only be one descriptor track for each process, we
384       // assume that its pid was reused. So, start a new process.
385       uint64_t old_uuid = it_and_inserted.first->second;
386       PERFETTO_DCHECK(old_uuid != uuid);  // Every track is only resolved once.
387 
388       PERFETTO_DLOG("Detected pid reuse (pid: %" PRIu32
389                     ") from track descriptors (old uuid: %" PRIu64
390                     " new uuid: %" PRIu64 " timestamp: %" PRId64 ")",
391                     *reservation.pid, old_uuid, uuid,
392                     reservation.min_timestamp);
393 
394       upid = context_->process_tracker->StartNewProcess(
395           base::nullopt, base::nullopt, *reservation.pid, kNullStringId);
396 
397       descriptor_uuids_by_upid_[upid] = uuid;
398     }
399     return ResolvedDescriptorTrack::Process(upid, false /* is_counter */,
400                                             true /* is_root*/);
401   }
402 
403   if (parent_resolved_track) {
404     switch (parent_resolved_track->scope()) {
405       case ResolvedDescriptorTrack::Scope::kThread:
406         // If parent is a thread track, create another thread-associated track.
407         return ResolvedDescriptorTrack::Thread(parent_resolved_track->utid(),
408                                                reservation.is_counter,
409                                                false /* is_root*/);
410       case ResolvedDescriptorTrack::Scope::kProcess:
411         // If parent is a process track, create another process-associated
412         // track.
413         return ResolvedDescriptorTrack::Process(parent_resolved_track->upid(),
414                                                 reservation.is_counter,
415                                                 false /* is_root*/);
416       case ResolvedDescriptorTrack::Scope::kGlobal:
417         break;
418     }
419   }
420 
421   // Otherwise create a global track.
422 
423   // The global track with no uuid is the default global track (e.g. for
424   // global instant events). Any other global tracks are considered children
425   // of the default track.
426   bool is_root_in_scope = !parent_resolved_track;
427   if (!parent_resolved_track && uuid) {
428     // Detect loops where the default track has a parent that itself is a
429     // global track (and thus should be parent of the default track).
430     if (descendent_uuids &&
431         std::find(descendent_uuids->begin(), descendent_uuids->end(),
432                   kDefaultDescriptorTrackUuid) != descendent_uuids->end()) {
433       PERFETTO_ELOG(
434           "Loop detected in parent_track_uuid hierarchy at track %" PRIu64
435           " with parent %" PRIu64,
436           uuid, kDefaultDescriptorTrackUuid);
437     } else {
438       // This track will be implicitly a child of the default global track.
439       is_root_in_scope = false;
440     }
441   }
442   return ResolvedDescriptorTrack::Global(reservation.is_counter,
443                                          is_root_in_scope);
444 }
445 
GetOrCreateDefaultDescriptorTrack()446 TrackId TrackEventTracker::GetOrCreateDefaultDescriptorTrack() {
447   // If the default track was already reserved (e.g. because a producer emitted
448   // a descriptor for it) or created, resolve and return it.
449   base::Optional<TrackId> track_id =
450       GetDescriptorTrack(kDefaultDescriptorTrackUuid);
451   if (track_id)
452     return *track_id;
453 
454   // Otherwise reserve a new track and resolve it.
455   ReserveDescriptorChildTrack(kDefaultDescriptorTrackUuid, /*parent_uuid=*/0,
456                               default_descriptor_track_name_);
457   return *GetDescriptorTrack(kDefaultDescriptorTrackUuid);
458 }
459 
ConvertToAbsoluteCounterValue(uint64_t counter_track_uuid,uint32_t packet_sequence_id,double value)460 base::Optional<double> TrackEventTracker::ConvertToAbsoluteCounterValue(
461     uint64_t counter_track_uuid,
462     uint32_t packet_sequence_id,
463     double value) {
464   auto reservation_it = reserved_descriptor_tracks_.find(counter_track_uuid);
465   if (reservation_it == reserved_descriptor_tracks_.end()) {
466     PERFETTO_DLOG("Unknown counter track with uuid %" PRIu64,
467                   counter_track_uuid);
468     return base::nullopt;
469   }
470 
471   DescriptorTrackReservation& reservation = reservation_it->second;
472   if (!reservation.is_counter) {
473     PERFETTO_DLOG("Track with uuid %" PRIu64 " is not a counter track",
474                   counter_track_uuid);
475     return base::nullopt;
476   }
477 
478   if (reservation.unit_multiplier > 0)
479     value *= static_cast<double>(reservation.unit_multiplier);
480 
481   if (reservation.is_incremental) {
482     if (reservation.packet_sequence_id != packet_sequence_id) {
483       PERFETTO_DLOG(
484           "Incremental counter track with uuid %" PRIu64
485           " was updated from the wrong packet sequence (expected: %" PRIu32
486           " got:%" PRIu32 ")",
487           counter_track_uuid, reservation.packet_sequence_id,
488           packet_sequence_id);
489       return base::nullopt;
490     }
491 
492     reservation.latest_value += value;
493     value = reservation.latest_value;
494   }
495 
496   return value;
497 }
498 
OnIncrementalStateCleared(uint32_t packet_sequence_id)499 void TrackEventTracker::OnIncrementalStateCleared(uint32_t packet_sequence_id) {
500   // TODO(eseckler): Improve on the runtime complexity of this. At O(hundreds)
501   // of packet sequences, incremental state clearing at O(trace second), and
502   // total number of tracks in O(thousands), a linear scan through all tracks
503   // here might not be fast enough.
504   for (auto& entry : reserved_descriptor_tracks_) {
505     DescriptorTrackReservation& reservation = entry.second;
506     // Only consider incremental counter tracks for current sequence.
507     if (!reservation.is_counter || !reservation.is_incremental ||
508         reservation.packet_sequence_id != packet_sequence_id) {
509       continue;
510     }
511     // Reset their value to 0, see CounterDescriptor's |is_incremental|.
512     reservation.latest_value = 0;
513   }
514 }
515 
516 TrackEventTracker::ResolvedDescriptorTrack
Process(UniquePid upid,bool is_counter,bool is_root)517 TrackEventTracker::ResolvedDescriptorTrack::Process(UniquePid upid,
518                                                     bool is_counter,
519                                                     bool is_root) {
520   ResolvedDescriptorTrack track;
521   track.scope_ = Scope::kProcess;
522   track.is_counter_ = is_counter;
523   track.is_root_in_scope_ = is_root;
524   track.upid_ = upid;
525   return track;
526 }
527 
528 TrackEventTracker::ResolvedDescriptorTrack
Thread(UniqueTid utid,bool is_counter,bool is_root)529 TrackEventTracker::ResolvedDescriptorTrack::Thread(UniqueTid utid,
530                                                    bool is_counter,
531                                                    bool is_root) {
532   ResolvedDescriptorTrack track;
533   track.scope_ = Scope::kThread;
534   track.is_counter_ = is_counter;
535   track.is_root_in_scope_ = is_root;
536   track.utid_ = utid;
537   return track;
538 }
539 
540 TrackEventTracker::ResolvedDescriptorTrack
Global(bool is_counter,bool is_root)541 TrackEventTracker::ResolvedDescriptorTrack::Global(bool is_counter,
542                                                    bool is_root) {
543   ResolvedDescriptorTrack track;
544   track.scope_ = Scope::kGlobal;
545   track.is_counter_ = is_counter;
546   track.is_root_in_scope_ = is_root;
547   return track;
548 }
549 
550 }  // namespace trace_processor
551 }  // namespace perfetto
552