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 #ifndef PANDA_RUNTIME_TESTS_PYGOTE_SPACE_ALLOCATOR_TEST_H_
16 #define PANDA_RUNTIME_TESTS_PYGOTE_SPACE_ALLOCATOR_TEST_H_
17
18 #include <sys/mman.h>
19 #include <gtest/gtest.h>
20
21 #include "libpandabase/os/mem.h"
22 #include "libpandabase/utils/logger.h"
23 #include "runtime/mem/runslots_allocator-inl.h"
24 #include "runtime/mem/pygote_space_allocator-inl.h"
25 #include "runtime/include/object_header.h"
26 #include "runtime/mem/refstorage/global_object_storage.h"
27
28 namespace panda::mem {
29
30 class PygoteSpaceAllocatorTest : public testing::Test {
31 public:
32 using PygoteAllocator = PygoteSpaceAllocator<ObjectAllocConfig>;
33
PygoteSpaceAllocatorTest()34 PygoteSpaceAllocatorTest() {}
35
~PygoteSpaceAllocatorTest()36 ~PygoteSpaceAllocatorTest() {}
37
38 protected:
GetPygoteSpaceAllocator()39 PygoteAllocator *GetPygoteSpaceAllocator()
40 {
41 return thread_->GetVM()->GetHeapManager()->GetObjectAllocator().AsObjectAllocator()->GetPygoteSpaceAllocator();
42 }
43
GetObjectClass()44 Class *GetObjectClass()
45 {
46 auto runtime = panda::Runtime::GetCurrent();
47 LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
48 return runtime->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::OBJECT);
49 }
50
PygoteFork()51 void PygoteFork()
52 {
53 thread_->ManagedCodeEnd();
54 auto runtime = panda::Runtime::GetCurrent();
55 runtime->PreZygoteFork();
56 runtime->PostZygoteFork();
57 thread_->ManagedCodeBegin();
58 }
59
TriggerGc()60 void TriggerGc()
61 {
62 auto gc = thread_->GetVM()->GetGC();
63 auto task = GCTask(GCTaskCause::EXPLICIT_CAUSE);
64 // trigger tenured gc
65 gc->WaitForGCInManaged(task);
66 gc->WaitForGCInManaged(task);
67 gc->WaitForGCInManaged(task);
68 }
69
70 panda::MTManagedThread *thread_ {nullptr};
71 RuntimeOptions options_;
72
73 void InitAllocTest();
74
75 void ForkedAllocTest();
76
77 void NonMovableLiveObjectAllocTest();
78
79 void NonMovableUnliveObjectAllocTest();
80
81 void MovableLiveObjectAllocTest();
82
83 void MovableUnliveObjectAllocTest();
84
85 void MuchObjectAllocTest();
86 };
87
InitAllocTest()88 inline void PygoteSpaceAllocatorTest::InitAllocTest()
89 {
90 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
91 auto cls = GetObjectClass();
92
93 auto non_movable_header = panda::ObjectHeader::CreateNonMovable(cls);
94 ASSERT_NE(non_movable_header, nullptr);
95 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
96 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
97
98 auto movable_header = panda::ObjectHeader::Create(cls);
99 ASSERT_NE(non_movable_header, nullptr);
100 ASSERT_FALSE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(movable_header)));
101
102 pygote_space_allocator->Free(non_movable_header);
103 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
104 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
105 }
106
ForkedAllocTest()107 inline void PygoteSpaceAllocatorTest::ForkedAllocTest()
108 {
109 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
110 auto cls = GetObjectClass();
111
112 PygoteFork();
113
114 auto non_movable_header = panda::ObjectHeader::CreateNonMovable(cls);
115 ASSERT_NE(non_movable_header, nullptr);
116 ASSERT_FALSE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
117
118 auto movable_header = panda::ObjectHeader::Create(cls);
119 ASSERT_NE(movable_header, nullptr);
120 ASSERT_FALSE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(movable_header)));
121 }
122
NonMovableLiveObjectAllocTest()123 inline void PygoteSpaceAllocatorTest::NonMovableLiveObjectAllocTest()
124 {
125 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
126 auto cls = GetObjectClass();
127 auto global_object_storage = thread_->GetVM()->GetGlobalObjectStorage();
128
129 auto non_movable_header = panda::ObjectHeader::CreateNonMovable(cls);
130 ASSERT_NE(non_movable_header, nullptr);
131 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
132 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
133 [[maybe_unused]] auto *ref =
134 global_object_storage->Add(non_movable_header, panda::mem::Reference::ObjectType::GLOBAL);
135
136 PygoteFork();
137
138 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
139 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
140
141 TriggerGc();
142
143 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
144 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
145
146 pygote_space_allocator->Free(non_movable_header);
147 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
148 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
149 }
150
NonMovableUnliveObjectAllocTest()151 inline void PygoteSpaceAllocatorTest::NonMovableUnliveObjectAllocTest()
152 {
153 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
154 auto cls = GetObjectClass();
155 auto global_object_storage = thread_->GetVM()->GetGlobalObjectStorage();
156
157 auto non_movable_header = panda::ObjectHeader::CreateNonMovable(cls);
158 ASSERT_NE(non_movable_header, nullptr);
159 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
160 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
161 [[maybe_unused]] auto *ref =
162 global_object_storage->Add(non_movable_header, panda::mem::Reference::ObjectType::GLOBAL);
163
164 PygoteFork();
165
166 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
167 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
168 global_object_storage->Remove(ref);
169
170 TriggerGc();
171
172 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movable_header)));
173 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movable_header)));
174 }
175
MovableLiveObjectAllocTest()176 inline void PygoteSpaceAllocatorTest::MovableLiveObjectAllocTest()
177 {
178 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
179 auto cls = GetObjectClass();
180 auto global_object_storage = thread_->GetVM()->GetGlobalObjectStorage();
181
182 auto movable_header = panda::ObjectHeader::Create(cls);
183 ASSERT_NE(movable_header, nullptr);
184 ASSERT_FALSE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(movable_header)));
185 [[maybe_unused]] auto *ref = global_object_storage->Add(movable_header, panda::mem::Reference::ObjectType::GLOBAL);
186
187 PygoteFork();
188
189 auto obj = global_object_storage->Get(ref);
190 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
191 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
192
193 TriggerGc();
194
195 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
196 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
197 }
198
MovableUnliveObjectAllocTest()199 inline void PygoteSpaceAllocatorTest::MovableUnliveObjectAllocTest()
200 {
201 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
202 auto cls = GetObjectClass();
203 auto global_object_storage = thread_->GetVM()->GetGlobalObjectStorage();
204
205 auto movable_header = panda::ObjectHeader::Create(cls);
206 ASSERT_NE(movable_header, nullptr);
207 ASSERT_FALSE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(movable_header)));
208 [[maybe_unused]] auto *ref = global_object_storage->Add(movable_header, panda::mem::Reference::ObjectType::GLOBAL);
209
210 PygoteFork();
211
212 auto obj = global_object_storage->Get(ref);
213 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
214 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
215 global_object_storage->Remove(ref);
216
217 TriggerGc();
218
219 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
220 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
221 }
222
MuchObjectAllocTest()223 inline void PygoteSpaceAllocatorTest::MuchObjectAllocTest()
224 {
225 [[maybe_unused]] auto pygote_space_allocator = GetPygoteSpaceAllocator();
226 auto cls = GetObjectClass();
227 auto global_object_storage = thread_->GetVM()->GetGlobalObjectStorage();
228
229 static constexpr size_t obj_num = 1024;
230
231 PandaVector<Reference *> movable_refs;
232 PandaVector<Reference *> non_movable_refs;
233 for (size_t i = 0; i < obj_num; i++) {
234 auto movable = panda::ObjectHeader::Create(cls);
235 movable_refs.push_back(global_object_storage->Add(movable, panda::mem::Reference::ObjectType::GLOBAL));
236 auto non_movable = panda::ObjectHeader::CreateNonMovable(cls);
237 non_movable_refs.push_back(global_object_storage->Add(non_movable, panda::mem::Reference::ObjectType::GLOBAL));
238 }
239
240 PygoteFork();
241
242 PandaVector<ObjectHeader *> movable_objs;
243 PandaVector<ObjectHeader *> non_movable_objs;
244 for (auto movalbe_ref : movable_refs) {
245 auto obj = global_object_storage->Get(movalbe_ref);
246 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
247 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
248 global_object_storage->Remove(movalbe_ref);
249 movable_objs.push_back(obj);
250 }
251
252 for (auto non_movalbe_ref : non_movable_refs) {
253 auto obj = global_object_storage->Get(non_movalbe_ref);
254 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(obj)));
255 ASSERT_TRUE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(obj)));
256 global_object_storage->Remove(non_movalbe_ref);
257 non_movable_objs.push_back(obj);
258 }
259
260 TriggerGc();
261
262 for (auto movalbe_obj : movable_objs) {
263 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(movalbe_obj)));
264 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(movalbe_obj)));
265 }
266
267 for (auto non_movalbe_obj : non_movable_objs) {
268 ASSERT_TRUE(pygote_space_allocator->ContainObject(static_cast<ObjectHeader *>(non_movalbe_obj)));
269 ASSERT_FALSE(pygote_space_allocator->IsLive(static_cast<ObjectHeader *>(non_movalbe_obj)));
270 }
271 }
272
273 } // namespace panda::mem
274
275 #endif // PANDA_RUNTIME_TESTS_PYGOTE_SPACE_ALLOCATOR_TEST_H_
276