xref: /aosp_15_r20/external/perfetto/src/traced/probes/power/android_power_data_source.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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 <optional>
20 #include <vector>
21 
22 #include "perfetto/base/logging.h"
23 #include "perfetto/base/task_runner.h"
24 #include "perfetto/base/time.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/android_entity_state_residency.pbzero.h"
37 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
38 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
39 #include "protos/perfetto/trace/trace_packet.pbzero.h"
40 
41 namespace perfetto {
42 
43 namespace {
44 constexpr uint32_t kMinPollIntervalMs = 100;
45 constexpr uint32_t kDefaultPollIntervalMs = 1000;
46 constexpr size_t kMaxNumRails = 32;
47 constexpr size_t kMaxNumEnergyConsumer = 32;
48 constexpr size_t kMaxNumPowerEntities = 1024;
49 }  // namespace
50 
51 // static
52 const ProbesDataSource::Descriptor AndroidPowerDataSource::descriptor = {
53     /*name*/ "android.power",
54     /*flags*/ Descriptor::kHandlesIncrementalState,
55     /*fill_descriptor_func*/ nullptr,
56 };
57 
58 // Dynamically loads the libperfetto_android_internal.so library which
59 // allows to proxy calls to android hwbinder in in-tree builds.
60 struct AndroidPowerDataSource::DynamicLibLoader {
61   PERFETTO_LAZY_LOAD(android_internal::GetBatteryCounter, get_battery_counter_);
62   PERFETTO_LAZY_LOAD(android_internal::GetAvailableRails, get_available_rails_);
63   PERFETTO_LAZY_LOAD(android_internal::GetRailEnergyData,
64                      get_rail_energy_data_);
65   PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumerInfo,
66                      get_energy_consumer_info_);
67   PERFETTO_LAZY_LOAD(android_internal::GetEnergyConsumed, get_energy_consumed_);
68   PERFETTO_LAZY_LOAD(android_internal::GetPowerEntityStates,
69                      get_power_entity_states_);
70   PERFETTO_LAZY_LOAD(android_internal::GetPowerEntityStateResidency,
71                      get_power_entity_state_residency_);
72 
GetCounterperfetto::AndroidPowerDataSource::DynamicLibLoader73   std::optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
74     if (!get_battery_counter_)
75       return std::nullopt;
76     int64_t value = 0;
77     if (get_battery_counter_(counter, &value))
78       return std::make_optional(value);
79     return std::nullopt;
80   }
81 
GetRailDescriptorsperfetto::AndroidPowerDataSource::DynamicLibLoader82   std::vector<android_internal::RailDescriptor> GetRailDescriptors() {
83     if (!get_available_rails_)
84       return std::vector<android_internal::RailDescriptor>();
85 
86     std::vector<android_internal::RailDescriptor> rail_descriptors(
87         kMaxNumRails);
88     size_t num_rails = rail_descriptors.size();
89     if (!get_available_rails_(&rail_descriptors[0], &num_rails)) {
90       PERFETTO_ELOG("Failed to retrieve rail descriptors.");
91       num_rails = 0;
92     }
93     rail_descriptors.resize(num_rails);
94     return rail_descriptors;
95   }
96 
GetRailEnergyDataperfetto::AndroidPowerDataSource::DynamicLibLoader97   std::vector<android_internal::RailEnergyData> GetRailEnergyData() {
98     if (!get_rail_energy_data_)
99       return std::vector<android_internal::RailEnergyData>();
100 
101     std::vector<android_internal::RailEnergyData> energy_data(kMaxNumRails);
102     size_t num_rails = energy_data.size();
103     if (!get_rail_energy_data_(&energy_data[0], &num_rails)) {
104       PERFETTO_ELOG("Failed to retrieve rail energy data.");
105       num_rails = 0;
106     }
107     energy_data.resize(num_rails);
108     return energy_data;
109   }
110 
GetEnergyConsumerInfoperfetto::AndroidPowerDataSource::DynamicLibLoader111   std::vector<android_internal::EnergyConsumerInfo> GetEnergyConsumerInfo() {
112     if (!get_energy_consumer_info_)
113       return std::vector<android_internal::EnergyConsumerInfo>();
114 
115     std::vector<android_internal::EnergyConsumerInfo> consumers(
116         kMaxNumEnergyConsumer);
117     size_t num_power_entities = consumers.size();
118     if (!get_energy_consumer_info_(&consumers[0], &num_power_entities)) {
119       PERFETTO_ELOG("Failed to retrieve energy consumer info.");
120       num_power_entities = 0;
121     }
122     consumers.resize(num_power_entities);
123     return consumers;
124   }
125 
GetEnergyConsumedperfetto::AndroidPowerDataSource::DynamicLibLoader126   std::vector<android_internal::EnergyEstimationBreakdown> GetEnergyConsumed() {
127     if (!get_energy_consumed_)
128       return std::vector<android_internal::EnergyEstimationBreakdown>();
129 
130     std::vector<android_internal::EnergyEstimationBreakdown> energy_breakdown(
131         kMaxNumPowerEntities);
132     size_t num_power_entities = energy_breakdown.size();
133     if (!get_energy_consumed_(&energy_breakdown[0], &num_power_entities)) {
134       PERFETTO_ELOG("Failed to retrieve energy estimation breakdown.");
135       num_power_entities = 0;
136     }
137     energy_breakdown.resize(num_power_entities);
138     return energy_breakdown;
139   }
140 
GetPowerEntityStatesperfetto::AndroidPowerDataSource::DynamicLibLoader141   std::vector<android_internal::PowerEntityState> GetPowerEntityStates() {
142     if (!get_power_entity_states_)
143       return std::vector<android_internal::PowerEntityState>();
144 
145     std::vector<android_internal::PowerEntityState> entity(
146         kMaxNumPowerEntities);
147     size_t num_power_entities = entity.size();
148     if (!get_power_entity_states_(&entity[0], &num_power_entities)) {
149       PERFETTO_ELOG("Failed to retrieve power entities.");
150       num_power_entities = 0;
151     }
152     entity.resize(num_power_entities);
153     return entity;
154   }
155 
156   std::vector<android_internal::PowerEntityStateResidency>
GetPowerEntityStateResidencyperfetto::AndroidPowerDataSource::DynamicLibLoader157   GetPowerEntityStateResidency() {
158     if (!get_power_entity_state_residency_)
159       return std::vector<android_internal::PowerEntityStateResidency>();
160 
161     std::vector<android_internal::PowerEntityStateResidency> entity(
162         kMaxNumPowerEntities);
163     size_t num_power_entities = entity.size();
164     if (!get_power_entity_state_residency_(&entity[0], &num_power_entities)) {
165       PERFETTO_ELOG("Failed to retrieve power entities.");
166       num_power_entities = 0;
167     }
168     entity.resize(num_power_entities);
169     return entity;
170   }
171 };
172 
AndroidPowerDataSource(DataSourceConfig cfg,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)173 AndroidPowerDataSource::AndroidPowerDataSource(
174     DataSourceConfig cfg,
175     base::TaskRunner* task_runner,
176     TracingSessionID session_id,
177     std::unique_ptr<TraceWriter> writer)
178     : ProbesDataSource(session_id, &descriptor),
179       task_runner_(task_runner),
180       writer_(std::move(writer)),
181       weak_factory_(this) {
182   using protos::pbzero::AndroidPowerConfig;
183   AndroidPowerConfig::Decoder pcfg(cfg.android_power_config_raw());
184   poll_interval_ms_ = pcfg.battery_poll_ms();
185   rails_collection_enabled_ = pcfg.collect_power_rails();
186   energy_breakdown_collection_enabled_ =
187       pcfg.collect_energy_estimation_breakdown();
188   entity_state_residency_collection_enabled_ =
189       pcfg.collect_entity_state_residency();
190 
191   if (poll_interval_ms_ == 0)
192     poll_interval_ms_ = kDefaultPollIntervalMs;
193 
194   if (poll_interval_ms_ < kMinPollIntervalMs) {
195     PERFETTO_ELOG("Battery poll interval of %" PRIu32
196                   " ms is too low. Capping to %" PRIu32 " ms",
197                   poll_interval_ms_, kMinPollIntervalMs);
198     poll_interval_ms_ = kMinPollIntervalMs;
199   }
200   for (auto counter = pcfg.battery_counters(); counter; ++counter) {
201     auto hal_id = android_internal::BatteryCounter::kUnspecified;
202     switch (*counter) {
203       case AndroidPowerConfig::BATTERY_COUNTER_UNSPECIFIED:
204         break;
205       case AndroidPowerConfig::BATTERY_COUNTER_CHARGE:
206         hal_id = android_internal::BatteryCounter::kCharge;
207         break;
208       case AndroidPowerConfig::BATTERY_COUNTER_CAPACITY_PERCENT:
209         hal_id = android_internal::BatteryCounter::kCapacityPercent;
210         break;
211       case AndroidPowerConfig::BATTERY_COUNTER_CURRENT:
212         hal_id = android_internal::BatteryCounter::kCurrent;
213         break;
214       case AndroidPowerConfig::BATTERY_COUNTER_CURRENT_AVG:
215         hal_id = android_internal::BatteryCounter::kCurrentAvg;
216         break;
217       case AndroidPowerConfig::BATTERY_COUNTER_VOLTAGE:
218         hal_id = android_internal::BatteryCounter::kVoltage;
219         break;
220     }
221     PERFETTO_CHECK(static_cast<size_t>(hal_id) < counters_enabled_.size());
222     counters_enabled_.set(static_cast<size_t>(hal_id));
223   }
224 }
225 
226 AndroidPowerDataSource::~AndroidPowerDataSource() = default;
227 
Start()228 void AndroidPowerDataSource::Start() {
229   lib_.reset(new DynamicLibLoader());
230   Tick();
231 }
232 
Tick()233 void AndroidPowerDataSource::Tick() {
234   // Post next task.
235   auto now_ms = base::GetWallTimeMs().count();
236   auto weak_this = weak_factory_.GetWeakPtr();
237   task_runner_->PostDelayedTask(
238       [weak_this] {
239         if (weak_this)
240           weak_this->Tick();
241       },
242       poll_interval_ms_ - static_cast<uint32_t>(now_ms % poll_interval_ms_));
243 
244   if (should_emit_descriptors_) {
245     // We write incremental state cleared in its own packet to avoid the subtle
246     // code we'd need if we were to set this on the first enabled data source.
247     auto packet = writer_->NewTracePacket();
248     packet->set_sequence_flags(
249         protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
250   }
251 
252   WriteBatteryCounters();
253   WritePowerRailsData();
254   WriteEnergyEstimationBreakdown();
255   WriteEntityStateResidency();
256 
257   should_emit_descriptors_ = false;
258 }
259 
WriteBatteryCounters()260 void AndroidPowerDataSource::WriteBatteryCounters() {
261   if (counters_enabled_.none())
262     return;
263 
264   auto packet = writer_->NewTracePacket();
265   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
266   auto* counters_proto = packet->set_battery();
267 
268   for (size_t i = 0; i < counters_enabled_.size(); i++) {
269     if (!counters_enabled_.test(i))
270       continue;
271     auto counter = static_cast<android_internal::BatteryCounter>(i);
272     auto value = lib_->GetCounter(counter);
273     if (!value.has_value())
274       continue;
275 
276     switch (counter) {
277       case android_internal::BatteryCounter::kUnspecified:
278         PERFETTO_DFATAL("Unspecified counter");
279         break;
280 
281       case android_internal::BatteryCounter::kCharge:
282         counters_proto->set_charge_counter_uah(*value);
283         break;
284 
285       case android_internal::BatteryCounter::kCapacityPercent:
286         counters_proto->set_capacity_percent(static_cast<float>(*value));
287         break;
288 
289       case android_internal::BatteryCounter::kCurrent:
290         counters_proto->set_current_ua(*value);
291         break;
292 
293       case android_internal::BatteryCounter::kCurrentAvg:
294         counters_proto->set_current_avg_ua(*value);
295         break;
296 
297       case android_internal::BatteryCounter::kVoltage:
298         counters_proto->set_voltage_uv(*value);
299         break;
300     }
301   }
302 }
303 
WritePowerRailsData()304 void AndroidPowerDataSource::WritePowerRailsData() {
305   if (!rails_collection_enabled_)
306     return;
307 
308   auto packet = writer_->NewTracePacket();
309   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
310   packet->set_sequence_flags(
311       protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
312 
313   auto* rails_proto = packet->set_power_rails();
314   if (should_emit_descriptors_) {
315     auto rail_descriptors = lib_->GetRailDescriptors();
316     if (rail_descriptors.empty()) {
317       // No rails to collect data for. Don't try again.
318       rails_collection_enabled_ = false;
319       return;
320     }
321 
322     for (const auto& rail_descriptor : rail_descriptors) {
323       auto* rail_desc_proto = rails_proto->add_rail_descriptor();
324       rail_desc_proto->set_index(rail_descriptor.index);
325       rail_desc_proto->set_rail_name(rail_descriptor.rail_name);
326       rail_desc_proto->set_subsys_name(rail_descriptor.subsys_name);
327       rail_desc_proto->set_sampling_rate(rail_descriptor.sampling_rate);
328     }
329   }
330 
331   for (const auto& energy_data : lib_->GetRailEnergyData()) {
332     auto* data = rails_proto->add_energy_data();
333     data->set_index(energy_data.index);
334     data->set_timestamp_ms(energy_data.timestamp);
335     data->set_energy(energy_data.energy);
336   }
337 }
338 
WriteEnergyEstimationBreakdown()339 void AndroidPowerDataSource::WriteEnergyEstimationBreakdown() {
340   if (!energy_breakdown_collection_enabled_)
341     return;
342   auto timestamp = static_cast<uint64_t>(base::GetBootTimeNs().count());
343 
344   TraceWriter::TracePacketHandle packet;
345   protos::pbzero::AndroidEnergyEstimationBreakdown* energy_estimation_proto =
346       nullptr;
347 
348   if (should_emit_descriptors_) {
349     packet = writer_->NewTracePacket();
350     energy_estimation_proto = packet->set_android_energy_estimation_breakdown();
351     auto* descriptor_proto =
352         energy_estimation_proto->set_energy_consumer_descriptor();
353     auto consumers = lib_->GetEnergyConsumerInfo();
354     for (const auto& consumer : consumers) {
355       auto* desc_proto = descriptor_proto->add_energy_consumers();
356       desc_proto->set_energy_consumer_id(consumer.energy_consumer_id);
357       desc_proto->set_ordinal(consumer.ordinal);
358       desc_proto->set_type(consumer.type);
359       desc_proto->set_name(consumer.name);
360     }
361   }
362 
363   auto energy_breakdowns = lib_->GetEnergyConsumed();
364   for (const auto& breakdown : energy_breakdowns) {
365     if (breakdown.uid == android_internal::ALL_UIDS_FOR_CONSUMER) {
366       // Finalize packet before calling NewTracePacket.
367       if (packet) {
368         packet->Finalize();
369       }
370       packet = writer_->NewTracePacket();
371       packet->set_timestamp(timestamp);
372       packet->set_sequence_flags(
373           protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
374 
375       energy_estimation_proto =
376           packet->set_android_energy_estimation_breakdown();
377       energy_estimation_proto->set_energy_consumer_id(
378           breakdown.energy_consumer_id);
379       energy_estimation_proto->set_energy_uws(breakdown.energy_uws);
380     } else {
381       PERFETTO_CHECK(energy_estimation_proto != nullptr);
382       auto* uid_breakdown_proto =
383           energy_estimation_proto->add_per_uid_breakdown();
384       uid_breakdown_proto->set_uid(breakdown.uid);
385       uid_breakdown_proto->set_energy_uws(breakdown.energy_uws);
386     }
387   }
388 }
389 
WriteEntityStateResidency()390 void AndroidPowerDataSource::WriteEntityStateResidency() {
391   if (!entity_state_residency_collection_enabled_)
392     return;
393 
394   auto packet = writer_->NewTracePacket();
395   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
396   packet->set_sequence_flags(
397       protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
398 
399   auto* outer_proto = packet->set_entity_state_residency();
400   if (should_emit_descriptors_) {
401     auto entity_states = lib_->GetPowerEntityStates();
402     if (entity_states.empty()) {
403       // No entities to collect data for. Don't try again.
404       entity_state_residency_collection_enabled_ = false;
405       return;
406     }
407 
408     for (const auto& entity_state : entity_states) {
409       auto* entity_state_proto = outer_proto->add_power_entity_state();
410       entity_state_proto->set_entity_index(entity_state.entity_id);
411       entity_state_proto->set_state_index(entity_state.state_id);
412       entity_state_proto->set_entity_name(entity_state.entity_name);
413       entity_state_proto->set_state_name(entity_state.state_name);
414     }
415   }
416 
417   for (const auto& residency_data : lib_->GetPowerEntityStateResidency()) {
418     auto* data = outer_proto->add_residency();
419     data->set_entity_index(residency_data.entity_id);
420     data->set_state_index(residency_data.state_id);
421     data->set_total_time_in_state_ms(residency_data.total_time_in_state_ms);
422     data->set_total_state_entry_count(residency_data.total_state_entry_count);
423     data->set_last_entry_timestamp_ms(residency_data.last_entry_timestamp_ms);
424   }
425 }
426 
Flush(FlushRequestID,std::function<void ()> callback)427 void AndroidPowerDataSource::Flush(FlushRequestID,
428                                    std::function<void()> callback) {
429   writer_->Flush(callback);
430 }
431 
ClearIncrementalState()432 void AndroidPowerDataSource::ClearIncrementalState() {
433   should_emit_descriptors_ = true;
434 }
435 
436 }  // namespace perfetto
437