1 /*
2  * Copyright (C) 2022 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 #define LOG_TAG "shared_memory_allocator_tests"
18 
19 #include <gtest/gtest.h>
20 #include <mediautils/SharedMemoryAllocator.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <utils/Log.h>
24 
25 using namespace android;
26 using namespace android::mediautils;
27 
28 namespace {
29 const size_t kPageSize = getpagesize();
30 constexpr size_t kMaxPageSize = 65536;  // arm64 supports 4k, 16k and 64k pages
31 
validate_block(const AllocationType & block)32 void validate_block(const AllocationType& block) {
33     ASSERT_TRUE(block != nullptr);
34     memset(block->unsecurePointer(), 10, kPageSize);
35     EXPECT_EQ(*(static_cast<char*>(block->unsecurePointer()) + 100), static_cast<char>(10));
36 }
37 
38 template <size_t N = 0, bool FatalOwn = true>
39 struct ValidateForwarding {
alignment__anon26ccdc380111::ValidateForwarding40     static size_t alignment() { return 1337; }
41 
owns__anon26ccdc380111::ValidateForwarding42     bool owns(const AllocationType& allocation) const {
43         if (allocation == owned) return true;
44         if constexpr (FatalOwn) {
45             LOG_ALWAYS_FATAL_IF(allocation != not_owned, "Invalid allocation passed to allocator");
46         }
47         return false;
48     }
49 
deallocate_all__anon26ccdc380111::ValidateForwarding50     void deallocate_all() { deallocate_all_count++; }
dump__anon26ccdc380111::ValidateForwarding51     std::string dump() const { return dump_string; }
52 
53     static inline size_t deallocate_all_count = 0;
54     static inline const AllocationType owned =
55             MemoryHeapBaseAllocator().allocate(BasicAllocRequest{kMaxPageSize});
56     static inline const AllocationType not_owned =
57             MemoryHeapBaseAllocator().allocate(BasicAllocRequest{kMaxPageSize});
58     static inline const std::string dump_string = std::to_string(N) + "Test Dump Forwarding";
59 };
60 
61 };  // namespace
62 static_assert(shared_allocator_impl::has_owns<MemoryHeapBaseAllocator> == false);
63 static_assert(shared_allocator_impl::has_dump<MemoryHeapBaseAllocator> == false);
64 static_assert(shared_allocator_impl::has_deallocate_all<MemoryHeapBaseAllocator> == false);
65 static_assert(shared_allocator_impl::has_owns<SnoopingAllocator<MemoryHeapBaseAllocator>> == true);
66 static_assert(shared_allocator_impl::has_dump<SnoopingAllocator<MemoryHeapBaseAllocator>> == true);
67 static_assert(
68         shared_allocator_impl::has_deallocate_all<SnoopingAllocator<MemoryHeapBaseAllocator>> ==
69         true);
70 static_assert(
71         shared_allocator_impl::has_owns<PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
72                                                         SizePolicy<kMaxPageSize>>> == true);
73 static_assert(
74         shared_allocator_impl::has_dump<PolicyAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
75                                                         SizePolicy<kMaxPageSize>>> == true);
76 static_assert(shared_allocator_impl::has_deallocate_all<PolicyAllocator<
77                       SnoopingAllocator<MemoryHeapBaseAllocator>, SizePolicy<kMaxPageSize>>> ==
78               true);
79 static_assert(shared_allocator_impl::has_owns<
80                       FallbackAllocator<SnoopingAllocator<MemoryHeapBaseAllocator>,
81                                         SnoopingAllocator<MemoryHeapBaseAllocator>>> == true);
82 
TEST(shared_memory_allocator_tests,roundup)83 TEST(shared_memory_allocator_tests, roundup) {
84     using namespace shared_allocator_impl;
85     EXPECT_EQ(roundup(1023, 1024), 1024ul);
86     EXPECT_EQ(roundup(1024, 1024), 1024ul);
87     EXPECT_EQ(roundup(1025, 1024), 2048ul);
88     EXPECT_DEATH(roundup(1023, 1023), "");
89     EXPECT_DEATH(roundup(1023, 0), "");
90 }
91 
TEST(shared_memory_allocator_tests,mheapbase_allocator)92 TEST(shared_memory_allocator_tests, mheapbase_allocator) {
93     MemoryHeapBaseAllocator allocator;
94     const auto memory = allocator.allocate(BasicAllocRequest{500});
95     ASSERT_TRUE(memory != nullptr);
96     const auto fd = dup(memory->getMemory()->getHeapID());
97     EXPECT_EQ(memory->size(), static_cast<unsigned>(kPageSize));
98     EXPECT_EQ(memory->size(), memory->getMemory()->getSize());
99     validate_block(memory);
100     allocator.deallocate(memory);
101     // Ensures we have closed the fd
102     EXPECT_EQ(memory->unsecurePointer(), nullptr);
103     EXPECT_EQ(memory->getMemory()->getBase(), nullptr);
104     struct stat st;
105     const auto err = fstat(fd, &st);
106     EXPECT_EQ(err, 0);
107     // Ensure we reclaim pages (overly-zealous)
108     EXPECT_EQ(st.st_size, 0);
109 }
110 
TEST(shared_memory_allocator_tests,mheapbase_allocator_independence)111 TEST(shared_memory_allocator_tests, mheapbase_allocator_independence) {
112     ASSERT_EQ(MemoryHeapBaseAllocator::alignment(), kPageSize);
113     MemoryHeapBaseAllocator allocator;
114     const auto first_memory = allocator.allocate(BasicAllocRequest{500});
115     const auto second_memory = allocator.allocate(BasicAllocRequest{500});
116     ASSERT_TRUE(first_memory != nullptr && second_memory != nullptr);
117     EXPECT_NE(first_memory->getMemory()->getHeapID(), second_memory->getMemory()->getHeapID());
118     allocator.deallocate(first_memory);
119     validate_block(second_memory);
120     allocator.deallocate(second_memory);
121 }
122 
TEST(shared_memory_allocator_tests,snooping_allocator)123 TEST(shared_memory_allocator_tests, snooping_allocator) {
124     ASSERT_EQ(SnoopingAllocator<ValidateForwarding<0>>::alignment(),
125               ValidateForwarding<0>::alignment());
126 
127     SnoopingAllocator<MemoryHeapBaseAllocator> allocator{"allocator"};
128     const auto first_memory = allocator.allocate(NamedAllocRequest{{500}, "allocate_1"});
129     auto second_memory = first_memory;
130     {
131         const auto tmp = allocator.allocate(NamedAllocRequest{{5000}, "allocate_2"});
132         // Test copying handle around
133         second_memory = tmp;
134     }
135     ASSERT_TRUE(first_memory && second_memory);
136     EXPECT_TRUE(allocator.owns(first_memory) && allocator.owns(second_memory));
137     const auto first_allocations = allocator.getAllocations();
138     EXPECT_EQ(first_allocations.size(), 2ull);
139     for (const auto& [key, val] : allocator.getAllocations()) {
140         if (val.allocation_number == 0) {
141             EXPECT_EQ(val.name, "allocate_1");
142             EXPECT_TRUE(first_memory == key);
143         }
144         if (val.allocation_number == 1) {
145             EXPECT_EQ(val.name, "allocate_2");
146             EXPECT_TRUE(second_memory == key);
147         }
148     }
149     // TODO test dump and deallocate forwarding
150     // EXPECT_EQ(allocator.dump(), std::string{});
151     validate_block(second_memory);
152     allocator.deallocate(second_memory);
153     EXPECT_EQ(second_memory->unsecurePointer(), nullptr);
154     EXPECT_FALSE(allocator.owns(second_memory));
155     EXPECT_TRUE(allocator.owns(first_memory));
156     const auto second_allocations = allocator.getAllocations();
157     EXPECT_EQ(second_allocations.size(), 1ul);
158     for (const auto& [key, val] : second_allocations) {
159         EXPECT_EQ(val.name, "allocate_1");
160         EXPECT_TRUE(first_memory == key);
161     }
162     // EXPECT_EQ(allocator.dump(), std::string{});
163     // TODO test deallocate_all O(1)
164 }
165 
166 // TODO generic policy test
TEST(shared_memory_allocator_tests,size_policy_allocator_enforcement)167 TEST(shared_memory_allocator_tests, size_policy_allocator_enforcement) {
168     PolicyAllocator allocator{MemoryHeapBaseAllocator{},
169                               SizePolicy<kMaxPageSize * 7, kMaxPageSize * 2, kMaxPageSize * 4>{}};
170     // Violate max size
171     EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize * 5}) == nullptr);
172     // Violate min alloc size
173     EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize}) == nullptr);
174     const auto first_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 4});
175     validate_block(first_memory);
176     // Violate pool size
177     EXPECT_TRUE(allocator.allocate(BasicAllocRequest{kMaxPageSize * 4}) == nullptr);
178     const auto second_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 3});
179     validate_block(second_memory);
180     allocator.deallocate(second_memory);
181     // Check pool size update after deallocation
182     const auto new_second_memory = allocator.allocate(BasicAllocRequest{kMaxPageSize * 2});
183     validate_block(new_second_memory);
184 }
185 
TEST(shared_memory_allocator_tests,indirect_allocator)186 TEST(shared_memory_allocator_tests, indirect_allocator) {
187     ASSERT_EQ(IndirectAllocator<ValidateForwarding<0>>::alignment(),
188               ValidateForwarding<0>::alignment());
189     const auto allocator_handle = std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>();
190     IndirectAllocator allocator{allocator_handle};
191     const auto memory = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation"});
192     EXPECT_TRUE(allocator_handle->owns(memory));
193     EXPECT_TRUE(allocator_handle->getAllocations().size() == 1);
194     allocator.deallocate(memory);
195     EXPECT_FALSE(allocator_handle->owns(memory));
196     EXPECT_TRUE(allocator_handle->getAllocations().size() == 0);
197 }
198 
TEST(shared_memory_allocator_tests,policy_allocator_forwarding)199 TEST(shared_memory_allocator_tests, policy_allocator_forwarding) {
200     // Test appropriate forwarding of allocator, deallocate
201     const auto primary_allocator =
202             std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>("allocator");
203     PolicyAllocator allocator{IndirectAllocator(primary_allocator), SizePolicy<kMaxPageSize>{}};
204     const auto memory = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation"});
205     EXPECT_TRUE(primary_allocator->owns(memory));
206     const auto& allocations = primary_allocator->getAllocations();
207     EXPECT_TRUE(allocations.size() == 1);
208     allocator.deallocate(memory);
209     EXPECT_TRUE(allocations.size() == 0);
210     const auto memory2 = allocator.allocate(NamedAllocRequest{{kPageSize}, "allocation_2"});
211     EXPECT_TRUE(allocations.size() == 1);
212     EXPECT_TRUE(primary_allocator->owns(memory2));
213     allocator.deallocate(memory2);
214     EXPECT_FALSE(primary_allocator->owns(memory2));
215     EXPECT_TRUE(allocations.size() == 0);
216     // Test appropriate forwarding of own, dump, alignment, deallocate_all
217     PolicyAllocator allocator2{ValidateForwarding<0>{}, SizePolicy<kMaxPageSize>{}};
218     EXPECT_TRUE(allocator2.owns(ValidateForwarding<0>::owned));
219     EXPECT_FALSE(allocator2.owns(ValidateForwarding<0>::not_owned));
220     EXPECT_TRUE(allocator2.dump().find(ValidateForwarding<0>::dump_string) != std::string::npos);
221     ASSERT_EQ(decltype(allocator2)::alignment(), ValidateForwarding<0>::alignment());
222     size_t prev = ValidateForwarding<0>::deallocate_all_count;
223     allocator2.deallocate_all();
224     EXPECT_EQ(ValidateForwarding<0>::deallocate_all_count, prev + 1);
225 }
226 
TEST(shared_memory_allocator_tests,snooping_allocator_nullptr)227 TEST(shared_memory_allocator_tests, snooping_allocator_nullptr) {
228     SnoopingAllocator allocator{
229             PolicyAllocator{MemoryHeapBaseAllocator{}, SizePolicy<kMaxPageSize * 2>{}}};
230     const auto memory = allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_1"});
231     validate_block(memory);
232     ASSERT_TRUE(allocator.allocate(NamedAllocRequest{{kMaxPageSize * 3}, "allocation_2"}) ==
233                 nullptr);
234     const auto& allocations = allocator.getAllocations();
235     EXPECT_EQ(allocations.size(), 1ul);
236     for (const auto& [key, val] : allocations) {
237         EXPECT_EQ(val.name, "allocation_1");
238         EXPECT_EQ(val.allocation_number, 0ul);
239         EXPECT_TRUE(key == memory);
240     }
241 }
242 
TEST(shared_memory_allocator_tests,fallback_allocator)243 TEST(shared_memory_allocator_tests, fallback_allocator) {
244     // Construct Fallback Allocator
245     const auto primary_allocator = std::make_shared<
246             SnoopingAllocator<PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<kMaxPageSize>>>>(
247             PolicyAllocator<MemoryHeapBaseAllocator, SizePolicy<kMaxPageSize>>{},
248             "primary_allocator");
249     const auto secondary_allocator =
250             std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>("secondary_allocator");
251 
252     FallbackAllocator fallback_allocator{SnoopingAllocator{IndirectAllocator{primary_allocator}},
253                                          SnoopingAllocator{IndirectAllocator{secondary_allocator}}};
254     ASSERT_EQ(decltype(fallback_allocator)::alignment(), kPageSize);
255     // Basic Allocation Test
256     const auto memory =
257             fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_1"});
258     validate_block(memory);
259     // Correct allocator selected
260     EXPECT_TRUE(fallback_allocator.owns(memory));
261     EXPECT_TRUE(primary_allocator->owns(memory));
262     EXPECT_FALSE(secondary_allocator->owns(memory));
263     // Test fallback allocation
264     const auto memory2 =
265             fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_2"});
266     validate_block(memory2);
267     // Correct allocator selected
268     EXPECT_TRUE(fallback_allocator.owns(memory2));
269     EXPECT_FALSE(primary_allocator->owns(memory2));
270     EXPECT_TRUE(secondary_allocator->owns(memory2));
271     // Allocations ended up in the correct allocators
272     const auto& primary_allocations = primary_allocator->getAllocations();
273     EXPECT_TRUE(primary_allocations.size() == 1ul);
274     ASSERT_TRUE(primary_allocations.find(memory) != primary_allocations.end());
275     EXPECT_EQ(primary_allocations.find(memory)->second.name, std::string{"allocation_1"});
276     const auto& secondary_allocations = secondary_allocator->getAllocations();
277     EXPECT_TRUE(secondary_allocations.size() == 1ul);
278     ASSERT_TRUE(secondary_allocations.find(memory2) != secondary_allocations.end());
279     EXPECT_EQ(secondary_allocations.find(memory2)->second.name, std::string{"allocation_2"});
280     // Test deallocate appropriate forwarding
281     fallback_allocator.deallocate(memory);
282     EXPECT_TRUE(primary_allocator->getAllocations().size() == 0ul);
283     EXPECT_TRUE(secondary_allocator->getAllocations().size() == 1ul);
284     // Appropriate fallback after deallocation
285     const auto memory3 =
286             fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_3"});
287     EXPECT_TRUE(fallback_allocator.owns(memory3));
288     EXPECT_TRUE(primary_allocator->owns(memory3));
289     EXPECT_FALSE(secondary_allocator->owns(memory3));
290     EXPECT_TRUE(primary_allocator->getAllocations().size() == 1ul);
291     // Test deallocate appropriate forwarding
292     EXPECT_TRUE(secondary_allocator->getAllocations().size() == 1ul);
293     fallback_allocator.deallocate(memory2);
294     EXPECT_TRUE(secondary_allocator->getAllocations().size() == 0ul);
295     const auto memory4 =
296             fallback_allocator.allocate(NamedAllocRequest{{kMaxPageSize}, "allocation_4"});
297     EXPECT_TRUE(fallback_allocator.owns(memory4));
298     EXPECT_FALSE(primary_allocator->owns(memory4));
299     EXPECT_TRUE(secondary_allocator->owns(memory4));
300     // Allocations ended up in the correct allocators
301     EXPECT_TRUE(primary_allocator->getAllocations().size() == 1ul);
302     EXPECT_TRUE(secondary_allocator->getAllocations().size() == 1ul);
303     ASSERT_TRUE(primary_allocations.find(memory3) != primary_allocations.end());
304     EXPECT_EQ(primary_allocations.find(memory3)->second.name, std::string{"allocation_3"});
305     ASSERT_TRUE(secondary_allocations.find(memory4) != secondary_allocations.end());
306     EXPECT_EQ(secondary_allocations.find(memory4)->second.name, std::string{"allocation_4"});
307 }
308 
TEST(shared_memory_allocator_tests,fallback_allocator_forwarding)309 TEST(shared_memory_allocator_tests, fallback_allocator_forwarding) {
310     // Test forwarding
311     using Alloc1 = ValidateForwarding<0, false>;
312     using Alloc2 = ValidateForwarding<1, false>;
313     FallbackAllocator forward_test{Alloc1{}, Alloc2{}};
314     EXPECT_TRUE(forward_test.dump().find(Alloc1::dump_string) != std::string::npos);
315     EXPECT_TRUE(forward_test.dump().find(Alloc2::dump_string) != std::string::npos);
316     // Test owned forwarding
317     EXPECT_TRUE(forward_test.owns(Alloc1::owned));
318     EXPECT_TRUE(forward_test.owns(Alloc2::owned));
319     EXPECT_FALSE(forward_test.owns(Alloc1::not_owned));
320     EXPECT_FALSE(forward_test.owns(Alloc2::not_owned));
321     // Test alignment forwarding
322     ASSERT_EQ(decltype(forward_test)::alignment(), Alloc1::alignment());
323     // Test deallocate_all forwarding
324     size_t prev1 = Alloc1::deallocate_all_count;
325     size_t prev2 = Alloc2::deallocate_all_count;
326     forward_test.deallocate_all();
327     EXPECT_EQ(prev1 + 1, Alloc1::deallocate_all_count);
328     EXPECT_EQ(prev2 + 1, Alloc2::deallocate_all_count);
329 }
330 
TEST(shared_memory_allocator_tests,scoped_allocator)331 TEST(shared_memory_allocator_tests, scoped_allocator) {
332     const auto underlying_allocator =
333             std::make_shared<SnoopingAllocator<MemoryHeapBaseAllocator>>("Allocator");
334     ScopedAllocator allocator{underlying_allocator};
335     const auto& allocations = underlying_allocator->getAllocations();
336     {
337         decltype(allocator.allocate(NamedAllocRequest{})) copy;
338         {
339             EXPECT_EQ(allocations.size(), 0ul);
340             const auto memory = allocator.allocate(NamedAllocRequest{{3000}, "allocation_1"});
341             copy = memory;
342             EXPECT_EQ(allocations.size(), 1ul);
343             EXPECT_TRUE(allocator.owns(copy));
344             EXPECT_TRUE(allocator.owns(memory));
345         }
346         EXPECT_TRUE(allocator.owns(copy));
347         EXPECT_EQ(allocations.size(), 1ul);
348         for (const auto& [key, value] : allocations) {
349             EXPECT_EQ(value.name, std::string{"allocation_1"});
350         }
351     }
352     EXPECT_EQ(allocations.size(), 0ul);
353     // Test forwarding
354     ASSERT_EQ(ScopedAllocator<ValidateForwarding<0>>::alignment(),
355               ValidateForwarding<0>::alignment());
356     ScopedAllocator<ValidateForwarding<0>> forwarding{};
357     EXPECT_EQ(forwarding.dump(), ValidateForwarding<0>::dump_string);
358 }
359