1 /*
2 * Copyright (c) 2021 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 "ecmascript/builtins/builtins_ark_tools.h"
17 #include "ecmascript/ecma_vm.h"
18 #include "ecmascript/mem/full_gc.h"
19 #include "ecmascript/object_factory.h"
20 #include "ecmascript/mem/concurrent_marker.h"
21 #include "ecmascript/mem/stw_young_gc.h"
22 #include "ecmascript/mem/partial_gc.h"
23 #include "ecmascript/tests/test_helper.h"
24
25 using namespace panda;
26
27 using namespace panda::ecmascript;
28
29 namespace panda::test {
30 class GCTest : public testing::Test {
31 public:
SetUpTestCase()32 static void SetUpTestCase()
33 {
34 GTEST_LOG_(INFO) << "SetUpTestCase";
35 }
36
TearDownTestCase()37 static void TearDownTestCase()
38 {
39 GTEST_LOG_(INFO) << "TearDownCase";
40 }
41
SetUp()42 void SetUp() override
43 {
44 JSRuntimeOptions options;
45 instance = JSNApi::CreateEcmaVM(options);
46 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
47 thread = instance->GetJSThread();
48 scope = new EcmaHandleScope(thread);
49 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
50 heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
51 heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
52 }
53
TearDown()54 void TearDown() override
55 {
56 TestHelper::DestroyEcmaVMWithScope(instance, scope);
57 }
58
59 EcmaVM *instance {nullptr};
60 ecmascript::EcmaHandleScope *scope {nullptr};
61 JSThread *thread {nullptr};
62 };
63
HWTEST_F_L0(GCTest,FullGCOne)64 HWTEST_F_L0(GCTest, FullGCOne)
65 {
66 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
67 auto heap = thread->GetEcmaVM()->GetHeap();
68 auto fullGc = heap->GetFullGC();
69 fullGc->RunPhases();
70 auto oldSizebase = heap->GetOldSpace()->GetHeapObjectSize();
71 size_t oldSizeBefore = 0;
72 {
73 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
74 for (int i = 0; i < 1024; i++) {
75 factory->NewTaggedArray(512, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE);
76 }
77 oldSizeBefore = heap->GetOldSpace()->GetHeapObjectSize();
78 EXPECT_TRUE(oldSizeBefore > oldSizebase);
79 }
80 fullGc->RunPhases();
81 auto oldSizeAfter = heap->GetOldSpace()->GetHeapObjectSize();
82 EXPECT_TRUE(oldSizeBefore > oldSizeAfter);
83 }
84
HWTEST_F_L0(GCTest,ChangeGCParams)85 HWTEST_F_L0(GCTest, ChangeGCParams)
86 {
87 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
88 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT);
89 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
90 EXPECT_TRUE(heap->GetConcurrentMarker()->IsEnabled());
91 uint32_t markTaskNum = heap->GetMaxMarkTaskCount();
92 #endif
93 EXPECT_TRUE(heap->GetSweeper()->ConcurrentSweepEnabled());
94 uint32_t evacuateTaskNum = heap->GetMaxEvacuateTaskCount();
95
96 auto partialGc = heap->GetPartialGC();
97 partialGc->RunPhases();
98 heap->ChangeGCParams(true);
99 heap->Prepare();
100 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
101 uint32_t markTaskNumBackground = heap->GetMaxMarkTaskCount();
102 EXPECT_TRUE(markTaskNum > markTaskNumBackground);
103 EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled());
104 #endif
105 uint32_t evacuateTaskNumBackground = heap->GetMaxEvacuateTaskCount();
106 EXPECT_TRUE(evacuateTaskNum > evacuateTaskNumBackground);
107 EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled());
108 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::CONSERVATIVE);
109
110 partialGc->RunPhases();
111 heap->ChangeGCParams(false);
112 heap->Prepare();
113 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
114 uint32_t markTaskNumForeground = heap->GetMaxMarkTaskCount();
115 EXPECT_EQ(markTaskNum, markTaskNumForeground);
116 EXPECT_TRUE(heap->GetConcurrentMarker()->IsEnabled());
117 #endif
118 uint32_t evacuateTaskNumForeground = heap->GetMaxEvacuateTaskCount();
119 EXPECT_EQ(evacuateTaskNum, evacuateTaskNumForeground);
120 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT);
121 EXPECT_TRUE(heap->GetSweeper()->ConcurrentSweepEnabled());
122 }
123
HWTEST_F_L0(GCTest,ConfigDisable)124 HWTEST_F_L0(GCTest, ConfigDisable)
125 {
126 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
127 heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::CONFIG_DISABLE);
128 heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::CONFIG_DISABLE);
129
130 EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled());
131 EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled());
132
133 auto partialGc = heap->GetPartialGC();
134 partialGc->RunPhases();
135 heap->ChangeGCParams(false);
136 heap->Prepare();
137
138 EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled());
139 EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled());
140 }
141
HWTEST_F_L0(GCTest,NotifyMemoryPressure)142 HWTEST_F_L0(GCTest, NotifyMemoryPressure)
143 {
144 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
145 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT);
146 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
147 uint32_t markTaskNum = heap->GetMaxMarkTaskCount();
148 #endif
149 uint32_t evacuateTaskNum = heap->GetMaxEvacuateTaskCount();
150
151 auto partialGc = heap->GetPartialGC();
152 partialGc->RunPhases();
153 heap->ChangeGCParams(true);
154 heap->NotifyMemoryPressure(true);
155 heap->Prepare();
156 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
157 uint32_t markTaskNumBackground = heap->GetMaxMarkTaskCount();
158 EXPECT_TRUE(markTaskNum > markTaskNumBackground);
159 EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled());
160 #endif
161 uint32_t evacuateTaskNumBackground = heap->GetMaxEvacuateTaskCount();
162 EXPECT_TRUE(evacuateTaskNum > evacuateTaskNumBackground);
163 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::PRESSURE);
164 EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled());
165
166 partialGc->RunPhases();
167 heap->ChangeGCParams(false);
168 heap->Prepare();
169 #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING
170 uint32_t markTaskNumForeground = heap->GetMaxMarkTaskCount();
171 EXPECT_EQ(markTaskNum, markTaskNumForeground);
172 #endif
173 uint32_t evacuateTaskNumForeground = heap->GetMaxEvacuateTaskCount();
174 EXPECT_EQ(evacuateTaskNum, evacuateTaskNumForeground);
175 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::PRESSURE);
176
177 heap->NotifyMemoryPressure(false);
178 EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::CONSERVATIVE);
179 }
180
HWTEST_F_L0(GCTest,NativeBindingCheckGCTest)181 HWTEST_F_L0(GCTest, NativeBindingCheckGCTest)
182 {
183 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
184 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
185 size_t oldNativeSize = heap->GetNativeBindingSize();
186 size_t newNativeSize = heap->GetNativeBindingSize();
187 {
188 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
189
190 auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
191 [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData,
192 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
193 newNativeSize = heap->GetNativeBindingSize();
194 EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024);
195
196 auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
197 [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1,
198 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
199
200 EXPECT_TRUE(newNativeSize - oldNativeSize > 0);
201 EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024);
202 for (int i = 0; i < 20; i++) {
203 auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
204 [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2,
205 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
206 }
207 // Young GC should be trigger here, so the size should be reduced.
208 EXPECT_TRUE(newNativeSize - oldNativeSize < 22 * 1024 *1024);
209 }
210 auto partialGc = heap->GetPartialGC();
211 heap->SetMarkType(MarkType::MARK_FULL);
212 partialGc->RunPhases();
213 newNativeSize = heap->GetNativeBindingSize();
214 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
215 }
216
HWTEST_F_L0(GCTest,NonNewSpaceNativeBindingCheckGCTest)217 HWTEST_F_L0(GCTest, NonNewSpaceNativeBindingCheckGCTest)
218 {
219 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
220 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
221 size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize();
222 size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
223 {
224 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
225 auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
226 [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData,
227 NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024);
228 newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
229 EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024);
230
231 auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
232 [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1,
233 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
234
235 EXPECT_TRUE(newNativeSize - oldNativeSize > 0);
236 EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024);
237 for (int i = 0; i < 300; i++) {
238 auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1024);
239 // malloc size is smaller to avoid test fail in the small devices.
240 [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2,
241 NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024);
242 }
243 // Old GC should be trigger here, so the size should be reduced.
244 EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024);
245 }
246 auto partialGc = heap->GetPartialGC();
247 heap->SetMarkType(MarkType::MARK_FULL);
248 partialGc->RunPhases();
249 newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
250 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
251 }
252
HWTEST_F_L0(GCTest,NativeGCTestConcurrentMarkDisabled)253 HWTEST_F_L0(GCTest, NativeGCTestConcurrentMarkDisabled)
254 {
255 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
256 // Disable concurrent mark.
257 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
258 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
259 size_t oldNativeSize = heap->GetNativeBindingSize();
260 size_t newNativeSize = heap->GetNativeBindingSize();
261 {
262 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
263
264 auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
265 [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData,
266 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
267 newNativeSize = heap->GetNativeBindingSize();
268 EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024);
269
270 auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
271 [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1,
272 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
273
274 EXPECT_TRUE(newNativeSize - oldNativeSize > 0);
275 EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024);
276 for (int i = 0; i < 20; i++) {
277 auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
278 [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2,
279 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
280 }
281 // Young GC should be trigger here, so the size should be reduced.
282 EXPECT_TRUE(newNativeSize - oldNativeSize < 22 * 1024 *1024);
283 }
284 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
285 newNativeSize = heap->GetNativeBindingSize();
286 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
287 }
288
HWTEST_F_L0(GCTest,NonNewSpaceNativeGCTestConcurrentMarkDisabled)289 HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled)
290 {
291 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
292 // Disable concurrent mark.
293 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
294 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
295 size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize();
296 size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
297 {
298 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
299 auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
300 [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData,
301 NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024);
302 newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
303 EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024);
304
305 auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024);
306 [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1,
307 NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024);
308
309 EXPECT_TRUE(newNativeSize - oldNativeSize > 0);
310 EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024);
311 for (int i = 0; i < 300; i++) {
312 auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1024);
313 // malloc size is smaller to avoid test fail in the small devices.
314 [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2,
315 NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024);
316 }
317 // Old GC should be trigger here, so the size should be reduced.
318 EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024);
319 }
320 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
321 newNativeSize = heap->GetNonNewSpaceNativeBindingSize();
322 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
323 }
324
HWTEST_F_L0(GCTest,ArkToolsForceFullGC)325 HWTEST_F_L0(GCTest, ArkToolsForceFullGC)
326 {
327 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC);
328 size_t originalHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
329 size_t newSize = originalHeapSize;
330 {
331 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
332
333 for (int i = 0; i < 10; i++) {
334 [[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1024 * 1024);
335 }
336 newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
337 }
338 EXPECT_TRUE(newSize > originalHeapSize);
339 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 0);
340
341 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
342 [[maybe_unused]] JSTaggedValue result1 = builtins::BuiltinsArkTools::ForceFullGC(ecmaRuntimeCallInfo);
343
344 ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize);
345 }
346
347 } // namespace panda::test
348