• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_->set_range_of_interest_us(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       counter_details.builtin_type_str = counter_name_thread_time_id_;
245     } else if (counter.type() ==
246                CounterDescriptor::COUNTER_THREAD_INSTRUCTION_COUNT) {
247       counter_details.unit = counter_unit_ids_[CounterDescriptor::UNIT_COUNT];
248       counter_details.builtin_type_str =
249           counter_name_thread_instruction_count_id_;
250     } else if (unit < counter_unit_ids_.size() &&
251                unit != CounterDescriptor::COUNTER_UNSPECIFIED) {
252       counter_details.unit = counter_unit_ids_[unit];
253     } else {
254       counter_details.unit =
255           context_->storage->InternString(counter.unit_name());
256     }
257 
258     // Incrementally encoded counters are only valid on a single sequence.
259     if (counter.is_incremental()) {
260       counter_details.packet_sequence_id = packet.trusted_packet_sequence_id();
261     }
262     track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
263 
264     return ModuleResult::Ignored();
265   }
266 
267   track_event_tracker_->ReserveDescriptorTrack(track.uuid(), reservation);
268 
269   // Let ProtoTraceReader forward the packet to the parser.
270   return ModuleResult::Ignored();
271 }  // namespace perfetto::trace_processor
272 
TokenizeThreadDescriptorPacket(RefPtr<PacketSequenceStateGeneration> state,const protos::pbzero::TracePacket::Decoder & packet)273 ModuleResult TrackEventTokenizer::TokenizeThreadDescriptorPacket(
274     RefPtr<PacketSequenceStateGeneration> state,
275     const protos::pbzero::TracePacket::Decoder& packet) {
276   if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
277     PERFETTO_ELOG("ThreadDescriptor packet without trusted_packet_sequence_id");
278     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
279     return ModuleResult::Handled();
280   }
281 
282   // TrackEvents will be ignored while incremental state is invalid. As a
283   // consequence, we should also ignore any ThreadDescriptors received in this
284   // state. Otherwise, any delta-encoded timestamps would be calculated
285   // incorrectly once we move out of the packet loss state. Instead, wait until
286   // the first subsequent descriptor after incremental state is cleared.
287   if (!state->IsIncrementalStateValid()) {
288     context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
289     return ModuleResult::Handled();
290   }
291 
292   protos::pbzero::ThreadDescriptor::Decoder thread(packet.thread_descriptor());
293   TokenizeThreadDescriptor(*state, thread);
294 
295   // Let ProtoTraceReader forward the packet to the parser.
296   return ModuleResult::Ignored();
297 }
298 
TokenizeThreadDescriptor(PacketSequenceStateGeneration & state,const protos::pbzero::ThreadDescriptor::Decoder & thread)299 void TrackEventTokenizer::TokenizeThreadDescriptor(
300     PacketSequenceStateGeneration& state,
301     const protos::pbzero::ThreadDescriptor::Decoder& thread) {
302   // TODO(eseckler): Remove support for legacy thread descriptor-based default
303   // tracks and delta timestamps.
304   state.SetThreadDescriptor(thread);
305 }
306 
TokenizeTrackEventPacket(RefPtr<PacketSequenceStateGeneration> state,const protos::pbzero::TracePacket::Decoder & packet,TraceBlobView * packet_blob,int64_t packet_timestamp)307 ModuleResult TrackEventTokenizer::TokenizeTrackEventPacket(
308     RefPtr<PacketSequenceStateGeneration> state,
309     const protos::pbzero::TracePacket::Decoder& packet,
310     TraceBlobView* packet_blob,
311     int64_t packet_timestamp) {
312   if (PERFETTO_UNLIKELY(!packet.has_trusted_packet_sequence_id())) {
313     PERFETTO_ELOG("TrackEvent packet without trusted_packet_sequence_id");
314     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
315     return ModuleResult::Handled();
316   }
317 
318   protos::pbzero::TrackEvent::Decoder event(packet.track_event());
319   protos::pbzero::TrackEventDefaults::Decoder* defaults =
320       state->GetTrackEventDefaults();
321 
322   int64_t timestamp;
323   TrackEventData data(std::move(*packet_blob), state);
324 
325   // TODO(eseckler): Remove handling of timestamps relative to ThreadDescriptors
326   // once all producers have switched to clock-domain timestamps (e.g.
327   // TracePacket's timestamp).
328 
329   if (event.has_timestamp_delta_us()) {
330     // Delta timestamps require a valid ThreadDescriptor packet since the last
331     // packet loss.
332     if (!state->track_event_timestamps_valid()) {
333       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
334       return ModuleResult::Handled();
335     }
336     timestamp = state->IncrementAndGetTrackEventTimeNs(
337         event.timestamp_delta_us() * 1000);
338 
339     // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
340     // trace time if we have a clock snapshot.
341     base::StatusOr<int64_t> trace_ts = context_->clock_tracker->ToTraceTime(
342         protos::pbzero::BUILTIN_CLOCK_MONOTONIC, timestamp);
343     if (trace_ts.ok())
344       timestamp = trace_ts.value();
345   } else if (int64_t ts_absolute_us = event.timestamp_absolute_us()) {
346     // One-off absolute timestamps don't affect delta computation.
347     timestamp = ts_absolute_us * 1000;
348 
349     // Legacy TrackEvent timestamp fields are in MONOTONIC domain. Adjust to
350     // trace time if we have a clock snapshot.
351     base::StatusOr<int64_t> trace_ts = context_->clock_tracker->ToTraceTime(
352         protos::pbzero::BUILTIN_CLOCK_MONOTONIC, timestamp);
353     if (trace_ts.ok())
354       timestamp = trace_ts.value();
355   } else if (packet.has_timestamp()) {
356     timestamp = packet_timestamp;
357   } else {
358     PERFETTO_ELOG("TrackEvent without valid timestamp");
359     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
360     return ModuleResult::Handled();
361   }
362 
363   // Handle legacy sample events which might have timestamps embedded inside.
364   if (PERFETTO_UNLIKELY(event.has_legacy_event())) {
365     protos::pbzero::TrackEvent::LegacyEvent::Decoder leg(event.legacy_event());
366     if (PERFETTO_UNLIKELY(leg.phase() == 'P')) {
367       RETURN_IF_ERROR(TokenizeLegacySampleEvent(
368           event, leg, *data.trace_packet_data.sequence_state));
369     }
370   }
371 
372   if (event.has_thread_time_delta_us()) {
373     // Delta timestamps require a valid ThreadDescriptor packet since the last
374     // packet loss.
375     if (!state->track_event_timestamps_valid()) {
376       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
377       return ModuleResult::Handled();
378     }
379     data.thread_timestamp = state->IncrementAndGetTrackEventThreadTimeNs(
380         event.thread_time_delta_us() * 1000);
381   } else if (event.has_thread_time_absolute_us()) {
382     // One-off absolute timestamps don't affect delta computation.
383     data.thread_timestamp = event.thread_time_absolute_us() * 1000;
384   }
385 
386   if (event.has_thread_instruction_count_delta()) {
387     // Delta timestamps require a valid ThreadDescriptor packet since the last
388     // packet loss.
389     if (!state->track_event_timestamps_valid()) {
390       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
391       return ModuleResult::Handled();
392     }
393     data.thread_instruction_count =
394         state->IncrementAndGetTrackEventThreadInstructionCount(
395             event.thread_instruction_count_delta());
396   } else if (event.has_thread_instruction_count_absolute()) {
397     // One-off absolute timestamps don't affect delta computation.
398     data.thread_instruction_count = event.thread_instruction_count_absolute();
399   }
400 
401   if (event.type() == protos::pbzero::TrackEvent::TYPE_COUNTER) {
402     // Consider track_uuid from the packet and TrackEventDefaults.
403     uint64_t track_uuid;
404     if (event.has_track_uuid()) {
405       track_uuid = event.track_uuid();
406     } else if (defaults && defaults->has_track_uuid()) {
407       track_uuid = defaults->track_uuid();
408     } else {
409       PERFETTO_DLOG(
410           "Ignoring TrackEvent with counter_value but without track_uuid");
411       context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
412       return ModuleResult::Handled();
413     }
414 
415     if (!event.has_counter_value() && !event.has_double_counter_value()) {
416       PERFETTO_DLOG(
417           "Ignoring TrackEvent with TYPE_COUNTER but without counter_value or "
418           "double_counter_value for "
419           "track_uuid %" PRIu64,
420           track_uuid);
421       context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
422       return ModuleResult::Handled();
423     }
424 
425     std::optional<double> value;
426     if (event.has_counter_value()) {
427       value = track_event_tracker_->ConvertToAbsoluteCounterValue(
428           track_uuid, packet.trusted_packet_sequence_id(),
429           static_cast<double>(event.counter_value()));
430     } else {
431       value = track_event_tracker_->ConvertToAbsoluteCounterValue(
432           track_uuid, packet.trusted_packet_sequence_id(),
433           event.double_counter_value());
434     }
435 
436     if (!value) {
437       PERFETTO_DLOG("Ignoring TrackEvent with invalid track_uuid %" PRIu64,
438                     track_uuid);
439       context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
440       return ModuleResult::Handled();
441     }
442 
443     data.counter_value = *value;
444   }
445 
446   size_t index = 0;
447   const protozero::RepeatedFieldIterator<uint64_t> kEmptyIterator;
448   auto result = AddExtraCounterValues(
449       data, index, packet.trusted_packet_sequence_id(),
450       event.extra_counter_values(), event.extra_counter_track_uuids(),
451       defaults ? defaults->extra_counter_track_uuids() : kEmptyIterator);
452   if (!result.ok()) {
453     PERFETTO_DLOG("%s", result.c_message());
454     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
455     return ModuleResult::Handled();
456   }
457   result = AddExtraCounterValues(
458       data, index, packet.trusted_packet_sequence_id(),
459       event.extra_double_counter_values(),
460       event.extra_double_counter_track_uuids(),
461       defaults ? defaults->extra_double_counter_track_uuids() : kEmptyIterator);
462   if (!result.ok()) {
463     PERFETTO_DLOG("%s", result.c_message());
464     context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
465     return ModuleResult::Handled();
466   }
467 
468   context_->sorter->PushTrackEventPacket(timestamp, std::move(data),
469                                          context_->machine_id());
470   return ModuleResult::Handled();
471 }
472 
473 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)474 base::Status TrackEventTokenizer::AddExtraCounterValues(
475     TrackEventData& data,
476     size_t& index,
477     uint32_t trusted_packet_sequence_id,
478     protozero::RepeatedFieldIterator<T> value_it,
479     protozero::RepeatedFieldIterator<uint64_t> packet_track_uuid_it,
480     protozero::RepeatedFieldIterator<uint64_t> default_track_uuid_it) {
481   if (!value_it)
482     return base::OkStatus();
483 
484   // Consider extra_{double_,}counter_track_uuids from the packet and
485   // TrackEventDefaults.
486   protozero::RepeatedFieldIterator<uint64_t> track_uuid_it;
487   if (packet_track_uuid_it) {
488     track_uuid_it = packet_track_uuid_it;
489   } else if (default_track_uuid_it) {
490     track_uuid_it = default_track_uuid_it;
491   } else {
492     return base::ErrStatus(
493         "Ignoring TrackEvent with extra_{double_,}counter_values but without "
494         "extra_{double_,}counter_track_uuids");
495   }
496 
497   for (; value_it; ++value_it, ++track_uuid_it, ++index) {
498     if (!*track_uuid_it) {
499       return base::ErrStatus(
500           "Ignoring TrackEvent with more extra_{double_,}counter_values than "
501           "extra_{double_,}counter_track_uuids");
502     }
503     if (index >= TrackEventData::kMaxNumExtraCounters) {
504       return base::ErrStatus(
505           "Ignoring TrackEvent with more extra_{double_,}counter_values than "
506           "TrackEventData::kMaxNumExtraCounters");
507     }
508     std::optional<double> abs_value =
509         track_event_tracker_->ConvertToAbsoluteCounterValue(
510             *track_uuid_it, trusted_packet_sequence_id,
511             static_cast<double>(*value_it));
512     if (!abs_value) {
513       return base::ErrStatus(
514           "Ignoring TrackEvent with invalid extra counter track");
515     }
516     data.extra_counter_values[index] = *abs_value;
517   }
518   return base::OkStatus();
519 }
520 
TokenizeLegacySampleEvent(const protos::pbzero::TrackEvent::Decoder & event,const protos::pbzero::TrackEvent::LegacyEvent::Decoder & legacy,PacketSequenceStateGeneration & state)521 base::Status TrackEventTokenizer::TokenizeLegacySampleEvent(
522     const protos::pbzero::TrackEvent::Decoder& event,
523     const protos::pbzero::TrackEvent::LegacyEvent::Decoder& legacy,
524     PacketSequenceStateGeneration& state) {
525   // We are just trying to parse out the V8 profiling events into the cpu
526   // sampling tables: if we don't have JSON enabled, just don't do this.
527   if (!context_->json_trace_parser) {
528     return base::OkStatus();
529   }
530 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
531   for (auto it = event.debug_annotations(); it; ++it) {
532     protos::pbzero::DebugAnnotation::Decoder da(*it);
533     auto* interned_name = state.LookupInternedMessage<
534         protos::pbzero::InternedData::kDebugAnnotationNamesFieldNumber,
535         protos::pbzero::DebugAnnotationName>(da.name_iid());
536     base::StringView name(interned_name->name());
537     if (name != "data" || !da.has_legacy_json_value()) {
538       continue;
539     }
540     auto opt_val = json::ParseJsonString(da.legacy_json_value());
541     if (!opt_val) {
542       continue;
543     }
544     const auto& val = *opt_val;
545     if (val.isMember("startTime")) {
546       ASSIGN_OR_RETURN(int64_t ts, context_->clock_tracker->ToTraceTime(
547                                        protos::pbzero::BUILTIN_CLOCK_MONOTONIC,
548                                        val["startTime"].asInt64() * 1000));
549       context_->legacy_v8_cpu_profile_tracker->SetStartTsForSessionAndPid(
550           legacy.unscoped_id(), static_cast<uint32_t>(state.pid()), ts);
551       continue;
552     }
553     const auto& profile = val["cpuProfile"];
554     for (const auto& n : profile["nodes"]) {
555       uint32_t node_id = n["id"].asUInt();
556       std::optional<uint32_t> parent_node_id =
557           n.isMember("parent") ? std::make_optional(n["parent"].asUInt())
558                                : std::nullopt;
559       const auto& frame = n["callFrame"];
560       base::StringView url =
561           frame.isMember("url") ? frame["url"].asCString() : base::StringView();
562       base::StringView function_name = frame["functionName"].asCString();
563       base::Status status =
564           context_->legacy_v8_cpu_profile_tracker->AddCallsite(
565               legacy.unscoped_id(), static_cast<uint32_t>(state.pid()), node_id,
566               parent_node_id, url, function_name);
567       if (!status.ok()) {
568         context_->storage->IncrementStats(
569             stats::legacy_v8_cpu_profile_invalid_callsite);
570         continue;
571       }
572     }
573     const auto& samples = profile["samples"];
574     const auto& deltas = val["timeDeltas"];
575     if (samples.size() != deltas.size()) {
576       return base::ErrStatus(
577           "v8 legacy profile: samples and timestamps do not have same size");
578     }
579     for (uint32_t i = 0; i < samples.size(); ++i) {
580       ASSIGN_OR_RETURN(
581           int64_t ts,
582           context_->legacy_v8_cpu_profile_tracker->AddDeltaAndGetTs(
583               legacy.unscoped_id(), static_cast<uint32_t>(state.pid()),
584               deltas[i].asInt64() * 1000));
585       context_->sorter->PushLegacyV8CpuProfileEvent(
586           ts, legacy.unscoped_id(), static_cast<uint32_t>(state.pid()),
587           static_cast<uint32_t>(state.tid()), samples[i].asUInt());
588     }
589   }
590 #else
591   base::ignore_result(event, legacy, state);
592 #endif
593   return base::OkStatus();
594 }
595 
596 }  // namespace perfetto::trace_processor
597