1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "libpandabase/mem/mem.h"
17 #include "libpandabase/os/mem.h"
18 #include "libpandabase/utils/logger.h"
19 #include "runtime/tests/allocator_test_base.h"
20 #include "runtime/mem/internal_allocator-inl.h"
21
22 #include <gtest/gtest.h>
23
24 namespace ark::mem::test {
25
26 class InternalAllocatorTest : public testing::Test {
27 public:
InternalAllocatorTest()28 InternalAllocatorTest()
29 {
30 ark::mem::MemConfig::Initialize(0, MEMORY_POOL_SIZE, 0, 0, 0, 0);
31 PoolManager::Initialize();
32 memStats_ = new mem::MemStatsType();
33 allocator_ = new InternalAllocatorT<InternalAllocatorConfig::PANDA_ALLOCATORS>(memStats_);
34 }
35
~InternalAllocatorTest()36 ~InternalAllocatorTest() override
37 {
38 delete static_cast<Allocator *>(allocator_);
39 PoolManager::Finalize();
40 ark::mem::MemConfig::Finalize();
41 delete memStats_;
42 }
43
44 NO_COPY_SEMANTIC(InternalAllocatorTest);
45 NO_MOVE_SEMANTIC(InternalAllocatorTest);
46
47 protected:
48 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
49 InternalAllocatorPtr allocator_;
50
51 static constexpr size_t MEMORY_POOL_SIZE = 16_MB;
52
InfinitiveAllocate(size_t allocSize)53 void InfinitiveAllocate(size_t allocSize)
54 {
55 void *mem = nullptr;
56 do {
57 mem = allocator_->Alloc(allocSize);
58 } while (mem != nullptr);
59 }
60
61 // Check that we don't have OOM and there is free space for mem pools
CheckFreeSpaceForPools()62 bool CheckFreeSpaceForPools()
63 {
64 size_t currentSpaceSize = PoolManager::GetMmapMemPool()
65 ->nonObjectSpacesCurrentSize_[SpaceTypeToIndex(SpaceType::SPACE_TYPE_INTERNAL)];
66 size_t maxSpaceSize =
67 PoolManager::GetMmapMemPool()->nonObjectSpacesMaxSize_[SpaceTypeToIndex(SpaceType::SPACE_TYPE_INTERNAL)];
68 ASSERT(currentSpaceSize <= maxSpaceSize);
69 return (maxSpaceSize - currentSpaceSize) >= InternalAllocator<>::RunSlotsAllocatorT::GetMinPoolSize();
70 }
71
72 private:
73 mem::MemStatsType *memStats_;
74 };
75
TEST_F(InternalAllocatorTest,AvoidInfiniteLoopTest)76 TEST_F(InternalAllocatorTest, AvoidInfiniteLoopTest)
77 {
78 // Regular object sizes
79 InfinitiveAllocate(RunSlots<>::MaxSlotSize());
80 // Large object sizes
81 InfinitiveAllocate(FreeListAllocator<EmptyMemoryConfig>::GetMaxSize());
82 // Humongous object sizes
83 InfinitiveAllocate(FreeListAllocator<EmptyMemoryConfig>::GetMaxSize() + 1);
84 }
85
86 struct A {
87 NO_COPY_SEMANTIC(A);
88 NO_MOVE_SEMANTIC(A);
89
90 static size_t count_;
Aark::mem::test::A91 A()
92 {
93 value = ++count_;
94 }
~Aark::mem::test::A95 ~A()
96 {
97 --count_;
98 }
99
100 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
101 uint8_t value;
102 };
103
104 size_t A::count_ = 0;
105
TEST_F(InternalAllocatorTest,NewDeleteArray)106 TEST_F(InternalAllocatorTest, NewDeleteArray)
107 {
108 constexpr size_t COUNT = 5;
109
110 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
111 auto arr = allocator_->New<A[]>(COUNT);
112 ASSERT_NE(arr, nullptr);
113 ASSERT_EQ(ToUintPtr(arr) % DEFAULT_INTERNAL_ALIGNMENT_IN_BYTES, 0);
114 ASSERT_EQ(A::count_, COUNT);
115 for (uint8_t i = 1; i <= COUNT; ++i) {
116 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
117 ASSERT_EQ(arr[i - 1].value, i);
118 }
119 allocator_->DeleteArray(arr);
120 ASSERT_EQ(A::count_, 0);
121 }
122
TEST_F(InternalAllocatorTest,ZeroSizeTest)123 TEST_F(InternalAllocatorTest, ZeroSizeTest)
124 {
125 ASSERT(allocator_->Alloc(0) == nullptr);
126 // Check that zero-size allocation did not result in infinite pool allocations
127 ASSERT(CheckFreeSpaceForPools());
128
129 // Checks on correct allocations of different size
130 // Regular object size
131 void *mem = allocator_->Alloc(RunSlots<>::MaxSlotSize());
132 ASSERT(mem != nullptr);
133 allocator_->Free(mem);
134
135 // Large object size
136 mem = allocator_->Alloc(FreeListAllocator<EmptyMemoryConfig>::GetMaxSize());
137 ASSERT(mem != nullptr);
138 allocator_->Free(mem);
139
140 // Humongous object size
141 mem = allocator_->Alloc(FreeListAllocator<EmptyMemoryConfig>::GetMaxSize() + 1);
142 ASSERT(mem != nullptr);
143 allocator_->Free(mem);
144 }
145
TEST_F(InternalAllocatorTest,AllocAlignmentTest)146 TEST_F(InternalAllocatorTest, AllocAlignmentTest)
147 {
148 constexpr size_t ALIGNMENT = DEFAULT_INTERNAL_ALIGNMENT_IN_BYTES * 2U;
149 constexpr size_t N = RunSlots<>::MaxSlotSize() + DEFAULT_INTERNAL_ALIGNMENT_IN_BYTES;
150
151 struct alignas(ALIGNMENT) S {
152 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
153 uint8_t a[N];
154 };
155
156 auto isAligned = [](void *ptr) { return IsAligned(reinterpret_cast<uintptr_t>(ptr), ALIGNMENT); };
157
158 auto *ptr = allocator_->Alloc(N);
159 if (!isAligned(ptr)) {
160 allocator_->Free(ptr);
161 ptr = nullptr;
162 }
163
164 {
165 auto *p = allocator_->AllocArray<S>(1);
166 ASSERT_TRUE(isAligned(p));
167 allocator_->Free(p);
168 }
169
170 {
171 auto *p = allocator_->New<S>();
172 ASSERT_TRUE(isAligned(p));
173 allocator_->Delete(p);
174 }
175
176 {
177 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
178 auto *p = allocator_->New<S[]>(1);
179 ASSERT_TRUE(isAligned(p));
180 allocator_->DeleteArray(p);
181 }
182
183 if (ptr != nullptr) {
184 allocator_->Free(ptr);
185 }
186 }
187
TEST_F(InternalAllocatorTest,AllocLocalAlignmentTest)188 TEST_F(InternalAllocatorTest, AllocLocalAlignmentTest)
189 {
190 constexpr size_t ALIGNMENT = DEFAULT_INTERNAL_ALIGNMENT_IN_BYTES * 2U;
191 constexpr size_t N = RunSlots<>::MaxSlotSize() + DEFAULT_INTERNAL_ALIGNMENT_IN_BYTES;
192
193 struct alignas(ALIGNMENT) S {
194 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
195 uint8_t a[N];
196 };
197
198 auto isAligned = [](void *ptr) { return IsAligned(reinterpret_cast<uintptr_t>(ptr), ALIGNMENT); };
199
200 auto *ptr = allocator_->AllocLocal(N);
201 if (!isAligned(ptr)) {
202 allocator_->Free(ptr);
203 ptr = nullptr;
204 }
205
206 {
207 auto *p = allocator_->AllocArrayLocal<S>(1);
208 ASSERT_TRUE(isAligned(p));
209 allocator_->Free(p);
210 }
211
212 {
213 auto *p = allocator_->AllocArrayLocal<S>(1);
214 ASSERT_TRUE(isAligned(p));
215 allocator_->Free(p);
216 }
217
218 {
219 auto *p = allocator_->NewLocal<S>();
220 ASSERT_TRUE(isAligned(p));
221 allocator_->Delete(p);
222 }
223
224 {
225 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
226 auto *p = allocator_->NewLocal<S[]>(1);
227 ASSERT_TRUE(isAligned(p));
228 allocator_->DeleteArray(p);
229 }
230
231 if (ptr != nullptr) {
232 allocator_->Free(ptr);
233 }
234 }
235
TEST_F(InternalAllocatorTest,MoveContainerTest)236 TEST_F(InternalAllocatorTest, MoveContainerTest)
237 {
238 using TestValueType = int;
239 constexpr auto MAGIC_VALUE = TestValueType {};
240 AllocatorAdapter<TestValueType> adapter = allocator_->Adapter();
241 using TestVector = std::vector<TestValueType, decltype(adapter)>;
242 TestVector vector1(adapter);
243 TestVector vector2(adapter);
244 // Swap
245 auto vector3 = std::move(vector2);
246 vector2 = std::move(vector1);
247 vector1 = std::move(vector3);
248 vector2.emplace_back(MAGIC_VALUE);
249 ASSERT_EQ(vector2.back(), MAGIC_VALUE);
250 ASSERT_EQ(vector2.get_allocator(), adapter);
251
252 using TestDeque = std::deque<TestValueType, decltype(adapter)>;
253 TestDeque *dequeTmp = allocator_->New<TestDeque>(allocator_->Adapter());
254 *dequeTmp = TestDeque(allocator_->Adapter());
255 dequeTmp->push_back(MAGIC_VALUE);
256 ASSERT_EQ(dequeTmp->get_allocator(), allocator_->Adapter());
257 ASSERT_EQ(dequeTmp->back(), MAGIC_VALUE);
258 allocator_->Delete(dequeTmp);
259 }
260
261 } // namespace ark::mem::test
262