xref: /aosp_15_r20/external/perfetto/src/traced/probes/power/linux_power_sysfs_data_source.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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