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