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(2500000);
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,CallbackTask)162 HWTEST_F_L0(GCTest, CallbackTask)
163 {
164 auto vm = thread->GetEcmaVM();
165 Heap *heap = const_cast<Heap *>(vm->GetHeap());
166 auto factory = vm->GetFactory();
167 {
168 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
169
170 for (int i = 0; i < 10; i++) {
171 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
172 void *externalPointer = malloc(10);
173 [[maybe_unused]] JSHandle<JSNativePointer> nativePointer = factory->NewJSNativePointer(
174 externalPointer, []([[maybe_unused]] void *env, void* pointer, [[maybe_unused]] void* data) {
175 if (pointer != nullptr) {
176 free(pointer);
177 }
178 },
179 nullptr, false, 10, Concurrent::YES);
180 }
181 }
182 size_t number = heap->concurrentNativePointerList_.size();
183 EXPECT_TRUE(number > 0);
184 heap->CollectGarbage(TriggerGCType::OLD_GC);
185 size_t newNumber = heap->concurrentNativePointerList_.size();
186 EXPECT_TRUE(number > newNumber);
187 }
188
HWTEST_F_L0(GCTest,RecomputeLimitsTest)189 HWTEST_F_L0(GCTest, RecomputeLimitsTest)
190 {
191 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
192 auto oldCapacity = heap->GetOldSpace()->GetInitialCapacity();
193 heap->CollectGarbage(TriggerGCType::FULL_GC);
194 EXPECT_FALSE(heap->IsConcurrentFullMark());
195 EXPECT_FALSE(heap->IsFullMarkRequested());
196 auto newCapacity = heap->GetOldSpace()->GetInitialCapacity();
197 EXPECT_NE(newCapacity, oldCapacity);
198 double gcSpeed = heap->GetMemController()->CalculateMarkCompactSpeedPerMS();
199 double mutatorSpeed = heap->GetMemController()->GetCurrentOldSpaceAllocationThroughputPerMS();
200 size_t oldSpaceSize = heap->GetOldSpace()->GetHeapObjectSize() + heap->GetHugeObjectSpace()->GetHeapObjectSize() +
201 heap->GetHugeMachineCodeSpace()->GetHeapObjectSize();
202 size_t newSpaceCapacity = heap->GetNewSpace()->GetInitialCapacity();
203 double growingFactor = heap->GetMemController()->CalculateGrowingFactor(gcSpeed, mutatorSpeed);
204 size_t maxOldSpaceCapacity = heap->GetOldSpace()->GetMaximumCapacity() - newSpaceCapacity;
205 auto newOldSpaceLimit = heap->GetMemController()->CalculateAllocLimit(oldSpaceSize, MIN_OLD_SPACE_LIMIT,
206 maxOldSpaceCapacity, newSpaceCapacity, growingFactor);
207 EXPECT_EQ(newCapacity, newOldSpaceLimit);
208 }
209
HWTEST_F_L0(GCTest,GlobalNativeSizeLargerThanLimitTest)210 HWTEST_F_L0(GCTest, GlobalNativeSizeLargerThanLimitTest)
211 {
212 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
213 auto ret = heap->GlobalNativeSizeLargerThanLimit();
214 EXPECT_FALSE(ret);
215 heap->GetNativeAreaAllocator()->IncreaseNativeMemoryUsage(300*1000*1000);
216 ret = heap->GlobalNativeSizeLargerThanLimit();
217 EXPECT_TRUE(ret);
218 }
219 #ifdef NDEBUG
HWTEST_F_L0(GCTest,IdleGCTriggerTest)220 HWTEST_F_L0(GCTest, IdleGCTriggerTest)
221 {
222 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
223 auto idleGCTrigger = heap->GetIdleGCTrigger();
224 auto sHeap = SharedHeap::GetInstance();
225 heap->CollectGarbage(TriggerGCType::FULL_GC);
226 int baseLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
227 int baseSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
228 heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
229 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
230 // Apply for some memory that cannot be released to simulate the actual situation
231 for (int i = 0; i < 5120; i++) {
232 factory->NewTaggedArray(1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
233 factory->NewSOldSpaceTaggedArray(1024, JSTaggedValue::Hole());
234 }
235 for (size_t i = 0; i < 10240; i++)
236 {
237 factory->NewTaggedArray(512, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
238 factory->NewSOldSpaceTaggedArray(512, JSTaggedValue::Hole());
239 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
240 [[maybe_unused]] JSHandle<TaggedArray> array = factory->NewTaggedArray(1024, JSTaggedValue::Hole(),
241 MemSpaceType::OLD_SPACE);
242 [[maybe_unused]] JSHandle<TaggedArray> sArray = factory->NewSOldSpaceTaggedArray(1024,
243 JSTaggedValue::Hole());
244 if (i%340 == 0) {
245 idleGCTrigger->NotifyVsyncIdleStart();
246 }
247 }
248 idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::FULL_GC);
249 idleGCTrigger->TryTriggerIdleGC(TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
250 int afterLocalGCCount = heap->GetEcmaGCStats()->GetGCCount();
251 int afterSharedGCCount = sHeap->GetEcmaGCStats()->GetGCCount();
252 EXPECT_TRUE(afterLocalGCCount - baseLocalGCCount < 10);
253 EXPECT_TRUE(afterSharedGCCount - baseSharedGCCount < 10);
254 heap->CollectGarbage(TriggerGCType::FULL_GC);
255 }
256 #endif // #ifndef NDEBUG
257
HWTEST_F_L0(GCTest,AdjustCapacity)258 HWTEST_F_L0(GCTest, AdjustCapacity)
259 {
260 #if defined(PANDA_TARGET_ARM64)
261 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
262 SemiSpace * space = heap->GetNewSpace();
263
264 EXPECT_EQ(space->GetSurvivalObjectSize(), 0);
265 {
266 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
267 for (int i = 0; i < 300; i++) {
268 [[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
269 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
270 }
271 }
272 EXPECT_GT(space->GetSurvivalObjectSize(), 0);
273
274 EXPECT_FALSE(space->AdjustCapacity(0, thread));
275 size_t size = space->GetInitialCapacity() * GROW_OBJECT_SURVIVAL_RATE / 2;
276 EXPECT_FALSE(space->AdjustCapacity(size, thread));
277
278 space->SetInitialCapacity(space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1);
279 size = space->GetSurvivalObjectSize() / GROW_OBJECT_SURVIVAL_RATE - 1;
280 size_t oldMaxCapacity = space->GetMaximumCapacity();
281 space->SetMaximumCapacity(space->GetInitialCapacity());
282 EXPECT_TRUE(space->AdjustCapacity(size, thread));
283 space->SetMaximumCapacity(oldMaxCapacity);
284 EXPECT_TRUE(space->AdjustCapacity(size, thread));
285 #endif
286 }
287
HWTEST_F_L0(GCTest,NativeMemAllocInSensitive)288 HWTEST_F_L0(GCTest, NativeMemAllocInSensitive)
289 {
290 auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
291 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
292 heap->NotifyHighSensitive(true);
293 for (size_t i = 0; i < 20; i++) {
294 [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
295 factory->NewJSArrayBuffer(300 * 1024 * 1024); // 300MB
296 }
297 EXPECT_TRUE(heap->GetGlobalNativeSize() < 1 * 1024 * 1024* 1024); // 1GB
298 }
299 } // namespace panda::test
300