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