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