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