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::kHandlesIncrementalState,
54 /*fill_descriptor_func*/ nullptr,
55 };
56
57 // Dynamically loads the libperfetto_android_internal.so library which
58 // allows to proxy calls to android hwbinder in in-tree builds.
59 struct AndroidPowerDataSource::DynamicLibLoader {
60 PERFETTO_LAZY_LOAD(android_internal::GetBatteryCounter, get_battery_counter_);
61 PERFETTO_LAZY_LOAD(android_internal::GetAvailableRails, get_available_rails_);
62 PERFETTO_LAZY_LOAD(android_internal::GetRailEnergyData,
63 get_rail_energy_data_);
64 PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumerInfo,
65 get_energy_consumer_info_);
66 PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumed, get_energy_consumed_);
67
GetCounterperfetto::AndroidPowerDataSource::DynamicLibLoader68 base::Optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
69 if (!get_battery_counter_)
70 return base::nullopt;
71 int64_t value = 0;
72 if (get_battery_counter_(counter, &value))
73 return base::make_optional(value);
74 return base::nullopt;
75 }
76
GetRailDescriptorsperfetto::AndroidPowerDataSource::DynamicLibLoader77 std::vector<android_internal::RailDescriptor> GetRailDescriptors() {
78 if (!get_available_rails_)
79 return std::vector<android_internal::RailDescriptor>();
80
81 std::vector<android_internal::RailDescriptor> rail_descriptors(
82 kMaxNumRails);
83 size_t num_rails = rail_descriptors.size();
84 if (!get_available_rails_(&rail_descriptors[0], &num_rails)) {
85 PERFETTO_ELOG("Failed to retrieve rail descriptors.");
86 num_rails = 0;
87 }
88 rail_descriptors.resize(num_rails);
89 return rail_descriptors;
90 }
91
GetRailEnergyDataperfetto::AndroidPowerDataSource::DynamicLibLoader92 std::vector<android_internal::RailEnergyData> GetRailEnergyData() {
93 if (!get_rail_energy_data_)
94 return std::vector<android_internal::RailEnergyData>();
95
96 std::vector<android_internal::RailEnergyData> energy_data(kMaxNumRails);
97 size_t num_rails = energy_data.size();
98 if (!get_rail_energy_data_(&energy_data[0], &num_rails)) {
99 PERFETTO_ELOG("Failed to retrieve rail energy data.");
100 num_rails = 0;
101 }
102 energy_data.resize(num_rails);
103 return energy_data;
104 }
105
GetEnergyConsumerInfoperfetto::AndroidPowerDataSource::DynamicLibLoader106 std::vector<android_internal::EnergyConsumerInfo> GetEnergyConsumerInfo() {
107 if (!get_energy_consumer_info_)
108 return std::vector<android_internal::EnergyConsumerInfo>();
109
110 std::vector<android_internal::EnergyConsumerInfo> consumers(
111 kMaxNumEnergyConsumer);
112 size_t num_power_entities = consumers.size();
113 if (!get_energy_consumer_info_(&consumers[0], &num_power_entities)) {
114 PERFETTO_ELOG("Failed to retrieve energy consumer info.");
115 num_power_entities = 0;
116 }
117 consumers.resize(num_power_entities);
118 return consumers;
119 }
120
GetEnergyConsumedperfetto::AndroidPowerDataSource::DynamicLibLoader121 std::vector<android_internal::EnergyEstimationBreakdown> GetEnergyConsumed() {
122 if (!get_energy_consumed_)
123 return std::vector<android_internal::EnergyEstimationBreakdown>();
124
125 std::vector<android_internal::EnergyEstimationBreakdown> energy_breakdown(
126 kMaxNumPowerEntities);
127 size_t num_power_entities = energy_breakdown.size();
128 if (!get_energy_consumed_(&energy_breakdown[0], &num_power_entities)) {
129 PERFETTO_ELOG("Failed to retrieve energy estimation breakdown.");
130 num_power_entities = 0;
131 }
132 energy_breakdown.resize(num_power_entities);
133 return energy_breakdown;
134 }
135 };
136
AndroidPowerDataSource(DataSourceConfig cfg,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)137 AndroidPowerDataSource::AndroidPowerDataSource(
138 DataSourceConfig cfg,
139 base::TaskRunner* task_runner,
140 TracingSessionID session_id,
141 std::unique_ptr<TraceWriter> writer)
142 : ProbesDataSource(session_id, &descriptor),
143 task_runner_(task_runner),
144 writer_(std::move(writer)),
145 weak_factory_(this) {
146 using protos::pbzero::AndroidPowerConfig;
147 AndroidPowerConfig::Decoder pcfg(cfg.android_power_config_raw());
148 poll_interval_ms_ = pcfg.battery_poll_ms();
149 rails_collection_enabled_ = pcfg.collect_power_rails();
150 energy_breakdown_collection_enabled_ =
151 pcfg.collect_energy_estimation_breakdown();
152
153 if (poll_interval_ms_ == 0)
154 poll_interval_ms_ = kDefaultPollIntervalMs;
155
156 if (poll_interval_ms_ < kMinPollIntervalMs) {
157 PERFETTO_ELOG("Battery poll interval of %" PRIu32
158 " ms is too low. Capping to %" PRIu32 " ms",
159 poll_interval_ms_, kMinPollIntervalMs);
160 poll_interval_ms_ = kMinPollIntervalMs;
161 }
162 for (auto counter = pcfg.battery_counters(); counter; ++counter) {
163 auto hal_id = android_internal::BatteryCounter::kUnspecified;
164 switch (*counter) {
165 case AndroidPowerConfig::BATTERY_COUNTER_UNSPECIFIED:
166 break;
167 case AndroidPowerConfig::BATTERY_COUNTER_CHARGE:
168 hal_id = android_internal::BatteryCounter::kCharge;
169 break;
170 case AndroidPowerConfig::BATTERY_COUNTER_CAPACITY_PERCENT:
171 hal_id = android_internal::BatteryCounter::kCapacityPercent;
172 break;
173 case AndroidPowerConfig::BATTERY_COUNTER_CURRENT:
174 hal_id = android_internal::BatteryCounter::kCurrent;
175 break;
176 case AndroidPowerConfig::BATTERY_COUNTER_CURRENT_AVG:
177 hal_id = android_internal::BatteryCounter::kCurrentAvg;
178 break;
179 }
180 PERFETTO_CHECK(static_cast<size_t>(hal_id) < counters_enabled_.size());
181 counters_enabled_.set(static_cast<size_t>(hal_id));
182 }
183 }
184
185 AndroidPowerDataSource::~AndroidPowerDataSource() = default;
186
Start()187 void AndroidPowerDataSource::Start() {
188 lib_.reset(new DynamicLibLoader());
189 Tick();
190 }
191
Tick()192 void AndroidPowerDataSource::Tick() {
193 // Post next task.
194 auto now_ms = base::GetWallTimeMs().count();
195 auto weak_this = weak_factory_.GetWeakPtr();
196 task_runner_->PostDelayedTask(
197 [weak_this] {
198 if (weak_this)
199 weak_this->Tick();
200 },
201 poll_interval_ms_ - static_cast<uint32_t>(now_ms % poll_interval_ms_));
202
203 if (should_emit_descriptors_) {
204 // We write incremental state cleared in its own packet to avoid the subtle
205 // code we'd need if we were to set this on the first enabled data source.
206 auto packet = writer_->NewTracePacket();
207 packet->set_sequence_flags(
208 protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
209 }
210
211 WriteBatteryCounters();
212 WritePowerRailsData();
213 WriteEnergyEstimationBreakdown();
214
215 should_emit_descriptors_ = false;
216 }
217
WriteBatteryCounters()218 void AndroidPowerDataSource::WriteBatteryCounters() {
219 if (counters_enabled_.none())
220 return;
221
222 auto packet = writer_->NewTracePacket();
223 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
224 auto* counters_proto = packet->set_battery();
225
226 for (size_t i = 0; i < counters_enabled_.size(); i++) {
227 if (!counters_enabled_.test(i))
228 continue;
229 auto counter = static_cast<android_internal::BatteryCounter>(i);
230 auto value = lib_->GetCounter(counter);
231 if (!value.has_value())
232 continue;
233
234 switch (counter) {
235 case android_internal::BatteryCounter::kUnspecified:
236 PERFETTO_DFATAL("Unspecified counter");
237 break;
238
239 case android_internal::BatteryCounter::kCharge:
240 counters_proto->set_charge_counter_uah(*value);
241 break;
242
243 case android_internal::BatteryCounter::kCapacityPercent:
244 counters_proto->set_capacity_percent(static_cast<float>(*value));
245 break;
246
247 case android_internal::BatteryCounter::kCurrent:
248 counters_proto->set_current_ua(*value);
249 break;
250
251 case android_internal::BatteryCounter::kCurrentAvg:
252 counters_proto->set_current_avg_ua(*value);
253 break;
254 }
255 }
256 }
257
WritePowerRailsData()258 void AndroidPowerDataSource::WritePowerRailsData() {
259 if (!rails_collection_enabled_)
260 return;
261
262 auto packet = writer_->NewTracePacket();
263 packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
264 packet->set_sequence_flags(
265 protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
266
267 auto* rails_proto = packet->set_power_rails();
268 if (should_emit_descriptors_) {
269 auto rail_descriptors = lib_->GetRailDescriptors();
270 if (rail_descriptors.empty()) {
271 // No rails to collect data for. Don't try again.
272 rails_collection_enabled_ = false;
273 return;
274 }
275
276 for (const auto& rail_descriptor : rail_descriptors) {
277 auto* rail_desc_proto = rails_proto->add_rail_descriptor();
278 rail_desc_proto->set_index(rail_descriptor.index);
279 rail_desc_proto->set_rail_name(rail_descriptor.rail_name);
280 rail_desc_proto->set_subsys_name(rail_descriptor.subsys_name);
281 rail_desc_proto->set_sampling_rate(rail_descriptor.sampling_rate);
282 }
283 }
284
285 for (const auto& energy_data : lib_->GetRailEnergyData()) {
286 auto* data = rails_proto->add_energy_data();
287 data->set_index(energy_data.index);
288 data->set_timestamp_ms(energy_data.timestamp);
289 data->set_energy(energy_data.energy);
290 }
291 }
292
WriteEnergyEstimationBreakdown()293 void AndroidPowerDataSource::WriteEnergyEstimationBreakdown() {
294 if (!energy_breakdown_collection_enabled_)
295 return;
296 auto timestamp = static_cast<uint64_t>(base::GetBootTimeNs().count());
297
298 TraceWriter::TracePacketHandle packet;
299 protos::pbzero::AndroidEnergyEstimationBreakdown* energy_estimation_proto =
300 nullptr;
301
302 if (should_emit_descriptors_) {
303 packet = writer_->NewTracePacket();
304 energy_estimation_proto = packet->set_android_energy_estimation_breakdown();
305 auto* descriptor_proto =
306 energy_estimation_proto->set_energy_consumer_descriptor();
307 auto consumers = lib_->GetEnergyConsumerInfo();
308 for (const auto& consumer : consumers) {
309 auto* desc_proto = descriptor_proto->add_energy_consumers();
310 desc_proto->set_energy_consumer_id(consumer.energy_consumer_id);
311 desc_proto->set_ordinal(consumer.ordinal);
312 desc_proto->set_type(consumer.type);
313 desc_proto->set_name(consumer.name);
314 }
315 }
316
317 auto energy_breakdowns = lib_->GetEnergyConsumed();
318 for (const auto& breakdown : energy_breakdowns) {
319 if (breakdown.uid == android_internal::ALL_UIDS_FOR_CONSUMER) {
320 // Finalize packet before calling NewTracePacket.
321 if (packet) {
322 packet->Finalize();
323 }
324 packet = writer_->NewTracePacket();
325 packet->set_timestamp(timestamp);
326 packet->set_sequence_flags(
327 protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
328
329 energy_estimation_proto =
330 packet->set_android_energy_estimation_breakdown();
331 energy_estimation_proto->set_energy_consumer_id(
332 breakdown.energy_consumer_id);
333 energy_estimation_proto->set_energy_uws(breakdown.energy_uws);
334 } else {
335 PERFETTO_CHECK(energy_estimation_proto != nullptr);
336 auto* uid_breakdown_proto =
337 energy_estimation_proto->add_per_uid_breakdown();
338 uid_breakdown_proto->set_uid(breakdown.uid);
339 uid_breakdown_proto->set_energy_uws(breakdown.energy_uws);
340 }
341 }
342 }
343
Flush(FlushRequestID,std::function<void ()> callback)344 void AndroidPowerDataSource::Flush(FlushRequestID,
345 std::function<void()> callback) {
346 writer_->Flush(callback);
347 }
348
ClearIncrementalState()349 void AndroidPowerDataSource::ClearIncrementalState() {
350 should_emit_descriptors_ = true;
351 }
352
353 } // namespace perfetto
354