1 /*
2 * Copyright (C) 2021 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/traced/probes/power/linux_power_sysfs_data_source.h"
18
19 #include <dirent.h>
20 #include <sys/types.h>
21 #include <optional>
22
23 #include "perfetto/base/logging.h"
24 #include "perfetto/base/task_runner.h"
25 #include "perfetto/base/time.h"
26 #include "perfetto/ext/base/file_utils.h"
27 #include "perfetto/ext/base/scoped_file.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/tracing/core/trace_packet.h"
30 #include "perfetto/ext/tracing/core/trace_writer.h"
31 #include "perfetto/tracing/core/data_source_config.h"
32
33 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
34 #include "protos/perfetto/trace/trace_packet.pbzero.h"
35
36 namespace perfetto {
37
38 namespace {
39 constexpr uint32_t kDefaultPollIntervalMs = 1000;
40
ReadFileAsInt64(std::string path)41 std::optional<int64_t> ReadFileAsInt64(std::string path) {
42 std::string buf;
43 if (!base::ReadFile(path, &buf))
44 return std::nullopt;
45 return base::StringToInt64(base::StripSuffix(buf, "\n"));
46 }
47 } // namespace
48
BatteryInfo(const char * power_supply_dir_path)49 LinuxPowerSysfsDataSource::BatteryInfo::BatteryInfo(
50 const char* power_supply_dir_path)
51 : power_supply_dir_path_(power_supply_dir_path) {
52 base::ScopedDir power_supply_dir(opendir(power_supply_dir_path_.c_str()));
53 if (!power_supply_dir)
54 return;
55
56 for (auto* ent = readdir(power_supply_dir.get()); ent;
57 ent = readdir(power_supply_dir.get())) {
58 if (ent->d_name[0] == '.')
59 continue;
60 std::string dir_name =
61 std::string(power_supply_dir_path) + "/" + ent->d_name;
62 std::string buf;
63 if (!base::ReadFile(dir_name + "/type", &buf) ||
64 base::StripSuffix(buf, "\n") != "Battery")
65 continue;
66 buf.clear();
67 if (!base::ReadFile(dir_name + "/present", &buf) ||
68 base::StripSuffix(buf, "\n") != "1")
69 continue;
70 sysfs_battery_subdirs_.push_back(ent->d_name);
71 }
72 }
73 LinuxPowerSysfsDataSource::BatteryInfo::~BatteryInfo() = default;
74
num_batteries() const75 size_t LinuxPowerSysfsDataSource::BatteryInfo::num_batteries() const {
76 return sysfs_battery_subdirs_.size();
77 }
78
79 std::optional<int64_t>
GetChargeCounterUah(size_t battery_idx)80 LinuxPowerSysfsDataSource::BatteryInfo::GetChargeCounterUah(
81 size_t battery_idx) {
82 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
83 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
84 sysfs_battery_subdirs_[battery_idx] + "/charge_now");
85 }
86
87 std::optional<int64_t>
GetEnergyCounterUah(size_t battery_idx)88 LinuxPowerSysfsDataSource::BatteryInfo::GetEnergyCounterUah(
89 size_t battery_idx) {
90 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
91 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
92 sysfs_battery_subdirs_[battery_idx] + "/energy_now");
93 }
94
GetVoltageUv(size_t battery_idx)95 std::optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetVoltageUv(
96 size_t battery_idx) {
97 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
98 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
99 sysfs_battery_subdirs_[battery_idx] + "/voltage_now");
100 }
101
102 std::optional<int64_t>
GetCapacityPercent(size_t battery_idx)103 LinuxPowerSysfsDataSource::BatteryInfo::GetCapacityPercent(size_t battery_idx) {
104 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
105 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
106 sysfs_battery_subdirs_[battery_idx] + "/capacity");
107 }
108
GetCurrentNowUa(size_t battery_idx)109 std::optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetCurrentNowUa(
110 size_t battery_idx) {
111 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
112 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
113 sysfs_battery_subdirs_[battery_idx] + "/current_now");
114 }
115
116 std::optional<int64_t>
GetAverageCurrentUa(size_t battery_idx)117 LinuxPowerSysfsDataSource::BatteryInfo::GetAverageCurrentUa(
118 size_t battery_idx) {
119 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
120 return ReadFileAsInt64(power_supply_dir_path_ + "/" +
121 sysfs_battery_subdirs_[battery_idx] + "/current_avg");
122 }
123
GetBatteryName(size_t battery_idx)124 std::string LinuxPowerSysfsDataSource::BatteryInfo::GetBatteryName(
125 size_t battery_idx) {
126 PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
127 return sysfs_battery_subdirs_[battery_idx];
128 }
129
130 // static
131 const ProbesDataSource::Descriptor LinuxPowerSysfsDataSource::descriptor = {
132 /*name*/ "linux.sysfs_power",
133 /*flags*/ Descriptor::kFlagsNone,
134 /*fill_descriptor_func*/ nullptr,
135 };
136
LinuxPowerSysfsDataSource(DataSourceConfig cfg,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)137 LinuxPowerSysfsDataSource::LinuxPowerSysfsDataSource(
138 DataSourceConfig cfg,
139 base::TaskRunner* task_runner,
140 TracingSessionID session_id,
141 std::unique_ptr<TraceWriter> writer)
142 : ProbesDataSource(session_id, &descriptor),
143 poll_interval_ms_(kDefaultPollIntervalMs),
144 task_runner_(task_runner),
145 writer_(std::move(writer)),
146 weak_factory_(this) {
147 base::ignore_result(cfg); // The data source doesn't need any config yet.
148 }
149
150 LinuxPowerSysfsDataSource::~LinuxPowerSysfsDataSource() = default;
151
Start()152 void LinuxPowerSysfsDataSource::Start() {
153 battery_info_.reset(new BatteryInfo());
154 Tick();
155 }
156
Tick()157 void LinuxPowerSysfsDataSource::Tick() {
158 // Post next task.
159 auto now_ms = base::GetWallTimeMs().count();
160 auto weak_this = weak_factory_.GetWeakPtr();
161 task_runner_->PostDelayedTask(
162 [weak_this] {
163 if (weak_this)
164 weak_this->Tick();
165 },
166 poll_interval_ms_ - static_cast<uint32_t>(now_ms % poll_interval_ms_));
167
168 WriteBatteryCounters();
169 }
170
WriteBatteryCounters()171 void LinuxPowerSysfsDataSource::WriteBatteryCounters() {
172 // Query battery counters from sysfs. Report the battery counters for each
173 // battery.
174 for (size_t battery_idx = 0; battery_idx < battery_info_->num_batteries();
175 battery_idx++) {
176 auto packet = writer_->NewTracePacket();
177 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
178
179 auto* counters_proto = packet->set_battery();
180 auto value = battery_info_->GetChargeCounterUah(battery_idx);
181 if (value)
182 counters_proto->set_charge_counter_uah(*value);
183 value = battery_info_->GetCapacityPercent(battery_idx);
184 if (value)
185 counters_proto->set_capacity_percent(static_cast<float>(*value));
186 value = battery_info_->GetCurrentNowUa(battery_idx);
187 if (value)
188 counters_proto->set_current_ua(*value);
189 value = battery_info_->GetAverageCurrentUa(battery_idx);
190 if (value)
191 counters_proto->set_current_ua(*value);
192 value = battery_info_->GetEnergyCounterUah(battery_idx);
193 if (value)
194 counters_proto->set_energy_counter_uwh(*value);
195 value = battery_info_->GetVoltageUv(battery_idx);
196 if (value)
197 counters_proto->set_voltage_uv(*value);
198 // On systems with multiple batteries, disambiguate with battery names.
199 if (battery_info_->num_batteries() > 1)
200 counters_proto->set_name(battery_info_->GetBatteryName(battery_idx));
201 }
202 }
203
Flush(FlushRequestID,std::function<void ()> callback)204 void LinuxPowerSysfsDataSource::Flush(FlushRequestID,
205 std::function<void()> callback) {
206 writer_->Flush(callback);
207 }
208
209 } // namespace perfetto
210