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-inl.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/ecma_test_common.h"
24 #include "ecmascript/napi/include/jsnapi_expo.h"
25
26 using namespace panda;
27
28 using namespace panda::ecmascript;
29 using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE;
30
31 namespace panda::test {
32 class GCTest : public BaseTestWithScope<false> {
33 public:
SetUp()34 void SetUp() override
35 {
36 JSRuntimeOptions options;
37 options.SetEnableEdenGC(true);
38 options.SetArkProperties(options.GetArkProperties() | ArkProperties::ENABLE_HEAP_VERIFY);
39 instance = JSNApi::CreateEcmaVM(options);
40 ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
41 thread = instance->GetJSThread();
42 thread->ManagedCodeBegin();
43 scope = new EcmaHandleScope(thread);
44 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
45 heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
46 heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
47 }
48 };
49
HWTEST_F_L0(GCTest,NativeGCTestConcurrentMarkDisabled)50 HWTEST_F_L0(GCTest, NativeGCTestConcurrentMarkDisabled)
51 {
52 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
53 // Disable concurrent mark.
54 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
55 size_t oldNativeSize = heap->GetNativeBindingSize();
56 EcmaTestCommon::GcCommonCase(thread, heap, false);
57 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
58 auto newNativeSize = heap->GetNativeBindingSize();
59 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
60 }
61
HWTEST_F_L0(GCTest,NonNewSpaceNativeGCTestConcurrentMarkDisabled)62 HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled)
63 {
64 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
65 // Disable concurrent mark.
66 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
67 size_t oldNativeSize = heap->GetNativeBindingSize();
68 EcmaTestCommon::GcCommonCase(thread, heap);
69 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC);
70 auto newNativeSize = heap->GetNativeBindingSize();
71 EXPECT_EQ(newNativeSize - oldNativeSize, 0UL);
72 }
73
HWTEST_F_L0(GCTest,ArkToolsForceFullGC)74 HWTEST_F_L0(GCTest, ArkToolsForceFullGC)
75 {
76 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC);
77 size_t originalHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
78 size_t newSize = originalHeapSize;
79 {
80 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
81
82 for (int i = 0; i < 10; i++) {
83 [[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1024 * 1024);
84 }
85 newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
86 }
87 EXPECT_TRUE(newSize > originalHeapSize);
88 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 0);
89
90 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
91 [[maybe_unused]] JSTaggedValue result1 = builtins::BuiltinsArkTools::ForceFullGC(ecmaRuntimeCallInfo);
92
93 ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize);
94 }
95
HWTEST_F_L0(GCTest,ColdStartForceExpand)96 HWTEST_F_L0(GCTest, ColdStartForceExpand)
97 {
98 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
99 size_t originalHeapSize = heap->GetCommittedSize();
100 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
101 heap->NotifyPostFork();
102 heap->NotifyFinishColdStartSoon();
103 {
104 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
105 for (int i = 0; i < 500; i++) {
106 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
107 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
108 }
109 }
110 size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
111 usleep(10000000);
112 size_t newSize = EcmaTestCommon::GcCommonCase(thread);
113 EXPECT_TRUE(originalHeapSize < expandHeapSize);
114 EXPECT_TRUE(expandHeapSize > newSize);
115 }
116
HWTEST_F_L0(GCTest,HighSensitiveForceExpand)117 HWTEST_F_L0(GCTest, HighSensitiveForceExpand)
118 {
119 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
120 size_t originalHeapSize = heap->GetCommittedSize();
121 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
122 heap->NotifyHighSensitive(true);
123 {
124 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
125 for (int i = 0; i < 500; i++) {
126 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
127 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
128 }
129 }
130 size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
131 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
132 size_t newSize = EcmaTestCommon::GcCommonCase(thread);
133 EXPECT_TRUE(originalHeapSize < expandHeapSize);
134 EXPECT_TRUE(expandHeapSize > newSize);
135 }
136
HWTEST_F_L0(GCTest,HighSensitiveExceedMaxHeapSize)137 HWTEST_F_L0(GCTest, HighSensitiveExceedMaxHeapSize)
138 {
139 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
140 heap->NotifyHighSensitive(true);
141 // First allocate about 250M TaggedArray, not reach max heap size
142 {
143 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
144 for (int i = 0; i < 16 * 1000; i++) {
145 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
146 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
147 }
148 }
149 // Continue allocate about 250M TaggedArray, now reach max heap size, must trigger gc to avoid OOM
150 {
151 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
152 for (int i = 0; i < 10 * 1000; i++) {
153 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
154 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
155 }
156 }
157 size_t commitSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize();
158 const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false);
159 EXPECT_TRUE(commitSize < thread->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxHeapSize());
160 }
161
HWTEST_F_L0(GCTest,ColdStartGCRestrainInternal)162 HWTEST_F_L0(GCTest, ColdStartGCRestrainInternal)
163 {
164 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
165 heap->NotifyPostFork();
166 heap->NotifyFinishColdStartSoon();
167 std::this_thread::sleep_for(std::chrono::seconds(3));
168 if (!heap->OnStartupEvent()) {
169 StartupStatus startupStatus = heap->GetStartupStatus();
170 EXPECT_TRUE(startupStatus == StartupStatus::JUST_FINISH_STARTUP);
171 }
172 }
173
HWTEST_F_L0(GCTest,ColdStartGCRestrainExternal)174 HWTEST_F_L0(GCTest, ColdStartGCRestrainExternal)
175 {
176 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
177 heap->NotifyPostFork();
178 heap->NotifyFinishColdStartSoon();
179 std::this_thread::sleep_for(std::chrono::seconds(1));
180 heap->NotifyFinishColdStart(true);
181 EXPECT_FALSE(heap->OnStartupEvent());
182 StartupStatus startupStatus = heap->GetStartupStatus();
183 EXPECT_TRUE(startupStatus == StartupStatus::JUST_FINISH_STARTUP);
184 }
185
HWTEST_F_L0(GCTest,CallbackTask)186 HWTEST_F_L0(GCTest, CallbackTask)
187 {
188 auto vm = thread->GetEcmaVM();
189 Heap *heap = const_cast<Heap *>(vm->GetHeap());
190 auto factory = vm->GetFactory();
191 {
192 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
193
194 for (int i = 0; i < 10; i++) {
195 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
196 void *externalPointer = malloc(10);
197 [[maybe_unused]] JSHandle<JSNativePointer> nativePointer = factory->NewJSNativePointer(
198 externalPointer, []([[maybe_unused]] void *env, void* pointer, [[maybe_unused]] void* data) {
199 if (pointer != nullptr) {
200 free(pointer);
201 }
202 },
203 nullptr, false, 10, Concurrent::YES);
204 }
205 }
206 size_t number = heap->concurrentNativePointerList_.size();
207 EXPECT_TRUE(number > 0);
208 heap->CollectGarbage(TriggerGCType::OLD_GC);
209 size_t newNumber = heap->concurrentNativePointerList_.size();
210 EXPECT_TRUE(number > newNumber);
211 }
212
HWTEST_F_L0(GCTest,RecomputeLimitsTest)213 HWTEST_F_L0(GCTest, RecomputeLimitsTest)
214 {
215 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
216 auto oldCapacity = heap->GetOldSpace()->GetInitialCapacity();
217 heap->CollectGarbage(TriggerGCType::FULL_GC);
218 EXPECT_FALSE(heap->IsConcurrentFullMark());
219 EXPECT_FALSE(heap->IsFullMarkRequested());
220 auto newCapacity = heap->GetOldSpace()->GetInitialCapacity();
221 EXPECT_NE(newCapacity, oldCapacity);
222 double gcSpeed = heap->GetMemController()->CalculateMarkCompactSpeedPerMS();
223 double mutatorSpeed = heap->GetMemController()->GetCurrentOldSpaceAllocationThroughputPerMS();
224 size_t oldSpaceSize = heap->GetOldSpace()->GetHeapObjectSize() + heap->GetHugeObjectSpace()->GetHeapObjectSize() +
225 heap->GetHugeMachineCodeSpace()->GetHeapObjectSize();
226 size_t newSpaceCapacity = heap->GetNewSpace()->GetInitialCapacity();
227 double growingFactor = heap->GetMemController()->CalculateGrowingFactor(gcSpeed, mutatorSpeed);
228 size_t maxOldSpaceCapacity = heap->GetOldSpace()->GetMaximumCapacity() - newSpaceCapacity;
229 auto newOldSpaceLimit = heap->GetMemController()->CalculateAllocLimit(oldSpaceSize, MIN_OLD_SPACE_LIMIT,
230 maxOldSpaceCapacity, newSpaceCapacity, growingFactor);
231 EXPECT_EQ(newCapacity, newOldSpaceLimit);
232 }
233
HWTEST_F_L0(GCTest,GlobalNativeSizeLargerThanLimitTest)234 HWTEST_F_L0(GCTest, GlobalNativeSizeLargerThanLimitTest)
235 {
236 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
237 auto ret = heap->GlobalNativeSizeLargerThanLimit();
238 EXPECT_FALSE(ret);
239 heap->GetNativeAreaAllocator()->IncreaseNativeMemoryUsage(300*1000*1000);
240 ret = heap->GlobalNativeSizeLargerThanLimit();
241 EXPECT_TRUE(ret);
242 }
243 #ifdef NDEBUG
HWTEST_F_L0(GCTest,IdleGCTriggerTest)244 HWTEST_F_L0(GCTest, IdleGCTriggerTest)
245 {
246 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
247 auto idleGCTrigger = heap->GetIdleGCTrigger();
248 auto sHeap = SharedHeap::GetInstance();
249 heap->CollectGarbage(TriggerGCType::FULL_GC);
250 int baseLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
251 int baseSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
252 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
253 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
254 // Apply for some memory that cannot be released to simulate the actual situation
255 for (int i = 0; i < 5120; i++) {
256 factory->NewTaggedArray(1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
257 factory->NewSOldSpaceTaggedArray(1024, JSTaggedValue::Hole());
258 }
259 for (size_t i = 0; i < 10240; i++)
260 {
261 factory->NewTaggedArray(512, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
262 factory->NewSOldSpaceTaggedArray(512, JSTaggedValue::Hole());
263 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
264 [[maybe_unused]] JSHandle<TaggedArray> array = factory->NewTaggedArray(1024, JSTaggedValue::Hole(),
265 MemSpaceType::OLD_SPACE);
266 [[maybe_unused]] JSHandle<TaggedArray> sArray = factory->NewSOldSpaceTaggedArray(1024,
267 JSTaggedValue::Hole());
268 if (i%340 == 0) {
269 idleGCTrigger->NotifyVsyncIdleStart();
270 }
271 }
272 idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::FULL_GC);
273 idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
274 int afterLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
275 int afterSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
276 EXPECT_TRUE(afterLocalGCCount - baseLocalGCCount < 10);
277 EXPECT_TRUE(afterSharedGCCount - baseSharedGCCount < 10);
278 heap->CollectGarbage(TriggerGCType::FULL_GC);
279 }
280 #endif // #ifndef NDEBUG
281
HWTEST_F_L0(GCTest,AdjustCapacity)282 HWTEST_F_L0(GCTest, AdjustCapacity)
283 {
284 #if defined(PANDA_TARGET_ARM64)
285 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
286 SemiSpace * space = heap->GetNewSpace();
287
288 EXPECT_EQ(space->GetSurvivalObjectSize(), 0);
289 {
290 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
291 for (int i = 0; i < 300; i++) {
292 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
293 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
294 }
295 }
296 EXPECT_GT(space->GetSurvivalObjectSize(), 0);
297
298 EXPECT_FALSE(space->AdjustCapacity(0, thread));
299 size_t size = space->GetInitialCapacity() * GROW_OBJECT_SURVIVAL_RATE / 2;
300 EXPECT_FALSE(space->AdjustCapacity(size, thread));
301
302 space->SetInitialCapacity(space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1);
303 size = space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1;
304 size_t oldMaxCapacity = space->GetMaximumCapacity();
305 space->SetMaximumCapacity(space->GetInitialCapacity());
306 EXPECT_TRUE(space->AdjustCapacity(size, thread));
307 space->SetMaximumCapacity(oldMaxCapacity);
308 EXPECT_TRUE(space->AdjustCapacity(size, thread));
309 #endif
310 }
311
HWTEST_F_L0(GCTest,NativeMemAllocInSensitive)312 HWTEST_F_L0(GCTest, NativeMemAllocInSensitive)
313 {
314 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
315 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
316 heap->NotifyHighSensitive(true);
317 for (size_t i = 0; i < 20; i++) {
318 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
319 factory->NewJSArrayBuffer(300 * 1024 * 1024); // 300MB
320 }
321 EXPECT_TRUE(heap->GetGlobalNativeSize() < 1 * 1024 * 1024* 1024); // 1GB
322 }
323 } // namespace panda::test
324