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_tokenizer.h"
18
19 #include <cinttypes>
20 #include <cstddef>
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <utility>
25
26 #include "perfetto/base/build_config.h"
27 #include "perfetto/base/logging.h"
28 #include "perfetto/base/status.h"
29 #include "perfetto/ext/base/status_or.h"
30 #include "perfetto/ext/base/string_view.h"
31 #include "perfetto/protozero/proto_decoder.h"
32 #include "perfetto/public/compiler.h"
33 #include "perfetto/trace_processor/ref_counted.h"
34 #include "perfetto/trace_processor/trace_blob_view.h"
35 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
36 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
37 #include "src/trace_processor/importers/common/clock_tracker.h"
38 #include "src/trace_processor/importers/common/legacy_v8_cpu_profile_tracker.h"
39 #include "src/trace_processor/importers/common/metadata_tracker.h"
40 #include "src/trace_processor/importers/common/parser_types.h"
41 #include "src/trace_processor/importers/common/process_tracker.h"
42 #include "src/trace_processor/importers/json/json_utils.h"
43 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
44 #include "src/trace_processor/importers/proto/proto_importer_module.h"
45 #include "src/trace_processor/importers/proto/proto_trace_reader.h"
46 #include "src/trace_processor/importers/proto/track_event_tracker.h"
47 #include "src/trace_processor/sorter/trace_sorter.h"
48 #include "src/trace_processor/storage/metadata.h"
49 #include "src/trace_processor/storage/stats.h"
50 #include "src/trace_processor/storage/trace_storage.h"
51
52 #include "protos/perfetto/common/builtin_clock.pbzero.h"
53 #include "protos/perfetto/trace/trace_packet.pbzero.h"
54 #include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
55 #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
56 #include "protos/perfetto/trace/track_event/range_of_interest.pbzero.h"
57 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
58 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
59 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
60 #include "src/trace_processor/types/variadic.h"
61 #include "src/trace_processor/util/status_macros.h"
62
63 namespace perfetto::trace_processor {
64
65 namespace {
66 using protos::pbzero::CounterDescriptor;
67 }
68
TrackEventTokenizer(TraceProcessorContext * context,TrackEventTracker * track_event_tracker)69 TrackEventTokenizer::TrackEventTokenizer(TraceProcessorContext* context,
70 TrackEventTracker* track_event_tracker)
71 : context_(context),
72 track_event_tracker_(track_event_tracker),
73 counter_name_thread_time_id_(
74 context_->storage->InternString("thread_time")),
75 counter_name_thread_instruction_count_id_(
76 context_->storage->InternString("thread_instruction_count")),
77 counter_unit_ids_{{kNullStringId, context_->storage->InternString("ns"),
78 context_->storage->InternString("count"),
79 context_->storage->InternString("bytes")}} {}
80
TokenizeRangeOfInterestPacket(RefPtr<PacketSequenceStateGeneration>,const protos::pbzero::TracePacket::Decoder & packet,int64_t)81 ModuleResult TrackEventTokenizer::TokenizeRangeOfInterestPacket(
82 RefPtr<PacketSequenceStateGeneration> /*state*/,
83 const protos::pbzero::TracePacket::Decoder& packet,
84 int64_t /*packet_timestamp*/) {
85 protos::pbzero::TrackEventRangeOfInterest::Decoder range_of_interest(
86 packet.track_event_range_of_interest());
87 if (!range_of_interest.has_start_us()) {
88 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
89 return ModuleResult::Handled();
90 }
91 track_event_tracker_->SetRangeOfInterestStartUs(range_of_interest.start_us());
92 context_->metadata_tracker->SetMetadata(
93 metadata::range_of_interest_start_us,
94 Variadic::Integer(range_of_interest.start_us()));
95 return ModuleResult::Handled();
96 }
97
TokenizeTrackDescriptorPacket(RefPtr<PacketSequenceStateGeneration> state,const protos::pbzero::TracePacket::Decoder & packet,int64_t packet_timestamp)98 ModuleResult TrackEventTokenizer::TokenizeTrackDescriptorPacket(
99 RefPtr<PacketSequenceStateGeneration> state,
100 const protos::pbzero::TracePacket::Decoder& packet,
101 int64_t packet_timestamp) {
102 using TrackDescriptorProto = protos::pbzero::TrackDescriptor;
103 using Reservation = TrackEventTracker::DescriptorTrackReservation;
104 auto track_descriptor_field = packet.track_descriptor();
105 TrackDescriptorProto::Decoder track(track_descriptor_field.data,
106 track_descriptor_field.size);
107
108 Reservation reservation;
109
110 if (!track.has_uuid()) {
111 PERFETTO_ELOG("TrackDescriptor packet without uuid");
112 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
113 return ModuleResult::Handled();
114 }
115
116 if (track.has_parent_uuid()) {
117 reservation.parent_uuid = track.parent_uuid();
118 }
119
120 if (track.has_child_ordering()) {
121 switch (track.child_ordering()) {
122 case TrackDescriptorProto::ChildTracksOrdering::UNKNOWN:
123 reservation.ordering = Reservation::ChildTracksOrdering::kUnknown;
124 break;
125 case TrackDescriptorProto::ChildTracksOrdering::CHRONOLOGICAL:
126 reservation.ordering = Reservation::ChildTracksOrdering::kChronological;
127 break;
128 case TrackDescriptorProto::ChildTracksOrdering::LEXICOGRAPHIC:
129 reservation.ordering = Reservation::ChildTracksOrdering::kLexicographic;
130 break;
131 case TrackDescriptorProto::ChildTracksOrdering::EXPLICIT:
132 reservation.ordering = Reservation::ChildTracksOrdering::kExplicit;
133 break;
134 default:
135 PERFETTO_FATAL("Unsupported ChildTracksOrdering");
136 }
137 }
138
139 if (track.has_sibling_order_rank()) {
140 reservation.sibling_order_rank = track.sibling_order_rank();
141 }
142
143 if (track.has_name())
144 reservation.name = context_->storage->InternString(track.name());
145 else if (track.has_static_name())
146 reservation.name = context_->storage->InternString(track.static_name());
147
148 if (packet.has_trusted_pid()) {
149 context_->process_tracker->UpdateTrustedPid(
150 static_cast<uint32_t>(packet.trusted_pid()), track.uuid());
151 }
152
153 if (track.has_thread()) {
154 protos::pbzero::ThreadDescriptor::Decoder thread(track.thread());
155
156 if (!thread.has_pid() || !thread.has_tid()) {
157 PERFETTO_ELOG(
158 "No pid or tid in ThreadDescriptor for track with uuid %" PRIu64,
159 track.uuid());
160 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
161 return ModuleResult::Handled();
162 }
163
164 if (state->IsIncrementalStateValid()) {
165 TokenizeThreadDescriptor(*state, thread);
166 }
167
168 reservation.min_timestamp = packet_timestamp;
169 reservation.pid = static_cast<uint32_t>(thread.pid());
170 reservation.tid = static_cast<uint32_t>(thread.tid());
171 reservation.use_separate_track =
172 track.disallow_merging_with_system_tracks();
173
174 track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
175
176 return ModuleResult::Ignored();
177 }
178
179 if (track.has_process()) {
180 protos::pbzero::ProcessDescriptor::Decoder process(track.process());
181
182 if (!process.has_pid()) {
183 PERFETTO_ELOG("No pid in ProcessDescriptor for track with uuid %" PRIu64,
184 track.uuid());
185 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
186 return ModuleResult::Handled();
187 }
188
189 reservation.pid = static_cast<uint32_t>(process.pid());
190 reservation.min_timestamp = packet_timestamp;
191 track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
192
193 return ModuleResult::Ignored();
194 }
195 if (track.has_counter()) {
196 protos::pbzero::CounterDescriptor::Decoder counter(track.counter());
197
198 StringId category_id = kNullStringId;
199 if (counter.has_categories()) {
200 // TODO(eseckler): Support multi-category events in the table schema.
201 std::string categories;
202 for (auto it = counter.categories(); it; ++it) {
203 if (!categories.empty())
204 categories += ",";
205 categories.append((*it).data, (*it).size);
206 }
207 if (!categories.empty()) {
208 category_id =
209 context_->storage->InternString(base::StringView(categories));
210 }
211 }
212
213 // TODO(eseckler): Intern counter tracks for specific counter types like
214 // thread time, so that the same counter can be referred to from tracks with
215 // different uuids. (Chrome may emit thread time values on behalf of other
216 // threads, in which case it has to use absolute values on a different
217 // track_uuid. Right now these absolute values are imported onto a separate
218 // counter track than the other thread's regular thread time values.)
219 if (reservation.name.is_null()) {
220 switch (counter.type()) {
221 case CounterDescriptor::COUNTER_UNSPECIFIED:
222 break;
223 case CounterDescriptor::COUNTER_THREAD_TIME_NS:
224 reservation.name = counter_name_thread_time_id_;
225 break;
226 case CounterDescriptor::COUNTER_THREAD_INSTRUCTION_COUNT:
227 reservation.name = counter_name_thread_instruction_count_id_;
228 break;
229 }
230 }
231
232 reservation.is_counter = true;
233 reservation.counter_details =
234 TrackEventTracker::DescriptorTrackReservation::CounterDetails{};
235
236 auto& counter_details = *reservation.counter_details;
237 counter_details.category = category_id;
238 counter_details.is_incremental = counter.is_incremental();
239 counter_details.unit_multiplier = counter.unit_multiplier();
240
241 auto unit = static_cast<uint32_t>(counter.unit());
242 if (counter.type() == CounterDescriptor::COUNTER_THREAD_TIME_NS) {
243 counter_details.unit = counter_unit_ids_[CounterDescriptor::UNIT_TIME_NS];
244 } else if (counter.type() ==
245 CounterDescriptor::COUNTER_THREAD_INSTRUCTION_COUNT) {
246 counter_details.unit = counter_unit_ids_[CounterDescriptor::UNIT_COUNT];
247 } else if (unit < counter_unit_ids_.size() &&
248 unit != CounterDescriptor::COUNTER_UNSPECIFIED) {
249 counter_details.unit = counter_unit_ids_[unit];
250 } else {
251 counter_details.unit =
252 context_->storage->InternString(counter.unit_name());
253 }
254
255 // Incrementally encoded counters are only valid on a single sequence.
256 if (counter.is_incremental()) {
257 counter_details.packet_sequence_id = packet.trusted_packet_sequence_id();
258 }
259 track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
260
261 return ModuleResult::Ignored();
262 }
263
264 track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
265
266 // Let ProtoTraceReader forward the packet to the parser.
267 return ModuleResult::Ignored();
268 } // namespace perfetto::trace_processor
269
TokenizeThreadDescriptorPacket(RefPtr<PacketSequenceStateGeneration> state,const protos::pbzero::TracePacket::Decoder & packet)270 ModuleResult TrackEventTokenizer::TokenizeThreadDescriptorPacket(
271 RefPtr<PacketSequenceStateGeneration> state,
272 const protos::pbzero::TracePacket::Decoder& packet) {
273 if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
274 PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id");
275 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
276 return ModuleResult::Handled();
277 }
278
279 // TrackEvents will be ignored while incremental state is invalid. As a
280 // consequence, we should also ignore any ThreadDescriptors received in this
281 // state. Otherwise, any delta-encoded timestamps would be calculated
282 // incorrectly once we move out of the packet loss state. Instead, wait until
283 // the first subsequent descriptor after incremental state is cleared.
284 if (!state->IsIncrementalStateValid()) {
285 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
286 return ModuleResult::Handled();
287 }
288
289 protos::pbzero::ThreadDescriptor::Decoder thread(packet.thread_descriptor());
290 TokenizeThreadDescriptor(*state, thread);
291
292 // Let ProtoTraceReader forward the packet to the parser.
293 return ModuleResult::Ignored();
294 }
295
TokenizeThreadDescriptor(PacketSequenceStateGeneration & state,const protos::pbzero::ThreadDescriptor::Decoder & thread)296 void TrackEventTokenizer::TokenizeThreadDescriptor(
297 PacketSequenceStateGeneration& state,
298 const protos::pbzero::ThreadDescriptor::Decoder& thread) {
299 // TODO(eseckler): Remove support for legacy thread descriptor-based default
300 // tracks and delta timestamps.
301 state.SetThreadDescriptor(thread);
302 }
303
TokenizeTrackEventPacket(RefPtr<PacketSequenceStateGeneration> state,const protos::pbzero::TracePacket::Decoder & packet,TraceBlobView * packet_blob,int64_t packet_timestamp)304 ModuleResult TrackEventTokenizer::TokenizeTrackEventPacket(
305 RefPtr<PacketSequenceStateGeneration> state,
306 const protos::pbzero::TracePacket::Decoder& packet,
307 TraceBlobView* packet_blob,
308 int64_t packet_timestamp) {
309 if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
310 PERFETTO_ELOG("TrackEvent packet without trusted_packet_sequence_id");
311 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
312 return ModuleResult::Handled();
313 }
314
315 protos::pbzero::TrackEvent::Decoder event(packet.track_event());
316 protos::pbzero::TrackEventDefaults::Decoder* defaults =
317 state->GetTrackEventDefaults();
318
319 int64_t timestamp;
320 TrackEventData data(std::move(*packet_blob), state);
321
322 // TODO(eseckler): Remove handling of timestamps relative to ThreadDescriptors
323 // once all producers have switched to clock-domain timestamps (e.g.
324 // TracePacket's timestamp).
325
326 if (event.has_timestamp_delta_us()) {
327 // Delta timestamps require a valid ThreadDescriptor packet since the last
328 // packet loss.
329 if (!state->track_event_timestamps_valid()) {
330 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
331 return ModuleResult::Handled();
332 }
333 timestamp = state->IncrementAndGetTrackEventTimeNs(
334 event.timestamp_delta_us() * 1000);
335
336 // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
337 // trace time if we have a clock snapshot.
338 base::StatusOr<int64_t> trace_ts = context_->clock_tracker->ToTraceTime(
339 protos::pbzero::BUILTIN_CLOCK_MONOTONIC, timestamp);
340 if (trace_ts.ok())
341 timestamp = trace_ts.value();
342 } else if (int64_t ts_absolute_us = event.timestamp_absolute_us()) {
343 // One-off absolute timestamps don't affect delta computation.
344 timestamp = ts_absolute_us * 1000;
345
346 // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
347 // trace time if we have a clock snapshot.
348 base::StatusOr<int64_t> trace_ts = context_->clock_tracker->ToTraceTime(
349 protos::pbzero::BUILTIN_CLOCK_MONOTONIC, timestamp);
350 if (trace_ts.ok())
351 timestamp = trace_ts.value();
352 } else if (packet.has_timestamp()) {
353 timestamp = packet_timestamp;
354 } else {
355 PERFETTO_ELOG("TrackEvent without valid timestamp");
356 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
357 return ModuleResult::Handled();
358 }
359
360 // Handle legacy sample events which might have timestamps embedded inside.
361 if (PERFETTO_UNLIKELY(event.has_legacy_event())) {
362 protos::pbzero::TrackEvent::LegacyEvent::Decoder leg(event.legacy_event());
363 if (PERFETTO_UNLIKELY(leg.phase() == 'P')) {
364 RETURN_IF_ERROR(TokenizeLegacySampleEvent(
365 event, leg, *data.trace_packet_data.sequence_state));
366 }
367 }
368
369 if (event.has_thread_time_delta_us()) {
370 // Delta timestamps require a valid ThreadDescriptor packet since the last
371 // packet loss.
372 if (!state->track_event_timestamps_valid()) {
373 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
374 return ModuleResult::Handled();
375 }
376 data.thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs(
377 event.thread_time_delta_us() * 1000);
378 } else if (event.has_thread_time_absolute_us()) {
379 // One-off absolute timestamps don't affect delta computation.
380 data.thread_timestamp = event.thread_time_absolute_us() * 1000;
381 }
382
383 if (event.has_thread_instruction_count_delta()) {
384 // Delta timestamps require a valid ThreadDescriptor packet since the last
385 // packet loss.
386 if (!state->track_event_timestamps_valid()) {
387 context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
388 return ModuleResult::Handled();
389 }
390 data.thread_instruction_count =
391 state->IncrementAndGetTrackEventThreadInstructionCount(
392 event.thread_instruction_count_delta());
393 } else if (event.has_thread_instruction_count_absolute()) {
394 // One-off absolute timestamps don't affect delta computation.
395 data.thread_instruction_count = event.thread_instruction_count_absolute();
396 }
397
398 if (event.type() == protos::pbzero::TrackEvent::TYPE_COUNTER) {
399 // Consider track_uuid from the packet and TrackEventDefaults.
400 uint64_t track_uuid;
401 if (event.has_track_uuid()) {
402 track_uuid = event.track_uuid();
403 } else if (defaults && defaults->has_track_uuid()) {
404 track_uuid = defaults->track_uuid();
405 } else {
406 PERFETTO_DLOG(
407 "Ignoring TrackEvent with counter_value but without track_uuid");
408 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
409 return ModuleResult::Handled();
410 }
411
412 if (!event.has_counter_value() && !event.has_double_counter_value()) {
413 PERFETTO_DLOG(
414 "Ignoring TrackEvent with TYPE_COUNTER but without counter_value or "
415 "double_counter_value for "
416 "track_uuid %" PRIu64,
417 track_uuid);
418 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
419 return ModuleResult::Handled();
420 }
421
422 std::optional<double> value;
423 if (event.has_counter_value()) {
424 value = track_event_tracker_->ConvertToAbsoluteCounterValue(
425 track_uuid, packet.trusted_packet_sequence_id(),
426 static_cast<double>(event.counter_value()));
427 } else {
428 value = track_event_tracker_->ConvertToAbsoluteCounterValue(
429 track_uuid, packet.trusted_packet_sequence_id(),
430 event.double_counter_value());
431 }
432
433 if (!value) {
434 PERFETTO_DLOG("Ignoring TrackEvent with invalid track_uuid %" PRIu64,
435 track_uuid);
436 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
437 return ModuleResult::Handled();
438 }
439
440 data.counter_value = *value;
441 }
442
443 size_t index = 0;
444 const protozero::RepeatedFieldIterator<uint64_t> kEmptyIterator;
445 auto result = AddExtraCounterValues(
446 data, index, packet.trusted_packet_sequence_id(),
447 event.extra_counter_values(), event.extra_counter_track_uuids(),
448 defaults ? defaults->extra_counter_track_uuids() : kEmptyIterator);
449 if (!result.ok()) {
450 PERFETTO_DLOG("%s", result.c_message());
451 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
452 return ModuleResult::Handled();
453 }
454 result = AddExtraCounterValues(
455 data, index, packet.trusted_packet_sequence_id(),
456 event.extra_double_counter_values(),
457 event.extra_double_counter_track_uuids(),
458 defaults ? defaults->extra_double_counter_track_uuids() : kEmptyIterator);
459 if (!result.ok()) {
460 PERFETTO_DLOG("%s", result.c_message());
461 context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
462 return ModuleResult::Handled();
463 }
464
465 context_->sorter->PushTrackEventPacket(timestamp, std::move(data),
466 context_->machine_id());
467 return ModuleResult::Handled();
468 }
469
470 template <typename T>
AddExtraCounterValues(TrackEventData & data,size_t & index,uint32_t trusted_packet_sequence_id,protozero::RepeatedFieldIterator<T> value_it,protozero::RepeatedFieldIterator<uint64_t> packet_track_uuid_it,protozero::RepeatedFieldIterator<uint64_t> default_track_uuid_it)471 base::Status TrackEventTokenizer::AddExtraCounterValues(
472 TrackEventData& data,
473 size_t& index,
474 uint32_t trusted_packet_sequence_id,
475 protozero::RepeatedFieldIterator<T> value_it,
476 protozero::RepeatedFieldIterator<uint64_t> packet_track_uuid_it,
477 protozero::RepeatedFieldIterator<uint64_t> default_track_uuid_it) {
478 if (!value_it)
479 return base::OkStatus();
480
481 // Consider extra_{double_,}counter_track_uuids from the packet and
482 // TrackEventDefaults.
483 protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
484 if (packet_track_uuid_it) {
485 track_uuid_it = packet_track_uuid_it;
486 } else if (default_track_uuid_it) {
487 track_uuid_it = default_track_uuid_it;
488 } else {
489 return base::ErrStatus(
490 "Ignoring TrackEvent with extra_{double_,}counter_values but without "
491 "extra_{double_,}counter_track_uuids");
492 }
493
494 for (; value_it; ++value_it, ++track_uuid_it, ++index) {
495 if (!*track_uuid_it) {
496 return base::ErrStatus(
497 "Ignoring TrackEvent with more extra_{double_,}counter_values than "
498 "extra_{double_,}counter_track_uuids");
499 }
500 if (index >= TrackEventData::kMaxNumExtraCounters) {
501 return base::ErrStatus(
502 "Ignoring TrackEvent with more extra_{double_,}counter_values than "
503 "TrackEventData::kMaxNumExtraCounters");
504 }
505 std::optional<double> abs_value =
506 track_event_tracker_->ConvertToAbsoluteCounterValue(
507 *track_uuid_it, trusted_packet_sequence_id,
508 static_cast<double>(*value_it));
509 if (!abs_value) {
510 return base::ErrStatus(
511 "Ignoring TrackEvent with invalid extra counter track");
512 }
513 data.extra_counter_values[index] = *abs_value;
514 }
515 return base::OkStatus();
516 }
517
TokenizeLegacySampleEvent(const protos::pbzero::TrackEvent::Decoder & event,const protos::pbzero::TrackEvent::LegacyEvent::Decoder & legacy,PacketSequenceStateGeneration & state)518 base::Status TrackEventTokenizer::TokenizeLegacySampleEvent(
519 const protos::pbzero::TrackEvent::Decoder& event,
520 const protos::pbzero::TrackEvent::LegacyEvent::Decoder& legacy,
521 PacketSequenceStateGeneration& state) {
522 // We are just trying to parse out the V8 profiling events into the cpu
523 // sampling tables: if we don't have JSON enabled, just don't do this.
524 if (!context_->json_trace_parser) {
525 return base::OkStatus();
526 }
527 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
528 for (auto it = event.debug_annotations(); it; ++it) {
529 protos::pbzero::DebugAnnotation::Decoder da(*it);
530 auto* interned_name = state.LookupInternedMessage<
531 protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber,
532 protos::pbzero::DebugAnnotationName>(da.name_iid());
533 base::StringView name(interned_name->name());
534 if (name != "data" || !da.has_legacy_json_value()) {
535 continue;
536 }
537 auto opt_val = json::ParseJsonString(da.legacy_json_value());
538 if (!opt_val) {
539 continue;
540 }
541 const auto& val = *opt_val;
542 if (val.isMember("startTime")) {
543 ASSIGN_OR_RETURN(int64_t ts, context_->clock_tracker->ToTraceTime(
544 protos::pbzero::BUILTIN_CLOCK_MONOTONIC,
545 val["startTime"].asInt64() * 1000));
546 context_->legacy_v8_cpu_profile_tracker->SetStartTsForSessionAndPid(
547 legacy.unscoped_id(), static_cast<uint32_t>(state.pid()), ts);
548 continue;
549 }
550 const auto& profile = val["cpuProfile"];
551 for (const auto& n : profile["nodes"]) {
552 uint32_t node_id = n["id"].asUInt();
553 std::optional<uint32_t> parent_node_id =
554 n.isMember("parent") ? std::make_optional(n["parent"].asUInt())
555 : std::nullopt;
556 const auto& frame = n["callFrame"];
557 base::StringView url =
558 frame.isMember("url") ? frame["url"].asCString() : base::StringView();
559 base::StringView function_name = frame["functionName"].asCString();
560 base::Status status =
561 context_->legacy_v8_cpu_profile_tracker->AddCallsite(
562 legacy.unscoped_id(), static_cast<uint32_t>(state.pid()), node_id,
563 parent_node_id, url, function_name);
564 if (!status.ok()) {
565 context_->storage->IncrementStats(
566 stats::legacy_v8_cpu_profile_invalid_callsite);
567 continue;
568 }
569 }
570 const auto& samples = profile["samples"];
571 const auto& deltas = val["timeDeltas"];
572 if (samples.size() != deltas.size()) {
573 return base::ErrStatus(
574 "v8 legacy profile: samples and timestamps do not have same size");
575 }
576 for (uint32_t i = 0; i < samples.size(); ++i) {
577 ASSIGN_OR_RETURN(
578 int64_t ts,
579 context_->legacy_v8_cpu_profile_tracker->AddDeltaAndGetTs(
580 legacy.unscoped_id(), static_cast<uint32_t>(state.pid()),
581 deltas[i].asInt64() * 1000));
582 context_->sorter->PushLegacyV8CpuProfileEvent(
583 ts, legacy.unscoped_id(), static_cast<uint32_t>(state.pid()),
584 static_cast<uint32_t>(state.tid()), samples[i].asUInt());
585 }
586 }
587 #else
588 base::ignore_result(event, legacy, state);
589 #endif
590 return base::OkStatus();
591 }
592
593 } // namespace perfetto::trace_processor
594