1 /*
2 * Copyright (C) 2018 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/android_power_data_source.h"
18
19 #include <vector>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/base/task_runner.h"
23 #include "perfetto/base/time.h"
24 #include "perfetto/ext/base/optional.h"
25 #include "perfetto/ext/base/scoped_file.h"
26 #include "perfetto/ext/tracing/core/trace_packet.h"
27 #include "perfetto/ext/tracing/core/trace_writer.h"
28 #include "perfetto/tracing/core/data_source_config.h"
29 #include "src/android_internal/health_hal.h"
30 #include "src/android_internal/lazy_library_loader.h"
31 #include "src/android_internal/power_stats.h"
32
33 #include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
34 #include "protos/perfetto/config/power/android_power_config.pbzero.h"
35 #include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
36 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
37 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
38 #include "protos/perfetto/trace/trace_packet.pbzero.h"
39
40 namespace perfetto {
41
42 namespace {
43 constexpr uint32_t kMinPollIntervalMs = 100;
44 constexpr uint32_t kDefaultPollIntervalMs = 1000;
45 constexpr size_t kMaxNumRails = 32;
46 constexpr size_t kMaxNumEnergyConsumer = 32;
47 constexpr size_t kMaxNumPowerEntities = 256;
48 } // namespace
49
50 // static
51 const ProbesDataSource::Descriptor AndroidPowerDataSource::descriptor = {
52 /*name*/ "android.power",
53 /*flags*/ Descriptor::kFlagsNone,
54 };
55
56 // Dynamically loads the libperfetto_android_internal.so library which
57 // allows to proxy calls to android hwbinder in in-tree builds.
58 struct AndroidPowerDataSource::DynamicLibLoader {
59 PERFETTO_LAZY_LOAD(android_internal::GetBatteryCounter, get_battery_counter_);
60 PERFETTO_LAZY_LOAD(android_internal::GetAvailableRails, get_available_rails_);
61 PERFETTO_LAZY_LOAD(android_internal::GetRailEnergyData,
62 get_rail_energy_data_);
63 PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumerInfo,
64 get_energy_consumer_info_);
65 PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumed, get_energy_consumed_);
66
GetCounterperfetto::AndroidPowerDataSource::DynamicLibLoader67 base::Optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
68 if (!get_battery_counter_)
69 return base::nullopt;
70 int64_t value = 0;
71 if (get_battery_counter_(counter, &value))
72 return base::make_optional(value);
73 return base::nullopt;
74 }
75
GetRailDescriptorsperfetto::AndroidPowerDataSource::DynamicLibLoader76 std::vector<android_internal::RailDescriptor> GetRailDescriptors() {
77 if (!get_available_rails_)
78 return std::vector<android_internal::RailDescriptor>();
79
80 std::vector<android_internal::RailDescriptor> rail_descriptors(
81 kMaxNumRails);
82 size_t num_rails = rail_descriptors.size();
83 if (!get_available_rails_(&rail_descriptors[0], &num_rails)) {
84 PERFETTO_ELOG("Failed to retrieve rail descriptors.");
85 num_rails = 0;
86 }
87 rail_descriptors.resize(num_rails);
88 return rail_descriptors;
89 }
90
GetRailEnergyDataperfetto::AndroidPowerDataSource::DynamicLibLoader91 std::vector<android_internal::RailEnergyData> GetRailEnergyData() {
92 if (!get_rail_energy_data_)
93 return std::vector<android_internal::RailEnergyData>();
94
95 std::vector<android_internal::RailEnergyData> energy_data(kMaxNumRails);
96 size_t num_rails = energy_data.size();
97 if (!get_rail_energy_data_(&energy_data[0], &num_rails)) {
98 PERFETTO_ELOG("Failed to retrieve rail energy data.");
99 num_rails = 0;
100 }
101 energy_data.resize(num_rails);
102 return energy_data;
103 }
104
GetEnergyConsumerInfoperfetto::AndroidPowerDataSource::DynamicLibLoader105 std::vector<android_internal::EnergyConsumerInfo> GetEnergyConsumerInfo() {
106 if (!get_energy_consumer_info_)
107 return std::vector<android_internal::EnergyConsumerInfo>();
108
109 std::vector<android_internal::EnergyConsumerInfo> consumers(
110 kMaxNumEnergyConsumer);
111 size_t num_power_entities = consumers.size();
112 if (!get_energy_consumer_info_(&consumers[0], &num_power_entities)) {
113 PERFETTO_ELOG("Failed to retrieve energy consumer info.");
114 num_power_entities = 0;
115 }
116 consumers.resize(num_power_entities);
117 return consumers;
118 }
119
GetEnergyConsumedperfetto::AndroidPowerDataSource::DynamicLibLoader120 std::vector<android_internal::EnergyEstimationBreakdown> GetEnergyConsumed() {
121 if (!get_energy_consumed_)
122 return std::vector<android_internal::EnergyEstimationBreakdown>();
123
124 std::vector<android_internal::EnergyEstimationBreakdown> energy_breakdown(
125 kMaxNumPowerEntities);
126 size_t num_power_entities = energy_breakdown.size();
127 if (!get_energy_consumed_(&energy_breakdown[0], &num_power_entities)) {
128 PERFETTO_ELOG("Failed to retrieve energy estimation breakdown.");
129 num_power_entities = 0;
130 }
131 energy_breakdown.resize(num_power_entities);
132 return energy_breakdown;
133 }
134 };
135
AndroidPowerDataSource(DataSourceConfig cfg,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)136 AndroidPowerDataSource::AndroidPowerDataSource(
137 DataSourceConfig cfg,
138 base::TaskRunner* task_runner,
139 TracingSessionID session_id,
140 std::unique_ptr<TraceWriter> writer)
141 : ProbesDataSource(session_id, &descriptor),
142 task_runner_(task_runner),
143 rail_descriptors_logged_(false),
144 energy_consumer_loggged_(false),
145 writer_(std::move(writer)),
146 weak_factory_(this) {
147 using protos::pbzero::AndroidPowerConfig;
148 AndroidPowerConfig::Decoder pcfg(cfg.android_power_config_raw());
149 poll_interval_ms_ = pcfg.battery_poll_ms();
150 rails_collection_enabled_ = pcfg.collect_power_rails();
151 energy_breakdown_collection_enabled_ =
152 pcfg.collect_energy_estimation_breakdown();
153
154 if (poll_interval_ms_ == 0)
155 poll_interval_ms_ = kDefaultPollIntervalMs;
156
157 if (poll_interval_ms_ < kMinPollIntervalMs) {
158 PERFETTO_ELOG("Battery poll interval of %" PRIu32
159 " ms is too low. Capping to %" PRIu32 " ms",
160 poll_interval_ms_, kMinPollIntervalMs);
161 poll_interval_ms_ = kMinPollIntervalMs;
162 }
163 for (auto counter = pcfg.battery_counters(); counter; ++counter) {
164 auto hal_id = android_internal::BatteryCounter::kUnspecified;
165 switch (*counter) {
166 case AndroidPowerConfig::BATTERY_COUNTER_UNSPECIFIED:
167 break;
168 case AndroidPowerConfig::BATTERY_COUNTER_CHARGE:
169 hal_id = android_internal::BatteryCounter::kCharge;
170 break;
171 case AndroidPowerConfig::BATTERY_COUNTER_CAPACITY_PERCENT:
172 hal_id = android_internal::BatteryCounter::kCapacityPercent;
173 break;
174 case AndroidPowerConfig::BATTERY_COUNTER_CURRENT:
175 hal_id = android_internal::BatteryCounter::kCurrent;
176 break;
177 case AndroidPowerConfig::BATTERY_COUNTER_CURRENT_AVG:
178 hal_id = android_internal::BatteryCounter::kCurrentAvg;
179 break;
180 }
181 PERFETTO_CHECK(static_cast<size_t>(hal_id) < counters_enabled_.size());
182 counters_enabled_.set(static_cast<size_t>(hal_id));
183 }
184 }
185
186 AndroidPowerDataSource::~AndroidPowerDataSource() = default;
187
Start()188 void AndroidPowerDataSource::Start() {
189 lib_.reset(new DynamicLibLoader());
190 Tick();
191 }
192
Tick()193 void AndroidPowerDataSource::Tick() {
194 // Post next task.
195 auto now_ms = base::GetWallTimeMs().count();
196 auto weak_this = weak_factory_.GetWeakPtr();
197 task_runner_->PostDelayedTask(
198 [weak_this] {
199 if (weak_this)
200 weak_this->Tick();
201 },
202 poll_interval_ms_ - static_cast<uint32_t>(now_ms % poll_interval_ms_));
203
204 WriteBatteryCounters();
205 WritePowerRailsData();
206 WriteEnergyEstimationBreakdown();
207 }
208
WriteBatteryCounters()209 void AndroidPowerDataSource::WriteBatteryCounters() {
210 if (counters_enabled_.none())
211 return;
212
213 auto packet = writer_->NewTracePacket();
214 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
215 auto* counters_proto = packet->set_battery();
216
217 for (size_t i = 0; i < counters_enabled_.size(); i++) {
218 if (!counters_enabled_.test(i))
219 continue;
220 auto counter = static_cast<android_internal::BatteryCounter>(i);
221 auto value = lib_->GetCounter(counter);
222 if (!value.has_value())
223 continue;
224
225 switch (counter) {
226 case android_internal::BatteryCounter::kUnspecified:
227 PERFETTO_DFATAL("Unspecified counter");
228 break;
229
230 case android_internal::BatteryCounter::kCharge:
231 counters_proto->set_charge_counter_uah(*value);
232 break;
233
234 case android_internal::BatteryCounter::kCapacityPercent:
235 counters_proto->set_capacity_percent(static_cast<float>(*value));
236 break;
237
238 case android_internal::BatteryCounter::kCurrent:
239 counters_proto->set_current_ua(*value);
240 break;
241
242 case android_internal::BatteryCounter::kCurrentAvg:
243 counters_proto->set_current_avg_ua(*value);
244 break;
245 }
246 }
247 }
248
WritePowerRailsData()249 void AndroidPowerDataSource::WritePowerRailsData() {
250 if (!rails_collection_enabled_)
251 return;
252
253 auto packet = writer_->NewTracePacket();
254 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
255 auto* rails_proto = packet->set_power_rails();
256
257 if (!rail_descriptors_logged_) {
258 // We only add the rail descriptors to the first package, to avoid logging
259 // all rail names etc. on each one.
260 rail_descriptors_logged_ = true;
261 auto rail_descriptors = lib_->GetRailDescriptors();
262 if (rail_descriptors.empty()) {
263 // No rails to collect data for. Don't try again in the next iteration.
264 rails_collection_enabled_ = false;
265 return;
266 }
267
268 for (const auto& rail_descriptor : rail_descriptors) {
269 auto* rail_desc_proto = rails_proto->add_rail_descriptor();
270 rail_desc_proto->set_index(rail_descriptor.index);
271 rail_desc_proto->set_rail_name(rail_descriptor.rail_name);
272 rail_desc_proto->set_subsys_name(rail_descriptor.subsys_name);
273 rail_desc_proto->set_sampling_rate(rail_descriptor.sampling_rate);
274 }
275 }
276
277 for (const auto& energy_data : lib_->GetRailEnergyData()) {
278 auto* data = rails_proto->add_energy_data();
279 data->set_index(energy_data.index);
280 data->set_timestamp_ms(energy_data.timestamp);
281 data->set_energy(energy_data.energy);
282 }
283 }
284
WriteEnergyEstimationBreakdown()285 void AndroidPowerDataSource::WriteEnergyEstimationBreakdown() {
286 if (!energy_breakdown_collection_enabled_)
287 return;
288 auto timestamp = static_cast<uint64_t>(base::GetBootTimeNs().count());
289
290 TraceWriter::TracePacketHandle packet;
291 protos::pbzero::AndroidEnergyEstimationBreakdown* energy_estimation_proto =
292 nullptr;
293
294 if (!energy_consumer_loggged_) {
295 energy_consumer_loggged_ = true;
296 packet = writer_->NewTracePacket();
297 energy_estimation_proto = packet->set_android_energy_estimation_breakdown();
298 auto* descriptor_proto =
299 energy_estimation_proto->set_energy_consumer_descriptor();
300 auto consumers = lib_->GetEnergyConsumerInfo();
301 for (const auto& consumer : consumers) {
302 auto* desc_proto = descriptor_proto->add_energy_consumers();
303 desc_proto->set_energy_consumer_id(consumer.energy_consumer_id);
304 desc_proto->set_ordinal(consumer.ordinal);
305 desc_proto->set_type(consumer.type);
306 desc_proto->set_name(consumer.name);
307 }
308 }
309
310 auto energy_breakdowns = lib_->GetEnergyConsumed();
311 for (const auto& breakdown : energy_breakdowns) {
312 if (breakdown.uid == android_internal::ALL_UIDS_FOR_CONSUMER) {
313 // Finalize packet before calling NewTracePacket.
314 if (packet) {
315 packet->Finalize();
316 }
317 packet = writer_->NewTracePacket();
318 packet->set_timestamp(timestamp);
319 energy_estimation_proto =
320 packet->set_android_energy_estimation_breakdown();
321 energy_estimation_proto->set_energy_consumer_id(
322 breakdown.energy_consumer_id);
323 energy_estimation_proto->set_energy_uws(breakdown.energy_uws);
324 } else {
325 PERFETTO_CHECK(energy_estimation_proto != nullptr);
326 auto* uid_breakdown_proto =
327 energy_estimation_proto->add_per_uid_breakdown();
328 uid_breakdown_proto->set_uid(breakdown.uid);
329 uid_breakdown_proto->set_energy_uws(breakdown.energy_uws);
330 }
331 }
332 }
333
Flush(FlushRequestID,std::function<void ()> callback)334 void AndroidPowerDataSource::Flush(FlushRequestID,
335 std::function<void()> callback) {
336 writer_->Flush(callback);
337 }
338
339 } // namespace perfetto
340