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