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