/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/builtins/builtins_ark_tools.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/mem/full_gc.h" #include "ecmascript/object_factory.h" #include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/mem/stw_young_gc.h" #include "ecmascript/mem/partial_gc.h" #include "ecmascript/tests/test_helper.h" using namespace panda; using namespace panda::ecmascript; namespace panda::test { class GCTest : public testing::Test { public: static void SetUpTestCase() { GTEST_LOG_(INFO) << "SetUpTestCase"; } static void TearDownTestCase() { GTEST_LOG_(INFO) << "TearDownCase"; } void SetUp() override { JSRuntimeOptions options; instance = JSNApi::CreateEcmaVM(options); ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; thread = instance->GetJSThread(); scope = new EcmaHandleScope(thread); auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE); heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE); } void TearDown() override { TestHelper::DestroyEcmaVMWithScope(instance, scope); } EcmaVM *instance {nullptr}; ecmascript::EcmaHandleScope *scope {nullptr}; JSThread *thread {nullptr}; }; HWTEST_F_L0(GCTest, FullGCOne) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); auto heap = thread->GetEcmaVM()->GetHeap(); auto fullGc = heap->GetFullGC(); fullGc->RunPhases(); auto oldSizebase = heap->GetOldSpace()->GetHeapObjectSize(); size_t oldSizeBefore = 0; { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); for (int i = 0; i < 1024; i++) { factory->NewTaggedArray(512, JSTaggedValue::Undefined(), MemSpaceType::OLD_SPACE); } oldSizeBefore = heap->GetOldSpace()->GetHeapObjectSize(); EXPECT_TRUE(oldSizeBefore > oldSizebase); } fullGc->RunPhases(); auto oldSizeAfter = heap->GetOldSpace()->GetHeapObjectSize(); EXPECT_TRUE(oldSizeBefore > oldSizeAfter); } HWTEST_F_L0(GCTest, ChangeGCParams) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING EXPECT_TRUE(heap->GetConcurrentMarker()->IsEnabled()); uint32_t markTaskNum = heap->GetMaxMarkTaskCount(); #endif EXPECT_TRUE(heap->GetSweeper()->ConcurrentSweepEnabled()); uint32_t evacuateTaskNum = heap->GetMaxEvacuateTaskCount(); auto partialGc = heap->GetPartialGC(); partialGc->RunPhases(); heap->ChangeGCParams(true); heap->Prepare(); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING uint32_t markTaskNumBackground = heap->GetMaxMarkTaskCount(); EXPECT_TRUE(markTaskNum > markTaskNumBackground); EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled()); #endif uint32_t evacuateTaskNumBackground = heap->GetMaxEvacuateTaskCount(); EXPECT_TRUE(evacuateTaskNum > evacuateTaskNumBackground); EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled()); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::CONSERVATIVE); partialGc->RunPhases(); heap->ChangeGCParams(false); heap->Prepare(); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING uint32_t markTaskNumForeground = heap->GetMaxMarkTaskCount(); EXPECT_EQ(markTaskNum, markTaskNumForeground); EXPECT_TRUE(heap->GetConcurrentMarker()->IsEnabled()); #endif uint32_t evacuateTaskNumForeground = heap->GetMaxEvacuateTaskCount(); EXPECT_EQ(evacuateTaskNum, evacuateTaskNumForeground); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT); EXPECT_TRUE(heap->GetSweeper()->ConcurrentSweepEnabled()); } HWTEST_F_L0(GCTest, ConfigDisable) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::CONFIG_DISABLE); heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::CONFIG_DISABLE); EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled()); EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled()); auto partialGc = heap->GetPartialGC(); partialGc->RunPhases(); heap->ChangeGCParams(false); heap->Prepare(); EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled()); EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled()); } HWTEST_F_L0(GCTest, NotifyMemoryPressure) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::HIGH_THROUGHPUT); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING uint32_t markTaskNum = heap->GetMaxMarkTaskCount(); #endif uint32_t evacuateTaskNum = heap->GetMaxEvacuateTaskCount(); auto partialGc = heap->GetPartialGC(); partialGc->RunPhases(); heap->ChangeGCParams(true); heap->NotifyMemoryPressure(true); heap->Prepare(); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING uint32_t markTaskNumBackground = heap->GetMaxMarkTaskCount(); EXPECT_TRUE(markTaskNum > markTaskNumBackground); EXPECT_FALSE(heap->GetConcurrentMarker()->IsEnabled()); #endif uint32_t evacuateTaskNumBackground = heap->GetMaxEvacuateTaskCount(); EXPECT_TRUE(evacuateTaskNum > evacuateTaskNumBackground); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::PRESSURE); EXPECT_FALSE(heap->GetSweeper()->ConcurrentSweepEnabled()); partialGc->RunPhases(); heap->ChangeGCParams(false); heap->Prepare(); #if !ECMASCRIPT_DISABLE_CONCURRENT_MARKING uint32_t markTaskNumForeground = heap->GetMaxMarkTaskCount(); EXPECT_EQ(markTaskNum, markTaskNumForeground); #endif uint32_t evacuateTaskNumForeground = heap->GetMaxEvacuateTaskCount(); EXPECT_EQ(evacuateTaskNum, evacuateTaskNumForeground); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::PRESSURE); heap->NotifyMemoryPressure(false); EXPECT_EQ(heap->GetMemGrowingType(), MemGrowingType::CONSERVATIVE); } HWTEST_F_L0(GCTest, NativeBindingCheckGCTest) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); size_t oldNativeSize = heap->GetNativeBindingSize(); size_t newNativeSize = heap->GetNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); EXPECT_TRUE(newNativeSize - oldNativeSize > 0); EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024); for (int i = 0; i < 20; i++) { auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); } // Young GC should be trigger here, so the size should be reduced. EXPECT_TRUE(newNativeSize - oldNativeSize < 22 * 1024 *1024); } auto partialGc = heap->GetPartialGC(); heap->SetMarkType(MarkType::MARK_FULL); partialGc->RunPhases(); newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } HWTEST_F_L0(GCTest, NonNewSpaceNativeBindingCheckGCTest) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize(); size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); EXPECT_TRUE(newNativeSize - oldNativeSize > 0); EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024); for (int i = 0; i < 300; i++) { auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1024); // malloc size is smaller to avoid test fail in the small devices. [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); } // Old GC should be trigger here, so the size should be reduced. EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024); } auto partialGc = heap->GetPartialGC(); heap->SetMarkType(MarkType::MARK_FULL); partialGc->RunPhases(); newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } HWTEST_F_L0(GCTest, NativeGCTestConcurrentMarkDisabled) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); // Disable concurrent mark. heap->GetConcurrentMarker()->ConfigConcurrentMark(false); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); size_t oldNativeSize = heap->GetNativeBindingSize(); size_t newNativeSize = heap->GetNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); EXPECT_TRUE(newNativeSize - oldNativeSize > 0); EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024); for (int i = 0; i < 20; i++) { auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); } // Young GC should be trigger here, so the size should be reduced. EXPECT_TRUE(newNativeSize - oldNativeSize < 22 * 1024 *1024); } const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC); newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled) { auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap()); // Disable concurrent mark. heap->GetConcurrentMarker()->ConfigConcurrentMark(false); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize(); size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle<JSNativePointer> obj2 = factory->NewJSNativePointer(newData1, NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); EXPECT_TRUE(newNativeSize - oldNativeSize > 0); EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024); for (int i = 0; i < 300; i++) { auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1024); // malloc size is smaller to avoid test fail in the small devices. [[maybe_unused]] JSHandle<JSNativePointer> obj3 = factory->NewJSNativePointer(newData2, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); } // Old GC should be trigger here, so the size should be reduced. EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024); } const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC); newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } HWTEST_F_L0(GCTest, ArkToolsForceFullGC) { const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC); size_t originalHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); size_t newSize = originalHeapSize; { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); for (int i = 0; i < 10; i++) { [[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1024 * 1024); } newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); } EXPECT_TRUE(newSize > originalHeapSize); auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 0); [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); [[maybe_unused]] JSTaggedValue result1 = builtins::BuiltinsArkTools::ForceFullGC(ecmaRuntimeCallInfo); ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize); } } // namespace panda::test