1 /*
2 * Copyright (C) 2023 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 <bpf/BpfMap.h>
18 #include <bpf/BpfRingbuf.h>
19 #include <bpf/WaitForProgsLoaded.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <libbpf.h>
23 #include <sys/epoll.h>
24 #include <unistd.h>
25
26 #include <algorithm>
27 #include <cstdio>
28 #include <functional>
29 #include <optional>
30 #include <sstream>
31 #include <string>
32
33 #include <android-base/file.h>
34 #include <android-base/logging.h>
35 #include <android-base/result.h>
36
37 #include <memevents/memevents.h>
38
39 namespace android {
40 namespace bpf {
41 namespace memevents {
42
43 static const std::string kClientRingBuffers[MemEventClient::NR_CLIENTS] = {
44 MEM_EVENTS_AMS_RB, MEM_EVENTS_LMKD_RB, MEM_EVENTS_TEST_RB};
45
46 static const bool isBpfRingBufferSupported = isAtLeastKernelVersion(5, 8, 0);
47
48 class MemBpfRingbuf : public BpfRingbufBase {
49 public:
50 using EventCallback = std::function<void(const mem_event_t&)>;
51
52 /*
53 * Non-initializing constructor, requires calling `Initialize` once.
54 * This allows us to handle gracefully when we encounter an init
55 * error, instead of using a full-constructions that aborts on error.
56 */
MemBpfRingbuf()57 MemBpfRingbuf() : BpfRingbufBase(sizeof(mem_event_t)) {}
58
59 /*
60 * Initialize the base ringbuffer components. Must be called exactly once.
61 */
Initialize(const char * path)62 base::Result<void> Initialize(const char* path) { return Init(path); }
63
64 /*
65 * Consumes all `mem_event_t` messages from the ring buffer, passing them
66 * to the callback.
67 */
ConsumeAll(const EventCallback & callback)68 base::Result<int> ConsumeAll(const EventCallback& callback) {
69 return BpfRingbufBase::ConsumeAll([&](const void* mem_event) {
70 callback(*reinterpret_cast<const mem_event_t*>(mem_event));
71 });
72 }
73
74 /*
75 * Expose ring buffer file descriptor for polling purposes, not intended for
76 * consume directly. To consume use `ConsumeAll()`.
77 */
getRingBufFd()78 int getRingBufFd() { return mRingFd.get(); }
79 };
80
81 struct MemBpfAttachment {
82 const std::string prog;
83 const std::string tpGroup;
84 const std::string tpEvent;
85 const mem_event_type_t event_type;
86 };
87
88 // clang-format off
89 static const std::vector<std::vector<struct MemBpfAttachment>> attachments = {
90 // AMS
91 {
92 {
93 .prog = MEM_EVENTS_AMS_OOM_MARK_VICTIM_TP,
94 .tpGroup = "oom",
95 .tpEvent = "mark_victim",
96 .event_type = MEM_EVENT_OOM_KILL
97 },
98 },
99 // LMKD
100 {
101 {
102 .prog = MEM_EVENTS_LMKD_VMSCAN_DR_BEGIN_TP,
103 .tpGroup = "vmscan",
104 .tpEvent = "mm_vmscan_direct_reclaim_begin",
105 .event_type = MEM_EVENT_DIRECT_RECLAIM_BEGIN
106 },
107 {
108 .prog = MEM_EVENTS_LMKD_VMSCAN_DR_END_TP,
109 .tpGroup = "vmscan",
110 .tpEvent = "mm_vmscan_direct_reclaim_end",
111 .event_type = MEM_EVENT_DIRECT_RECLAIM_END
112 },
113 {
114 .prog = MEM_EVENTS_LMKD_VMSCAN_KSWAPD_WAKE_TP,
115 .tpGroup = "vmscan",
116 .tpEvent = "mm_vmscan_kswapd_wake",
117 .event_type = MEM_EVENT_KSWAPD_WAKE
118 },
119 {
120 .prog = MEM_EVENTS_LMKD_VMSCAN_KSWAPD_SLEEP_TP,
121 .tpGroup = "vmscan",
122 .tpEvent = "mm_vmscan_kswapd_sleep",
123 .event_type = MEM_EVENT_KSWAPD_SLEEP
124 },
125 },
126 // MemEventsTest
127 {
128 {
129 .prog = MEM_EVENTS_TEST_OOM_MARK_VICTIM_TP,
130 .tpGroup = "oom",
131 .tpEvent = "mark_victim",
132 .event_type = MEM_EVENT_OOM_KILL
133 },
134 },
135 // ... next service/client
136 };
137 // clang-format on
138
findAttachment(mem_event_type_t event_type,MemEventClient client)139 static std::optional<MemBpfAttachment> findAttachment(mem_event_type_t event_type,
140 MemEventClient client) {
141 auto it = std::find_if(attachments[client].begin(), attachments[client].end(),
142 [event_type](const MemBpfAttachment memBpfAttch) {
143 return memBpfAttch.event_type == event_type;
144 });
145 if (it == attachments[client].end()) return std::nullopt;
146 return it[0];
147 }
148
149 /**
150 * Helper function that determines if an event type is valid.
151 * We define "valid" as an actual event type that we can listen and register to.
152 *
153 * @param event_type memory event type to validate.
154 * @return true if it's less than `NR_MEM_EVENTS` and greater, or equal, to
155 * `MEM_EVENT_BASE`, false otherwise.
156 */
isValidEventType(mem_event_type_t event_type) const157 bool MemEventListener::isValidEventType(mem_event_type_t event_type) const {
158 return event_type < NR_MEM_EVENTS && event_type >= MEM_EVENT_BASE;
159 }
160
161 // Public methods
162
MemEventListener(MemEventClient client,bool attachTpForTests)163 MemEventListener::MemEventListener(MemEventClient client, bool attachTpForTests) {
164 if (client >= MemEventClient::NR_CLIENTS || client < MemEventClient::BASE) {
165 LOG(ERROR) << "memevent listener failed to initialize, invalid client: " << client;
166 std::abort();
167 }
168
169 mClient = client;
170 mAttachTpForTests = attachTpForTests;
171 std::fill_n(mEventsRegistered, NR_MEM_EVENTS, false);
172 mNumEventsRegistered = 0;
173
174 /*
175 * This flag allows for the MemoryPressureTest suite to hook into a BPF tracepoint
176 * and NOT allowing, this testing instance, to skip any skip internal calls.
177 * This flag is only allowed to be set for a testing instance, not for normal clients.
178 */
179 if (mClient != MemEventClient::TEST_CLIENT && attachTpForTests) {
180 LOG(ERROR) << "memevent listener failed to initialize, invalid configuration";
181 std::abort();
182 }
183
184 memBpfRb = std::make_unique<MemBpfRingbuf>();
185 if (auto status = memBpfRb->Initialize(kClientRingBuffers[client].c_str()); !status.ok()) {
186 /*
187 * We allow for the listener to load gracefully, but we added safeguad
188 * throughout the public APIs to prevent the listener to do any actions.
189 */
190 memBpfRb.reset(nullptr);
191 if (isBpfRingBufferSupported) {
192 LOG(ERROR) << "memevent listener MemBpfRingbuf init failed: "
193 << status.error().message();
194 /*
195 * Do not perform an `std::abort()`, there are some AMS test suites inadvertently
196 * initialize a memlistener to resolve test dependencies. We don't expect it
197 * to succeed since the test doesn't have the correct permissions.
198 */
199 } else {
200 LOG(ERROR) << "memevent listener failed to initialize, not supported kernel";
201 }
202 }
203 }
204
~MemEventListener()205 MemEventListener::~MemEventListener() {
206 deregisterAllEvents();
207 }
208
ok()209 bool MemEventListener::ok() {
210 return isBpfRingBufferSupported && memBpfRb;
211 }
212
registerEvent(mem_event_type_t event_type)213 bool MemEventListener::registerEvent(mem_event_type_t event_type) {
214 if (!ok()) {
215 LOG(ERROR) << "memevent register failed, failure to initialize";
216 return false;
217 }
218 if (!isValidEventType(event_type)) {
219 LOG(ERROR) << "memevent register failed, received invalid event type";
220 return false;
221 }
222 if (mEventsRegistered[event_type]) {
223 // We are already registered to this event
224 return true;
225 }
226
227 if (mClient == MemEventClient::TEST_CLIENT && !mAttachTpForTests) {
228 mEventsRegistered[event_type] = true;
229 mNumEventsRegistered++;
230 return true;
231 }
232
233 const std::optional<MemBpfAttachment> maybeAttachment = findAttachment(event_type, mClient);
234 if (!maybeAttachment.has_value()) {
235 /*
236 * Not all clients have access to the same tracepoints, for example,
237 * AMS doesn't have a bpf prog for the direct reclaim start/end tracepoints.
238 */
239 LOG(ERROR) << "memevent register failed, client " << mClient
240 << " doesn't support event: " << event_type;
241 return false;
242 }
243
244 const auto attachment = maybeAttachment.value();
245 int bpf_prog_fd = retrieveProgram(attachment.prog.c_str());
246 if (bpf_prog_fd < 0) {
247 PLOG(ERROR) << "memevent failed to retrieve pinned program from: " << attachment.prog;
248 return false;
249 }
250
251 /*
252 * Attach the bpf program to the tracepoint
253 *
254 * We get an errno `EEXIST` when a client attempts to register back to its events of interest.
255 * This occurs because the latest implementation of `bpf_detach_tracepoint` doesn't actually
256 * detach anything.
257 * https://github.com/iovisor/bcc/blob/7d350d90b638ddaf2c137a609b542e997597910a/src/cc/libbpf.c#L1495-L1501
258 */
259 if (bpf_attach_tracepoint(bpf_prog_fd, attachment.tpGroup.c_str(), attachment.tpEvent.c_str()) <
260 0 &&
261 errno != EEXIST) {
262 PLOG(ERROR) << "memevent failed to attach bpf program to " << attachment.tpGroup << "/"
263 << attachment.tpEvent << " tracepoint";
264 return false;
265 }
266
267 mEventsRegistered[event_type] = true;
268 mNumEventsRegistered++;
269 return true;
270 }
271
listen(int timeout_ms)272 bool MemEventListener::listen(int timeout_ms) {
273 if (!ok()) {
274 LOG(ERROR) << "memevent listen failed, failure to initialize";
275 return false;
276 }
277 if (mNumEventsRegistered == 0) {
278 LOG(ERROR) << "memevents listen failed, not registered to any events";
279 return false;
280 }
281
282 return memBpfRb->wait(timeout_ms);
283 }
284
deregisterEvent(mem_event_type_t event_type)285 bool MemEventListener::deregisterEvent(mem_event_type_t event_type) {
286 if (!ok()) {
287 LOG(ERROR) << "memevent failed to deregister, failure to initialize";
288 return false;
289 }
290 if (!isValidEventType(event_type)) {
291 LOG(ERROR) << "memevent failed to deregister, invalid event type";
292 return false;
293 }
294
295 if (!mEventsRegistered[event_type]) return true;
296
297 if (mClient == MemEventClient::TEST_CLIENT && !mAttachTpForTests) {
298 mEventsRegistered[event_type] = false;
299 mNumEventsRegistered--;
300 return true;
301 }
302
303 const std::optional<MemBpfAttachment> maybeAttachment = findAttachment(event_type, mClient);
304 if (!maybeAttachment.has_value()) {
305 /*
306 * We never expect to get here since the listener wouldn't have been to register this
307 * `event_type` in the first place.
308 */
309 LOG(ERROR) << "memevent failed deregister event " << event_type
310 << ", not tp attachment found";
311 return false;
312 }
313
314 const auto attachment = maybeAttachment.value();
315 if (bpf_detach_tracepoint(attachment.tpGroup.c_str(), attachment.tpEvent.c_str()) < 0) {
316 PLOG(ERROR) << "memevent failed to deregister event " << event_type << " from bpf prog to "
317 << attachment.tpGroup << "/" << attachment.tpEvent << " tracepoint";
318 return false;
319 }
320
321 mEventsRegistered[event_type] = false;
322 mNumEventsRegistered--;
323 return true;
324 }
325
deregisterAllEvents()326 void MemEventListener::deregisterAllEvents() {
327 if (!ok()) {
328 LOG(ERROR) << "memevent deregister all events failed, failure to initialize";
329 return;
330 }
331 if (mNumEventsRegistered == 0) return;
332 for (int i = 0; i < NR_MEM_EVENTS; i++) {
333 if (mEventsRegistered[i]) deregisterEvent(i);
334 }
335 }
336
getMemEvents(std::vector<mem_event_t> & mem_events)337 bool MemEventListener::getMemEvents(std::vector<mem_event_t>& mem_events) {
338 if (!ok()) {
339 LOG(ERROR) << "memevent failed getting memory events, failure to initialize";
340 return false;
341 }
342
343 base::Result<int> ret = memBpfRb->ConsumeAll([&](const mem_event_t& mem_event) {
344 if (mEventsRegistered[mem_event.type]) mem_events.emplace_back(mem_event);
345 });
346
347 if (!ret.ok()) {
348 LOG(ERROR) << "memevent failed getting memory events: " << ret.error().message();
349 return false;
350 }
351
352 return true;
353 }
354
getRingBufferFd()355 int MemEventListener::getRingBufferFd() {
356 if (!ok()) {
357 LOG(ERROR) << "memevent failed getting ring-buffer fd, failure to initialize";
358 return -1;
359 }
360 return memBpfRb->getRingBufFd();
361 }
362
363 } // namespace memevents
364 } // namespace bpf
365 } // namespace android
366