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