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 #include <chrono>
17 #include <condition_variable>
18 #include <filesystem>
19 #include <mutex>
20 #include <string>
21 #include <thread>
22
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <bpf/BpfUtils.h>
26 #include <gtest/gtest.h>
27 #include <poll.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/sysinfo.h>
32 #include <unistd.h>
33
34 #include <BpfSyscallWrappers.h>
35
36 #include <memevents/memevents.h>
37 #include <memevents/memevents_test.h>
38
39 using namespace ::android::base;
40 using namespace ::android::bpf::memevents;
41
42 using android::bpf::isAtLeastKernelVersion;
43
44 namespace fs = std::filesystem;
45
46 static const MemEventClient mem_test_client = MemEventClient::TEST_CLIENT;
47 static const int page_size = getpagesize();
48 static const bool isBpfRingBufferSupported = isAtLeastKernelVersion(5, 8, 0);
49 static const std::string bpfRbsPaths[MemEventClient::NR_CLIENTS] = {
50 MEM_EVENTS_AMS_RB, MEM_EVENTS_LMKD_RB, MEM_EVENTS_TEST_RB};
51 static const std::string testBpfSkfilterProgPaths[NR_MEM_EVENTS] = {
52 MEM_EVENTS_TEST_OOM_KILL_TP, MEM_EVENTS_TEST_DIRECT_RECLAIM_START_TP,
53 MEM_EVENTS_TEST_DIRECT_RECLAIM_END_TP, MEM_EVENTS_TEST_KSWAPD_WAKE_TP,
54 MEM_EVENTS_TEST_KSWAPD_SLEEP_TP, MEM_EVENTS_TEST_LMKD_TRIGGER_VENDOR_LMK_KILL_TP,
55 MEM_EVENTS_TEST_CALCULATE_TOTALRESERVE_PAGES_TP};
56 static const std::filesystem::path sysrq_trigger_path = "proc/sysrq-trigger";
57
initializeTestListener(std::unique_ptr<MemEventListener> & memevent_listener,const bool attachTpForTests)58 static void initializeTestListener(std::unique_ptr<MemEventListener>& memevent_listener,
59 const bool attachTpForTests) {
60 if (!memevent_listener) {
61 memevent_listener = std::make_unique<MemEventListener>(mem_test_client, attachTpForTests);
62 }
63 ASSERT_TRUE(memevent_listener) << "Memory event listener is not initialized";
64
65 /*
66 * Some test suite seems to have issues when trying to re-initialize
67 * the BPF manager for the MemEventsTest, therefore we retry.
68 */
69 if (!memevent_listener->ok()) {
70 memevent_listener.reset();
71 /* This sleep is needed in order to allow for the BPF manager to
72 * initialize without failure.
73 */
74 sleep(1);
75 memevent_listener = std::make_unique<MemEventListener>(mem_test_client);
76 }
77 ASSERT_TRUE(memevent_listener->ok()) << "BPF ring buffer manager didn't initialize";
78 }
79
80 /*
81 * Test suite to test on devices that don't support BPF, kernel <= 5.8.
82 * We allow for the listener to iniailize gracefully, but every public API will
83 * return false/fail.
84 */
85 class MemEventListenerUnsupportedKernel : public ::testing::Test {
86 protected:
87 std::unique_ptr<MemEventListener> memevent_listener;
88
SetUpTestSuite()89 static void SetUpTestSuite() {
90 if (isBpfRingBufferSupported) {
91 GTEST_SKIP()
92 << "BPF ring buffers is supported on this kernel, running alternative tests";
93 }
94 }
95
SetUp()96 void SetUp() override { initializeTestListener(memevent_listener, false); }
97
TearDown()98 void TearDown() override { memevent_listener.reset(); }
99 };
100
101 /*
102 * Listener shouldn't fail when initializing on a kernel that doesn't support BPF.
103 */
TEST_F(MemEventListenerUnsupportedKernel,initialize_invalid_client)104 TEST_F(MemEventListenerUnsupportedKernel, initialize_invalid_client) {
105 std::unique_ptr<MemEventListener> listener =
106 std::make_unique<MemEventListener>(MemEventClient::AMS);
107 ASSERT_TRUE(listener) << "Failed to initialize listener on older kernel";
108 }
109
110 /*
111 * Register will fail when running on a older kernel, even when we pass a valid event type.
112 */
TEST_F(MemEventListenerUnsupportedKernel,fail_to_register)113 TEST_F(MemEventListenerUnsupportedKernel, fail_to_register) {
114 ASSERT_FALSE(memevent_listener->registerEvent(MEM_EVENT_OOM_KILL))
115 << "Listener should fail to register valid event type on an unsupported kernel";
116 ASSERT_FALSE(memevent_listener->registerEvent(NR_MEM_EVENTS))
117 << "Listener should fail to register invalid event type";
118 }
119
120 /*
121 * Listen will fail when running on a older kernel.
122 * The listen() function always checks first if we are running on an older kernel,
123 * therefore we don't need to register for an event before trying to call listen.
124 */
TEST_F(MemEventListenerUnsupportedKernel,fail_to_listen)125 TEST_F(MemEventListenerUnsupportedKernel, fail_to_listen) {
126 ASSERT_FALSE(memevent_listener->listen()) << "listen() should fail on unsupported kernel";
127 }
128
129 /*
130 * Just like the other APIs, deregister will return false immediately on an older
131 * kernel.
132 */
TEST_F(MemEventListenerUnsupportedKernel,fail_to_unregister_event)133 TEST_F(MemEventListenerUnsupportedKernel, fail_to_unregister_event) {
134 ASSERT_FALSE(memevent_listener->deregisterEvent(MEM_EVENT_OOM_KILL))
135 << "Listener should fail to deregister valid event type on an older kernel";
136 ASSERT_FALSE(memevent_listener->deregisterEvent(NR_MEM_EVENTS))
137 << "Listener should fail to deregister invalid event type, regardless of kernel "
138 "version";
139 }
140
141 /*
142 * The `getMemEvents()` API should fail on an older kernel.
143 */
TEST_F(MemEventListenerUnsupportedKernel,fail_to_get_mem_events)144 TEST_F(MemEventListenerUnsupportedKernel, fail_to_get_mem_events) {
145 std::vector<mem_event_t> mem_events;
146 ASSERT_FALSE(memevent_listener->getMemEvents(mem_events))
147 << "Fetching memory events should fail on an older kernel";
148 }
149
150 /*
151 * The `getRingBufferFd()` API should fail on an older kernel
152 */
TEST_F(MemEventListenerUnsupportedKernel,fail_to_get_rb_fd)153 TEST_F(MemEventListenerUnsupportedKernel, fail_to_get_rb_fd) {
154 ASSERT_LT(memevent_listener->getRingBufferFd(), 0)
155 << "Fetching bpf-rb file descriptor should fail on an older kernel";
156 }
157
158 /*
159 * Test suite verifies that all the BPF programs and ring buffers are loaded.
160 */
161 class MemEventsBpfSetupTest : public ::testing::Test {
162 protected:
SetUpTestSuite()163 static void SetUpTestSuite() {
164 if (!isBpfRingBufferSupported) {
165 GTEST_SKIP() << "BPF ring buffers not supported in kernels below 5.8";
166 }
167 }
168 };
169
170 /*
171 * Verify that all the ams bpf-programs are loaded.
172 */
TEST_F(MemEventsBpfSetupTest,loaded_ams_progs)173 TEST_F(MemEventsBpfSetupTest, loaded_ams_progs) {
174 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_AMS_OOM_MARK_VICTIM_TP))
175 << "Failed to find ams mark_victim bpf-program";
176 }
177
178 /*
179 * Verify that all the lmkd bpf-programs are loaded.
180 */
TEST_F(MemEventsBpfSetupTest,loaded_lmkd_progs)181 TEST_F(MemEventsBpfSetupTest, loaded_lmkd_progs) {
182 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_LMKD_VMSCAN_DR_BEGIN_TP))
183 << "Failed to find lmkd direct_reclaim_begin bpf-program";
184 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_LMKD_VMSCAN_DR_END_TP))
185 << "Failed to find lmkd direct_reclaim_end bpf-program";
186 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_LMKD_VMSCAN_KSWAPD_WAKE_TP))
187 << "Failed to find lmkd kswapd_wake bpf-program";
188 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_LMKD_VMSCAN_KSWAPD_SLEEP_TP))
189 << "Failed to find lmkd kswapd_sleep bpf-program";
190 }
191
192 /*
193 * Verify that all the memevents test bpf-skfilter-programs are loaded.
194 */
TEST_F(MemEventsBpfSetupTest,loaded_test_skfilter_progs)195 TEST_F(MemEventsBpfSetupTest, loaded_test_skfilter_progs) {
196 for (int i = 0; i < NR_MEM_EVENTS; i++) {
197 ASSERT_TRUE(std::filesystem::exists(testBpfSkfilterProgPaths[i]))
198 << "Failed to find testing bpf-prog: " << testBpfSkfilterProgPaths[i];
199 }
200 }
201
202 /*
203 * Verify that all [bpf] ring buffer's are loaded.
204 * We expect to have at least 1 ring buffer for each client in `MemEventClient`.
205 */
TEST_F(MemEventsBpfSetupTest,loaded_ring_buffers)206 TEST_F(MemEventsBpfSetupTest, loaded_ring_buffers) {
207 for (int i = 0; i < MemEventClient::NR_CLIENTS; i++) {
208 ASSERT_TRUE(std::filesystem::exists(bpfRbsPaths[i]))
209 << "Failed to find bpf ring-buffer: " << bpfRbsPaths[i];
210 }
211 }
212
213 class MemEventsListenerTest : public ::testing::Test {
214 protected:
215 std::unique_ptr<MemEventListener> memevent_listener;
216
SetUpTestSuite()217 static void SetUpTestSuite() {
218 if (!isBpfRingBufferSupported) {
219 GTEST_SKIP() << "BPF ring buffers not supported in kernels below 5.8";
220 }
221 }
222
SetUp()223 void SetUp() override { initializeTestListener(memevent_listener, false); }
224
TearDown()225 void TearDown() override { memevent_listener.reset(); }
226 };
227
228 /*
229 * MemEventListener should fail, through a `std::abort()`, when attempted to initialize
230 * with an invalid `MemEventClient`. By passing `MemEventClient::NR_CLIENTS`, and attempting
231 * to convert/pass `-1` as a client, we expect the listener initialization to fail.
232 */
TEST_F(MemEventsListenerTest,initialize_invalid_client)233 TEST_F(MemEventsListenerTest, initialize_invalid_client) {
234 EXPECT_DEATH(MemEventListener listener(MemEventClient::NR_CLIENTS), "");
235 EXPECT_DEATH(MemEventListener listener(static_cast<MemEventClient>(-1)), "");
236 }
237
238 /*
239 * MemEventListener should fail when a valid, non-testing, client tries to initialize
240 * by passing the optional test flag.
241 */
TEST_F(MemEventsListenerTest,initialize_valid_client_with_test_flag)242 TEST_F(MemEventsListenerTest, initialize_valid_client_with_test_flag) {
243 for (int i = 0; i < MemEventClient::TEST_CLIENT; i++) {
244 const MemEventClient valid_client = static_cast<MemEventClient>(i);
245 EXPECT_DEATH(MemEventListener listener(valid_client, true), "")
246 << "Only test client is allowed to set the test flag to true";
247 }
248 }
249
250 /*
251 * MemEventClient base client should equal to AMS client.
252 */
TEST_F(MemEventsListenerTest,base_client_equal_ams_client)253 TEST_F(MemEventsListenerTest, base_client_equal_ams_client) {
254 ASSERT_EQ(static_cast<int>(MemEventClient::BASE), static_cast<int>(MemEventClient::AMS))
255 << "Base client should be equal to AMS client";
256 }
257
258 /*
259 * Validate `registerEvent()` fails with values >= `NR_MEM_EVENTS`.
260 */
TEST_F(MemEventsListenerTest,register_event_invalid_values)261 TEST_F(MemEventsListenerTest, register_event_invalid_values) {
262 ASSERT_FALSE(memevent_listener->registerEvent(NR_MEM_EVENTS));
263 ASSERT_FALSE(memevent_listener->registerEvent(NR_MEM_EVENTS + 1));
264 ASSERT_FALSE(memevent_listener->registerEvent(-1));
265 }
266
267 /*
268 * Validate that `registerEvent()` always returns true when we try registering
269 * the same [valid] event/value.
270 */
TEST_F(MemEventsListenerTest,register_event_repeated_event)271 TEST_F(MemEventsListenerTest, register_event_repeated_event) {
272 const int event_type = MEM_EVENT_OOM_KILL;
273 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
274 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
275 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
276 }
277
278 /*
279 * Validate that `registerEvent()` is able to register all the `MEM_EVENT_*` values
280 * from `bpf_types.h`.
281 */
TEST_F(MemEventsListenerTest,register_event_valid_values)282 TEST_F(MemEventsListenerTest, register_event_valid_values) {
283 for (unsigned int i = 0; i < NR_MEM_EVENTS; i++)
284 ASSERT_TRUE(memevent_listener->registerEvent(i)) << "Failed to register event: " << i;
285 }
286
287 /*
288 * `listen()` should return false when no events have been registered.
289 */
TEST_F(MemEventsListenerTest,listen_no_registered_events)290 TEST_F(MemEventsListenerTest, listen_no_registered_events) {
291 ASSERT_FALSE(memevent_listener->listen());
292 }
293
294 /*
295 * Validate `deregisterEvent()` fails with values >= `NR_MEM_EVENTS`.
296 * Exactly like `register_event_invalid_values` test.
297 */
TEST_F(MemEventsListenerTest,deregister_event_invalid_values)298 TEST_F(MemEventsListenerTest, deregister_event_invalid_values) {
299 ASSERT_FALSE(memevent_listener->deregisterEvent(NR_MEM_EVENTS));
300 ASSERT_FALSE(memevent_listener->deregisterEvent(NR_MEM_EVENTS + 1));
301 ASSERT_FALSE(memevent_listener->deregisterEvent(-1));
302 }
303
304 /*
305 * Validate that `deregisterEvent()` always returns true when we try
306 * deregistering the same [valid] event/value.
307 */
TEST_F(MemEventsListenerTest,deregister_repeated_event)308 TEST_F(MemEventsListenerTest, deregister_repeated_event) {
309 const int event_type = MEM_EVENT_DIRECT_RECLAIM_BEGIN;
310 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
311 ASSERT_TRUE(memevent_listener->deregisterEvent(event_type));
312 ASSERT_TRUE(memevent_listener->deregisterEvent(event_type));
313 }
314
315 /*
316 * Verify that the `deregisterEvent()` will return true
317 * when we deregister a non-registered, valid, event.
318 */
TEST_F(MemEventsListenerTest,deregister_unregistered_event)319 TEST_F(MemEventsListenerTest, deregister_unregistered_event) {
320 ASSERT_TRUE(memevent_listener->deregisterEvent(MEM_EVENT_DIRECT_RECLAIM_END));
321 }
322
323 /*
324 * Validate that the `deregisterAllEvents()` closes all the registered
325 * events.
326 */
TEST_F(MemEventsListenerTest,deregister_all_events)327 TEST_F(MemEventsListenerTest, deregister_all_events) {
328 ASSERT_TRUE(memevent_listener->registerEvent(MEM_EVENT_OOM_KILL));
329 ASSERT_TRUE(memevent_listener->registerEvent(MEM_EVENT_DIRECT_RECLAIM_BEGIN));
330 memevent_listener->deregisterAllEvents();
331 ASSERT_FALSE(memevent_listener->listen())
332 << "Expected to fail since we are not registered to any events";
333 }
334
335 /*
336 * Validating that `MEM_EVENT_BASE` is equal to `MEM_EVENT_OOM_KILL`.
337 */
TEST_F(MemEventsListenerTest,base_and_oom_events_are_equal)338 TEST_F(MemEventsListenerTest, base_and_oom_events_are_equal) {
339 ASSERT_EQ(MEM_EVENT_OOM_KILL, MEM_EVENT_BASE)
340 << "MEM_EVENT_BASE should be equal to MEM_EVENT_OOM_KILL";
341 }
342
343 /*
344 * Validate that `getRingBufferFd()` returns a valid file descriptor.
345 */
TEST_F(MemEventsListenerTest,get_client_rb_fd)346 TEST_F(MemEventsListenerTest, get_client_rb_fd) {
347 ASSERT_GE(memevent_listener->getRingBufferFd(), 0)
348 << "Failed to get a valid bpf-rb file descriptor";
349 }
350
351 class MemEventsListenerBpf : public ::testing::Test {
352 private:
353 android::base::unique_fd mProgram;
354
setUpProgram(unsigned int event_type)355 void setUpProgram(unsigned int event_type) {
356 ASSERT_TRUE(event_type < NR_MEM_EVENTS) << "Invalid event type provided";
357
358 int bpf_fd = android::bpf::retrieveProgram(testBpfSkfilterProgPaths[event_type].c_str());
359 ASSERT_NE(bpf_fd, -1) << "Retrieve bpf program failed with prog path: "
360 << testBpfSkfilterProgPaths[event_type];
361 mProgram.reset(bpf_fd);
362
363 ASSERT_GE(mProgram.get(), 0)
364 << testBpfSkfilterProgPaths[event_type] << " was either not found or inaccessible.";
365 }
366
367 /*
368 * Always call this after `setUpProgram()`, in order to make sure that the
369 * correct `mProgram` was set.
370 */
RunProgram(unsigned int event_type)371 void RunProgram(unsigned int event_type) {
372 errno = 0;
373 switch (event_type) {
374 case MEM_EVENT_OOM_KILL:
375 struct mark_victim_args mark_victim_fake_args;
376 android::bpf::runProgram(mProgram, &mark_victim_fake_args,
377 sizeof(mark_victim_fake_args));
378 break;
379 case MEM_EVENT_DIRECT_RECLAIM_BEGIN:
380 struct direct_reclaim_begin_args dr_begin_fake_args;
381 android::bpf::runProgram(mProgram, &dr_begin_fake_args, sizeof(dr_begin_fake_args));
382 break;
383 case MEM_EVENT_DIRECT_RECLAIM_END:
384 struct direct_reclaim_end_args dr_end_fake_args;
385 android::bpf::runProgram(mProgram, &dr_end_fake_args, sizeof(dr_end_fake_args));
386 break;
387 case MEM_EVENT_KSWAPD_WAKE:
388 struct kswapd_wake_args kswapd_wake_fake_args;
389 android::bpf::runProgram(mProgram, &kswapd_wake_fake_args,
390 sizeof(kswapd_wake_fake_args));
391 break;
392 case MEM_EVENT_KSWAPD_SLEEP:
393 struct kswapd_sleep_args kswapd_sleep_fake_args;
394 android::bpf::runProgram(mProgram, &kswapd_sleep_fake_args,
395 sizeof(kswapd_sleep_fake_args));
396 break;
397 case MEM_EVENT_VENDOR_LMK_KILL:
398 struct vendor_lmk_kill_args vendor_lmk_kill_args;
399 android::bpf::runProgram(mProgram, &vendor_lmk_kill_args,
400 sizeof(vendor_lmk_kill_args));
401 break;
402 case MEM_EVENT_UPDATE_ZONEINFO:
403 struct calculate_totalreserve_pages_args ctp_fake_args;
404 android::bpf::runProgram(mProgram, &ctp_fake_args, sizeof(ctp_fake_args));
405 break;
406 default:
407 FAIL() << "Invalid event type provided";
408 }
409 EXPECT_EQ(errno, 0);
410 }
411
412 protected:
413 std::unique_ptr<MemEventListener> memevent_listener;
414
SetUpTestSuite()415 static void SetUpTestSuite() {
416 if (!isAtLeastKernelVersion(5, 8, 0)) {
417 GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
418 }
419 }
420
SetUp()421 void SetUp() override { initializeTestListener(memevent_listener, false); }
422
TearDown()423 void TearDown() override { memevent_listener.reset(); }
424
425 /*
426 * Helper function to insert mocked data into the testing [bpf] ring buffer.
427 * This will trigger the `listen()` if its registered to the given `event_type`.
428 */
setMockDataInRb(mem_event_type_t event_type)429 void setMockDataInRb(mem_event_type_t event_type) {
430 setUpProgram(event_type);
431 RunProgram(event_type);
432 }
433
434 /*
435 * Test that the `listen()` returns true.
436 * We setup some mocked event data into the testing [bpf] ring-buffer, to make
437 * sure the `listen()` is triggered.
438 */
testListenEvent(unsigned int event_type)439 void testListenEvent(unsigned int event_type) {
440 ASSERT_TRUE(event_type < NR_MEM_EVENTS) << "Invalid event type provided";
441
442 setMockDataInRb(event_type);
443
444 ASSERT_TRUE(memevent_listener->listen(5000)); // 5 second timeout
445 }
446
validateMockedEvent(const mem_event_t & mem_event)447 void validateMockedEvent(const mem_event_t& mem_event) {
448 /*
449 * These values are set inside the testing prog `memevents_test.h`,
450 * they can't be passed from the test to the bpf-prog.
451 */
452 switch (mem_event.type) {
453 case MEM_EVENT_OOM_KILL:
454 ASSERT_EQ(mem_event.event_data.oom_kill.pid,
455 mocked_oom_event.event_data.oom_kill.pid)
456 << "MEM_EVENT_OOM_KILL: Didn't receive expected PID";
457 ASSERT_EQ(mem_event.event_data.oom_kill.uid,
458 mocked_oom_event.event_data.oom_kill.uid)
459 << "MEM_EVENT_OOM_KILL: Didn't receive expected UID";
460 ASSERT_EQ(mem_event.event_data.oom_kill.oom_score_adj,
461 mocked_oom_event.event_data.oom_kill.oom_score_adj)
462 << "MEM_EVENT_OOM_KILL: Didn't receive expected OOM score";
463 ASSERT_EQ(strcmp(mem_event.event_data.oom_kill.process_name,
464 mocked_oom_event.event_data.oom_kill.process_name),
465 0)
466 << "MEM_EVENT_OOM_KILL: Didn't receive expected process name";
467 ASSERT_EQ(mem_event.event_data.oom_kill.timestamp_ms,
468 mocked_oom_event.event_data.oom_kill.timestamp_ms)
469 << "MEM_EVENT_OOM_KILL: Didn't receive expected timestamp";
470 ASSERT_EQ(mem_event.event_data.oom_kill.total_vm_kb,
471 mocked_oom_event.event_data.oom_kill.total_vm_kb)
472 << "MEM_EVENT_OOM_KILL: Didn't receive expected total vm";
473 ASSERT_EQ(mem_event.event_data.oom_kill.anon_rss_kb,
474 mocked_oom_event.event_data.oom_kill.anon_rss_kb)
475 << "MEM_EVENT_OOM_KILL: Didn't receive expected anon rss";
476 ASSERT_EQ(mem_event.event_data.oom_kill.file_rss_kb,
477 mocked_oom_event.event_data.oom_kill.file_rss_kb)
478 << "MEM_EVENT_OOM_KILL: Didn't receive expected file rss";
479 ASSERT_EQ(mem_event.event_data.oom_kill.shmem_rss_kb,
480 mocked_oom_event.event_data.oom_kill.shmem_rss_kb)
481 << "MEM_EVENT_OOM_KILL: Didn't receive expected shmem rss";
482 ASSERT_EQ(mem_event.event_data.oom_kill.pgtables_kb,
483 mocked_oom_event.event_data.oom_kill.pgtables_kb)
484 << "MEM_EVENT_OOM_KILL: Didn't receive expected pgtables";
485 break;
486 case MEM_EVENT_DIRECT_RECLAIM_BEGIN:
487 /* TP doesn't contain any data to mock */
488 break;
489 case MEM_EVENT_DIRECT_RECLAIM_END:
490 /* TP doesn't contain any data to mock */
491 break;
492 case MEM_EVENT_KSWAPD_WAKE:
493 ASSERT_EQ(mem_event.event_data.kswapd_wake.node_id,
494 mocked_kswapd_wake_event.event_data.kswapd_wake.node_id)
495 << "MEM_EVENT_KSWAPD_WAKE: Didn't receive expected node id";
496 ASSERT_EQ(mem_event.event_data.kswapd_wake.zone_id,
497 mocked_kswapd_wake_event.event_data.kswapd_wake.zone_id)
498 << "MEM_EVENT_KSWAPD_WAKE: Didn't receive expected zone id";
499 ASSERT_EQ(mem_event.event_data.kswapd_wake.alloc_order,
500 mocked_kswapd_wake_event.event_data.kswapd_wake.alloc_order)
501 << "MEM_EVENT_KSWAPD_WAKE: Didn't receive expected alloc_order";
502 break;
503 case MEM_EVENT_KSWAPD_SLEEP:
504 ASSERT_EQ(mem_event.event_data.kswapd_sleep.node_id,
505 mocked_kswapd_sleep_event.event_data.kswapd_sleep.node_id)
506 << "MEM_EVENT_KSWAPD_SLEEP: Didn't receive expected node id";
507 break;
508 case MEM_EVENT_VENDOR_LMK_KILL:
509 ASSERT_EQ(mem_event.event_data.vendor_kill.reason,
510 mocked_vendor_lmk_kill_event.event_data.vendor_kill.reason)
511 << "MEM_EVENT_VENDOR_LMK_KILL: Didn't receive expected reason";
512 ASSERT_EQ(mem_event.event_data.vendor_kill.min_oom_score_adj,
513 mocked_vendor_lmk_kill_event.event_data.vendor_kill.min_oom_score_adj)
514 << "MEM_EVENT_VENDOR_LMK_KILL: Didn't receive expected min_oom_score_adj";
515 break;
516 case MEM_EVENT_UPDATE_ZONEINFO:
517 ASSERT_EQ(mem_event.event_data.reserve_pages.num_pages,
518 mocked_total_reserve_pages_event.event_data.reserve_pages.num_pages)
519 << "MEM_EVENT_UPDATE_ZONEINFO: Didn't receive expected reserved pages";
520 break;
521 }
522 }
523 };
524
525 /*
526 * Validate that `listen()` is triggered when we the bpf-rb receives
527 * a OOM event.
528 */
TEST_F(MemEventsListenerBpf,listener_bpf_oom_kill)529 TEST_F(MemEventsListenerBpf, listener_bpf_oom_kill) {
530 const mem_event_type_t event_type = MEM_EVENT_OOM_KILL;
531
532 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
533 testListenEvent(event_type);
534
535 std::vector<mem_event_t> mem_events;
536 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
537 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
538 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a OOM event";
539 validateMockedEvent(mem_events[0]);
540 }
541
542 /*
543 * Validate that `listen()` is triggered when we the bpf-rb receives
544 * a direct reclain start event.
545 */
TEST_F(MemEventsListenerBpf,listener_bpf_direct_reclaim_begin)546 TEST_F(MemEventsListenerBpf, listener_bpf_direct_reclaim_begin) {
547 const mem_event_type_t event_type = MEM_EVENT_DIRECT_RECLAIM_BEGIN;
548
549 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
550 testListenEvent(event_type);
551
552 std::vector<mem_event_t> mem_events;
553 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
554 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
555 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a direct reclaim begin event";
556 validateMockedEvent(mem_events[0]);
557 }
558
559 /*
560 * Validate that `listen()` is triggered when we the bpf-rb receives
561 * a direct reclain end event.
562 */
TEST_F(MemEventsListenerBpf,listener_bpf_direct_reclaim_end)563 TEST_F(MemEventsListenerBpf, listener_bpf_direct_reclaim_end) {
564 const mem_event_type_t event_type = MEM_EVENT_DIRECT_RECLAIM_END;
565
566 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
567 testListenEvent(event_type);
568
569 std::vector<mem_event_t> mem_events;
570 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
571 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
572 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a direct reclaim end event";
573 validateMockedEvent(mem_events[0]);
574 }
575
TEST_F(MemEventsListenerBpf,listener_bpf_kswapd_wake)576 TEST_F(MemEventsListenerBpf, listener_bpf_kswapd_wake) {
577 const mem_event_type_t event_type = MEM_EVENT_KSWAPD_WAKE;
578
579 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
580 testListenEvent(event_type);
581
582 std::vector<mem_event_t> mem_events;
583 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
584 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
585 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a kswapd wake event";
586 validateMockedEvent(mem_events[0]);
587 }
588
TEST_F(MemEventsListenerBpf,listener_bpf_kswapd_sleep)589 TEST_F(MemEventsListenerBpf, listener_bpf_kswapd_sleep) {
590 const mem_event_type_t event_type = MEM_EVENT_KSWAPD_SLEEP;
591
592 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
593 testListenEvent(event_type);
594
595 std::vector<mem_event_t> mem_events;
596 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
597 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
598 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a kswapd sleep event";
599 validateMockedEvent(mem_events[0]);
600 }
601
TEST_F(MemEventsListenerBpf,listener_bpf_vendor_lmk_kill)602 TEST_F(MemEventsListenerBpf, listener_bpf_vendor_lmk_kill) {
603 const mem_event_type_t event_type = MEM_EVENT_VENDOR_LMK_KILL;
604
605 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
606 testListenEvent(event_type);
607
608 std::vector<mem_event_t> mem_events;
609 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
610 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
611 ASSERT_EQ(mem_events[0].type, event_type) << "Didn't receive a vendor lmk kill event";
612 validateMockedEvent(mem_events[0]);
613 }
614
TEST_F(MemEventsListenerBpf,listener_bpf_calculate_totalreserve_pages)615 TEST_F(MemEventsListenerBpf, listener_bpf_calculate_totalreserve_pages) {
616 const mem_event_type_t event_type = MEM_EVENT_UPDATE_ZONEINFO;
617
618 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
619 testListenEvent(event_type);
620
621 std::vector<mem_event_t> mem_events;
622 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
623 ASSERT_FALSE(mem_events.empty()) << "Expected for mem_events to have at least 1 mocked event";
624 ASSERT_EQ(mem_events[0].type, event_type)
625 << "Didn't receive a calculate totalreserve pages event";
626 validateMockedEvent(mem_events[0]);
627 }
628
629 /*
630 * `listen()` should timeout, and return false, when a memory event that
631 * we are not registered for is triggered.
632 */
TEST_F(MemEventsListenerBpf,no_register_events_listen_fails)633 TEST_F(MemEventsListenerBpf, no_register_events_listen_fails) {
634 const mem_event_type_t event_type = MEM_EVENT_DIRECT_RECLAIM_END;
635 setMockDataInRb(event_type);
636 ASSERT_FALSE(memevent_listener->listen(5000)); // 5 second timeout
637 }
638
639 /*
640 * `getMemEvents()` should return an empty list, when a memory event that
641 * we are not registered for, is triggered.
642 */
TEST_F(MemEventsListenerBpf,getMemEvents_no_register_events)643 TEST_F(MemEventsListenerBpf, getMemEvents_no_register_events) {
644 const mem_event_type_t event_type = MEM_EVENT_OOM_KILL;
645 setMockDataInRb(event_type);
646
647 std::vector<mem_event_t> mem_events;
648 ASSERT_TRUE(memevent_listener->getMemEvents(mem_events)) << "Failed fetching events";
649 ASSERT_TRUE(mem_events.empty());
650 }
651
652 /*
653 * Verify that the listener receives a notification when:
654 * 1. We start listening
655 * 2. Memory event is added in the bpf ring-buffer
656 * 3. Listening is notified of the new event.
657 */
TEST_F(MemEventsListenerBpf,listen_then_create_event)658 TEST_F(MemEventsListenerBpf, listen_then_create_event) {
659 const mem_event_type_t event_type = MEM_EVENT_DIRECT_RECLAIM_BEGIN;
660 std::mutex mtx;
661 std::condition_variable cv;
662 bool didReceiveEvent = false;
663
664 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
665
666 std::thread t([&] {
667 bool listen_result = memevent_listener->listen(10000);
668 std::lock_guard lk(mtx);
669 didReceiveEvent = listen_result;
670 cv.notify_one();
671 });
672
673 setMockDataInRb(event_type);
674
675 std::unique_lock lk(mtx);
676 cv.wait_for(lk, std::chrono::seconds(10), [&] { return didReceiveEvent; });
677 ASSERT_TRUE(didReceiveEvent) << "Listen never received a memory event notification";
678 t.join();
679 }
680
681 /*
682 * Similarly to `listen_then_create_event`, but instead of using
683 * `listen()`, we want to poll from `getRingBufferFd()` value.
684 */
TEST_F(MemEventsListenerBpf,getRb_poll_and_create_event)685 TEST_F(MemEventsListenerBpf, getRb_poll_and_create_event) {
686 const mem_event_type_t event_type = MEM_EVENT_DIRECT_RECLAIM_BEGIN;
687 std::mutex mtx;
688 std::condition_variable cv;
689 bool didReceiveEvent = false;
690
691 ASSERT_TRUE(memevent_listener->registerEvent(event_type));
692
693 int rb_fd = memevent_listener->getRingBufferFd();
694 ASSERT_GE(rb_fd, 0) << "Received invalid file descriptor";
695
696 std::thread t([&] {
697 struct pollfd pfd = {
698 .fd = rb_fd,
699 .events = POLLIN,
700 };
701 int poll_result = poll(&pfd, 1, 10000);
702 std::lock_guard lk(mtx);
703 didReceiveEvent = poll_result > 0;
704 cv.notify_one();
705 });
706
707 setMockDataInRb(event_type);
708
709 std::unique_lock lk(mtx);
710 cv.wait_for(lk, std::chrono::seconds(10), [&] { return didReceiveEvent; });
711 ASSERT_TRUE(didReceiveEvent) << "Poll never received a memory event notification";
712 t.join();
713 }
714
715 class MemoryPressureTest : public ::testing::Test {
716 public:
SetUpTestSuite()717 static void SetUpTestSuite() {
718 if (!isAtLeastKernelVersion(5, 8, 0))
719 GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
720
721 if (!std::filesystem::exists(sysrq_trigger_path))
722 GTEST_SKIP() << "sysrq-trigger is required to wake up the OOM killer";
723
724 ASSERT_TRUE(std::filesystem::exists(MEM_EVENTS_TEST_OOM_MARK_VICTIM_TP))
725 << "Failed to find test bpf program: " << MEM_EVENTS_TEST_OOM_MARK_VICTIM_TP;
726 }
727
728 protected:
729 std::unique_ptr<MemEventListener> memevent_listener;
730
SetUp()731 void SetUp() override { initializeTestListener(memevent_listener, true); }
732
TearDown()733 void TearDown() override { memevent_listener.reset(); }
734
735 /**
736 * Helper function that will force the OOM killer to claim a [random]
737 * victim. Note that there is no deterministic way to ensure what process
738 * will be claimed by the OOM killer.
739 *
740 * We utilize [sysrq]
741 * (https://www.kernel.org/doc/html/v4.10/admin-guide/sysrq.html)
742 * to help us attempt to wake up the out-of-memory killer.
743 *
744 * @return true if we were able to trigger an OOM event, false otherwise.
745 */
triggerOom()746 bool triggerOom() {
747 const std::filesystem::path process_oom_path = "proc/self/oom_score_adj";
748
749 // Make sure that we don't kill the parent process
750 if (!android::base::WriteStringToFile("-999", process_oom_path)) {
751 LOG(ERROR) << "Failed writing oom score adj for parent process";
752 return false;
753 }
754
755 int pid = fork();
756 if (pid < 0) {
757 LOG(ERROR) << "Failed to fork";
758 return false;
759 }
760 if (pid == 0) {
761 /*
762 * We want to make sure that the OOM killer claims our child
763 * process, this way we ensure we don't kill anything critical
764 * (including this test).
765 */
766 if (!android::base::WriteStringToFile("1000", process_oom_path)) {
767 LOG(ERROR) << "Failed writing oom score adj for child process";
768 return false;
769 }
770
771 struct sysinfo info;
772 if (sysinfo(&info) != 0) {
773 LOG(ERROR) << "Failed to get sysinfo";
774 return false;
775 }
776 size_t length = info.freeram / 2;
777
778 // Allocate memory
779 void* addr =
780 mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
781 if (addr == MAP_FAILED) {
782 LOG(ERROR) << "Failed creating mmap";
783 return false;
784 }
785
786 // Fault pages
787 srand(67);
788 for (int i = 0; i < length; i += page_size) memset((char*)addr + i, (char)rand(), 1);
789
790 // Use sysrq-trigger to attempt waking up the OOM killer
791 if (!android::base::WriteStringToFile("f", sysrq_trigger_path)) {
792 LOG(ERROR) << "Failed calling sysrq to trigger OOM killer";
793 return false;
794 }
795 sleep(10); // Give some time in for sysrq to wake up the OOM killer
796 } else {
797 /*
798 * Wait for child process to finish, this will prevent scenario where the `listen()`
799 * is called by the parent, but the child hasn't even been scheduled to run yet.
800 */
801 wait(NULL);
802 if (!memevent_listener->listen(2000)) {
803 LOG(ERROR) << "Failed to receive a memory event";
804 return false;
805 }
806 }
807 return true;
808 }
809
810 /*
811 * This wrapper function exists to facilitate the use of ASSERT, with
812 * non-void helper functions, that want to use `ReadFileToString()`.
813 * We can only assert on void functions.
814 */
fileToString(const std::string & file_path,std::string * content)815 void fileToString(const std::string& file_path, std::string* content) {
816 ASSERT_TRUE(android::base::ReadFileToString(file_path, content))
817 << "Failed to read file: " << file_path;
818 }
819
820 /*
821 * Check if the current device supports the new oom/mark_victim tracepoints.
822 * The original oom/mark_victim tracepoint only supports the `pid` field, while
823 * the newer version supports: pid, uid, comm, oom score, pgtables, and rss stats.
824 */
isUpdatedMarkVictimTpSupported()825 bool isUpdatedMarkVictimTpSupported() {
826 const std::string path_mark_victim_format =
827 "/sys/kernel/tracing/events/oom/mark_victim/format";
828 std::string mark_victim_format_content;
829 fileToString(path_mark_victim_format, &mark_victim_format_content);
830
831 /*
832 * Check if the device is running the with latest mark_victim fields:
833 * total_vm, anon_rss, file_rss, shmem_rss, uid, pgtables.
834 */
835 return (mark_victim_format_content.find("total_vm") != std::string::npos) &&
836 (mark_victim_format_content.find("anon_rss") != std::string::npos) &&
837 (mark_victim_format_content.find("file_rss") != std::string::npos) &&
838 (mark_victim_format_content.find("shmem_rss") != std::string::npos) &&
839 (mark_victim_format_content.find("uid") != std::string::npos) &&
840 (mark_victim_format_content.find("pgtables") != std::string::npos);
841 }
842 };
843
844 /**
845 * End-to-end test for listening, and consuming, out-of-memory (OOM) events.
846 *
847 * We don't perform a listen here since the `triggerOom()` already does
848 * that for us.
849 */
TEST_F(MemoryPressureTest,oom_e2e_flow)850 TEST_F(MemoryPressureTest, oom_e2e_flow) {
851 if (!isUpdatedMarkVictimTpSupported())
852 GTEST_SKIP() << "New oom/mark_victim fields not supported";
853
854 ASSERT_TRUE(memevent_listener->registerEvent(MEM_EVENT_OOM_KILL))
855 << "Failed registering OOM events as an event of interest";
856
857 ASSERT_TRUE(triggerOom()) << "Failed to trigger OOM killer";
858
859 std::vector<mem_event_t> oom_events;
860 ASSERT_TRUE(memevent_listener->getMemEvents(oom_events)) << "Failed to fetch memory oom events";
861 ASSERT_FALSE(oom_events.empty()) << "We expect at least 1 OOM event";
862 }
863
864 /*
865 * Verify that we can register to an event after deregistering from it.
866 */
TEST_F(MemoryPressureTest,register_after_deregister_event)867 TEST_F(MemoryPressureTest, register_after_deregister_event) {
868 if (!isUpdatedMarkVictimTpSupported())
869 GTEST_SKIP() << "New oom/mark_victim fields not supported";
870
871 ASSERT_TRUE(memevent_listener->registerEvent(MEM_EVENT_OOM_KILL))
872 << "Failed registering OOM events as an event of interest";
873
874 ASSERT_TRUE(memevent_listener->deregisterEvent(MEM_EVENT_OOM_KILL))
875 << "Failed deregistering OOM events";
876
877 ASSERT_TRUE(memevent_listener->registerEvent(MEM_EVENT_OOM_KILL))
878 << "Failed to register for OOM events after deregister it";
879 }
880
main(int argc,char ** argv)881 int main(int argc, char** argv) {
882 ::testing::InitGoogleTest(&argc, argv);
883 ::android::base::InitLogging(argv, android::base::StderrLogger);
884 return RUN_ALL_TESTS();
885 }
886