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 <sys/mman.h>
17
18 #include "libpandabase/mem/mem.h"
19 #include "libpandabase/os/mem.h"
20 #include "libpandabase/utils/asan_interface.h"
21 #include "libpandabase/utils/logger.h"
22 #include "libpandabase/utils/math_helpers.h"
23 #include "runtime/mem/alloc_config.h"
24 #include "runtime/mem/humongous_obj_allocator-inl.h"
25 #include "runtime/tests/allocator_test_base.h"
26
27 namespace ark::mem {
28
29 using NonObjectHumongousObjAllocator = HumongousObjAllocator<EmptyAllocConfigWithCrossingMap>;
30
31 class HumongousObjAllocatorTest : public AllocatorTest<NonObjectHumongousObjAllocator> {
32 public:
33 NO_COPY_SEMANTIC(HumongousObjAllocatorTest);
34 NO_MOVE_SEMANTIC(HumongousObjAllocatorTest);
35
HumongousObjAllocatorTest()36 HumongousObjAllocatorTest()
37 {
38 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
39 // NOLINTNEXTLINE(readability-magic-numbers)
40 ark::mem::MemConfig::Initialize(0, 1024_MB, 0, 0, 0, 0);
41 PoolManager::Initialize();
42 }
43
~HumongousObjAllocatorTest()44 ~HumongousObjAllocatorTest() override
45 {
46 ClearPoolManager();
47 PoolManager::Finalize();
48 ark::mem::MemConfig::Finalize();
49 // Logger::Destroy();
50 }
51
52 protected:
53 static constexpr size_t MIN_ALLOC_SIZE = 1_MB;
54 static constexpr size_t MAX_ALLOC_SIZE = 9_MB;
55 static constexpr Alignment HUMONGOUS_LOG_MAX_ALIGN = LOG_ALIGN_11;
56 static constexpr size_t DEFAULT_POOL_SIZE_FOR_ALLOC =
57 NonObjectHumongousObjAllocator::GetMinPoolSize(MAX_ALLOC_SIZE);
58 static constexpr size_t POOL_HEADER_SIZE = sizeof(NonObjectHumongousObjAllocator::MemoryPoolHeader);
59
AddMemoryPoolToAllocator(NonObjectHumongousObjAllocator & alloc)60 void AddMemoryPoolToAllocator(NonObjectHumongousObjAllocator &alloc) override
61 {
62 AddMemoryPoolToAllocator(alloc, DEFAULT_POOL_SIZE_FOR_ALLOC);
63 }
64
AddMemoryPoolToAllocator(NonObjectHumongousObjAllocator & alloc,size_t size)65 void AddMemoryPoolToAllocator(NonObjectHumongousObjAllocator &alloc, size_t size)
66 {
67 os::memory::LockHolder lock(poolLock_);
68 size = AlignUp(size, PANDA_POOL_ALIGNMENT_IN_BYTES);
69 Pool pool = PoolManager::GetMmapMemPool()->AllocPool(AlignUp(size, PANDA_POOL_ALIGNMENT_IN_BYTES),
70 SpaceType::SPACE_TYPE_INTERNAL,
71 AllocatorType::HUMONGOUS_ALLOCATOR, &alloc);
72 ASSERT(pool.GetSize() >= size);
73 if (pool.GetMem() == nullptr) {
74 ASSERT_TRUE(0 && "Can't get a new pool from PoolManager");
75 }
76 allocatedPoolsByPoolManager_.push_back(pool);
77 if (!alloc.AddMemoryPool(pool.GetMem(), size)) {
78 ASSERT_TRUE(0 && "Can't add mem pool to allocator");
79 }
80 }
81
AddMemoryPoolToAllocatorProtected(NonObjectHumongousObjAllocator & alloc)82 void AddMemoryPoolToAllocatorProtected(NonObjectHumongousObjAllocator &alloc) override
83 {
84 // We use common PoolManager from Runtime. Therefore, we have the same pool allocation for both cases.
85 AddMemoryPoolToAllocator(alloc);
86 }
87
AllocatedByThisAllocator(NonObjectHumongousObjAllocator & allocator,void * mem)88 bool AllocatedByThisAllocator(NonObjectHumongousObjAllocator &allocator, void *mem) override
89 {
90 return allocator.AllocatedByHumongousObjAllocator(mem);
91 }
92
ClearPoolManager()93 void ClearPoolManager()
94 {
95 for (auto i : allocatedPoolsByPoolManager_) {
96 PoolManager::GetMmapMemPool()->FreePool(i.GetMem(), i.GetSize());
97 }
98 allocatedPoolsByPoolManager_.clear();
99 }
100
101 private:
102 std::vector<Pool> allocatedPoolsByPoolManager_;
103 // Mutex, which allows only one thread to add pool to the pool vector
104 os::memory::Mutex poolLock_;
105 };
106
TEST_F(HumongousObjAllocatorTest,CheckIncorrectMemoryPoolReusageTest)107 TEST_F(HumongousObjAllocatorTest, CheckIncorrectMemoryPoolReusageTest)
108 {
109 static constexpr size_t POOL_SIZE = 4_MB;
110 static constexpr Alignment OBJECT_ALIGNMENT = DEFAULT_ALIGNMENT;
111 static constexpr size_t FIRST_OBJECT_SIZE = POOL_SIZE - PANDA_POOL_ALIGNMENT_IN_BYTES;
112 static constexpr size_t SECOND_OBJECT_SIZE = POOL_SIZE - GetAlignmentInBytes(OBJECT_ALIGNMENT);
113 ASSERT(PANDA_POOL_ALIGNMENT_IN_BYTES > GetAlignmentInBytes(OBJECT_ALIGNMENT));
114 ASSERT_TRUE(NonObjectHumongousObjAllocator::GetMinPoolSize(FIRST_OBJECT_SIZE) == POOL_SIZE);
115 mem::MemStatsType memStats;
116 NonObjectHumongousObjAllocator allocator(&memStats);
117 AddMemoryPoolToAllocator(allocator, POOL_SIZE);
118 void *firstObject = allocator.Alloc(FIRST_OBJECT_SIZE, OBJECT_ALIGNMENT);
119 ASSERT_TRUE(firstObject != nullptr);
120 allocator.Free(firstObject);
121 void *secondObject = allocator.Alloc(SECOND_OBJECT_SIZE, OBJECT_ALIGNMENT);
122 ASSERT_TRUE(secondObject == nullptr);
123 }
124
TEST_F(HumongousObjAllocatorTest,SimpleAllocateDifferentObjSizeTest)125 TEST_F(HumongousObjAllocatorTest, SimpleAllocateDifferentObjSizeTest)
126 {
127 LOG(DEBUG, ALLOC) << "SimpleAllocateDifferentObjSizeTest";
128 auto *memStats = new mem::MemStatsType();
129 NonObjectHumongousObjAllocator allocator(memStats);
130 std::vector<void *> values;
131 // NOLINTNEXTLINE(readability-magic-numbers)
132 for (size_t i = 0; i < 20U; i++) {
133 size_t poolSize = DEFAULT_POOL_SIZE_FOR_ALLOC + PAGE_SIZE * i;
134 size_t allocSize = poolSize - sizeof(POOL_HEADER_SIZE) - GetAlignmentInBytes(LOG_ALIGN_MAX);
135 AddMemoryPoolToAllocator(allocator, poolSize);
136 void *mem = allocator.Alloc(allocSize);
137 ASSERT_TRUE(mem != nullptr);
138 values.push_back(mem);
139 LOG(DEBUG, ALLOC) << "Allocate obj with size " << allocSize << " at " << std::hex << mem;
140 }
141 for (auto i : values) {
142 allocator.Free(i);
143 }
144 // NOLINTNEXTLINE(readability-magic-numbers)
145 for (size_t i = 0; i < 20U; i++) {
146 void *mem = allocator.Alloc(MAX_ALLOC_SIZE);
147 ASSERT_TRUE(mem != nullptr);
148 }
149 delete memStats;
150 }
151
TEST_F(HumongousObjAllocatorTest,AllocateWriteFreeTest)152 TEST_F(HumongousObjAllocatorTest, AllocateWriteFreeTest)
153 {
154 static constexpr size_t ELEMENTS_COUNT = 100;
155 static constexpr size_t POOLS_COUNT = ELEMENTS_COUNT;
156 AllocateAndFree(MIN_ALLOC_SIZE, ELEMENTS_COUNT, POOLS_COUNT);
157 }
158
TEST_F(HumongousObjAllocatorTest,AllocateRandomFreeTest)159 TEST_F(HumongousObjAllocatorTest, AllocateRandomFreeTest)
160 {
161 static constexpr size_t ELEMENTS_COUNT = 100;
162 static constexpr size_t POOLS_COUNT = ELEMENTS_COUNT;
163 AllocateFreeDifferentSizesTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE>(ELEMENTS_COUNT, POOLS_COUNT);
164 }
165
TEST_F(HumongousObjAllocatorTest,AlignmentAllocTest)166 TEST_F(HumongousObjAllocatorTest, AlignmentAllocTest)
167 {
168 static constexpr size_t MAX_ALLOC = MIN_ALLOC_SIZE + 10U;
169 static constexpr size_t POOLS_COUNT =
170 (MAX_ALLOC - MIN_ALLOC_SIZE + 1) * (HUMONGOUS_LOG_MAX_ALIGN - LOG_ALIGN_MIN + 1);
171 AlignedAllocFreeTest<MIN_ALLOC_SIZE, MAX_ALLOC, LOG_ALIGN_MIN, HUMONGOUS_LOG_MAX_ALIGN>(POOLS_COUNT);
172 }
173
TEST_F(HumongousObjAllocatorTest,AllocateTooMuchTest)174 TEST_F(HumongousObjAllocatorTest, AllocateTooMuchTest)
175 {
176 static constexpr size_t ELEMENTS_COUNT = 2;
177 AllocateTooMuchTest(MIN_ALLOC_SIZE, ELEMENTS_COUNT);
178 }
179
TEST_F(HumongousObjAllocatorTest,ObjectIteratorTest)180 TEST_F(HumongousObjAllocatorTest, ObjectIteratorTest)
181 {
182 static constexpr size_t FREE_GRANULARITY = 1;
183 static constexpr size_t POOLS_COUNT = 50;
184 ObjectIteratorTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, LOG_ALIGN_MIN, HUMONGOUS_LOG_MAX_ALIGN>(FREE_GRANULARITY,
185 POOLS_COUNT);
186 }
187
TEST_F(HumongousObjAllocatorTest,ObjectCollectionTest)188 TEST_F(HumongousObjAllocatorTest, ObjectCollectionTest)
189 {
190 static constexpr size_t FREE_GRANULARITY = 1;
191 static constexpr size_t POOLS_COUNT = 50;
192 ObjectCollectionTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, LOG_ALIGN_MIN, HUMONGOUS_LOG_MAX_ALIGN>(FREE_GRANULARITY,
193 POOLS_COUNT);
194 }
195
TEST_F(HumongousObjAllocatorTest,ObjectIteratorInRangeTest)196 TEST_F(HumongousObjAllocatorTest, ObjectIteratorInRangeTest)
197 {
198 static constexpr size_t FREE_GRANULARITY = 4;
199 static constexpr size_t POOLS_COUNT = 50;
200 ObjectIteratorInRangeTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, LOG_ALIGN_MIN, HUMONGOUS_LOG_MAX_ALIGN>(
201 CrossingMapSingleton::GetCrossingMapGranularity(), FREE_GRANULARITY, POOLS_COUNT);
202 }
203
TEST_F(HumongousObjAllocatorTest,AsanTest)204 TEST_F(HumongousObjAllocatorTest, AsanTest)
205 {
206 static constexpr size_t ELEMENTS_COUNT = 100;
207 static constexpr size_t POOLS_COUNT = ELEMENTS_COUNT;
208 static constexpr size_t FREE_GRANULARITY = 3;
209 AsanTest<ELEMENTS_COUNT>(FREE_GRANULARITY, POOLS_COUNT);
210 }
211
TEST_F(HumongousObjAllocatorTest,VisitAndRemoveFreePoolsTest)212 TEST_F(HumongousObjAllocatorTest, VisitAndRemoveFreePoolsTest)
213 {
214 static_assert(PANDA_HUMONGOUS_OBJ_ALLOCATOR_RESERVED_MEM_MAX_POOL_SIZE < MAX_ALLOC_SIZE);
215 static constexpr size_t POOLS_COUNT = 5;
216 VisitAndRemoveFreePools<POOLS_COUNT>(MAX_ALLOC_SIZE);
217 }
218
TEST_F(HumongousObjAllocatorTest,AllocatedByHumongousObjAllocatorTest)219 TEST_F(HumongousObjAllocatorTest, AllocatedByHumongousObjAllocatorTest)
220 {
221 AllocatedByThisAllocatorTest();
222 }
223
TEST_F(HumongousObjAllocatorTest,MTAllocFreeTest)224 TEST_F(HumongousObjAllocatorTest, MTAllocFreeTest)
225 {
226 static constexpr size_t MIN_ELEMENTS_COUNT = 10;
227 static constexpr size_t MAX_ELEMENTS_COUNT = 20;
228 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32)
229 // We have an issue with QEMU during MT tests. Issue 2852
230 static constexpr size_t THREADS_COUNT = 1;
231 #else
232 static constexpr size_t THREADS_COUNT = 5;
233 #endif
234 static constexpr size_t MT_TEST_RUN_COUNT = 5;
235 for (size_t i = 0; i < MT_TEST_RUN_COUNT; i++) {
236 MtAllocFreeTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, THREADS_COUNT>(MIN_ELEMENTS_COUNT, MAX_ELEMENTS_COUNT);
237 ClearPoolManager();
238 }
239 }
240
TEST_F(HumongousObjAllocatorTest,MTAllocIterateTest)241 TEST_F(HumongousObjAllocatorTest, MTAllocIterateTest)
242 {
243 static constexpr size_t MIN_ELEMENTS_COUNT = 10;
244 static constexpr size_t MAX_ELEMENTS_COUNT = 20;
245 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32)
246 // We have an issue with QEMU during MT tests. Issue 2852
247 static constexpr size_t THREADS_COUNT = 1;
248 #else
249 static constexpr size_t THREADS_COUNT = 5;
250 #endif
251 static constexpr size_t MT_TEST_RUN_COUNT = 5;
252 for (size_t i = 0; i < MT_TEST_RUN_COUNT; i++) {
253 MtAllocIterateTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, THREADS_COUNT>(
254 MIN_ELEMENTS_COUNT, MAX_ELEMENTS_COUNT, CrossingMapSingleton::GetCrossingMapGranularity());
255 ClearPoolManager();
256 }
257 }
258
TEST_F(HumongousObjAllocatorTest,MTAllocCollectTest)259 TEST_F(HumongousObjAllocatorTest, MTAllocCollectTest)
260 {
261 static constexpr size_t MIN_ELEMENTS_COUNT = 10;
262 static constexpr size_t MAX_ELEMENTS_COUNT = 20;
263 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32)
264 // We have an issue with QEMU during MT tests. Issue 2852
265 static constexpr size_t THREADS_COUNT = 1;
266 #else
267 static constexpr size_t THREADS_COUNT = 5;
268 #endif
269 static constexpr size_t MT_TEST_RUN_COUNT = 5;
270 for (size_t i = 0; i < MT_TEST_RUN_COUNT; i++) {
271 MtAllocCollectTest<MIN_ALLOC_SIZE, MAX_ALLOC_SIZE, THREADS_COUNT>(MIN_ELEMENTS_COUNT, MAX_ELEMENTS_COUNT);
272 ClearPoolManager();
273 }
274 }
275
276 } // namespace ark::mem
277