• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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