xref: /aosp_15_r20/external/cronet/base/memory/madv_free_discardable_memory_posix_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fcntl.h>
6 #include <stdint.h>
7 
8 #include <sys/mman.h>
9 #include <memory>
10 
11 #include "base/files/scoped_file.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/functional/callback_helpers.h"
15 #include "base/memory/madv_free_discardable_memory_allocator_posix.h"
16 #include "base/memory/madv_free_discardable_memory_posix.h"
17 #include "base/memory/page_size.h"
18 #include "build/build_config.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 #define SUCCEED_IF_MADV_FREE_UNSUPPORTED()                                  \
22   do {                                                                      \
23     if (GetMadvFreeSupport() != base::MadvFreeSupport::kSupported) {        \
24       SUCCEED()                                                             \
25           << "MADV_FREE is not supported (Linux 4.5+ required), vacuously " \
26              "passing test";                                                \
27       return;                                                               \
28     }                                                                       \
29   } while (0)
30 
31 namespace base {
32 std::atomic<size_t> allocator_byte_count;
33 class MadvFreeDiscardableMemoryPosixTester
34     : public MadvFreeDiscardableMemoryPosix {
35  public:
MadvFreeDiscardableMemoryPosixTester(size_t size_in_bytes)36   MadvFreeDiscardableMemoryPosixTester(size_t size_in_bytes)
37       : MadvFreeDiscardableMemoryPosix(size_in_bytes, &allocator_byte_count) {}
38 
39   using MadvFreeDiscardableMemoryPosix::DiscardPage;
40   using MadvFreeDiscardableMemoryPosix::GetPageCount;
41   using MadvFreeDiscardableMemoryPosix::IsLockedForTesting;
42   using MadvFreeDiscardableMemoryPosix::IsValid;
43   using MadvFreeDiscardableMemoryPosix::SetKeepMemoryForTesting;
44 };
45 
46 class MadvFreeDiscardableMemoryTest : public ::testing::Test {
47  protected:
MadvFreeDiscardableMemoryTest()48   MadvFreeDiscardableMemoryTest() {}
~MadvFreeDiscardableMemoryTest()49   ~MadvFreeDiscardableMemoryTest() override {}
50 
51   const size_t kPageSize = base::GetPageSize();
52 
53   std::unique_ptr<MadvFreeDiscardableMemoryPosixTester>
AllocateLockedDiscardableMemoryPagesForTest(size_t size_in_pages)54   AllocateLockedDiscardableMemoryPagesForTest(size_t size_in_pages) {
55     return std::make_unique<MadvFreeDiscardableMemoryPosixTester>(
56         size_in_pages * kPageSize);
57   }
58 };
59 
60 using MadvFreeDiscardableMemoryDeathTest = MadvFreeDiscardableMemoryTest;
61 
62 constexpr char kTestPattern[] =
63     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
64 
TEST_F(MadvFreeDiscardableMemoryTest,AllocateAndUse)65 TEST_F(MadvFreeDiscardableMemoryTest, AllocateAndUse) {
66   SUCCEED_IF_MADV_FREE_UNSUPPORTED();
67 
68   std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
69       AllocateLockedDiscardableMemoryPagesForTest(1);
70 
71   mem->SetKeepMemoryForTesting(true);
72 
73   ASSERT_TRUE(mem->IsValid());
74   ASSERT_TRUE(mem->IsLockedForTesting());
75 
76   char buffer[sizeof(kTestPattern)];
77 
78   // Write test pattern to block
79   uint8_t* data = mem->data_as<uint8_t>();
80   memcpy(data, kTestPattern, sizeof(kTestPattern));
81 
82   // Read test pattern from block
83   data = mem->data_as<uint8_t>();
84   memcpy(buffer, data, sizeof(kTestPattern));
85 
86   EXPECT_EQ(memcmp(kTestPattern, buffer, sizeof(kTestPattern)), 0);
87 
88   // Memory contents should not change after successful unlock and lock.
89   mem->Unlock();
90   ASSERT_TRUE(mem->Lock());
91 
92   EXPECT_EQ(memcmp(kTestPattern, buffer, sizeof(kTestPattern)), 0);
93 }
94 
TEST_F(MadvFreeDiscardableMemoryTest,LockAndUnlock)95 TEST_F(MadvFreeDiscardableMemoryTest, LockAndUnlock) {
96   SUCCEED_IF_MADV_FREE_UNSUPPORTED();
97 
98   const size_t kPageCount = 10;
99   std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
100       AllocateLockedDiscardableMemoryPagesForTest(kPageCount);
101 
102   ASSERT_TRUE(mem->IsValid());
103   ASSERT_TRUE(mem->IsLockedForTesting());
104   memset(mem->data(), 0xE7, kPageSize * kPageCount);
105   mem->Unlock();
106   ASSERT_FALSE(mem->IsLockedForTesting());
107   bool result = mem->Lock();
108   // If Lock() succeeded, the memory region should be valid. If Lock() failed,
109   // the memory region should be invalid.
110   ASSERT_EQ(result, mem->IsValid());
111 }
112 
TEST_F(MadvFreeDiscardableMemoryTest,LockShouldFailAfterDiscard)113 TEST_F(MadvFreeDiscardableMemoryTest, LockShouldFailAfterDiscard) {
114   SUCCEED_IF_MADV_FREE_UNSUPPORTED();
115 
116   constexpr size_t kPageCount = 10;
117 
118   std::unique_ptr<MadvFreeDiscardableMemoryPosixTester> mem =
119       AllocateLockedDiscardableMemoryPagesForTest(kPageCount);
120   uint8_t* data = mem->data_as<uint8_t>();
121 
122   ASSERT_TRUE(mem->IsValid());
123   ASSERT_TRUE(mem->IsLockedForTesting());
124   // Modify block data such that at least one page is non-zero.
125   memset(data, 0xff, kPageSize * kPageCount);
126 
127   mem->Unlock();
128   ASSERT_FALSE(mem->IsLockedForTesting());
129   // Forcefully discard at least one non-zero page.
130   mem->DiscardPage(5);
131 
132   // Locking when a page has been discarded should fail.
133   ASSERT_FALSE(mem->Lock());
134   // Locking after memory is deallocated should fail.
135   ASSERT_FALSE(mem->Lock());
136 
137   // Check that memory has been deallocated.
138   ASSERT_FALSE(mem->IsValid());
139 }
140 
141 }  // namespace base
142