1 /*
2 * Copyright (C) 2024 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/android_bugreport/android_dumpstate_event_parser_impl.h"
18
19 #include <cstdint>
20 #include <utility>
21
22 #include "perfetto/base/status.h"
23 #include "perfetto/ext/base/no_destructor.h"
24 #include "perfetto/ext/base/status_or.h"
25 #include "perfetto/ext/base/string_splitter.h"
26 #include "perfetto/ext/base/string_utils.h"
27 #include "src/trace_processor/importers/android_bugreport/android_battery_stats_history_string_tracker.h"
28 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_event.h"
29 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
30 #include "src/trace_processor/importers/common/event_tracker.h"
31 #include "src/trace_processor/importers/common/process_tracker.h"
32 #include "src/trace_processor/importers/common/slice_tracker.h"
33 #include "src/trace_processor/importers/common/track_tracker.h"
34 #include "src/trace_processor/importers/common/tracks_common.h"
35 #include "src/trace_processor/importers/common/tracks_internal.h"
36 #include "src/trace_processor/tables/android_tables_py.h"
37 #include "src/trace_processor/types/trace_processor_context.h"
38 #include "src/trace_processor/util/status_macros.h"
39
40 namespace perfetto::trace_processor {
41
42 namespace {
GetEventFromShortName(base::StringView short_name)43 base::StatusOr<std::string> GetEventFromShortName(base::StringView short_name) {
44 static const base::NoDestructor<
45 std::unordered_map<base::StringView, std::string> >
46 checkin_event_name_to_enum(
47 std::unordered_map<base::StringView, std::string>({
48 {"Enl", "null"}, {"Epr", "proc"},
49 {"Efg", "fg"}, {"Etp", "top"},
50 {"Esy", "sync"}, {"Ewl", "wake_lock_in"},
51 {"Ejb", "job"}, {"Eur", "user"},
52 {"Euf", "userfg"}, {"Ecn", "conn"},
53 {"Eac", "active"}, {"Epi", "pkginst"},
54 {"Epu", "pkgunin"}, {"Eal", "alarm"},
55 {"Est", "stats"}, {"Eai", "pkginactive"},
56 {"Eaa", "pkgactive"}, {"Etw", "tmpwhitelist"},
57 {"Esw", "screenwake"}, {"Ewa", "wakeupap"},
58 {"Elw", "longwake"}, {"Eec", "est_capacity"},
59 }));
60 auto result = checkin_event_name_to_enum.ref().find(short_name);
61 if (result == checkin_event_name_to_enum.ref().end()) {
62 return base::ErrStatus("Failed to find historty event name mapping");
63 }
64 return result->second;
65 }
66
67 struct StateStringTranslationInfo {
68 const std::string long_name;
69 const std::unordered_map<base::StringView, uint64_t> short_string_to_value;
70 };
71
GetStateAndValueFromShortName(base::StringView state_short_name,base::StringView value_short_name,uint64_t * value_out)72 base::StatusOr<std::string> GetStateAndValueFromShortName(
73 base::StringView state_short_name,
74 base::StringView value_short_name,
75 uint64_t* value_out) {
76 // Mappings of all the state checkin names from BatteryStats.java and their
77 // corresponding value mappings
78 static const base::NoDestructor<
79 std::unordered_map<base::StringView, StateStringTranslationInfo> >
80 checkin_state_name_to_enum_and_values(
81 std::unordered_map<base::StringView, StateStringTranslationInfo>(
82 {{"r", {"running", {}}},
83 {"w", {"wake_lock", {}}},
84 {"s", {"sensor", {}}},
85 {"g", {"gps", {}}},
86 {"Wl", {"wifi_full_lock", {}}},
87 {"Ws", {"wifi_scan", {}}},
88 {"Wm", {"wifi_multicast", {}}},
89 {"Wr", {"wifi_radio", {}}},
90 {"Pr", {"mobile_radio", {}}},
91 {"Psc", {"phone_scanning", {}}},
92 {"a", {"audio", {}}},
93 {"S", {"screen", {}}},
94 {"BP", {"plugged", {}}},
95 {"Sd", {"screen_doze", {}}},
96 {"Pcn",
97 {"data_conn",
98 {
99 {"oos", 0}, {"gprs", 1}, {"edge", 2},
100 {"umts", 3}, {"cdma", 4}, {"evdo_0", 5},
101 {"evdo_A", 6}, {"1xrtt", 7}, {"hsdpa", 8},
102 {"hsupa", 9}, {"hspa", 10}, {"iden", 11},
103 {"evdo_b", 12}, {"lte", 13}, {"ehrpd", 14},
104 {"hspap", 15}, {"gsm", 16}, {"td_scdma", 17},
105 {"iwlan", 18}, {"lte_ca", 19}, {"nr", 20},
106 {"emngcy", 21}, {"other", 22},
107 }}},
108 {"Pst",
109 {"phone_state",
110 {
111 {"in", 0},
112 {"out", 1},
113 {"em", 2},
114 {"off", 3},
115 }}},
116 {"Pss", {"phone_signal_strength", {}}},
117 {"Sb", {"brightness", {}}},
118 {"ps", {"power_save", {}}},
119 {"v", {"video", {}}},
120 {"Ww", {"wifi_running", {}}},
121 {"W", {"wifi", {}}},
122 {"fl", {"flashlight", {}}},
123 {"di",
124 {"device_idle",
125 {
126 {"off", 0},
127 {"light", 1},
128 {"full", 2},
129 {"???", 3},
130 }}},
131 {"ch", {"charging", {}}},
132 {"Ud", {"usb_data", {}}},
133 {"Pcl", {"phone_in_call", {}}},
134 {"b", {"bluetooth", {}}},
135 {"Wss", {"wifi_signal_strength", {}}},
136 {"Wsp",
137 {"wifi_suppl",
138 {
139 {"inv", 0},
140 {"dsc", 1},
141 {"dis", 2},
142 {"inact", 3},
143 {"scan", 4},
144 {"auth", 5},
145 {"ascing", 6},
146 {"asced", 7},
147 {"4-way", 8},
148 {"group", 9},
149 {"compl", 10},
150 {"dorm", 11},
151 {"uninit", 12},
152 }}},
153 {"ca", {"camera", {}}},
154 {"bles", {"ble_scan", {}}},
155 {"Chtp", {"cellular_high_tx_power", {}}},
156 {"Gss",
157 {"gps_signal_quality",
158 {
159 {"poor", 0},
160 {"good", 1},
161 {"none", 2},
162 }}},
163 {"nrs", {"nr_state", {}}}}));
164
165 auto result =
166 checkin_state_name_to_enum_and_values.ref().find(state_short_name);
167 if (result == checkin_state_name_to_enum_and_values.ref().end()) {
168 return base::ErrStatus("Failed to find state short to long name mapping");
169 }
170
171 StateStringTranslationInfo translation_info = result->second;
172
173 // If caller isn't requesting a value, just return the item type.
174 if (value_out == nullptr) {
175 return translation_info.long_name;
176 }
177
178 // If the value short name is already a number, just do a direct conversion
179 std::optional<uint64_t> possible_int_value =
180 base::StringToUInt64(value_short_name.ToStdString());
181 if (possible_int_value.has_value()) {
182 *value_out = possible_int_value.value();
183 return translation_info.long_name;
184 }
185 // value has a non-numerical string, so translate it
186 auto short_name_mapping =
187 translation_info.short_string_to_value.find(value_short_name);
188 if (short_name_mapping == translation_info.short_string_to_value.end()) {
189 return base::ErrStatus("Failed to translate value for state");
190 }
191 *value_out = short_name_mapping->second;
192 return translation_info.long_name;
193 }
194
StringToStatusOrUInt64(base::StringView str)195 base::StatusOr<uint64_t> StringToStatusOrUInt64(base::StringView str) {
196 std::optional<uint64_t> possible_result =
197 base::StringToUInt64(str.ToStdString());
198 if (!possible_result.has_value()) {
199 return base::ErrStatus("Failed to convert string to uint64_t");
200 }
201 return possible_result.value();
202 }
203
204 } // namespace
205
206 AndroidDumpstateEventParserImpl::~AndroidDumpstateEventParserImpl() = default;
207
ParseAndroidDumpstateEvent(int64_t ts,AndroidDumpstateEvent event)208 void AndroidDumpstateEventParserImpl::ParseAndroidDumpstateEvent(
209 int64_t ts,
210 AndroidDumpstateEvent event) {
211 switch (event.type) {
212 case AndroidDumpstateEvent::EventType::kBatteryStatsHistoryEvent:
213 ProcessBatteryStatsHistoryItem(ts, event.raw_event);
214 return;
215 case AndroidDumpstateEvent::EventType::kNull:
216 return;
217 }
218 }
219
ProcessBatteryStatsHistoryItem(int64_t ts,const std::string & raw_event)220 base::Status AndroidDumpstateEventParserImpl::ProcessBatteryStatsHistoryItem(
221 int64_t ts,
222 const std::string& raw_event) {
223 // TODO: migrate to future StringViewSplitter when availabile.
224 base::StringSplitter splitter(raw_event, '=');
225 base::StringView key =
226 base::StringView(splitter.Next() ? splitter.cur_token() : "");
227 base::StringView value =
228 base::StringView(splitter.Next() ? splitter.cur_token() : "");
229
230 AndroidBatteryStatsHistoryStringTracker* history_string_tracker =
231 AndroidBatteryStatsHistoryStringTracker::GetOrCreate(context_);
232
233 std::string item_name;
234 if (key.StartsWith("+E") || key.StartsWith("-E") || key.StartsWith("E")) {
235 // Process a history event
236 base::StringView prefix = "";
237 if (key.at(0) == '+' || key.at(0) == '-') {
238 prefix = key.substr(0, 1);
239 key = key.substr(1);
240 }
241 ASSIGN_OR_RETURN(item_name, GetEventFromShortName(key));
242 ASSIGN_OR_RETURN(uint64_t hsp_index, StringToStatusOrUInt64(value));
243 const int32_t uid = history_string_tracker->GetUid(hsp_index);
244 const std::string& event_str = history_string_tracker->GetString(hsp_index);
245 StringId track_name_id = context_->storage->InternString(
246 std::string("battery_stats.").append(item_name));
247 const std::string slice_name = prefix.ToStdString()
248 .append(item_name)
249 .append("=")
250 .append(std::to_string(uid))
251 .append(":\"")
252 .append(event_str)
253 .append("\"");
254 StringId name_id = context_->storage->InternString(slice_name);
255 AsyncTrackSetTracker::TrackSetId track_set_id =
256 context_->async_track_set_tracker->InternGlobalTrackSet(track_name_id);
257 TrackId track_id =
258 context_->async_track_set_tracker->Scoped(track_set_id, ts, 0);
259 context_->slice_tracker->Scoped(ts, track_id, kNullStringId, name_id, 0);
260 return base::OkStatus();
261 } else if ((key.StartsWith("+") || key.StartsWith("-")) && value.empty()) {
262 // Process a history state of the form "+state" or "-state"
263
264 // To match behavior of the battery stats atrace implementation, avoid
265 // including Wakelock events in the trace
266 if (key == "+w" || key == "-w") {
267 return base::OkStatus();
268 }
269
270 ASSIGN_OR_RETURN(item_name,
271 GetStateAndValueFromShortName(key.substr(1), "", nullptr));
272 TrackId track = context_->track_tracker->InternTrack(
273 tracks::kAndroidBatteryStatsBlueprint,
274 tracks::Dimensions(
275 base::StringView(std::string("battery_stats.").append(item_name))));
276 context_->event_tracker->PushCounter(ts, (key.at(0) == '+') ? 1.0 : 0.0,
277 track);
278 // Also add a screen events to the screen state track
279 if (item_name == "screen") {
280 track = context_->track_tracker->InternTrack(
281 tracks::kAndroidScreenStateBlueprint);
282 // battery_stats.screen event is 0 for off and 1 for on, but the
283 // ScreenState track uses the convention 1 for off and 2 for on, so add
284 // 1 to the current counter value.
285 context_->event_tracker->PushCounter(
286 ts, static_cast<double>((key.at(0) == '+') ? 2.0 : 1.0), track);
287 }
288 return base::OkStatus();
289 } else if (!key.StartsWith("+") && !key.StartsWith("-") && !value.empty()) {
290 // AndroidProbesParser will use the empty string for the battery name if no
291 // battery name is associated with the data, which is common on most pixel
292 // phones. Adopt the same convention here. Battery stats does not provide
293 // a battery name in the checking format, so we'll always have an unknown
294 // battery.
295 const base::StringView kUnknownBatteryName = "";
296
297 // process history state of form "state=12345" or "state=abcde"
298 TrackId counter_track;
299 uint64_t counter_value;
300 base::StatusOr<std::string> possible_history_state_item =
301 GetStateAndValueFromShortName(key, value, &counter_value);
302 if (possible_history_state_item.ok()) {
303 item_name = possible_history_state_item.value();
304 counter_track = context_->track_tracker->InternTrack(
305 tracks::kAndroidBatteryStatsBlueprint,
306 tracks::Dimensions(base::StringView(
307 std::string("battery_stats.").append(item_name))));
308 } else if (key == "Bl") {
309 counter_track = context_->track_tracker->InternTrack(
310 tracks::kBatteryCounterBlueprint,
311 tracks::Dimensions(kUnknownBatteryName, "capacity_pct"));
312 ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
313 } else if (key == "Bcc") {
314 counter_track = context_->track_tracker->InternTrack(
315 tracks::kBatteryCounterBlueprint,
316 tracks::Dimensions(kUnknownBatteryName, "charge_uah"));
317 ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
318 // battery stats gives us charge in milli-amp-hours, but the track
319 // expects the value to be in micro-amp-hours
320 counter_value *= 1000;
321 } else if (key == "Bv") {
322 counter_track = context_->track_tracker->InternTrack(
323 tracks::kBatteryCounterBlueprint,
324 tracks::Dimensions(kUnknownBatteryName, "voltage_uv"));
325 ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
326 // battery stats gives us charge in milli-volts, but the track
327 // expects the value to be in micro-volts
328 counter_value *= 1000;
329 } else if (key == "Bs") {
330 static constexpr auto kBatteryStatusBlueprint = tracks::CounterBlueprint(
331 "battery_status", tracks::UnknownUnitBlueprint(),
332 tracks::DimensionBlueprints(),
333 tracks::StaticNameBlueprint("BatteryStatus"));
334 counter_track =
335 context_->track_tracker->InternTrack(kBatteryStatusBlueprint);
336 switch (value.at(0)) {
337 case '?':
338 counter_value = 1; // BatteryManager.BATTERY_STATUS_UNKNOWN
339 break;
340 case 'c':
341 counter_value = 2; // BatteryManager.BATTERY_STATUS_CHARGING
342 break;
343 case 'd':
344 counter_value = 3; // BatteryManager.BATTERY_STATUS_DISCHARGING
345 break;
346 case 'n':
347 counter_value = 4; // BatteryManager.BATTERY_STATUS_NOT_CHARGING
348 break;
349 case 'f':
350 counter_value = 5; // BatteryManager.BATTERY_STATUS_FULL
351 break;
352 default:
353 PERFETTO_ELOG("unknown battery status: %c", value.at(0));
354 counter_value = 0; // not a valid enum
355 }
356 } else if (key == "Bp") {
357 static constexpr auto kPluggedStatusBluePrint = tracks::CounterBlueprint(
358 "battery_plugged_status", tracks::UnknownUnitBlueprint(),
359 tracks::DimensionBlueprints(),
360 tracks::StaticNameBlueprint("PlugType"));
361 counter_track =
362 context_->track_tracker->InternTrack(kPluggedStatusBluePrint);
363 switch (value.at(0)) {
364 case 'n':
365 counter_value = 0; // BatteryManager.BATTERY_PLUGGED_NONE
366 break;
367 case 'a':
368 counter_value = 1; // BatteryManager.BATTERY_PLUGGED_AC
369 break;
370 case 'u':
371 counter_value = 2; // BatteryManager.BATTERY_PLUGGED_USB
372 break;
373 case 'w':
374 counter_value = 4; // BatteryManager.BATTERY_PLUGGED_WIRELESS
375 break;
376 default:
377 counter_value = 0; // BatteryManager.BATTERY_PLUGGED_NONE
378 }
379 } else {
380 return base::ErrStatus("Unhandled event");
381 }
382
383 context_->event_tracker->PushCounter(ts, static_cast<double>(counter_value),
384 counter_track);
385 return base::OkStatus();
386 } else {
387 return base::ErrStatus("Unhandled event");
388 }
389 }
390
391 } // namespace perfetto::trace_processor
392