1 /*
2 * Copyright (C) 2019 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_parser.h"
18
19 #include <iostream>
20 #include <optional>
21 #include <string>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/ext/base/base64.h"
25 #include "perfetto/ext/base/string_writer.h"
26 #include "perfetto/trace_processor/status.h"
27 #include "src/trace_processor/importers/common/args_tracker.h"
28 #include "src/trace_processor/importers/common/args_translation_table.h"
29 #include "src/trace_processor/importers/common/cpu_tracker.h"
30 #include "src/trace_processor/importers/common/event_tracker.h"
31 #include "src/trace_processor/importers/common/flow_tracker.h"
32 #include "src/trace_processor/importers/common/process_track_translation_table.h"
33 #include "src/trace_processor/importers/common/process_tracker.h"
34 #include "src/trace_processor/importers/common/track_tracker.h"
35 #include "src/trace_processor/importers/common/virtual_memory_mapping.h"
36 #include "src/trace_processor/importers/json/json_utils.h"
37 #include "src/trace_processor/importers/proto/args_parser.h"
38 #include "src/trace_processor/importers/proto/packet_analyzer.h"
39 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
40 #include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
41 #include "src/trace_processor/importers/proto/track_event_tracker.h"
42 #include "src/trace_processor/util/debug_annotation_parser.h"
43 #include "src/trace_processor/util/proto_to_args_parser.h"
44 #include "src/trace_processor/util/status_macros.h"
45
46 #include "protos/perfetto/common/android_log_constants.pbzero.h"
47 #include "protos/perfetto/trace/extension_descriptor.pbzero.h"
48 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
49 #include "protos/perfetto/trace/track_event/chrome_active_processes.pbzero.h"
50 #include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
51 #include "protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h"
52 #include "protos/perfetto/trace/track_event/chrome_legacy_ipc.pbzero.h"
53 #include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
54 #include "protos/perfetto/trace/track_event/chrome_thread_descriptor.pbzero.h"
55 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
56 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
57 #include "protos/perfetto/trace/track_event/log_message.pbzero.h"
58 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
59 #include "protos/perfetto/trace/track_event/source_location.pbzero.h"
60 #include "protos/perfetto/trace/track_event/task_execution.pbzero.h"
61 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
62 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
63 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
64
65 namespace perfetto {
66 namespace trace_processor {
67
68 namespace {
69 using BoundInserter = ArgsTracker::BoundInserter;
70 using protos::pbzero::TrackEvent;
71 using LegacyEvent = TrackEvent::LegacyEvent;
72 using protozero::ConstBytes;
73
74 // Slices which have been opened but haven't been closed yet will be marked
75 // with these placeholder values.
76 constexpr int64_t kPendingThreadDuration = -1;
77 constexpr int64_t kPendingThreadInstructionDelta = -1;
78
79 // Paths on Windows use backslash rather than slash as a separator.
80 // Normalise the paths by replacing backslashes with slashes to make it
81 // easier to write cross-platform scripts.
NormalizePathSeparators(const protozero::ConstChars & path)82 std::string NormalizePathSeparators(const protozero::ConstChars& path) {
83 std::string result(path.data, path.size);
84 for (char& c : result) {
85 if (c == '\\')
86 c = '/';
87 }
88 return result;
89 }
90
MaybeParseUnsymbolizedSourceLocation(std::string prefix,const protozero::Field & field,util::ProtoToArgsParser::Delegate & delegate)91 std::optional<base::Status> MaybeParseUnsymbolizedSourceLocation(
92 std::string prefix,
93 const protozero::Field& field,
94 util::ProtoToArgsParser::Delegate& delegate) {
95 auto* decoder = delegate.GetInternedMessage(
96 protos::pbzero::InternedData::kUnsymbolizedSourceLocations,
97 field.as_uint64());
98 if (!decoder) {
99 // Lookup failed fall back on default behaviour which will just put
100 // the iid into the args table.
101 return std::nullopt;
102 }
103 // Interned mapping_id loses it's meaning when the sequence ends. So we need
104 // to get an id from stack_profile_mapping table.
105 auto mapping = delegate.seq_state()
106 ->GetCustomState<StackProfileSequenceState>()
107 ->FindOrInsertMapping(decoder->mapping_id());
108 if (!mapping) {
109 return std::nullopt;
110 }
111 delegate.AddUnsignedInteger(
112 util::ProtoToArgsParser::Key(prefix + ".mapping_id"),
113 mapping->mapping_id().value);
114 delegate.AddUnsignedInteger(util::ProtoToArgsParser::Key(prefix + ".rel_pc"),
115 decoder->rel_pc());
116 return base::OkStatus();
117 }
118
MaybeParseSourceLocation(std::string prefix,const protozero::Field & field,util::ProtoToArgsParser::Delegate & delegate)119 std::optional<base::Status> MaybeParseSourceLocation(
120 std::string prefix,
121 const protozero::Field& field,
122 util::ProtoToArgsParser::Delegate& delegate) {
123 auto* decoder = delegate.GetInternedMessage(
124 protos::pbzero::InternedData::kSourceLocations, field.as_uint64());
125 if (!decoder) {
126 // Lookup failed fall back on default behaviour which will just put
127 // the source_location_iid into the args table.
128 return std::nullopt;
129 }
130
131 delegate.AddString(util::ProtoToArgsParser::Key(prefix + ".file_name"),
132 NormalizePathSeparators(decoder->file_name()));
133 delegate.AddString(util::ProtoToArgsParser::Key(prefix + ".function_name"),
134 decoder->function_name());
135 if (decoder->has_line_number()) {
136 delegate.AddInteger(util::ProtoToArgsParser::Key(prefix + ".line_number"),
137 decoder->line_number());
138 }
139
140 return base::OkStatus();
141 }
142
ToAndroidLogPriority(protos::pbzero::LogMessage::Priority prio)143 protos::pbzero::AndroidLogPriority ToAndroidLogPriority(
144 protos::pbzero::LogMessage::Priority prio) {
145 switch (prio) {
146 case protos::pbzero::LogMessage::Priority::PRIO_UNSPECIFIED:
147 return protos::pbzero::AndroidLogPriority::PRIO_UNSPECIFIED;
148 case protos::pbzero::LogMessage::Priority::PRIO_UNUSED:
149 return protos::pbzero::AndroidLogPriority::PRIO_UNUSED;
150 case protos::pbzero::LogMessage::Priority::PRIO_VERBOSE:
151 return protos::pbzero::AndroidLogPriority::PRIO_VERBOSE;
152 case protos::pbzero::LogMessage::Priority::PRIO_DEBUG:
153 return protos::pbzero::AndroidLogPriority::PRIO_DEBUG;
154 case protos::pbzero::LogMessage::Priority::PRIO_INFO:
155 return protos::pbzero::AndroidLogPriority::PRIO_INFO;
156 case protos::pbzero::LogMessage::Priority::PRIO_WARN:
157 return protos::pbzero::AndroidLogPriority::PRIO_WARN;
158 case protos::pbzero::LogMessage::Priority::PRIO_ERROR:
159 return protos::pbzero::AndroidLogPriority::PRIO_ERROR;
160 case protos::pbzero::LogMessage::Priority::PRIO_FATAL:
161 return protos::pbzero::AndroidLogPriority::PRIO_FATAL;
162 }
163 return protos::pbzero::AndroidLogPriority::PRIO_UNSPECIFIED;
164 }
165
166 } // namespace
167
168 class TrackEventParser::EventImporter {
169 public:
EventImporter(TrackEventParser * parser,int64_t ts,const TrackEventData * event_data,ConstBytes blob,uint32_t packet_sequence_id)170 EventImporter(TrackEventParser* parser,
171 int64_t ts,
172 const TrackEventData* event_data,
173 ConstBytes blob,
174 uint32_t packet_sequence_id)
175 : context_(parser->context_),
176 track_event_tracker_(parser->track_event_tracker_),
177 storage_(context_->storage.get()),
178 parser_(parser),
179 args_translation_table_(context_->args_translation_table.get()),
180 ts_(ts),
181 event_data_(event_data),
182 sequence_state_(event_data->trace_packet_data.sequence_state.get()),
183 blob_(std::move(blob)),
184 event_(blob_),
185 legacy_event_(event_.legacy_event()),
186 defaults_(event_data->trace_packet_data.sequence_state
187 ->GetTrackEventDefaults()),
188 thread_timestamp_(event_data->thread_timestamp),
189 thread_instruction_count_(event_data->thread_instruction_count),
190 packet_sequence_id_(packet_sequence_id) {}
191
Import()192 util::Status Import() {
193 // TODO(eseckler): This legacy event field will eventually be replaced by
194 // fields in TrackEvent itself.
195 if (PERFETTO_UNLIKELY(!event_.type() && !legacy_event_.has_phase()))
196 return util::ErrStatus("TrackEvent without type or phase");
197
198 category_id_ = ParseTrackEventCategory();
199 name_id_ = ParseTrackEventName();
200
201 if (context_->content_analyzer) {
202 PacketAnalyzer::SampleAnnotation annotation;
203 annotation.push_back({parser_->event_category_key_id_, category_id_});
204 annotation.push_back({parser_->event_name_key_id_, name_id_});
205 PacketAnalyzer::Get(context_)->ProcessPacket(
206 event_data_->trace_packet_data.packet, annotation);
207 }
208
209 RETURN_IF_ERROR(ParseTrackAssociation());
210
211 // Counter-type events don't support arguments (those are on the
212 // CounterDescriptor instead). All they have is a |{double_,}counter_value|.
213 if (event_.type() == TrackEvent::TYPE_COUNTER) {
214 ParseCounterEvent();
215 return util::OkStatus();
216 }
217
218 // If we have legacy thread time / instruction count fields, also parse them
219 // into the counters tables.
220 ParseLegacyThreadTimeAndInstructionsAsCounters();
221
222 // Parse extra counter values before parsing the actual event. This way, we
223 // can update the slice's thread time / instruction count fields based on
224 // these counter values and also parse them as slice attributes / arguments.
225 ParseExtraCounterValues();
226
227 // TODO(eseckler): Replace phase with type and remove handling of
228 // legacy_event_.phase() once it is no longer used by producers.
229 char phase = static_cast<char>(ParsePhaseOrType());
230
231 switch (phase) {
232 case 'B': // TRACE_EVENT_PHASE_BEGIN.
233 return ParseThreadBeginEvent();
234 case 'E': // TRACE_EVENT_PHASE_END.
235 return ParseThreadEndEvent();
236 case 'X': // TRACE_EVENT_PHASE_COMPLETE.
237 return ParseThreadCompleteEvent();
238 case 's': // TRACE_EVENT_PHASE_FLOW_BEGIN.
239 case 't': // TRACE_EVENT_PHASE_FLOW_STEP.
240 case 'f': // TRACE_EVENT_PHASE_FLOW_END.
241 return ParseFlowEventV1(phase);
242 case 'i':
243 case 'I': // TRACE_EVENT_PHASE_INSTANT.
244 case 'R': // TRACE_EVENT_PHASE_MARK.
245 return ParseThreadInstantEvent(phase);
246 case 'b': // TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN
247 case 'S':
248 return ParseAsyncBeginEvent(phase);
249 case 'e': // TRACE_EVENT_PHASE_NESTABLE_ASYNC_END
250 case 'F':
251 return ParseAsyncEndEvent();
252 case 'n': // TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT
253 return ParseAsyncInstantEvent();
254 case 'T':
255 case 'p':
256 return ParseAsyncStepEvent(phase);
257 case 'M': // TRACE_EVENT_PHASE_METADATA (process and thread names).
258 return ParseMetadataEvent();
259 default:
260 // Other events are proxied via the raw table for JSON export.
261 return ParseLegacyEventAsRawEvent();
262 }
263 }
264
265 private:
ParseTrackEventCategory()266 StringId ParseTrackEventCategory() {
267 StringId category_id = kNullStringId;
268
269 std::vector<uint64_t> category_iids;
270 for (auto it = event_.category_iids(); it; ++it) {
271 category_iids.push_back(*it);
272 }
273 std::vector<protozero::ConstChars> category_strings;
274 for (auto it = event_.categories(); it; ++it) {
275 category_strings.push_back(*it);
276 }
277
278 // If there's a single category, we can avoid building a concatenated
279 // string.
280 if (PERFETTO_LIKELY(category_iids.size() == 1 &&
281 category_strings.empty())) {
282 auto* decoder = sequence_state_->LookupInternedMessage<
283 protos::pbzero::InternedData::kEventCategoriesFieldNumber,
284 protos::pbzero::EventCategory>(category_iids[0]);
285 if (decoder) {
286 category_id = storage_->InternString(decoder->name());
287 } else {
288 char buffer[32];
289 base::StringWriter writer(buffer, sizeof(buffer));
290 writer.AppendLiteral("unknown(");
291 writer.AppendUnsignedInt(category_iids[0]);
292 writer.AppendChar(')');
293 category_id = storage_->InternString(writer.GetStringView());
294 }
295 } else if (category_iids.empty() && category_strings.size() == 1) {
296 category_id = storage_->InternString(category_strings[0]);
297 } else if (category_iids.size() + category_strings.size() > 1) {
298 // We concatenate the category strings together since we currently only
299 // support a single "cat" column.
300 // TODO(eseckler): Support multi-category events in the table schema.
301 std::string categories;
302 for (uint64_t iid : category_iids) {
303 auto* decoder = sequence_state_->LookupInternedMessage<
304 protos::pbzero::InternedData::kEventCategoriesFieldNumber,
305 protos::pbzero::EventCategory>(iid);
306 if (!decoder)
307 continue;
308 base::StringView name = decoder->name();
309 if (!categories.empty())
310 categories.append(",");
311 categories.append(name.data(), name.size());
312 }
313 for (const protozero::ConstChars& cat : category_strings) {
314 if (!categories.empty())
315 categories.append(",");
316 categories.append(cat.data, cat.size);
317 }
318 if (!categories.empty())
319 category_id = storage_->InternString(base::StringView(categories));
320 }
321
322 return category_id;
323 }
324
ParseTrackEventName()325 StringId ParseTrackEventName() {
326 uint64_t name_iid = event_.name_iid();
327 if (!name_iid)
328 name_iid = legacy_event_.name_iid();
329
330 if (PERFETTO_LIKELY(name_iid)) {
331 auto* decoder = sequence_state_->LookupInternedMessage<
332 protos::pbzero::InternedData::kEventNamesFieldNumber,
333 protos::pbzero::EventName>(name_iid);
334 if (decoder)
335 return storage_->InternString(decoder->name());
336 } else if (event_.has_name()) {
337 return storage_->InternString(event_.name());
338 }
339
340 return kNullStringId;
341 }
342
ParseTrackAssociation()343 util::Status ParseTrackAssociation() {
344 TrackTracker* track_tracker = context_->track_tracker.get();
345 ProcessTracker* procs = context_->process_tracker.get();
346
347 // Consider track_uuid from the packet and TrackEventDefaults, fall back to
348 // the default descriptor track (uuid 0).
349 track_uuid_ = event_.has_track_uuid()
350 ? event_.track_uuid()
351 : (defaults_ && defaults_->has_track_uuid()
352 ? defaults_->track_uuid()
353 : 0u);
354
355 // Determine track from track_uuid specified in either TrackEvent or
356 // TrackEventDefaults. If a non-default track is not set, we either:
357 // a) fall back to the track specified by the sequence's (or event's) pid
358 // + tid (only in case of legacy tracks/events, i.e. events that don't
359 // specify an explicit track uuid or use legacy event phases instead of
360 // TrackEvent types), or
361 // b) a default track.
362 if (track_uuid_) {
363 std::optional<TrackId> opt_track_id =
364 track_event_tracker_->GetDescriptorTrack(track_uuid_, name_id_,
365 packet_sequence_id_);
366 if (!opt_track_id) {
367 track_event_tracker_->ReserveDescriptorChildTrack(track_uuid_,
368 /*parent_uuid=*/0,
369 name_id_);
370 opt_track_id = track_event_tracker_->GetDescriptorTrack(
371 track_uuid_, name_id_, packet_sequence_id_);
372 }
373 track_id_ = *opt_track_id;
374
375 auto thread_track_row =
376 storage_->thread_track_table().id().IndexOf(track_id_);
377 if (thread_track_row) {
378 utid_ = storage_->thread_track_table().utid()[*thread_track_row];
379 upid_ = storage_->thread_table().upid()[*utid_];
380 } else {
381 auto process_track_row =
382 storage_->process_track_table().id().IndexOf(track_id_);
383 if (process_track_row) {
384 upid_ = storage_->process_track_table().upid()[*process_track_row];
385 if (sequence_state_->pid_and_tid_valid()) {
386 uint32_t pid = static_cast<uint32_t>(sequence_state_->pid());
387 uint32_t tid = static_cast<uint32_t>(sequence_state_->tid());
388 UniqueTid utid_candidate = procs->UpdateThread(tid, pid);
389 if (storage_->thread_table().upid()[utid_candidate] == upid_)
390 legacy_passthrough_utid_ = utid_candidate;
391 }
392 } else {
393 auto* tracks = context_->storage->mutable_track_table();
394 auto track_index = tracks->id().IndexOf(track_id_);
395 if (track_index) {
396 const StringPool::Id& id = tracks->name()[*track_index];
397 if (id.is_null())
398 tracks->mutable_name()->Set(*track_index, name_id_);
399 }
400
401 if (sequence_state_->pid_and_tid_valid()) {
402 uint32_t pid = static_cast<uint32_t>(sequence_state_->pid());
403 uint32_t tid = static_cast<uint32_t>(sequence_state_->tid());
404 legacy_passthrough_utid_ = procs->UpdateThread(tid, pid);
405 }
406 }
407 }
408 } else {
409 bool pid_tid_state_valid = sequence_state_->pid_and_tid_valid();
410
411 // We have a 0-value |track_uuid|. Nevertheless, we should only fall back
412 // if we have either no |track_uuid| specified at all or |track_uuid| was
413 // set explicitly to 0 (e.g. to override a default track_uuid) and we have
414 // a legacy phase. Events with real phases should use |track_uuid| to
415 // specify a different track (or use the pid/tid_override fields).
416 bool fallback_to_legacy_pid_tid_tracks =
417 (!event_.has_track_uuid() || !event_.has_type()) &&
418 pid_tid_state_valid;
419
420 // Always allow fallback if we have a process override.
421 fallback_to_legacy_pid_tid_tracks |= legacy_event_.has_pid_override();
422
423 // A thread override requires a valid pid.
424 fallback_to_legacy_pid_tid_tracks |=
425 legacy_event_.has_tid_override() && pid_tid_state_valid;
426
427 if (fallback_to_legacy_pid_tid_tracks) {
428 uint32_t pid = static_cast<uint32_t>(sequence_state_->pid());
429 uint32_t tid = static_cast<uint32_t>(sequence_state_->tid());
430 if (legacy_event_.has_pid_override()) {
431 pid = static_cast<uint32_t>(legacy_event_.pid_override());
432 tid = static_cast<uint32_t>(-1);
433 }
434 if (legacy_event_.has_tid_override())
435 tid = static_cast<uint32_t>(legacy_event_.tid_override());
436
437 utid_ = procs->UpdateThread(tid, pid);
438 upid_ = storage_->thread_table().upid()[*utid_];
439 track_id_ = track_tracker->InternThreadTrack(*utid_);
440 } else {
441 track_id_ = track_event_tracker_->GetOrCreateDefaultDescriptorTrack();
442 }
443 }
444
445 if (!legacy_event_.has_phase())
446 return util::OkStatus();
447
448 // Legacy phases may imply a different track than the one specified by
449 // the fallback (or default track uuid) above.
450 switch (legacy_event_.phase()) {
451 case 'b':
452 case 'e':
453 case 'n':
454 case 'S':
455 case 'T':
456 case 'p':
457 case 'F': {
458 // Intern tracks for legacy async events based on legacy event ids.
459 int64_t source_id = 0;
460 bool source_id_is_process_scoped = false;
461 if (legacy_event_.has_unscoped_id()) {
462 source_id = static_cast<int64_t>(legacy_event_.unscoped_id());
463 } else if (legacy_event_.has_global_id()) {
464 source_id = static_cast<int64_t>(legacy_event_.global_id());
465 } else if (legacy_event_.has_local_id()) {
466 if (!upid_) {
467 return util::ErrStatus(
468 "TrackEvent with local_id without process association");
469 }
470
471 source_id = static_cast<int64_t>(legacy_event_.local_id());
472 source_id_is_process_scoped = true;
473 } else {
474 return util::ErrStatus("Async LegacyEvent without ID");
475 }
476
477 // Catapult treats nestable async events of different categories with
478 // the same ID as separate tracks. We replicate the same behavior
479 // here. For legacy async events, it uses different tracks based on
480 // event names.
481 const bool legacy_async =
482 legacy_event_.phase() == 'S' || legacy_event_.phase() == 'T' ||
483 legacy_event_.phase() == 'p' || legacy_event_.phase() == 'F';
484 StringId id_scope = legacy_async ? name_id_ : category_id_;
485 if (legacy_event_.has_id_scope()) {
486 std::string concat = storage_->GetString(category_id_).ToStdString() +
487 ":" + legacy_event_.id_scope().ToStdString();
488 id_scope = storage_->InternString(base::StringView(concat));
489 }
490
491 track_id_ = context_->track_tracker->InternLegacyChromeAsyncTrack(
492 name_id_, upid_.value_or(0), source_id, source_id_is_process_scoped,
493 id_scope);
494 legacy_passthrough_utid_ = utid_;
495 break;
496 }
497 case 'i':
498 case 'I': {
499 // Intern tracks for global or process-scoped legacy instant events.
500 switch (legacy_event_.instant_event_scope()) {
501 case LegacyEvent::SCOPE_UNSPECIFIED:
502 case LegacyEvent::SCOPE_THREAD:
503 // Thread-scoped legacy instant events already have the right
504 // track based on the tid/pid of the sequence.
505 if (!utid_) {
506 return util::ErrStatus(
507 "Thread-scoped instant event without thread association");
508 }
509 break;
510 case LegacyEvent::SCOPE_GLOBAL:
511 track_id_ = context_->track_tracker
512 ->GetOrCreateLegacyChromeGlobalInstantTrack();
513 legacy_passthrough_utid_ = utid_;
514 utid_ = std::nullopt;
515 break;
516 case LegacyEvent::SCOPE_PROCESS:
517 if (!upid_) {
518 return util::ErrStatus(
519 "Process-scoped instant event without process association");
520 }
521
522 track_id_ =
523 context_->track_tracker->InternLegacyChromeProcessInstantTrack(
524 *upid_);
525 legacy_passthrough_utid_ = utid_;
526 utid_ = std::nullopt;
527 break;
528 }
529 break;
530 }
531 default:
532 break;
533 }
534
535 return util::OkStatus();
536 }
537
ParsePhaseOrType()538 int32_t ParsePhaseOrType() {
539 if (legacy_event_.has_phase())
540 return legacy_event_.phase();
541
542 switch (event_.type()) {
543 case TrackEvent::TYPE_SLICE_BEGIN:
544 return utid_ ? 'B' : 'b';
545 case TrackEvent::TYPE_SLICE_END:
546 return utid_ ? 'E' : 'e';
547 case TrackEvent::TYPE_INSTANT:
548 return utid_ ? 'i' : 'n';
549 default:
550 PERFETTO_ELOG("unexpected event type %d", event_.type());
551 return 0;
552 }
553 }
554
ParseCounterEvent()555 void ParseCounterEvent() {
556 // Tokenizer ensures that TYPE_COUNTER events are associated with counter
557 // tracks and have values.
558 PERFETTO_DCHECK(storage_->counter_track_table().id().IndexOf(track_id_));
559 PERFETTO_DCHECK(event_.has_counter_value() ||
560 event_.has_double_counter_value());
561
562 context_->event_tracker->PushCounter(
563 ts_, static_cast<double>(event_data_->counter_value), track_id_);
564 }
565
ParseLegacyThreadTimeAndInstructionsAsCounters()566 void ParseLegacyThreadTimeAndInstructionsAsCounters() {
567 if (!utid_)
568 return;
569 // When these fields are set, we don't expect TrackDescriptor-based counters
570 // for thread time or instruction count for this thread in the trace, so we
571 // intern separate counter tracks based on name + utid. Note that we cannot
572 // import the counter values from the end of a complete event, because the
573 // EventTracker expects counters to be pushed in order of their timestamps.
574 // One more reason to switch to split begin/end events.
575 if (thread_timestamp_) {
576 TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
577 parser_->counter_name_thread_time_id_, *utid_);
578 context_->event_tracker->PushCounter(
579 ts_, static_cast<double>(*thread_timestamp_), track_id);
580 }
581 if (thread_instruction_count_) {
582 TrackId track_id = context_->track_tracker->InternThreadCounterTrack(
583 parser_->counter_name_thread_instruction_count_id_, *utid_);
584 context_->event_tracker->PushCounter(
585 ts_, static_cast<double>(*thread_instruction_count_), track_id);
586 }
587 }
588
ParseExtraCounterValues()589 void ParseExtraCounterValues() {
590 if (!event_.has_extra_counter_values() &&
591 !event_.has_extra_double_counter_values()) {
592 return;
593 }
594
595 // Add integer extra counter values.
596 size_t index = 0;
597 protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
598 if (event_.has_extra_counter_track_uuids()) {
599 track_uuid_it = event_.extra_counter_track_uuids();
600 } else if (defaults_ && defaults_->has_extra_counter_track_uuids()) {
601 track_uuid_it = defaults_->extra_counter_track_uuids();
602 }
603 for (auto value_it = event_.extra_counter_values(); value_it;
604 ++value_it, ++track_uuid_it, ++index) {
605 AddExtraCounterValue(track_uuid_it, index);
606 }
607
608 // Add double extra counter values.
609 track_uuid_it = protozero::RepeatedFieldIterator<uint64_t>();
610 if (event_.has_extra_double_counter_track_uuids()) {
611 track_uuid_it = event_.extra_double_counter_track_uuids();
612 } else if (defaults_ && defaults_->has_extra_double_counter_track_uuids()) {
613 track_uuid_it = defaults_->extra_double_counter_track_uuids();
614 }
615 for (auto value_it = event_.extra_double_counter_values(); value_it;
616 ++value_it, ++track_uuid_it, ++index) {
617 AddExtraCounterValue(track_uuid_it, index);
618 }
619 }
620
AddExtraCounterValue(protozero::RepeatedFieldIterator<uint64_t> track_uuid_it,size_t index)621 void AddExtraCounterValue(
622 protozero::RepeatedFieldIterator<uint64_t> track_uuid_it,
623 size_t index) {
624 // Tokenizer ensures that there aren't more values than uuids, that we
625 // don't have more values than kMaxNumExtraCounters and that the
626 // track_uuids are for valid counter tracks.
627 PERFETTO_DCHECK(track_uuid_it);
628 PERFETTO_DCHECK(index < TrackEventData::kMaxNumExtraCounters);
629
630 std::optional<TrackId> track_id = track_event_tracker_->GetDescriptorTrack(
631 *track_uuid_it, kNullStringId, packet_sequence_id_);
632 std::optional<uint32_t> counter_row =
633 storage_->counter_track_table().id().IndexOf(*track_id);
634
635 double value = event_data_->extra_counter_values[index];
636 context_->event_tracker->PushCounter(ts_, value, *track_id);
637
638 // Also import thread_time and thread_instruction_count counters into
639 // slice columns to simplify JSON export.
640 StringId counter_name =
641 storage_->counter_track_table().name()[*counter_row];
642 if (counter_name == parser_->counter_name_thread_time_id_) {
643 thread_timestamp_ = static_cast<int64_t>(value);
644 } else if (counter_name ==
645 parser_->counter_name_thread_instruction_count_id_) {
646 thread_instruction_count_ = static_cast<int64_t>(value);
647 }
648 }
649
ParseThreadBeginEvent()650 util::Status ParseThreadBeginEvent() {
651 if (!utid_) {
652 return util::ErrStatus(
653 "TrackEvent with phase B without thread association");
654 }
655
656 auto* thread_slices = storage_->mutable_slice_table();
657 auto opt_slice_id = context_->slice_tracker->BeginTyped(
658 thread_slices, MakeThreadSliceRow(),
659 [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
660
661 if (opt_slice_id.has_value()) {
662 MaybeParseFlowEvents(opt_slice_id.value());
663 }
664 return util::OkStatus();
665 }
666
ParseThreadEndEvent()667 util::Status ParseThreadEndEvent() {
668 if (!utid_) {
669 return util::ErrStatus(
670 "TrackEvent with phase E without thread association");
671 }
672 auto opt_slice_id = context_->slice_tracker->End(
673 ts_, track_id_, category_id_, name_id_,
674 [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
675 if (!opt_slice_id)
676 return base::OkStatus();
677
678 MaybeParseFlowEvents(*opt_slice_id);
679 auto* thread_slices = storage_->mutable_slice_table();
680 auto opt_thread_slice_ref = thread_slices->FindById(*opt_slice_id);
681 if (!opt_thread_slice_ref) {
682 // This means that the end event did not match a corresponding track event
683 // begin packet so we likely closed the wrong slice. There's not much we
684 // can do about this beyond flag it as a stat.
685 context_->storage->IncrementStats(stats::track_event_thread_invalid_end);
686 return base::OkStatus();
687 }
688
689 tables::SliceTable::RowReference slice_ref = *opt_thread_slice_ref;
690 std::optional<int64_t> tts = slice_ref.thread_ts();
691 if (tts) {
692 PERFETTO_DCHECK(thread_timestamp_);
693 slice_ref.set_thread_dur(*thread_timestamp_ - *tts);
694 }
695 std::optional<int64_t> tic = slice_ref.thread_instruction_count();
696 if (tic) {
697 PERFETTO_DCHECK(event_data_->thread_instruction_count);
698 slice_ref.set_thread_instruction_delta(
699 *event_data_->thread_instruction_count - *tic);
700 }
701 return util::OkStatus();
702 }
703
ParseThreadCompleteEvent()704 util::Status ParseThreadCompleteEvent() {
705 if (!utid_) {
706 return util::ErrStatus(
707 "TrackEvent with phase X without thread association");
708 }
709
710 auto duration_ns = legacy_event_.duration_us() * 1000;
711 if (duration_ns < 0)
712 return util::ErrStatus("TrackEvent with phase X with negative duration");
713
714 auto* thread_slices = storage_->mutable_slice_table();
715 tables::SliceTable::Row row = MakeThreadSliceRow();
716 row.dur = duration_ns;
717 if (legacy_event_.has_thread_duration_us()) {
718 row.thread_dur = legacy_event_.thread_duration_us() * 1000;
719 }
720 if (legacy_event_.has_thread_instruction_delta()) {
721 row.thread_instruction_delta = legacy_event_.thread_instruction_delta();
722 }
723 auto opt_slice_id = context_->slice_tracker->ScopedTyped(
724 thread_slices, std::move(row),
725 [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
726
727 if (opt_slice_id.has_value()) {
728 MaybeParseFlowEvents(opt_slice_id.value());
729 }
730 return util::OkStatus();
731 }
732
GetLegacyEventId()733 std::optional<uint64_t> GetLegacyEventId() {
734 if (legacy_event_.has_unscoped_id())
735 return legacy_event_.unscoped_id();
736 // TODO(andrewbb): Catapult doesn't support global_id and local_id on flow
737 // events. We could add support in trace processor (e.g. because there seem
738 // to be some callsites supplying local_id in chromium), but we would have
739 // to consider the process ID for local IDs and use a separate ID scope for
740 // global_id and unscoped_id.
741 return std::nullopt;
742 }
743
ParseFlowEventV1(char phase)744 util::Status ParseFlowEventV1(char phase) {
745 auto opt_source_id = GetLegacyEventId();
746 if (!opt_source_id) {
747 storage_->IncrementStats(stats::flow_invalid_id);
748 return util::ErrStatus("Invalid id for flow event v1");
749 }
750 FlowId flow_id = context_->flow_tracker->GetFlowIdForV1Event(
751 opt_source_id.value(), category_id_, name_id_);
752 switch (phase) {
753 case 's':
754 context_->flow_tracker->Begin(track_id_, flow_id);
755 break;
756 case 't':
757 context_->flow_tracker->Step(track_id_, flow_id);
758 break;
759 case 'f':
760 context_->flow_tracker->End(track_id_, flow_id,
761 legacy_event_.bind_to_enclosing(),
762 /* close_flow = */ false);
763 break;
764 }
765 return util::OkStatus();
766 }
767
MaybeParseTrackEventFlows(SliceId slice_id)768 void MaybeParseTrackEventFlows(SliceId slice_id) {
769 if (event_.has_flow_ids_old() || event_.has_flow_ids()) {
770 auto it =
771 event_.has_flow_ids() ? event_.flow_ids() : event_.flow_ids_old();
772 for (; it; ++it) {
773 FlowId flow_id = *it;
774 if (!context_->flow_tracker->IsActive(flow_id)) {
775 context_->flow_tracker->Begin(slice_id, flow_id);
776 continue;
777 }
778 context_->flow_tracker->Step(slice_id, flow_id);
779 }
780 }
781 if (event_.has_terminating_flow_ids_old() ||
782 event_.has_terminating_flow_ids()) {
783 auto it = event_.has_terminating_flow_ids()
784 ? event_.terminating_flow_ids()
785 : event_.terminating_flow_ids_old();
786 for (; it; ++it) {
787 FlowId flow_id = *it;
788 if (!context_->flow_tracker->IsActive(flow_id)) {
789 // If we should terminate a flow, do not begin a new one if it's not
790 // active already.
791 continue;
792 }
793 context_->flow_tracker->End(slice_id, flow_id,
794 /* close_flow = */ true);
795 }
796 }
797 }
798
MaybeParseFlowEventV2(SliceId slice_id)799 void MaybeParseFlowEventV2(SliceId slice_id) {
800 if (!legacy_event_.has_bind_id()) {
801 return;
802 }
803 if (!legacy_event_.has_flow_direction()) {
804 storage_->IncrementStats(stats::flow_without_direction);
805 return;
806 }
807
808 auto bind_id = legacy_event_.bind_id();
809 switch (legacy_event_.flow_direction()) {
810 case LegacyEvent::FLOW_OUT:
811 context_->flow_tracker->Begin(slice_id, bind_id);
812 break;
813 case LegacyEvent::FLOW_INOUT:
814 context_->flow_tracker->Step(slice_id, bind_id);
815 break;
816 case LegacyEvent::FLOW_IN:
817 context_->flow_tracker->End(slice_id, bind_id,
818 /* close_flow = */ false);
819 break;
820 default:
821 storage_->IncrementStats(stats::flow_without_direction);
822 }
823 }
824
MaybeParseFlowEvents(SliceId slice_id)825 void MaybeParseFlowEvents(SliceId slice_id) {
826 MaybeParseFlowEventV2(slice_id);
827 MaybeParseTrackEventFlows(slice_id);
828 }
829
ParseThreadInstantEvent(char phase)830 util::Status ParseThreadInstantEvent(char phase) {
831 // Handle instant events as slices with zero duration, so that they end
832 // up nested underneath their parent slices.
833 int64_t duration_ns = 0;
834 int64_t tidelta = 0;
835 std::optional<tables::SliceTable::Id> opt_slice_id;
836 auto args_inserter = [this, phase](BoundInserter* inserter) {
837 ParseTrackEventArgs(inserter);
838 // For legacy MARK event, add phase for JSON exporter.
839 if (phase == 'R') {
840 std::string phase_string(1, static_cast<char>(phase));
841 StringId phase_id = storage_->InternString(phase_string.c_str());
842 inserter->AddArg(parser_->legacy_event_phase_key_id_,
843 Variadic::String(phase_id));
844 }
845 };
846 if (utid_) {
847 auto* thread_slices = storage_->mutable_slice_table();
848 tables::SliceTable::Row row = MakeThreadSliceRow();
849 row.dur = duration_ns;
850 if (thread_timestamp_) {
851 row.thread_dur = duration_ns;
852 }
853 if (thread_instruction_count_) {
854 row.thread_instruction_delta = tidelta;
855 }
856 opt_slice_id = context_->slice_tracker->ScopedTyped(
857 thread_slices, row, std::move(args_inserter));
858 } else {
859 opt_slice_id = context_->slice_tracker->Scoped(
860 ts_, track_id_, category_id_, name_id_, duration_ns,
861 std::move(args_inserter));
862 }
863 if (!opt_slice_id.has_value()) {
864 return util::OkStatus();
865 }
866 MaybeParseFlowEvents(opt_slice_id.value());
867 return util::OkStatus();
868 }
869
ParseAsyncBeginEvent(char phase)870 util::Status ParseAsyncBeginEvent(char phase) {
871 auto args_inserter = [this, phase](BoundInserter* inserter) {
872 ParseTrackEventArgs(inserter);
873
874 if (phase == 'b')
875 return;
876 PERFETTO_DCHECK(phase == 'S');
877 // For legacy ASYNC_BEGIN, add phase for JSON exporter.
878 std::string phase_string(1, static_cast<char>(phase));
879 StringId phase_id = storage_->InternString(phase_string.c_str());
880 inserter->AddArg(parser_->legacy_event_phase_key_id_,
881 Variadic::String(phase_id));
882 };
883 auto opt_slice_id = context_->slice_tracker->Begin(
884 ts_, track_id_, category_id_, name_id_, args_inserter);
885 if (!opt_slice_id.has_value()) {
886 return util::OkStatus();
887 }
888 MaybeParseFlowEvents(opt_slice_id.value());
889 // For the time being, we only create vtrack slice rows if we need to
890 // store thread timestamps/counters.
891 if (legacy_event_.use_async_tts()) {
892 auto* vtrack_slices = storage_->mutable_virtual_track_slices();
893 PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
894 vtrack_slices->slice_ids().back() < opt_slice_id.value());
895 int64_t tts = thread_timestamp_.value_or(0);
896 int64_t tic = thread_instruction_count_.value_or(0);
897 vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts,
898 kPendingThreadDuration, tic,
899 kPendingThreadInstructionDelta);
900 }
901 return util::OkStatus();
902 }
903
ParseAsyncEndEvent()904 util::Status ParseAsyncEndEvent() {
905 auto opt_slice_id = context_->slice_tracker->End(
906 ts_, track_id_, category_id_, name_id_,
907 [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
908 if (!opt_slice_id)
909 return base::OkStatus();
910
911 MaybeParseFlowEvents(*opt_slice_id);
912 if (legacy_event_.use_async_tts()) {
913 auto* vtrack_slices = storage_->mutable_virtual_track_slices();
914 int64_t tts = event_data_->thread_timestamp.value_or(0);
915 int64_t tic = event_data_->thread_instruction_count.value_or(0);
916 vtrack_slices->UpdateThreadDeltasForSliceId(*opt_slice_id, tts, tic);
917 }
918 return util::OkStatus();
919 }
920
ParseAsyncStepEvent(char phase)921 util::Status ParseAsyncStepEvent(char phase) {
922 // Parse step events as instant events. Reconstructing the begin/end times
923 // of the child slice would be too complicated, see b/178540838. For JSON
924 // export, we still record the original step's phase in an arg.
925 int64_t duration_ns = 0;
926 context_->slice_tracker->Scoped(
927 ts_, track_id_, category_id_, name_id_, duration_ns,
928 [this, phase](BoundInserter* inserter) {
929 ParseTrackEventArgs(inserter);
930
931 PERFETTO_DCHECK(phase == 'T' || phase == 'p');
932 std::string phase_string(1, static_cast<char>(phase));
933 StringId phase_id = storage_->InternString(phase_string.c_str());
934 inserter->AddArg(parser_->legacy_event_phase_key_id_,
935 Variadic::String(phase_id));
936 });
937 // Step events don't support thread timestamps, so no need to add a row to
938 // virtual_track_slices.
939 return util::OkStatus();
940 }
941
ParseAsyncInstantEvent()942 util::Status ParseAsyncInstantEvent() {
943 // Handle instant events as slices with zero duration, so that they end
944 // up nested underneath their parent slices.
945 int64_t duration_ns = 0;
946 int64_t tidelta = 0;
947 auto opt_slice_id = context_->slice_tracker->Scoped(
948 ts_, track_id_, category_id_, name_id_, duration_ns,
949 [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
950 if (!opt_slice_id.has_value()) {
951 return util::OkStatus();
952 }
953 MaybeParseFlowEvents(opt_slice_id.value());
954 if (legacy_event_.use_async_tts()) {
955 auto* vtrack_slices = storage_->mutable_virtual_track_slices();
956 PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
957 vtrack_slices->slice_ids().back() < opt_slice_id.value());
958 int64_t tts = thread_timestamp_.value_or(0);
959 int64_t tic = thread_instruction_count_.value_or(0);
960 vtrack_slices->AddVirtualTrackSlice(opt_slice_id.value(), tts,
961 duration_ns, tic, tidelta);
962 }
963 return util::OkStatus();
964 }
965
ParseMetadataEvent()966 util::Status ParseMetadataEvent() {
967 ProcessTracker* procs = context_->process_tracker.get();
968
969 if (name_id_ == kNullStringId)
970 return util::ErrStatus("Metadata event without name");
971
972 // Parse process and thread names from correspondingly named events.
973 NullTermStringView event_name = storage_->GetString(name_id_);
974 PERFETTO_DCHECK(event_name.data());
975 if (strcmp(event_name.c_str(), "thread_name") == 0) {
976 if (!utid_) {
977 return util::ErrStatus(
978 "thread_name metadata event without thread association");
979 }
980
981 auto it = event_.debug_annotations();
982 if (!it) {
983 return util::ErrStatus(
984 "thread_name metadata event without debug annotations");
985 }
986 protos::pbzero::DebugAnnotation::Decoder annotation(*it);
987 auto thread_name = annotation.string_value();
988 if (!thread_name.size)
989 return util::OkStatus();
990 auto thread_name_id = storage_->InternString(thread_name);
991 procs->UpdateThreadNameByUtid(
992 *utid_, thread_name_id,
993 ThreadNamePriority::kTrackDescriptorThreadType);
994 return util::OkStatus();
995 }
996 if (strcmp(event_name.c_str(), "process_name") == 0) {
997 if (!upid_) {
998 return util::ErrStatus(
999 "process_name metadata event without process association");
1000 }
1001
1002 auto it = event_.debug_annotations();
1003 if (!it) {
1004 return util::ErrStatus(
1005 "process_name metadata event without debug annotations");
1006 }
1007 protos::pbzero::DebugAnnotation::Decoder annotation(*it);
1008 auto process_name = annotation.string_value();
1009 if (!process_name.size)
1010 return util::OkStatus();
1011 auto process_name_id =
1012 storage_->InternString(base::StringView(process_name));
1013 // Don't override system-provided names.
1014 procs->SetProcessNameIfUnset(*upid_, process_name_id);
1015 return util::OkStatus();
1016 }
1017 // Other metadata events are proxied via the raw table for JSON export.
1018 ParseLegacyEventAsRawEvent();
1019 return util::OkStatus();
1020 }
1021
ParseLegacyEventAsRawEvent()1022 util::Status ParseLegacyEventAsRawEvent() {
1023 if (!utid_)
1024 return util::ErrStatus("raw legacy event without thread association");
1025
1026 auto ucpu = context_->cpu_tracker->GetOrCreateCpu(0);
1027 RawId id =
1028 storage_->mutable_raw_table()
1029 ->Insert({ts_, parser_->raw_legacy_event_id_, *utid_, 0, 0, ucpu})
1030 .id;
1031
1032 auto inserter = context_->args_tracker->AddArgsTo(id);
1033 inserter
1034 .AddArg(parser_->legacy_event_category_key_id_,
1035 Variadic::String(category_id_))
1036 .AddArg(parser_->legacy_event_name_key_id_, Variadic::String(name_id_));
1037
1038 std::string phase_string(1, static_cast<char>(legacy_event_.phase()));
1039 StringId phase_id = storage_->InternString(phase_string.c_str());
1040 inserter.AddArg(parser_->legacy_event_phase_key_id_,
1041 Variadic::String(phase_id));
1042
1043 if (legacy_event_.has_duration_us()) {
1044 inserter.AddArg(parser_->legacy_event_duration_ns_key_id_,
1045 Variadic::Integer(legacy_event_.duration_us() * 1000));
1046 }
1047
1048 if (thread_timestamp_) {
1049 inserter.AddArg(parser_->legacy_event_thread_timestamp_ns_key_id_,
1050 Variadic::Integer(*thread_timestamp_));
1051 if (legacy_event_.has_thread_duration_us()) {
1052 inserter.AddArg(
1053 parser_->legacy_event_thread_duration_ns_key_id_,
1054 Variadic::Integer(legacy_event_.thread_duration_us() * 1000));
1055 }
1056 }
1057
1058 if (thread_instruction_count_) {
1059 inserter.AddArg(parser_->legacy_event_thread_instruction_count_key_id_,
1060 Variadic::Integer(*thread_instruction_count_));
1061 if (legacy_event_.has_thread_instruction_delta()) {
1062 inserter.AddArg(
1063 parser_->legacy_event_thread_instruction_delta_key_id_,
1064 Variadic::Integer(legacy_event_.thread_instruction_delta()));
1065 }
1066 }
1067
1068 if (legacy_event_.use_async_tts()) {
1069 inserter.AddArg(parser_->legacy_event_use_async_tts_key_id_,
1070 Variadic::Boolean(true));
1071 }
1072
1073 bool has_id = false;
1074 if (legacy_event_.has_unscoped_id()) {
1075 // Unscoped ids are either global or local depending on the phase. Pass
1076 // them through as unscoped IDs to JSON export to preserve this behavior.
1077 inserter.AddArg(parser_->legacy_event_unscoped_id_key_id_,
1078 Variadic::UnsignedInteger(legacy_event_.unscoped_id()));
1079 has_id = true;
1080 } else if (legacy_event_.has_global_id()) {
1081 inserter.AddArg(parser_->legacy_event_global_id_key_id_,
1082 Variadic::UnsignedInteger(legacy_event_.global_id()));
1083 has_id = true;
1084 } else if (legacy_event_.has_local_id()) {
1085 inserter.AddArg(parser_->legacy_event_local_id_key_id_,
1086 Variadic::UnsignedInteger(legacy_event_.local_id()));
1087 has_id = true;
1088 }
1089
1090 if (has_id && legacy_event_.has_id_scope() &&
1091 legacy_event_.id_scope().size) {
1092 inserter.AddArg(
1093 parser_->legacy_event_id_scope_key_id_,
1094 Variadic::String(storage_->InternString(legacy_event_.id_scope())));
1095 }
1096
1097 // No need to parse legacy_event.instant_event_scope() because we import
1098 // instant events into the slice table.
1099
1100 ParseTrackEventArgs(&inserter);
1101 return util::OkStatus();
1102 }
1103
ParseTrackEventArgs(BoundInserter * inserter)1104 void ParseTrackEventArgs(BoundInserter* inserter) {
1105 auto log_errors = [this](util::Status status) {
1106 if (status.ok())
1107 return;
1108 // Log error but continue parsing the other args.
1109 storage_->IncrementStats(stats::track_event_parser_errors);
1110 PERFETTO_DLOG("ParseTrackEventArgs error: %s", status.c_message());
1111 };
1112
1113 if (event_.has_source_location_iid()) {
1114 log_errors(AddSourceLocationArgs(event_.source_location_iid(), inserter));
1115 }
1116
1117 if (event_.has_task_execution()) {
1118 log_errors(ParseTaskExecutionArgs(event_.task_execution(), inserter));
1119 }
1120 if (event_.has_log_message()) {
1121 log_errors(ParseLogMessage(event_.log_message(), inserter));
1122 }
1123 if (event_.has_chrome_histogram_sample()) {
1124 log_errors(
1125 ParseHistogramName(event_.chrome_histogram_sample(), inserter));
1126 }
1127 if (event_.has_chrome_active_processes()) {
1128 protos::pbzero::ChromeActiveProcesses::Decoder message(
1129 event_.chrome_active_processes());
1130 for (auto it = message.pid(); it; ++it) {
1131 parser_->AddActiveProcess(ts_, *it);
1132 }
1133 }
1134
1135 ArgsParser args_writer(ts_, *inserter, *storage_, sequence_state_,
1136 /*support_json=*/true);
1137 int unknown_extensions = 0;
1138 log_errors(parser_->args_parser_.ParseMessage(
1139 blob_, ".perfetto.protos.TrackEvent", &parser_->reflect_fields_,
1140 args_writer, &unknown_extensions));
1141 if (unknown_extensions > 0) {
1142 context_->storage->IncrementStats(stats::unknown_extension_fields,
1143 unknown_extensions);
1144 }
1145
1146 {
1147 auto key = parser_->args_parser_.EnterDictionary("debug");
1148 util::DebugAnnotationParser parser(parser_->args_parser_);
1149 for (auto it = event_.debug_annotations(); it; ++it) {
1150 log_errors(parser.Parse(*it, args_writer));
1151 }
1152 }
1153
1154 if (legacy_passthrough_utid_) {
1155 inserter->AddArg(parser_->legacy_event_passthrough_utid_id_,
1156 Variadic::UnsignedInteger(*legacy_passthrough_utid_),
1157 ArgsTracker::UpdatePolicy::kSkipIfExists);
1158 }
1159 }
1160
ParseTaskExecutionArgs(ConstBytes task_execution,BoundInserter * inserter)1161 util::Status ParseTaskExecutionArgs(ConstBytes task_execution,
1162 BoundInserter* inserter) {
1163 protos::pbzero::TaskExecution::Decoder task(task_execution);
1164 uint64_t iid = task.posted_from_iid();
1165 if (!iid)
1166 return util::ErrStatus("TaskExecution with invalid posted_from_iid");
1167
1168 auto* decoder = sequence_state_->LookupInternedMessage<
1169 protos::pbzero::InternedData::kSourceLocationsFieldNumber,
1170 protos::pbzero::SourceLocation>(iid);
1171 if (!decoder)
1172 return util::ErrStatus("TaskExecution with invalid posted_from_iid");
1173
1174 StringId file_name_id = kNullStringId;
1175 StringId function_name_id = kNullStringId;
1176 uint32_t line_number = 0;
1177
1178 std::string file_name = NormalizePathSeparators(decoder->file_name());
1179 file_name_id = storage_->InternString(base::StringView(file_name));
1180 function_name_id = storage_->InternString(decoder->function_name());
1181 line_number = decoder->line_number();
1182
1183 inserter->AddArg(parser_->task_file_name_args_key_id_,
1184 Variadic::String(file_name_id));
1185 inserter->AddArg(parser_->task_function_name_args_key_id_,
1186 Variadic::String(function_name_id));
1187 inserter->AddArg(parser_->task_line_number_args_key_id_,
1188 Variadic::UnsignedInteger(line_number));
1189 return util::OkStatus();
1190 }
1191
AddSourceLocationArgs(uint64_t iid,BoundInserter * inserter)1192 util::Status AddSourceLocationArgs(uint64_t iid, BoundInserter* inserter) {
1193 if (!iid)
1194 return util::ErrStatus("SourceLocation with invalid iid");
1195
1196 auto* decoder = sequence_state_->LookupInternedMessage<
1197 protos::pbzero::InternedData::kSourceLocationsFieldNumber,
1198 protos::pbzero::SourceLocation>(iid);
1199 if (!decoder)
1200 return util::ErrStatus("SourceLocation with invalid iid");
1201
1202 StringId file_name_id = kNullStringId;
1203 StringId function_name_id = kNullStringId;
1204 uint32_t line_number = 0;
1205
1206 std::string file_name = NormalizePathSeparators(decoder->file_name());
1207 file_name_id = storage_->InternString(base::StringView(file_name));
1208 function_name_id = storage_->InternString(decoder->function_name());
1209 line_number = decoder->line_number();
1210
1211 inserter->AddArg(parser_->source_location_file_name_key_id_,
1212 Variadic::String(file_name_id));
1213 inserter->AddArg(parser_->source_location_function_name_key_id_,
1214 Variadic::String(function_name_id));
1215 inserter->AddArg(parser_->source_location_line_number_key_id_,
1216 Variadic::UnsignedInteger(line_number));
1217 return util::OkStatus();
1218 }
1219
ParseLogMessage(ConstBytes blob,BoundInserter * inserter)1220 util::Status ParseLogMessage(ConstBytes blob, BoundInserter* inserter) {
1221 if (!utid_)
1222 return util::ErrStatus("LogMessage without thread association");
1223
1224 protos::pbzero::LogMessage::Decoder message(blob);
1225
1226 auto* body_decoder = sequence_state_->LookupInternedMessage<
1227 protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
1228 protos::pbzero::LogMessageBody>(message.body_iid());
1229 if (!body_decoder)
1230 return util::ErrStatus("LogMessage with invalid body_iid");
1231
1232 const StringId log_message_id =
1233 storage_->InternString(body_decoder->body());
1234 inserter->AddArg(parser_->log_message_body_key_id_,
1235 Variadic::String(log_message_id));
1236
1237 StringId source_location_id = kNullStringId;
1238 if (message.has_source_location_iid()) {
1239 auto* source_location_decoder = sequence_state_->LookupInternedMessage<
1240 protos::pbzero::InternedData::kSourceLocationsFieldNumber,
1241 protos::pbzero::SourceLocation>(message.source_location_iid());
1242 if (!source_location_decoder)
1243 return util::ErrStatus("LogMessage with invalid source_location_iid");
1244 const std::string source_location =
1245 source_location_decoder->file_name().ToStdString() + ":" +
1246 std::to_string(source_location_decoder->line_number());
1247 source_location_id =
1248 storage_->InternString(base::StringView(source_location));
1249
1250 inserter->AddArg(parser_->log_message_source_location_file_name_key_id_,
1251 Variadic::String(storage_->InternString(
1252 source_location_decoder->file_name())));
1253 inserter->AddArg(
1254 parser_->log_message_source_location_function_name_key_id_,
1255 Variadic::String(storage_->InternString(
1256 source_location_decoder->function_name())));
1257 inserter->AddArg(
1258 parser_->log_message_source_location_line_number_key_id_,
1259 Variadic::Integer(source_location_decoder->line_number()));
1260 }
1261
1262 // The track event log message doesn't specify any priority. UI never
1263 // displays priorities < 2 (VERBOSE in android). Let's make all the track
1264 // event logs show up as INFO.
1265 int32_t priority = protos::pbzero::AndroidLogPriority::PRIO_INFO;
1266 if (message.has_prio()) {
1267 priority = ToAndroidLogPriority(
1268 static_cast<protos::pbzero::LogMessage::Priority>(message.prio()));
1269 inserter->AddArg(parser_->log_message_priority_id_,
1270 Variadic::Integer(priority));
1271 }
1272
1273 storage_->mutable_android_log_table()->Insert(
1274 {ts_, *utid_,
1275 /*priority*/ static_cast<uint32_t>(priority),
1276 /*tag_id*/ source_location_id, log_message_id});
1277
1278 return util::OkStatus();
1279 }
1280
ParseHistogramName(ConstBytes blob,BoundInserter * inserter)1281 util::Status ParseHistogramName(ConstBytes blob, BoundInserter* inserter) {
1282 protos::pbzero::ChromeHistogramSample::Decoder sample(blob);
1283 if (!sample.has_name_iid())
1284 return util::OkStatus();
1285
1286 if (sample.has_name()) {
1287 return util::ErrStatus(
1288 "name is already set for ChromeHistogramSample: only one of name and "
1289 "name_iid can be set.");
1290 }
1291
1292 auto* decoder = sequence_state_->LookupInternedMessage<
1293 protos::pbzero::InternedData::kHistogramNamesFieldNumber,
1294 protos::pbzero::HistogramName>(sample.name_iid());
1295 if (!decoder)
1296 return util::ErrStatus("HistogramName with invalid name_iid");
1297
1298 inserter->AddArg(parser_->histogram_name_key_id_,
1299 Variadic::String(storage_->InternString(decoder->name())));
1300 return util::OkStatus();
1301 }
1302
MakeThreadSliceRow()1303 tables::SliceTable::Row MakeThreadSliceRow() {
1304 tables::SliceTable::Row row;
1305 row.ts = ts_;
1306 row.track_id = track_id_;
1307 row.category = category_id_;
1308 row.name = name_id_;
1309 row.thread_ts = thread_timestamp_;
1310 row.thread_dur = std::nullopt;
1311 row.thread_instruction_count = thread_instruction_count_;
1312 row.thread_instruction_delta = std::nullopt;
1313 return row;
1314 }
1315
1316 TraceProcessorContext* context_;
1317 TrackEventTracker* track_event_tracker_;
1318 TraceStorage* storage_;
1319 TrackEventParser* parser_;
1320 ArgsTranslationTable* args_translation_table_;
1321 int64_t ts_;
1322 const TrackEventData* event_data_;
1323 PacketSequenceStateGeneration* sequence_state_;
1324 ConstBytes blob_;
1325 TrackEvent::Decoder event_;
1326 LegacyEvent::Decoder legacy_event_;
1327 protos::pbzero::TrackEventDefaults::Decoder* defaults_;
1328
1329 // Importing state.
1330 StringId category_id_;
1331 StringId name_id_;
1332 uint64_t track_uuid_;
1333 TrackId track_id_;
1334 std::optional<UniqueTid> utid_;
1335 std::optional<UniqueTid> upid_;
1336 std::optional<int64_t> thread_timestamp_;
1337 std::optional<int64_t> thread_instruction_count_;
1338 // All events in legacy JSON require a thread ID, but for some types of
1339 // events (e.g. async events or process/global-scoped instants), we don't
1340 // store it in the slice/track model. To pass the utid through to the json
1341 // export, we store it in an arg.
1342 std::optional<UniqueTid> legacy_passthrough_utid_;
1343
1344 uint32_t packet_sequence_id_;
1345 };
1346
TrackEventParser(TraceProcessorContext * context,TrackEventTracker * track_event_tracker)1347 TrackEventParser::TrackEventParser(TraceProcessorContext* context,
1348 TrackEventTracker* track_event_tracker)
1349 : args_parser_(*context->descriptor_pool_.get()),
1350 context_(context),
1351 track_event_tracker_(track_event_tracker),
1352 counter_name_thread_time_id_(
1353 context->storage->InternString("thread_time")),
1354 counter_name_thread_instruction_count_id_(
1355 context->storage->InternString("thread_instruction_count")),
1356 task_file_name_args_key_id_(
1357 context->storage->InternString("task.posted_from.file_name")),
1358 task_function_name_args_key_id_(
1359 context->storage->InternString("task.posted_from.function_name")),
1360 task_line_number_args_key_id_(
1361 context->storage->InternString("task.posted_from.line_number")),
1362 log_message_body_key_id_(
1363 context->storage->InternString("track_event.log_message")),
1364 log_message_source_location_function_name_key_id_(
1365 context->storage->InternString(
1366 "track_event.log_message.function_name")),
1367 log_message_source_location_file_name_key_id_(
1368 context->storage->InternString("track_event.log_message.file_name")),
1369 log_message_source_location_line_number_key_id_(
1370 context->storage->InternString(
1371 "track_event.log_message.line_number")),
1372 log_message_priority_id_(
1373 context->storage->InternString("track_event.priority")),
1374 source_location_function_name_key_id_(
1375 context->storage->InternString("source.function_name")),
1376 source_location_file_name_key_id_(
1377 context->storage->InternString("source.file_name")),
1378 source_location_line_number_key_id_(
1379 context->storage->InternString("source.line_number")),
1380 raw_legacy_event_id_(
1381 context->storage->InternString("track_event.legacy_event")),
1382 legacy_event_passthrough_utid_id_(
1383 context->storage->InternString("legacy_event.passthrough_utid")),
1384 legacy_event_category_key_id_(
1385 context->storage->InternString("legacy_event.category")),
1386 legacy_event_name_key_id_(
1387 context->storage->InternString("legacy_event.name")),
1388 legacy_event_phase_key_id_(
1389 context->storage->InternString("legacy_event.phase")),
1390 legacy_event_duration_ns_key_id_(
1391 context->storage->InternString("legacy_event.duration_ns")),
1392 legacy_event_thread_timestamp_ns_key_id_(
1393 context->storage->InternString("legacy_event.thread_timestamp_ns")),
1394 legacy_event_thread_duration_ns_key_id_(
1395 context->storage->InternString("legacy_event.thread_duration_ns")),
1396 legacy_event_thread_instruction_count_key_id_(
1397 context->storage->InternString(
1398 "legacy_event.thread_instruction_count")),
1399 legacy_event_thread_instruction_delta_key_id_(
1400 context->storage->InternString(
1401 "legacy_event.thread_instruction_delta")),
1402 legacy_event_use_async_tts_key_id_(
1403 context->storage->InternString("legacy_event.use_async_tts")),
1404 legacy_event_unscoped_id_key_id_(
1405 context->storage->InternString("legacy_event.unscoped_id")),
1406 legacy_event_global_id_key_id_(
1407 context->storage->InternString("legacy_event.global_id")),
1408 legacy_event_local_id_key_id_(
1409 context->storage->InternString("legacy_event.local_id")),
1410 legacy_event_id_scope_key_id_(
1411 context->storage->InternString("legacy_event.id_scope")),
1412 legacy_event_bind_id_key_id_(
1413 context->storage->InternString("legacy_event.bind_id")),
1414 legacy_event_bind_to_enclosing_key_id_(
1415 context->storage->InternString("legacy_event.bind_to_enclosing")),
1416 legacy_event_flow_direction_key_id_(
1417 context->storage->InternString("legacy_event.flow_direction")),
1418 histogram_name_key_id_(
1419 context->storage->InternString("chrome_histogram_sample.name")),
1420 flow_direction_value_in_id_(context->storage->InternString("in")),
1421 flow_direction_value_out_id_(context->storage->InternString("out")),
1422 flow_direction_value_inout_id_(context->storage->InternString("inout")),
1423 chrome_legacy_ipc_class_args_key_id_(
1424 context->storage->InternString("legacy_ipc.class")),
1425 chrome_legacy_ipc_line_args_key_id_(
1426 context->storage->InternString("legacy_ipc.line")),
1427 chrome_host_app_package_name_id_(
1428 context->storage->InternString("chrome.host_app_package_name")),
1429 chrome_crash_trace_id_name_id_(
1430 context->storage->InternString("chrome.crash_trace_id")),
1431 chrome_process_label_flat_key_id_(
1432 context->storage->InternString("chrome.process_label")),
1433 chrome_process_type_id_(
1434 context_->storage->InternString("chrome.process_type")),
1435 event_category_key_id_(context_->storage->InternString("event.category")),
1436 event_name_key_id_(context_->storage->InternString("event.name")),
1437 chrome_string_lookup_(context->storage.get()),
1438 counter_unit_ids_{{kNullStringId, context_->storage->InternString("ns"),
1439 context_->storage->InternString("count"),
1440 context_->storage->InternString("bytes")}},
1441 active_chrome_processes_tracker_(context) {
1442 args_parser_.AddParsingOverrideForField(
1443 "chrome_mojo_event_info.mojo_interface_method_iid",
1444 [](const protozero::Field& field,
__anon99809ebe0b02(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1445 util::ProtoToArgsParser::Delegate& delegate) {
1446 return MaybeParseUnsymbolizedSourceLocation(
1447 "chrome_mojo_event_info.mojo_interface_method.native_symbol", field,
1448 delegate);
1449 });
1450 // Switch |source_location_iid| into its interned data variant.
1451 args_parser_.AddParsingOverrideForField(
1452 "begin_impl_frame_args.current_args.source_location_iid",
1453 [](const protozero::Field& field,
__anon99809ebe0c02(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1454 util::ProtoToArgsParser::Delegate& delegate) {
1455 return MaybeParseSourceLocation("begin_impl_frame_args.current_args",
1456 field, delegate);
1457 });
1458 args_parser_.AddParsingOverrideForField(
1459 "begin_impl_frame_args.last_args.source_location_iid",
1460 [](const protozero::Field& field,
__anon99809ebe0d02(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1461 util::ProtoToArgsParser::Delegate& delegate) {
1462 return MaybeParseSourceLocation("begin_impl_frame_args.last_args",
1463 field, delegate);
1464 });
1465 args_parser_.AddParsingOverrideForField(
1466 "begin_frame_observer_state.last_begin_frame_args.source_location_iid",
1467 [](const protozero::Field& field,
__anon99809ebe0e02(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1468 util::ProtoToArgsParser::Delegate& delegate) {
1469 return MaybeParseSourceLocation(
1470 "begin_frame_observer_state.last_begin_frame_args", field,
1471 delegate);
1472 });
1473 args_parser_.AddParsingOverrideForField(
1474 "chrome_memory_pressure_notification.creation_location_iid",
1475 [](const protozero::Field& field,
__anon99809ebe0f02(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1476 util::ProtoToArgsParser::Delegate& delegate) {
1477 return MaybeParseSourceLocation("chrome_memory_pressure_notification",
1478 field, delegate);
1479 });
1480
1481 // Parse DebugAnnotations.
1482 args_parser_.AddParsingOverrideForType(
1483 ".perfetto.protos.DebugAnnotation",
1484 [&](util::ProtoToArgsParser::ScopedNestedKeyContext& key,
1485 const protozero::ConstBytes& data,
__anon99809ebe1002(util::ProtoToArgsParser::ScopedNestedKeyContext& key, const protozero::ConstBytes& data, util::ProtoToArgsParser::Delegate& delegate) 1486 util::ProtoToArgsParser::Delegate& delegate) {
1487 // Do not add "debug_annotations" to the final key.
1488 key.RemoveFieldSuffix();
1489 util::DebugAnnotationParser annotation_parser(args_parser_);
1490 return annotation_parser.Parse(data, delegate);
1491 });
1492
1493 args_parser_.AddParsingOverrideForField(
1494 "active_processes.pid", [&](const protozero::Field& field,
__anon99809ebe1102(const protozero::Field& field, util::ProtoToArgsParser::Delegate& delegate) 1495 util::ProtoToArgsParser::Delegate& delegate) {
1496 AddActiveProcess(delegate.packet_timestamp(), field.as_int32());
1497 // Fallthrough so that the parser adds pid as a regular arg.
1498 return std::nullopt;
1499 });
1500
1501 for (uint16_t index : kReflectFields) {
1502 reflect_fields_.push_back(index);
1503 }
1504 }
1505
ParseTrackDescriptor(int64_t packet_timestamp,protozero::ConstBytes track_descriptor,uint32_t packet_sequence_id)1506 void TrackEventParser::ParseTrackDescriptor(
1507 int64_t packet_timestamp,
1508 protozero::ConstBytes track_descriptor,
1509 uint32_t packet_sequence_id) {
1510 protos::pbzero::TrackDescriptor::Decoder decoder(track_descriptor);
1511
1512 // Ensure that the track and its parents are resolved. This may start a new
1513 // process and/or thread (i.e. new upid/utid).
1514 TrackId track_id = *track_event_tracker_->GetDescriptorTrack(
1515 decoder.uuid(), kNullStringId, packet_sequence_id);
1516
1517 if (decoder.has_thread()) {
1518 UniqueTid utid = ParseThreadDescriptor(decoder.thread());
1519 if (decoder.has_chrome_thread())
1520 ParseChromeThreadDescriptor(utid, decoder.chrome_thread());
1521 } else if (decoder.has_process()) {
1522 UniquePid upid =
1523 ParseProcessDescriptor(packet_timestamp, decoder.process());
1524 if (decoder.has_chrome_process())
1525 ParseChromeProcessDescriptor(upid, decoder.chrome_process());
1526 } else if (decoder.has_counter()) {
1527 ParseCounterDescriptor(track_id, decoder.counter());
1528 }
1529
1530 // Override the name with the most recent name seen (after sorting by ts).
1531 if (decoder.has_name() || decoder.has_static_name()) {
1532 auto* tracks = context_->storage->mutable_track_table();
1533 const StringId raw_name_id = context_->storage->InternString(
1534 decoder.has_name() ? decoder.name() : decoder.static_name());
1535 const StringId name_id =
1536 context_->process_track_translation_table->TranslateName(raw_name_id);
1537 tracks->mutable_name()->Set(*tracks->id().IndexOf(track_id), name_id);
1538 }
1539 }
1540
ParseProcessDescriptor(int64_t packet_timestamp,protozero::ConstBytes process_descriptor)1541 UniquePid TrackEventParser::ParseProcessDescriptor(
1542 int64_t packet_timestamp,
1543 protozero::ConstBytes process_descriptor) {
1544 protos::pbzero::ProcessDescriptor::Decoder decoder(process_descriptor);
1545 UniquePid upid = context_->process_tracker->GetOrCreateProcess(
1546 static_cast<uint32_t>(decoder.pid()));
1547 active_chrome_processes_tracker_.AddProcessDescriptor(packet_timestamp, upid);
1548 if (decoder.has_process_name() && decoder.process_name().size) {
1549 // Don't override system-provided names.
1550 context_->process_tracker->SetProcessNameIfUnset(
1551 upid, context_->storage->InternString(decoder.process_name()));
1552 }
1553 if (decoder.has_start_timestamp_ns() && decoder.start_timestamp_ns() > 0) {
1554 context_->process_tracker->SetStartTsIfUnset(upid,
1555 decoder.start_timestamp_ns());
1556 }
1557 // TODO(skyostil): Remove parsing for legacy chrome_process_type field.
1558 if (decoder.has_chrome_process_type()) {
1559 StringId name_id =
1560 chrome_string_lookup_.GetProcessName(decoder.chrome_process_type());
1561 // Don't override system-provided names.
1562 context_->process_tracker->SetProcessNameIfUnset(upid, name_id);
1563 }
1564 int label_index = 0;
1565 for (auto it = decoder.process_labels(); it; it++) {
1566 StringId label_id = context_->storage->InternString(*it);
1567 std::string key = "chrome.process_label[";
1568 key.append(std::to_string(label_index++));
1569 key.append("]");
1570 context_->process_tracker->AddArgsTo(upid).AddArg(
1571 chrome_process_label_flat_key_id_,
1572 context_->storage->InternString(base::StringView(key)),
1573 Variadic::String(label_id));
1574 }
1575 return upid;
1576 }
1577
ParseChromeProcessDescriptor(UniquePid upid,protozero::ConstBytes chrome_process_descriptor)1578 void TrackEventParser::ParseChromeProcessDescriptor(
1579 UniquePid upid,
1580 protozero::ConstBytes chrome_process_descriptor) {
1581 protos::pbzero::ChromeProcessDescriptor::Decoder decoder(
1582 chrome_process_descriptor);
1583
1584 StringId name_id =
1585 chrome_string_lookup_.GetProcessName(decoder.process_type());
1586 // Don't override system-provided names.
1587 context_->process_tracker->SetProcessNameIfUnset(upid, name_id);
1588
1589 ArgsTracker::BoundInserter process_args =
1590 context_->process_tracker->AddArgsTo(upid);
1591 // For identifying Chrome processes in system traces.
1592 process_args.AddArg(chrome_process_type_id_, Variadic::String(name_id));
1593 if (decoder.has_host_app_package_name()) {
1594 process_args.AddArg(chrome_host_app_package_name_id_,
1595 Variadic::String(context_->storage->InternString(
1596 decoder.host_app_package_name())));
1597 }
1598 if (decoder.has_crash_trace_id()) {
1599 process_args.AddArg(chrome_crash_trace_id_name_id_,
1600 Variadic::UnsignedInteger(decoder.crash_trace_id()));
1601 }
1602 }
1603
ParseThreadDescriptor(protozero::ConstBytes thread_descriptor)1604 UniqueTid TrackEventParser::ParseThreadDescriptor(
1605 protozero::ConstBytes thread_descriptor) {
1606 protos::pbzero::ThreadDescriptor::Decoder decoder(thread_descriptor);
1607 UniqueTid utid = context_->process_tracker->UpdateThread(
1608 static_cast<uint32_t>(decoder.tid()),
1609 static_cast<uint32_t>(decoder.pid()));
1610 StringId name_id = kNullStringId;
1611 if (decoder.has_thread_name() && decoder.thread_name().size) {
1612 name_id = context_->storage->InternString(decoder.thread_name());
1613 } else if (decoder.has_chrome_thread_type()) {
1614 // TODO(skyostil): Remove parsing for legacy chrome_thread_type field.
1615 name_id = chrome_string_lookup_.GetThreadName(decoder.chrome_thread_type());
1616 }
1617 context_->process_tracker->UpdateThreadNameByUtid(
1618 utid, name_id, ThreadNamePriority::kTrackDescriptor);
1619 return utid;
1620 }
1621
ParseChromeThreadDescriptor(UniqueTid utid,protozero::ConstBytes chrome_thread_descriptor)1622 void TrackEventParser::ParseChromeThreadDescriptor(
1623 UniqueTid utid,
1624 protozero::ConstBytes chrome_thread_descriptor) {
1625 protos::pbzero::ChromeThreadDescriptor::Decoder decoder(
1626 chrome_thread_descriptor);
1627 if (!decoder.has_thread_type())
1628 return;
1629
1630 StringId name_id = chrome_string_lookup_.GetThreadName(decoder.thread_type());
1631 context_->process_tracker->UpdateThreadNameByUtid(
1632 utid, name_id, ThreadNamePriority::kTrackDescriptorThreadType);
1633 }
1634
ParseCounterDescriptor(TrackId track_id,protozero::ConstBytes counter_descriptor)1635 void TrackEventParser::ParseCounterDescriptor(
1636 TrackId track_id,
1637 protozero::ConstBytes counter_descriptor) {
1638 using protos::pbzero::CounterDescriptor;
1639
1640 CounterDescriptor::Decoder decoder(counter_descriptor);
1641 auto* counter_tracks = context_->storage->mutable_counter_track_table();
1642
1643 size_t unit_index = static_cast<size_t>(decoder.unit());
1644 if (unit_index >= counter_unit_ids_.size())
1645 unit_index = CounterDescriptor::UNIT_UNSPECIFIED;
1646
1647 auto opt_track_idx = counter_tracks->id().IndexOf(track_id);
1648 if (!opt_track_idx) {
1649 context_->storage->IncrementStats(stats::track_event_parser_errors);
1650 return;
1651 }
1652
1653 auto track_idx = *opt_track_idx;
1654
1655 switch (decoder.type()) {
1656 case CounterDescriptor::COUNTER_UNSPECIFIED:
1657 break;
1658 case CounterDescriptor::COUNTER_THREAD_TIME_NS:
1659 unit_index = CounterDescriptor::UNIT_TIME_NS;
1660 counter_tracks->mutable_name()->Set(track_idx,
1661 counter_name_thread_time_id_);
1662 break;
1663 case CounterDescriptor::COUNTER_THREAD_INSTRUCTION_COUNT:
1664 unit_index = CounterDescriptor::UNIT_COUNT;
1665 counter_tracks->mutable_name()->Set(
1666 track_idx, counter_name_thread_instruction_count_id_);
1667 break;
1668 }
1669
1670 counter_tracks->mutable_unit()->Set(track_idx, counter_unit_ids_[unit_index]);
1671 }
1672
ParseTrackEvent(int64_t ts,const TrackEventData * event_data,ConstBytes blob,uint32_t packet_sequence_id)1673 void TrackEventParser::ParseTrackEvent(int64_t ts,
1674 const TrackEventData* event_data,
1675 ConstBytes blob,
1676 uint32_t packet_sequence_id) {
1677 const auto range_of_interest_start_us =
1678 track_event_tracker_->range_of_interest_start_us();
1679 if (context_->config.drop_track_event_data_before ==
1680 DropTrackEventDataBefore::kTrackEventRangeOfInterest &&
1681 range_of_interest_start_us && ts < *range_of_interest_start_us * 1000) {
1682 // The event is outside of the range of interest, and dropping is enabled.
1683 // So we drop the event.
1684 context_->storage->IncrementStats(
1685 stats::track_event_dropped_packets_outside_of_range_of_interest);
1686 return;
1687 }
1688 util::Status status =
1689 EventImporter(this, ts, event_data, std::move(blob), packet_sequence_id)
1690 .Import();
1691 if (!status.ok()) {
1692 context_->storage->IncrementStats(stats::track_event_parser_errors);
1693 PERFETTO_DLOG("ParseTrackEvent error: %s", status.c_message());
1694 }
1695 }
1696
AddActiveProcess(int64_t packet_timestamp,int32_t pid)1697 void TrackEventParser::AddActiveProcess(int64_t packet_timestamp, int32_t pid) {
1698 UniquePid upid =
1699 context_->process_tracker->GetOrCreateProcess(static_cast<uint32_t>(pid));
1700 active_chrome_processes_tracker_.AddActiveProcessMetadata(packet_timestamp,
1701 upid);
1702 }
1703
NotifyEndOfFile()1704 void TrackEventParser::NotifyEndOfFile() {
1705 active_chrome_processes_tracker_.NotifyEndOfFile();
1706 }
1707
1708 } // namespace trace_processor
1709 } // namespace perfetto
1710