xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/android_probes_parser.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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/android_probes_parser.h"
18 
19 #include <atomic>
20 #include <cinttypes>
21 #include <cstddef>
22 #include <cstdint>
23 #include <cstdlib>
24 #include <cstring>
25 #include <optional>
26 
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/status_or.h"
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/ext/base/string_view.h"
31 #include "src/trace_processor/importers/common/args_tracker.h"
32 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
33 #include "src/trace_processor/importers/common/clock_tracker.h"
34 #include "src/trace_processor/importers/common/event_tracker.h"
35 #include "src/trace_processor/importers/common/metadata_tracker.h"
36 #include "src/trace_processor/importers/common/process_tracker.h"
37 #include "src/trace_processor/importers/common/slice_tracker.h"
38 #include "src/trace_processor/importers/common/track_tracker.h"
39 #include "src/trace_processor/importers/common/tracks.h"
40 #include "src/trace_processor/importers/common/tracks_common.h"
41 #include "src/trace_processor/importers/proto/android_probes_tracker.h"
42 #include "src/trace_processor/storage/metadata.h"
43 #include "src/trace_processor/storage/stats.h"
44 #include "src/trace_processor/storage/trace_storage.h"
45 #include "src/trace_processor/types/trace_processor_context.h"
46 #include "src/trace_processor/types/variadic.h"
47 
48 #include "protos/perfetto/common/android_log_constants.pbzero.h"
49 #include "protos/perfetto/common/builtin_clock.pbzero.h"
50 #include "protos/perfetto/config/trace_config.pbzero.h"
51 #include "protos/perfetto/trace/android/android_game_intervention_list.pbzero.h"
52 #include "protos/perfetto/trace/android/android_log.pbzero.h"
53 #include "protos/perfetto/trace/android/android_system_property.pbzero.h"
54 #include "protos/perfetto/trace/android/initial_display_state.pbzero.h"
55 #include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
56 #include "protos/perfetto/trace/power/android_entity_state_residency.pbzero.h"
57 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
58 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
59 
60 namespace perfetto::trace_processor {
61 
AndroidProbesParser(TraceProcessorContext * context)62 AndroidProbesParser::AndroidProbesParser(TraceProcessorContext* context)
63     : context_(context),
64       device_state_id_(context->storage->InternString("DeviceStateChanged")),
65       battery_status_id_(context->storage->InternString("BatteryStatus")),
66       plug_type_id_(context->storage->InternString("PlugType")),
67       rail_packet_timestamp_id_(context->storage->InternString("packet_ts")),
68       energy_consumer_id_(
69           context_->storage->InternString("energy_consumer_id")),
70       consumer_type_id_(context_->storage->InternString("consumer_type")),
71       ordinal_id_(context_->storage->InternString("ordinal")) {}
72 
ParseBatteryCounters(int64_t ts,ConstBytes blob)73 void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) {
74   protos::pbzero::BatteryCounters::Decoder evt(blob);
75   if (evt.has_charge_counter_uah()) {
76     TrackId track = context_->track_tracker->InternTrack(
77         tracks::kBatteryCounterBlueprint,
78         tracks::Dimensions(evt.name(), "charge_uah"));
79     context_->event_tracker->PushCounter(
80         ts, static_cast<double>(evt.charge_counter_uah()), track);
81   } else if (evt.has_energy_counter_uwh() && evt.has_voltage_uv()) {
82     // Calculate charge counter from energy counter and voltage.
83     TrackId track = context_->track_tracker->InternTrack(
84         tracks::kBatteryCounterBlueprint,
85         tracks::Dimensions(evt.name(), "charge_uah"));
86     auto energy = evt.energy_counter_uwh();
87     auto voltage = evt.voltage_uv();
88     if (voltage > 0) {
89       context_->event_tracker->PushCounter(
90           ts, static_cast<double>(energy * 1000000 / voltage), track);
91     }
92   }
93   if (evt.has_capacity_percent()) {
94     TrackId track = context_->track_tracker->InternTrack(
95         tracks::kBatteryCounterBlueprint,
96         tracks::Dimensions(evt.name(), "capacity_pct"));
97     context_->event_tracker->PushCounter(
98         ts, static_cast<double>(evt.capacity_percent()), track);
99   }
100   if (evt.has_current_ua()) {
101     TrackId track = context_->track_tracker->InternTrack(
102         tracks::kBatteryCounterBlueprint,
103         tracks::Dimensions(evt.name(), "current_ua"));
104     context_->event_tracker->PushCounter(
105         ts, static_cast<double>(evt.current_ua()), track);
106   }
107   if (evt.has_current_avg_ua()) {
108     TrackId track = context_->track_tracker->InternTrack(
109         tracks::kBatteryCounterBlueprint,
110         tracks::Dimensions(evt.name(), "current.avg_ua"));
111     context_->event_tracker->PushCounter(
112         ts, static_cast<double>(evt.current_avg_ua()), track);
113   }
114   if (evt.has_voltage_uv()) {
115     TrackId track = context_->track_tracker->InternTrack(
116         tracks::kBatteryCounterBlueprint,
117         tracks::Dimensions(evt.name(), "voltage_uv"));
118     context_->event_tracker->PushCounter(
119         ts, static_cast<double>(evt.voltage_uv()), track);
120   }
121   if (evt.has_current_ua() && evt.has_voltage_uv()) {
122     // Calculate power from current and voltage.
123     TrackId track = context_->track_tracker->InternTrack(
124         tracks::kBatteryCounterBlueprint,
125         tracks::Dimensions(evt.name(), "power_mw"));
126     auto current = evt.current_ua();
127     auto voltage = evt.voltage_uv();
128     // Current is negative when discharging, but we want the power counter to
129     // always be positive, so take the absolute value.
130     auto power = std::abs(static_cast<double>(current * voltage / 1000000000));
131     context_->event_tracker->PushCounter(ts, power, track);
132   }
133 }
134 
ParsePowerRails(int64_t ts,uint64_t trace_packet_ts,ConstBytes blob)135 void AndroidProbesParser::ParsePowerRails(int64_t ts,
136                                           uint64_t trace_packet_ts,
137                                           ConstBytes blob) {
138   protos::pbzero::PowerRails::Decoder evt(blob.data, blob.size);
139 
140   // Descriptors should have been processed at tokenization time.
141   PERFETTO_DCHECK(evt.has_energy_data());
142 
143   // Because we have some special code in the tokenization phase, we
144   // will only every get one EnergyData message per packet. Therefore,
145   // we can just read the data directly.
146   auto it = evt.energy_data();
147   protos::pbzero::PowerRails::EnergyData::Decoder desc(*it);
148 
149   auto* tracker = AndroidProbesTracker::GetOrCreate(context_);
150   auto opt_track = tracker->GetPowerRailTrack(desc.index());
151   if (opt_track.has_value()) {
152     // The tokenization makes sure that this field is always present and
153     // is equal to the packet's timestamp that was passed to us via the sorter.
154     PERFETTO_DCHECK(desc.has_timestamp_ms());
155     PERFETTO_DCHECK(ts / 1000000 == static_cast<int64_t>(desc.timestamp_ms()));
156     auto maybe_counter_id = context_->event_tracker->PushCounter(
157         ts, static_cast<double>(desc.energy()), *opt_track);
158     if (maybe_counter_id) {
159       context_->args_tracker->AddArgsTo(*maybe_counter_id)
160           .AddArg(rail_packet_timestamp_id_,
161                   Variadic::UnsignedInteger(trace_packet_ts));
162     }
163   } else {
164     context_->storage->IncrementStats(stats::power_rail_unknown_index);
165   }
166 
167   // DCHECK that we only got one message.
168   PERFETTO_DCHECK(!++it);
169 }
170 
ParseEnergyBreakdown(int64_t ts,ConstBytes blob)171 void AndroidProbesParser::ParseEnergyBreakdown(int64_t ts, ConstBytes blob) {
172   protos::pbzero::AndroidEnergyEstimationBreakdown::Decoder event(blob);
173   if (!event.has_energy_consumer_id() || !event.has_energy_uws()) {
174     context_->storage->IncrementStats(stats::energy_breakdown_missing_values);
175     return;
176   }
177 
178   auto consumer_id = event.energy_consumer_id();
179   auto* tracker = AndroidProbesTracker::GetOrCreate(context_);
180   auto descriptor = tracker->GetEnergyBreakdownDescriptor(consumer_id);
181   if (!descriptor) {
182     context_->storage->IncrementStats(stats::energy_breakdown_missing_values);
183     return;
184   }
185 
186   auto total_energy = static_cast<double>(event.energy_uws());
187   static constexpr auto kEnergyConsumerDimension =
188       tracks::UintDimensionBlueprint("energy_consumer_id");
189   static constexpr auto kGlobalBlueprint = tracks::CounterBlueprint(
190       "android_energy_estimation_breakdown", tracks::UnknownUnitBlueprint(),
191       tracks::DimensionBlueprints(kEnergyConsumerDimension),
192       tracks::DynamicNameBlueprint());
193   TrackId energy_track = context_->track_tracker->InternTrack(
194       kGlobalBlueprint, tracks::Dimensions(consumer_id),
195       tracks::DynamicName(descriptor->name),
196       [&](ArgsTracker::BoundInserter& inserter) {
197         inserter.AddArg(consumer_type_id_, Variadic::String(descriptor->type));
198         inserter.AddArg(ordinal_id_, Variadic::Integer(descriptor->ordinal));
199       });
200   context_->event_tracker->PushCounter(ts, total_energy, energy_track);
201 
202   // Consumers providing per-uid energy breakdown
203   for (auto it = event.per_uid_breakdown(); it; ++it) {
204     protos::pbzero::AndroidEnergyEstimationBreakdown_EnergyUidBreakdown::Decoder
205         breakdown(*it);
206 
207     if (!breakdown.has_uid() || !breakdown.has_energy_uws()) {
208       context_->storage->IncrementStats(
209           stats::energy_uid_breakdown_missing_values);
210       continue;
211     }
212 
213     static constexpr auto kUidBlueprint = tracks::CounterBlueprint(
214         "android_energy_estimation_breakdown_per_uid",
215         tracks::UnknownUnitBlueprint(),
216         tracks::DimensionBlueprints(kEnergyConsumerDimension,
217                                     tracks::kUidDimensionBlueprint),
218         tracks::DynamicNameBlueprint());
219     TrackId energy_uid_track = context_->track_tracker->InternTrack(
220         kUidBlueprint,
221         tracks::Dimensions(consumer_id, static_cast<uint32_t>(breakdown.uid())),
222         tracks::DynamicName(descriptor->name));
223     context_->event_tracker->PushCounter(
224         ts, static_cast<double>(breakdown.energy_uws()), energy_uid_track);
225   }
226 }
227 
ParseEntityStateResidency(int64_t ts,ConstBytes blob)228 void AndroidProbesParser::ParseEntityStateResidency(int64_t ts,
229                                                     ConstBytes blob) {
230   protos::pbzero::EntityStateResidency::Decoder event(blob);
231   if (!event.has_residency()) {
232     context_->storage->IncrementStats(stats::entity_state_residency_invalid);
233     return;
234   }
235   static constexpr auto kBlueprint = tracks::CounterBlueprint(
236       "entity_state", tracks::UnknownUnitBlueprint(),
237       tracks::DimensionBlueprints(
238           tracks::StringDimensionBlueprint("entity_name"),
239           tracks::StringDimensionBlueprint("state_name")),
240       tracks::DynamicNameBlueprint());
241   auto* tracker = AndroidProbesTracker::GetOrCreate(context_);
242   for (auto it = event.residency(); it; ++it) {
243     protos::pbzero::EntityStateResidency::StateResidency::Decoder residency(
244         *it);
245     auto entity_state = tracker->GetEntityStateDescriptor(
246         residency.entity_index(), residency.state_index());
247     if (!entity_state) {
248       context_->storage->IncrementStats(
249           stats::entity_state_residency_lookup_failed);
250       return;
251     }
252     TrackId track = context_->track_tracker->InternTrack(
253         kBlueprint,
254         tracks::Dimensions(
255             context_->storage->GetString(entity_state->entity_name),
256             context_->storage->GetString(entity_state->state_name)),
257         tracks::DynamicName(entity_state->overall_name));
258     context_->event_tracker->PushCounter(
259         ts, double(residency.total_time_in_state_ms()), track);
260   }
261 }
262 
ParseAndroidLogPacket(ConstBytes blob)263 void AndroidProbesParser::ParseAndroidLogPacket(ConstBytes blob) {
264   protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size);
265   for (auto it = packet.events(); it; ++it)
266     ParseAndroidLogEvent(*it);
267 
268   if (packet.has_stats())
269     ParseAndroidLogStats(packet.stats());
270 }
271 
ParseAndroidLogEvent(ConstBytes blob)272 void AndroidProbesParser::ParseAndroidLogEvent(ConstBytes blob) {
273   // TODO(primiano): Add events and non-stringified fields to the "raw" table.
274   protos::pbzero::AndroidLogPacket::LogEvent::Decoder evt(blob.data, blob.size);
275   auto ts = static_cast<int64_t>(evt.timestamp());
276   auto pid = static_cast<uint32_t>(evt.pid());
277   auto tid = static_cast<uint32_t>(evt.tid());
278   auto prio = static_cast<uint8_t>(evt.prio());
279   StringId tag_id = context_->storage->InternString(
280       evt.has_tag() ? evt.tag() : base::StringView());
281   StringId msg_id = context_->storage->InternString(
282       evt.has_message() ? evt.message() : base::StringView());
283 
284   char arg_msg[4096];
285   char* arg_str = &arg_msg[0];
286   *arg_str = '\0';
287   auto arg_avail = [&arg_msg, &arg_str]() {
288     size_t used = static_cast<size_t>(arg_str - arg_msg);
289     PERFETTO_CHECK(used <= sizeof(arg_msg));
290     return sizeof(arg_msg) - used;
291   };
292   for (auto it = evt.args(); it; ++it) {
293     protos::pbzero::AndroidLogPacket::LogEvent::Arg::Decoder arg(*it);
294     if (!arg.has_name())
295       continue;
296     arg_str += base::SprintfTrunc(arg_str, arg_avail(),
297                                   " %.*s=", static_cast<int>(arg.name().size),
298                                   arg.name().data);
299     if (arg.has_string_value()) {
300       arg_str += base::SprintfTrunc(arg_str, arg_avail(), "\"%.*s\"",
301                                     static_cast<int>(arg.string_value().size),
302                                     arg.string_value().data);
303     } else if (arg.has_int_value()) {
304       arg_str +=
305           base::SprintfTrunc(arg_str, arg_avail(), "%" PRId64, arg.int_value());
306     } else if (arg.has_float_value()) {
307       arg_str += base::SprintfTrunc(arg_str, arg_avail(), "%f",
308                                     static_cast<double>(arg.float_value()));
309     }
310   }
311 
312   if (prio == 0)
313     prio = protos::pbzero::AndroidLogPriority::PRIO_INFO;
314 
315   if (arg_str != &arg_msg[0]) {
316     PERFETTO_DCHECK(msg_id.is_null());
317     // Skip the first space char (" foo=1 bar=2" -> "foo=1 bar=2").
318     msg_id = context_->storage->InternString(&arg_msg[1]);
319   }
320   UniquePid utid = tid ? context_->process_tracker->UpdateThread(tid, pid) : 0;
321   base::StatusOr<int64_t> trace_time = context_->clock_tracker->ToTraceTime(
322       protos::pbzero::BUILTIN_CLOCK_REALTIME, ts);
323   if (!trace_time.ok()) {
324     static std::atomic<uint32_t> dlog_count(0);
325     if (dlog_count++ < 10)
326       PERFETTO_DLOG("%s", trace_time.status().c_message());
327     return;
328   }
329 
330   // Log events are NOT required to be sorted by trace_time. The virtual table
331   // will take care of sorting on-demand.
332   context_->storage->mutable_android_log_table()->Insert(
333       {trace_time.value(), utid, prio, tag_id, msg_id});
334 }
335 
ParseAndroidLogStats(ConstBytes blob)336 void AndroidProbesParser::ParseAndroidLogStats(ConstBytes blob) {
337   protos::pbzero::AndroidLogPacket::Stats::Decoder evt(blob.data, blob.size);
338   if (evt.has_num_failed()) {
339     context_->storage->SetStats(stats::android_log_num_failed,
340                                 static_cast<int64_t>(evt.num_failed()));
341   }
342 
343   if (evt.has_num_skipped()) {
344     context_->storage->SetStats(stats::android_log_num_skipped,
345                                 static_cast<int64_t>(evt.num_skipped()));
346   }
347 
348   if (evt.has_num_total()) {
349     context_->storage->SetStats(stats::android_log_num_total,
350                                 static_cast<int64_t>(evt.num_total()));
351   }
352 }
353 
ParseStatsdMetadata(ConstBytes blob)354 void AndroidProbesParser::ParseStatsdMetadata(ConstBytes blob) {
355   protos::pbzero::TraceConfig::StatsdMetadata::Decoder metadata(blob.data,
356                                                                 blob.size);
357   if (metadata.has_triggering_subscription_id()) {
358     context_->metadata_tracker->SetMetadata(
359         metadata::statsd_triggering_subscription_id,
360         Variadic::Integer(metadata.triggering_subscription_id()));
361   }
362 }
363 
ParseAndroidGameIntervention(ConstBytes blob)364 void AndroidProbesParser::ParseAndroidGameIntervention(ConstBytes blob) {
365   protos::pbzero::AndroidGameInterventionList::Decoder intervention_list(
366       blob.data, blob.size);
367   constexpr static int kGameModeStandard = 1;
368   constexpr static int kGameModePerformance = 2;
369   constexpr static int kGameModeBattery = 3;
370 
371   context_->storage->SetStats(stats::game_intervention_has_read_errors,
372                               intervention_list.read_error());
373   context_->storage->SetStats(stats::game_intervention_has_parse_errors,
374                               intervention_list.parse_error());
375 
376   for (auto pkg_it = intervention_list.game_packages(); pkg_it; ++pkg_it) {
377     protos::pbzero::AndroidGameInterventionList_GamePackageInfo::Decoder
378         game_pkg(*pkg_it);
379     int64_t uid = static_cast<int64_t>(game_pkg.uid());
380     int32_t cur_mode = static_cast<int32_t>(game_pkg.current_mode());
381 
382     bool is_standard_mode = false;
383     std::optional<double> standard_downscale;
384     std::optional<int32_t> standard_angle;
385     std::optional<double> standard_fps;
386 
387     bool is_performance_mode = false;
388     std::optional<double> perf_downscale;
389     std::optional<int32_t> perf_angle;
390     std::optional<double> perf_fps;
391 
392     bool is_battery_mode = false;
393     std::optional<double> battery_downscale;
394     std::optional<int32_t> battery_angle;
395     std::optional<double> battery_fps;
396 
397     for (auto mode_it = game_pkg.game_mode_info(); mode_it; ++mode_it) {
398       protos::pbzero::AndroidGameInterventionList_GameModeInfo::Decoder
399           game_mode(*mode_it);
400 
401       uint32_t mode_num = game_mode.mode();
402       if (mode_num == kGameModeStandard) {
403         is_standard_mode = true;
404         standard_downscale =
405             static_cast<double>(game_mode.resolution_downscale());
406         standard_angle = game_mode.use_angle();
407         standard_fps = static_cast<double>(game_mode.fps());
408       } else if (mode_num == kGameModePerformance) {
409         is_performance_mode = true;
410         perf_downscale = static_cast<double>(game_mode.resolution_downscale());
411         perf_angle = game_mode.use_angle();
412         perf_fps = static_cast<double>(game_mode.fps());
413       } else if (mode_num == kGameModeBattery) {
414         is_battery_mode = true;
415         battery_downscale =
416             static_cast<double>(game_mode.resolution_downscale());
417         battery_angle = game_mode.use_angle();
418         battery_fps = static_cast<double>(game_mode.fps());
419       }
420     }
421 
422     context_->storage->mutable_android_game_intervenion_list_table()->Insert(
423         {context_->storage->InternString(game_pkg.name()), uid, cur_mode,
424          is_standard_mode, standard_downscale, standard_angle, standard_fps,
425          is_performance_mode, perf_downscale, perf_angle, perf_fps,
426          is_battery_mode, battery_downscale, battery_angle, battery_fps});
427   }
428 }
429 
ParseInitialDisplayState(int64_t ts,ConstBytes blob)430 void AndroidProbesParser::ParseInitialDisplayState(int64_t ts,
431                                                    ConstBytes blob) {
432   protos::pbzero::InitialDisplayState::Decoder state(blob);
433   TrackId track = context_->track_tracker->InternTrack(
434       tracks::kAndroidScreenStateBlueprint);
435   context_->event_tracker->PushCounter(ts, state.display_state(), track);
436 }
437 
ParseAndroidSystemProperty(int64_t ts,ConstBytes blob)438 void AndroidProbesParser::ParseAndroidSystemProperty(int64_t ts,
439                                                      ConstBytes blob) {
440   protos::pbzero::AndroidSystemProperty::Decoder properties(blob);
441   for (auto it = properties.values(); it; ++it) {
442     protos::pbzero::AndroidSystemProperty::PropertyValue::Decoder kv(*it);
443     base::StringView name(kv.name());
444     if (name == "debug.tracing.device_state") {
445       auto state = kv.value();
446 
447       StringId state_id = context_->storage->InternString(state);
448       auto track_set_id =
449           context_->async_track_set_tracker->InternGlobalTrackSet(
450               device_state_id_);
451       TrackId track_id =
452           context_->async_track_set_tracker->Scoped(track_set_id, ts, 0);
453       context_->slice_tracker->Scoped(ts, track_id, kNullStringId, state_id, 0);
454       continue;
455     }
456 
457     std::optional<int32_t> state =
458         base::StringToInt32(kv.value().ToStdString());
459     if (!state) {
460       continue;
461     }
462 
463     if (name == "debug.tracing.screen_state") {
464       TrackId track = context_->track_tracker->InternTrack(
465           tracks::kAndroidScreenStateBlueprint);
466       context_->event_tracker->PushCounter(ts, *state, track);
467       continue;
468     }
469 
470     static constexpr auto kBlueprint = tracks::CounterBlueprint(
471         "sysprop_counter", tracks::UnknownUnitBlueprint(),
472         tracks::DimensionBlueprints(
473             tracks::StringDimensionBlueprint("sysprop_name")),
474         tracks::DynamicNameBlueprint());
475 
476     if (name.StartsWith("debug.tracing.battery_stats.") ||
477         name == "debug.tracing.mcc" || name == "debug.tracing.mnc" ||
478         name == "debug.tracing.desktop_mode_visible_tasks") {
479       StringId name_id = context_->storage->InternString(
480           name.substr(strlen("debug.tracing.")));
481       TrackId track = context_->track_tracker->InternTrack(
482           kBlueprint, tracks::Dimensions(name), tracks::DynamicName(name_id));
483       context_->event_tracker->PushCounter(ts, *state, track);
484       continue;
485     }
486 
487     std::optional<StringId> mapped_name_id;
488     if (name == "debug.tracing.battery_status") {
489       mapped_name_id = battery_status_id_;
490     } else if (name == "debug.tracing.plug_type") {
491       mapped_name_id = plug_type_id_;
492     }
493     if (mapped_name_id) {
494       TrackId track = context_->track_tracker->InternTrack(
495           kBlueprint, tracks::Dimensions(name), *mapped_name_id);
496       context_->event_tracker->PushCounter(ts, *state, track);
497     }
498   }
499 }
500 
501 }  // namespace perfetto::trace_processor
502