• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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/android_kernel_wakelocks/android_kernel_wakelocks_data_source.h"
18 
19 #include <cctype>
20 #include <optional>
21 #include <vector>
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/scoped_file.h"
27 #include "perfetto/ext/tracing/core/trace_packet.h"
28 #include "perfetto/ext/tracing/core/trace_writer.h"
29 #include "perfetto/tracing/core/data_source_config.h"
30 #include "src/android_internal/lazy_library_loader.h"
31 #include "src/android_internal/suspend_control_service.h"
32 
33 #include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
34 #include "protos/perfetto/config/android/kernel_wakelocks_config.pbzero.h"
35 #include "protos/perfetto/trace/android/kernel_wakelock_data.pbzero.h"
36 #include "protos/perfetto/trace/trace_packet.pbzero.h"
37 
38 namespace perfetto {
39 
40 struct KernelWakelockInfo {
41   uint32_t id;
42   uint64_t last_value;
43 };
44 
45 namespace {
46 constexpr uint32_t kMinPollIntervalMs = 100;
47 constexpr uint32_t kDefaultPollIntervalMs = 1000;
48 constexpr size_t kMaxNumWakelocks = 1024;
49 }  // namespace
50 
51 // static
52 const ProbesDataSource::Descriptor
53     AndroidKernelWakelocksDataSource::descriptor = {
54         /*name*/ "android.kernel_wakelocks",
55         /*flags*/ Descriptor::kHandlesIncrementalState,
56         /*fill_descriptor_func*/ nullptr,
57 };
58 
59 // Dynamically loads the libperfetto_android_internal.so library which
60 // allows to proxy calls to android hwbinder in in-tree builds.
61 struct AndroidKernelWakelocksDataSource::DynamicLibLoader {
62   PERFETTO_LAZY_LOAD(android_internal::GetKernelWakelocks,
63                      get_kernel_wakelocks_);
64 
GetKernelWakelocksperfetto::AndroidKernelWakelocksDataSource::DynamicLibLoader65   std::vector<android_internal::KernelWakelock> GetKernelWakelocks() {
66     if (!get_kernel_wakelocks_)
67       return std::vector<android_internal::KernelWakelock>();
68 
69     std::vector<android_internal::KernelWakelock> wakelock(kMaxNumWakelocks);
70     size_t num_wakelocks = wakelock.size();
71     if (!get_kernel_wakelocks_(&wakelock[0], &num_wakelocks)) {
72       PERFETTO_ELOG("Failed to retrieve kernel wakelocks.");
73       num_wakelocks = 0;
74     }
75     wakelock.resize(num_wakelocks);
76     return wakelock;
77   }
78 };
79 
AndroidKernelWakelocksDataSource(const DataSourceConfig & cfg,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)80 AndroidKernelWakelocksDataSource::AndroidKernelWakelocksDataSource(
81     const DataSourceConfig& cfg,
82     base::TaskRunner* task_runner,
83     TracingSessionID session_id,
84     std::unique_ptr<TraceWriter> writer)
85     : ProbesDataSource(session_id, &descriptor),
86       task_runner_(task_runner),
87       writer_(std::move(writer)),
88       weak_factory_(this) {
89   using protos::pbzero::KernelWakelocksConfig;
90   KernelWakelocksConfig::Decoder kcfg(cfg.kernel_wakelocks_config_raw());
91   poll_interval_ms_ = kcfg.poll_ms();
92 
93   if (poll_interval_ms_ == 0)
94     poll_interval_ms_ = kDefaultPollIntervalMs;
95 
96   if (poll_interval_ms_ < kMinPollIntervalMs) {
97     PERFETTO_ELOG("Kernel wakelock poll interval of %" PRIu32
98                   " ms is too low. Capping to %" PRIu32 " ms",
99                   poll_interval_ms_, kMinPollIntervalMs);
100     poll_interval_ms_ = kMinPollIntervalMs;
101   }
102 }
103 
104 AndroidKernelWakelocksDataSource::~AndroidKernelWakelocksDataSource() = default;
105 
Start()106 void AndroidKernelWakelocksDataSource::Start() {
107   lib_.reset(new DynamicLibLoader());
108   Tick();
109 }
110 
Tick()111 void AndroidKernelWakelocksDataSource::Tick() {
112   // Post next task.
113   auto now_ms = base::GetWallTimeMs().count();
114   auto weak_this = weak_factory_.GetWeakPtr();
115   task_runner_->PostDelayedTask(
116       [weak_this] {
117         if (weak_this)
118           weak_this->Tick();
119       },
120       poll_interval_ms_ - static_cast<uint32_t>(now_ms % poll_interval_ms_));
121 
122   WriteKernelWakelocks();
123 }
124 
WriteKernelWakelocks()125 void AndroidKernelWakelocksDataSource::WriteKernelWakelocks() {
126   auto packet = writer_->NewTracePacket();
127   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
128 
129   if (wakelocks_.size() == 0) {
130     packet->set_sequence_flags(
131         protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED |
132         protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
133   } else {
134     packet->set_sequence_flags(
135         protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
136   }
137 
138   // Some native wakelocks can have duplicated names; merge them before
139   // calculating deltas.
140   base::FlatHashMap<std::string, uint64_t> totals;
141 
142   auto* proto = packet->set_kernel_wakelock_data();
143 
144   std::vector<android_internal::KernelWakelock> wakelocks =
145       lib_->GetKernelWakelocks();
146   for (const auto& wakelock : wakelocks) {
147     std::string name = std::string(wakelock.wakelock_name);
148     totals[name] += wakelock.total_time_ms;
149 
150     auto [info, inserted] = wakelocks_.Insert(name, KernelWakelockInfo{});
151     if (inserted) {
152       info->id = ++next_id_;
153       info->last_value = 0;
154       auto* wakelock_descriptor = proto->add_wakelock();
155       wakelock_descriptor->set_wakelock_id(info->id);
156       wakelock_descriptor->set_wakelock_name(name);
157       wakelock_descriptor->set_wakelock_type(
158           wakelock.is_kernel ? protos::pbzero::KernelWakelockData::Wakelock::
159                                    Type::WAKELOCK_TYPE_KERNEL
160                              : protos::pbzero::KernelWakelockData::Wakelock::
161                                    Type::WAKELOCK_TYPE_NATIVE);
162     }
163   }
164 
165   protozero::PackedVarInt wakelock_id;
166   protozero::PackedVarInt time_held_millis;
167 
168   for (auto it = totals.GetIterator(); it; ++it) {
169     KernelWakelockInfo* info = wakelocks_.Find(it.key());
170 
171     uint64_t total = it.value();
172     if (it.value() != info->last_value) {
173       uint64_t last_value = info->last_value;
174       info->last_value = total;
175       wakelock_id.Append(info->id);
176       time_held_millis.Append(total - last_value);
177     }
178   }
179 
180   proto->set_wakelock_id(wakelock_id);
181   proto->set_time_held_millis(time_held_millis);
182 }
183 
Flush(FlushRequestID,std::function<void ()> callback)184 void AndroidKernelWakelocksDataSource::Flush(FlushRequestID,
185                                              std::function<void()> callback) {
186   writer_->Flush(callback);
187 }
188 
ClearIncrementalState()189 void AndroidKernelWakelocksDataSource::ClearIncrementalState() {
190   wakelocks_.Clear();
191   next_id_ = 0;
192 }
193 
194 }  // namespace perfetto
195