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_battery_stats_reader.h"
18 
19 #include <algorithm>
20 #include <chrono>
21 #include <cstdint>
22 #include <ctime>
23 #include <optional>
24 #include <string>
25 #include <unordered_map>
26 #include <utility>
27 
28 #include "perfetto/base/status.h"
29 #include "perfetto/base/time.h"
30 #include "perfetto/ext/base/no_destructor.h"
31 #include "perfetto/ext/base/string_splitter.h"
32 #include "perfetto/ext/base/string_utils.h"
33 #include "src/trace_processor/importers/android_bugreport/android_battery_stats_history_string_tracker.h"
34 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_event.h"
35 #include "src/trace_processor/importers/common/clock_converter.h"
36 #include "src/trace_processor/importers/common/clock_tracker.h"
37 #include "src/trace_processor/sorter/trace_sorter.h"
38 #include "src/trace_processor/storage/stats.h"
39 #include "src/trace_processor/types/trace_processor_context.h"
40 #include "src/trace_processor/util/status_macros.h"
41 
42 namespace perfetto::trace_processor {
43 
44 namespace {
45 
StringToStatusOrUInt64(base::StringView str)46 base::StatusOr<uint64_t> StringToStatusOrUInt64(base::StringView str) {
47   std::optional<uint64_t> possible_result =
48       base::StringToUInt64(str.ToStdString());
49   if (!possible_result.has_value()) {
50     return base::ErrStatus("Failed to convert string to uint64_t");
51   }
52   return possible_result.value();
53 }
54 }  // namespace
55 
AndroidBatteryStatsReader(TraceProcessorContext * context)56 AndroidBatteryStatsReader::AndroidBatteryStatsReader(
57     TraceProcessorContext* context)
58     : context_(context) {}
59 
60 AndroidBatteryStatsReader::~AndroidBatteryStatsReader() = default;
61 
ParseLine(base::StringView line)62 util::Status AndroidBatteryStatsReader::ParseLine(base::StringView line) {
63   // TODO: migrate to future StringViewSplitter when availabile.
64   base::StringSplitter splitter(line.ToStdString(), ',');
65 
66   // consume the legacy version number which we expect to be at the start of
67   // every line.
68   if ((splitter.Next() ? std::string(splitter.cur_token()) : "") != "9") {
69     return base::ErrStatus("Unexpected start of battery stats checkin line");
70   }
71 
72   const base::StringView possible_event_type =
73       splitter.Next() ? splitter.cur_token() : "";
74 
75   if (possible_event_type == "hsp") {
76     ASSIGN_OR_RETURN(
77         uint64_t index,
78         StringToStatusOrUInt64(splitter.Next() ? splitter.cur_token() : ""));
79     const std::optional<int32_t> possible_uid =
80         base::StringToInt32(splitter.Next() ? splitter.cur_token() : "");
81     // the next element is quoted and can contain commas. Instead of
82     // implementing general logic to parse quoted CSV elements just grab the
83     // rest of the line, which is possible since this element should be the
84     // last one on the line.
85     base::StringView remainder =
86         base::StringView(splitter.remainder(), splitter.remainder_size());
87     // remove the leading and trailing quotes from the hsp string
88     size_t substr_start = remainder.find('"') + 1;
89     size_t substr_end = remainder.rfind('"');
90     base::StringView hsp_string =
91         remainder.substr(substr_start, substr_end - substr_start);
92     AndroidBatteryStatsHistoryStringTracker::GetOrCreate(context_)
93         ->SetStringPoolItem(index, possible_uid.value(),
94                             hsp_string.ToStdString());
95   } else if (possible_event_type == "h") {
96     const base::StringView time_adjustment_marker = ":TIME:";
97     const base::StringView possible_timestamp =
98         splitter.Next() ? splitter.cur_token() : "";
99     size_t time_marker_index = possible_timestamp.find(time_adjustment_marker);
100     if (time_marker_index != base::StringView::npos) {
101       // Special case timestamp adjustment event.
102       ASSIGN_OR_RETURN(current_timestamp_ms_,
103                        StringToStatusOrUInt64(possible_timestamp.substr(
104                            time_marker_index + time_adjustment_marker.size())));
105       return base::OkStatus();
106     } else if (possible_timestamp.find(":START") != base::StringView::npos) {
107       // Ignore line
108       return base::OkStatus();
109     } else if (possible_timestamp.find(":SHUTDOWN") != base::StringView::npos) {
110       // Ignore line
111       return base::OkStatus();
112     } else {
113       ASSIGN_OR_RETURN(uint64_t parsed_timestamp_delta,
114                        StringToStatusOrUInt64(possible_timestamp));
115       current_timestamp_ms_ += parsed_timestamp_delta;
116       for (base::StringView item = splitter.Next() ? splitter.cur_token() : "";
117            !item.empty(); item = splitter.Next() ? splitter.cur_token() : "") {
118         RETURN_IF_ERROR(ProcessBatteryStatsHistoryEvent(item));
119       }
120     }
121   } else {
122     // TODO Implement UID parsing and other kinds of events.
123   }
124 
125   return base::OkStatus();
126 }
127 
ProcessBatteryStatsHistoryEvent(base::StringView raw_event)128 util::Status AndroidBatteryStatsReader::ProcessBatteryStatsHistoryEvent(
129     base::StringView raw_event) {
130   AndroidDumpstateEvent event{
131       AndroidDumpstateEvent::EventType::kBatteryStatsHistoryEvent,
132       raw_event.ToStdString()};
133   return SendToSorter(std::chrono::milliseconds(current_timestamp_ms_), event);
134 }
135 
SendToSorter(std::chrono::nanoseconds event_ts,AndroidDumpstateEvent event)136 util::Status AndroidBatteryStatsReader::SendToSorter(
137     std::chrono::nanoseconds event_ts,
138     AndroidDumpstateEvent event) {
139   ASSIGN_OR_RETURN(
140       int64_t trace_ts,
141       context_->clock_tracker->ToTraceTime(
142           protos::pbzero::ClockSnapshot::Clock::REALTIME, event_ts.count()));
143   context_->sorter->PushAndroidDumpstateEvent(trace_ts, std::move(event));
144   return base::OkStatus();
145 }
146 
EndOfStream(base::StringView)147 void AndroidBatteryStatsReader::EndOfStream(base::StringView) {}
148 
149 }  // namespace perfetto::trace_processor
150