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