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