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 <ctime>
17
18 #include "gtest/gtest.h"
19 #include "runtime/mem/gc/heap-space-misc/crossing_map.h"
20 #include "runtime/mem/internal_allocator-inl.h"
21
22 namespace ark::mem {
23
24 class CrossingMapTest : public testing::Test {
25 public:
CrossingMapTest()26 CrossingMapTest()
27 {
28 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
29 #ifdef PANDA_NIGHTLY_TEST_ON
30 seed_ = std::time(NULL);
31 #else
32 // NOLINTNEXTLINE(readability-magic-numbers)
33 seed_ = 0xDEADBEEF;
34 #endif
35 srand(seed_);
36 ark::mem::MemConfig::Initialize(MEMORY_POOL_SIZE, MEMORY_POOL_SIZE, 0, 0, 0, 0);
37 PoolManager::Initialize();
38 startAddr_ = GetPoolMinAddress();
39 memStats_ = new mem::MemStatsType();
40 internalAllocator_ = new InternalAllocatorT<InternalAllocatorConfig::PANDA_ALLOCATORS>(memStats_);
41 crossingMap_ = new CrossingMap(internalAllocator_, startAddr_, GetPoolSize());
42 crossingMap_->Initialize();
43 crossingMap_->InitializeCrossingMapForMemory(ToVoidPtr(startAddr_), GetPoolSize());
44 }
45
~CrossingMapTest()46 ~CrossingMapTest() override
47 {
48 crossingMap_->RemoveCrossingMapForMemory(ToVoidPtr(startAddr_), GetPoolSize());
49 crossingMap_->Destroy();
50 delete crossingMap_;
51 delete static_cast<Allocator *>(internalAllocator_);
52 PoolManager::Finalize();
53 ark::mem::MemConfig::Finalize();
54 // Logger::Destroy();
55 delete memStats_;
56 }
57
58 NO_COPY_SEMANTIC(CrossingMapTest);
59 NO_MOVE_SEMANTIC(CrossingMapTest);
60
61 protected:
GetCrossingMap()62 CrossingMap *GetCrossingMap()
63 {
64 return crossingMap_;
65 }
66
GetRandomObjAddr(size_t size)67 void *GetRandomObjAddr(size_t size)
68 {
69 ASSERT(size < GetPoolSize());
70 // NOLINTNEXTLINE(cert-msc50-cpp)
71 uintptr_t randOffset = rand() % (GetPoolSize() - size);
72 randOffset = (randOffset >> CrossingMap::CROSSING_MAP_OBJ_ALIGNMENT) << CrossingMap::CROSSING_MAP_OBJ_ALIGNMENT;
73 return ToVoidPtr(startAddr_ + randOffset);
74 }
75
AddPage(void * addr)76 void *AddPage(void *addr)
77 {
78 return ToVoidPtr(ToUintPtr(addr) + PAGE_SIZE);
79 }
80
IncreaseAddr(void * addr,size_t value)81 void *IncreaseAddr(void *addr, size_t value)
82 {
83 return ToVoidPtr(ToUintPtr(addr) + value);
84 }
85
DecreaseAddr(void * addr,size_t value)86 void *DecreaseAddr(void *addr, size_t value)
87 {
88 return ToVoidPtr(ToUintPtr(addr) - value);
89 }
90
GetMapNumFromAddr(void * addr)91 size_t GetMapNumFromAddr(void *addr)
92 {
93 return crossingMap_->GetMapNumFromAddr(addr);
94 }
95
96 static constexpr size_t MIN_GAP_BETWEEN_OBJECTS = 1U << CrossingMap::CROSSING_MAP_OBJ_ALIGNMENT;
97
98 static constexpr size_t MEMORY_POOL_SIZE = 64_MB;
99
100 static constexpr size_t POOLS_SIZE = CrossingMap::CROSSING_MAP_STATIC_ARRAY_GRANULARITY;
101
GetPoolMinAddress()102 uintptr_t GetPoolMinAddress()
103 {
104 return PoolManager::GetMmapMemPool()->GetMinObjectAddress();
105 }
106
GetPoolSize()107 size_t GetPoolSize()
108 {
109 return PoolManager::GetMmapMemPool()->GetMaxObjectAddress() -
110 PoolManager::GetMmapMemPool()->GetMinObjectAddress();
111 }
112
GetLastObjectByte(void * objAddr,size_t objSize)113 void *GetLastObjectByte(void *objAddr, size_t objSize)
114 {
115 ASSERT(objSize != 0);
116 return ToVoidPtr(ToUintPtr(objAddr) + objSize - 1U);
117 }
118
GetSeed()119 unsigned int GetSeed()
120 {
121 return seed_;
122 }
123
124 private:
125 unsigned int seed_;
126 InternalAllocatorPtr internalAllocator_;
127 uintptr_t startAddr_;
128 CrossingMap *crossingMap_;
129 mem::MemStatsType *memStats_;
130 };
131
TEST_F(CrossingMapTest,OneSmallObjTest)132 TEST_F(CrossingMapTest, OneSmallObjTest)
133 {
134 static constexpr size_t OBJ_SIZE = 1;
135 // Use OBJ_SIZE + PAGE_SIZE here or we can get an overflow during AddPage(obj_addr)
136 void *objAddr = GetRandomObjAddr(OBJ_SIZE + PAGE_SIZE);
137 GetCrossingMap()->AddObject(objAddr, OBJ_SIZE);
138 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(objAddr, objAddr) == objAddr) << " seed = " << GetSeed();
139 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(AddPage(objAddr), AddPage(objAddr)) == nullptr)
140 << " seed = " << GetSeed();
141 }
142
TEST_F(CrossingMapTest,BigSmallObjTest)143 TEST_F(CrossingMapTest, BigSmallObjTest)
144 {
145 static constexpr size_t OBJ_SIZE = PAGE_SIZE * 2U;
146 void *objAddr = GetRandomObjAddr(OBJ_SIZE);
147 GetCrossingMap()->AddObject(objAddr, OBJ_SIZE);
148 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(objAddr, ToVoidPtr(ToUintPtr(objAddr) + OBJ_SIZE)) == objAddr)
149 << " seed = " << GetSeed();
150 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
151 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(AddPage(objAddr), ToVoidPtr(ToUintPtr(objAddr) + OBJ_SIZE)) ==
152 objAddr)
153 << " seed = " << GetSeed();
154 }
155 GetCrossingMap()->RemoveObject(objAddr, OBJ_SIZE);
156 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(objAddr, ToVoidPtr(ToUintPtr(objAddr) + OBJ_SIZE)) == nullptr)
157 << " seed = " << GetSeed();
158 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
159 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(AddPage(objAddr), ToVoidPtr(ToUintPtr(objAddr) + OBJ_SIZE)) ==
160 nullptr)
161 << " seed = " << GetSeed();
162 }
163 }
164
TEST_F(CrossingMapTest,HugeObjTest)165 TEST_F(CrossingMapTest, HugeObjTest)
166 {
167 static constexpr size_t OBJ_SIZE = MEMORY_POOL_SIZE >> 1U;
168 void *objAddr = GetRandomObjAddr(OBJ_SIZE);
169 GetCrossingMap()->AddObject(objAddr, OBJ_SIZE);
170 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(objAddr, objAddr) == objAddr) << " seed = " << GetSeed();
171 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
172 for (size_t i = 1_MB; i < OBJ_SIZE; i += 1_MB) {
173 void *addr = ToVoidPtr(ToUintPtr(objAddr) + i);
174 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(addr, addr) == objAddr) << " seed = " << GetSeed();
175 }
176 }
177 GetCrossingMap()->RemoveObject(objAddr, OBJ_SIZE);
178 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(objAddr, objAddr) == nullptr) << " seed = " << GetSeed();
179 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
180 for (size_t i = 1_MB; i < OBJ_SIZE; i += 1_MB) {
181 void *addr = ToVoidPtr(ToUintPtr(objAddr) + i);
182 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(addr, addr) == nullptr) << " seed = " << GetSeed();
183 }
184 }
185 }
186
TEST_F(CrossingMapTest,TwoSequentialObjectsTest)187 TEST_F(CrossingMapTest, TwoSequentialObjectsTest)
188 {
189 static constexpr size_t FIRST_OBJ_SIZE = MIN_GAP_BETWEEN_OBJECTS;
190 static constexpr size_t SECOND_OBJ_SIZE = 1_KB;
191 // Add some extra memory for possible shifts
192 void *firstObjAddr = GetRandomObjAddr(FIRST_OBJ_SIZE + SECOND_OBJ_SIZE + FIRST_OBJ_SIZE);
193 void *secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
194 // We must be sure that these objects will be saved in the same locations
195 if (GetMapNumFromAddr(firstObjAddr) != GetMapNumFromAddr(secondObjAddr)) {
196 firstObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
197 secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
198 ASSERT_TRUE(GetMapNumFromAddr(firstObjAddr) == GetMapNumFromAddr(secondObjAddr)) << " seed = " << GetSeed();
199 }
200 GetCrossingMap()->AddObject(firstObjAddr, FIRST_OBJ_SIZE);
201 GetCrossingMap()->AddObject(secondObjAddr, SECOND_OBJ_SIZE);
202
203 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, firstObjAddr) == firstObjAddr)
204 << " seed = " << GetSeed();
205
206 GetCrossingMap()->RemoveObject(firstObjAddr, FIRST_OBJ_SIZE, secondObjAddr);
207 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, firstObjAddr) == secondObjAddr)
208 << " seed = " << GetSeed();
209
210 GetCrossingMap()->RemoveObject(secondObjAddr, SECOND_OBJ_SIZE);
211 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, firstObjAddr) == nullptr) << " seed = " << GetSeed();
212 }
213
TEST_F(CrossingMapTest,TwoNonSequentialObjectsTest)214 TEST_F(CrossingMapTest, TwoNonSequentialObjectsTest)
215 {
216 static constexpr size_t FIRST_OBJ_SIZE = MIN_GAP_BETWEEN_OBJECTS;
217 static constexpr size_t GAP_BETWEEN_OBJECTS = 1_MB;
218 static constexpr size_t SECOND_OBJ_SIZE = 1_KB;
219 // Add some extra memory for possible shifts
220 void *firstObjAddr = GetRandomObjAddr(FIRST_OBJ_SIZE + SECOND_OBJ_SIZE + GAP_BETWEEN_OBJECTS);
221 void *secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE + GAP_BETWEEN_OBJECTS);
222
223 GetCrossingMap()->AddObject(firstObjAddr, FIRST_OBJ_SIZE);
224 GetCrossingMap()->AddObject(secondObjAddr, SECOND_OBJ_SIZE);
225
226 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, secondObjAddr) == firstObjAddr)
227 << " seed = " << GetSeed();
228
229 GetCrossingMap()->RemoveObject(firstObjAddr, FIRST_OBJ_SIZE, secondObjAddr);
230 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, firstObjAddr) == nullptr) << " seed = " << GetSeed();
231 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, secondObjAddr) == secondObjAddr)
232 << " seed = " << GetSeed();
233
234 GetCrossingMap()->RemoveObject(secondObjAddr, SECOND_OBJ_SIZE);
235 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(firstObjAddr, secondObjAddr) == nullptr) << " seed = " << GetSeed();
236 }
237
TEST_F(CrossingMapTest,ThreeSequentialObjectsTest)238 TEST_F(CrossingMapTest, ThreeSequentialObjectsTest)
239 {
240 static constexpr size_t FIRST_OBJ_SIZE = 4_MB;
241 static constexpr size_t SECOND_OBJ_SIZE = MIN_GAP_BETWEEN_OBJECTS;
242 static constexpr size_t THIRD_OBJ_SIZE = 1_KB;
243 auto seedInfo = " seed = " + std::to_string(GetSeed());
244
245 // Add some extra memory for possible shifts
246 void *firstObjAddr = GetRandomObjAddr(FIRST_OBJ_SIZE + SECOND_OBJ_SIZE + THIRD_OBJ_SIZE + 3U * SECOND_OBJ_SIZE);
247 void *secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
248 void *thirdObjAddr = IncreaseAddr(secondObjAddr, SECOND_OBJ_SIZE);
249
250 // We must be sure that the first object will cross the borders for the second one
251 if (GetMapNumFromAddr(GetLastObjectByte(firstObjAddr, FIRST_OBJ_SIZE)) != GetMapNumFromAddr(secondObjAddr)) {
252 firstObjAddr = IncreaseAddr(firstObjAddr, SECOND_OBJ_SIZE);
253 secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
254 thirdObjAddr = IncreaseAddr(secondObjAddr, SECOND_OBJ_SIZE);
255 ASSERT_TRUE(GetMapNumFromAddr(GetLastObjectByte(firstObjAddr, FIRST_OBJ_SIZE)) ==
256 GetMapNumFromAddr(secondObjAddr))
257 << seedInfo;
258 }
259
260 // We must be sure that the second and the third object will be saved in the same locations
261 if (GetMapNumFromAddr(secondObjAddr) != GetMapNumFromAddr(thirdObjAddr)) {
262 firstObjAddr = IncreaseAddr(firstObjAddr, 2U * SECOND_OBJ_SIZE);
263 secondObjAddr = IncreaseAddr(firstObjAddr, FIRST_OBJ_SIZE);
264 thirdObjAddr = IncreaseAddr(secondObjAddr, SECOND_OBJ_SIZE);
265 ASSERT_TRUE(GetMapNumFromAddr(GetLastObjectByte(firstObjAddr, FIRST_OBJ_SIZE)) ==
266 GetMapNumFromAddr(secondObjAddr))
267 << seedInfo;
268 ASSERT_TRUE(GetMapNumFromAddr(secondObjAddr) == GetMapNumFromAddr(thirdObjAddr)) << seedInfo;
269 }
270
271 GetCrossingMap()->AddObject(firstObjAddr, FIRST_OBJ_SIZE);
272 GetCrossingMap()->AddObject(secondObjAddr, SECOND_OBJ_SIZE);
273 GetCrossingMap()->AddObject(thirdObjAddr, THIRD_OBJ_SIZE);
274
275 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
276 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == firstObjAddr) << seedInfo;
277 } else {
278 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == secondObjAddr) << seedInfo;
279 }
280
281 GetCrossingMap()->RemoveObject(secondObjAddr, SECOND_OBJ_SIZE, thirdObjAddr, firstObjAddr, FIRST_OBJ_SIZE);
282 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
283 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == firstObjAddr) << seedInfo;
284 } else {
285 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == thirdObjAddr) << seedInfo;
286 }
287 GetCrossingMap()->RemoveObject(thirdObjAddr, THIRD_OBJ_SIZE, nullptr, firstObjAddr, FIRST_OBJ_SIZE);
288 if (PANDA_CROSSING_MAP_MANAGE_CROSSED_BORDER) {
289 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == firstObjAddr) << seedInfo;
290 } else {
291 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == nullptr) << seedInfo;
292 }
293
294 GetCrossingMap()->RemoveObject(firstObjAddr, FIRST_OBJ_SIZE);
295 ASSERT_TRUE(GetCrossingMap()->FindFirstObject(secondObjAddr, secondObjAddr) == nullptr) << seedInfo;
296 }
297
TEST_F(CrossingMapTest,InitializeCrosingMapForMemoryTest)298 TEST_F(CrossingMapTest, InitializeCrosingMapForMemoryTest)
299 {
300 static constexpr size_t POOL_COUNT = 6;
301 static constexpr size_t GRANULARITY = 2;
302 GetCrossingMap()->RemoveCrossingMapForMemory(ToVoidPtr(GetPoolMinAddress()), GetPoolSize());
303 void *startAddr = GetRandomObjAddr((POOLS_SIZE * 2U + PANDA_POOL_ALIGNMENT_IN_BYTES) * POOL_COUNT +
304 PANDA_POOL_ALIGNMENT_IN_BYTES);
305 uintptr_t alignedStartAddr = AlignUp(ToUintPtr(startAddr), PANDA_POOL_ALIGNMENT_IN_BYTES);
306
307 std::array<bool, POOL_COUNT> deletedPools {};
308 for (size_t i = 0; i < POOL_COUNT; i++) {
309 void *poolAddr = ToVoidPtr(alignedStartAddr + i * (POOLS_SIZE * 2U + PANDA_POOL_ALIGNMENT_IN_BYTES));
310 GetCrossingMap()->InitializeCrossingMapForMemory(poolAddr, POOLS_SIZE * 2U);
311 deletedPools[i] = false;
312 }
313
314 for (size_t i = 0; i < POOL_COUNT; i += GRANULARITY) {
315 void *poolAddr = ToVoidPtr(alignedStartAddr + i * (POOLS_SIZE * 2U + PANDA_POOL_ALIGNMENT_IN_BYTES));
316 GetCrossingMap()->RemoveCrossingMapForMemory(poolAddr, POOLS_SIZE * 2U);
317 deletedPools[i] = true;
318 }
319
320 for (size_t i = 0; i < POOL_COUNT; i++) {
321 if (deletedPools[i]) {
322 continue;
323 }
324 void *poolAddr = ToVoidPtr(alignedStartAddr + i * (POOLS_SIZE * 2U + PANDA_POOL_ALIGNMENT_IN_BYTES));
325 GetCrossingMap()->RemoveCrossingMapForMemory(poolAddr, POOLS_SIZE * 2U);
326 }
327
328 GetCrossingMap()->InitializeCrossingMapForMemory(ToVoidPtr(GetPoolMinAddress()), GetPoolSize());
329 }
330
331 } // namespace ark::mem
332