* 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 <chrono>
#include <thread>
#include "assembler/assembly-emitter.h"
#include "assembler/assembly-parser.h"
#include "ecmascript/builtins/builtins_ark_tools.h"
#include "ecmascript/containers/containers_bitvector.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_api/js_api_bitvector.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/mem/full_gc.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/runtime_lock.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/mem/sparse_space.h"
#include "ecmascript/mem/mem_controller.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_marker.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#include "ecmascript/mem/gc_key_stats.h"
#include "ecmascript/mem/gc_stats.h"
#include "ecmascript/mem/allocation_inspector.h"
#include "ecmascript/dfx/hprof/heap_sampling.h"
#include "ecmascript/tests/ecma_test_common.h"
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
#include "parameters.h"
#endif
using namespace panda;
using namespace panda::ecmascript;
using namespace panda::panda_file;
using namespace panda::pandasm;
namespace panda::test {
class GCTest : public BaseTestWithScope<false> {
public:
void SetUp() override
{
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
OHOS::system::SetParameter("persist.ark.sheap.growfactor", "1");
OHOS::system::SetParameter("persist.ark.sheap.growstep", "2");
OHOS::system::SetParameter("persist.ark.sensitive.threshold", "3");
OHOS::system::SetParameter("persist.ark.native.stepsize", "4");
OHOS::system::SetParameter("persist.ark.global.alloclimit", "4");
#endif
JSRuntimeOptions options;
instance = JSNApi::CreateEcmaVM(options);
ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
thread = instance->GetJSThread();
thread->ManagedCodeBegin();
scope = new EcmaHandleScope(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
}
};
HWTEST_F_L0(GCTest, ArkToolsHintGC)
{
Heap *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::CONFIG_DISABLE);
auto getSizeAfterCreateAndCallHintGC = [this, heap] (size_t &newSize, size_t &finalSize) -> bool {
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 2048; i++) {
[[maybe_unused]] JSHandle<TaggedArray> obj = thread->GetEcmaVM()->GetFactory()->
NewTaggedArray(10 * 1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
}
newSize = heap->GetCommittedSize();
}
std::vector<JSTaggedValue> vals{JSTaggedValue(static_cast<double>(2))};
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, vals,
6);
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = builtins::BuiltinsArkTools::HintGC(ecmaRuntimeCallInfo);
finalSize = heap->GetCommittedSize();
TestHelper::TearDownFrame(thread, prev);
return result.ToBoolean();
};
{
heap->CollectGarbage(TriggerGCType::FULL_GC);
heap->NotifyHighSensitive(true);
size_t originSize = heap->GetCommittedSize();
size_t newSize = 0;
size_t finalSize = 0;
bool res = getSizeAfterCreateAndCallHintGC(newSize, finalSize);
EXPECT_FALSE(res);
EXPECT_TRUE(newSize > originSize);
EXPECT_TRUE(finalSize == newSize);
heap->NotifyHighSensitive(false);
}
{
#ifdef NDEBUG
if constexpr (G_USE_CMS_GC) {
return;
}
size_t newSize = 0;
size_t finalSize = 0;
bool res = getSizeAfterCreateAndCallHintGC(newSize, finalSize);
EXPECT_TRUE(res);
#endif
}
}
HWTEST_F_L0(GCTest, LargeOverShootSizeTest)
{
if constexpr (G_USE_CMS_GC) {
return;
}
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::FULL_GC);
size_t originalYoungSize = heap->GetNewSpace()->GetCommittedSize();
EXPECT_FALSE(heap->GetNewSpace()->CommittedSizeIsLarge());
heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
heap->NotifyHighSensitive(true);
size_t originalCapacity = heap->GetNewSpace()->GetInitialCapacity();
size_t originalOverShootSize = heap->GetNewSpace()->GetOvershootSize();
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 300; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
}
size_t newYoungSize = heap->GetNewSpace()->GetCommittedSize();
EXPECT_TRUE(originalYoungSize < newYoungSize);
heap->NotifyHighSensitive(false);
heap->CollectGarbage(TriggerGCType::YOUNG_GC);
newYoungSize = heap->GetNewSpace()->GetCommittedSize();
size_t newOverShootSize = heap->GetNewSpace()->GetOvershootSize();
size_t newCapacity = heap->GetNewSpace()->GetInitialCapacity();
EXPECT_TRUE(originalYoungSize < newYoungSize);
EXPECT_TRUE(originalOverShootSize < newOverShootSize);
EXPECT_TRUE(0 < newOverShootSize);
EXPECT_TRUE(originalCapacity < newCapacity);
EXPECT_TRUE(heap->GetNewSpace()->GetMaximumCapacity() == newCapacity);
}
originalOverShootSize = heap->GetNewSpace()->GetOvershootSize();
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 2049; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE);
}
}
size_t newSize = heap->GetNewSpace()->GetCommittedSize();
EXPECT_TRUE(originalYoungSize <= newSize);
}
HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest001)
{
SharedHeap *heap = SharedHeap::GetInstance();
ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), false);
}
HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest001)
{
SharedHeap *heap = SharedHeap::GetInstance();
ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1374210560), true);
}
HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest002)
{
SharedHeap *heap = SharedHeap::GetInstance();
ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}
HWTEST_F_L0(GCTest, ObjectExceedMaxHeapSizeTest001)
{
SharedHeap *heap = SharedHeap::GetInstance();
ASSERT_EQ(heap->ObjectExceedMaxHeapSize(), false);
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest001)
{
#if !USE_STICKY_CMS_GC && defined(NDEBUG)
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::OLD_GC);
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 4048; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
}
}
#ifndef PANDA_TARGET_32
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), true);
#else
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
#endif
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest002)
{
#ifdef NDEBUG
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::FULL_GC);
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 4048; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
}
}
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), true);
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest003)
{
#ifdef NDEBUG
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CollectGarbage(TriggerGCType::FULL_GC);
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 1049; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(
1024, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
}
}
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), true);
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest004)
{
#ifdef NDEBUG
if constexpr (G_USE_CMS_GC) {
return;
}
auto sHeap = SharedHeap::GetInstance();
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 4048; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
}
}
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::LOW, GCReason::HINT_GC), true);
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest005)
{
#ifdef NDEBUG
if constexpr (G_USE_CMS_GC) {
return;
}
auto sHeap = SharedHeap::GetInstance();
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 4048; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
}
}
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::MIDDLE, GCReason::HINT_GC), true);
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerHintGCTest006)
{
#ifdef NDEBUG
if constexpr (G_USE_CMS_GC) {
return;
}
auto sHeap = SharedHeap::GetInstance();
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
for (int i = 0; i < 2049; i++) {
[[maybe_unused]] JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->
NewSOldSpaceTaggedArray(1024, JSTaggedValue::Undefined());
}
}
ASSERT_EQ(heap->CheckAndTriggerHintGC(MemoryReduceDegree::HIGH, GCReason::HINT_GC), true);
#endif
}
HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), false);
}
HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest003)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
for (size_t i = 0; i < 4; i++) {
ConcurrentMarker::TryIncreaseTaskCounts();
}
#ifndef PANDA_TARGET_32
ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), true);
#else
ASSERT_EQ(heap->TryTriggerFullMarkBySharedLimit(), false);
#endif
}
HWTEST_F_L0(GCTest, CheckAndTriggerTaskFinishedGCTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->CheckAndTriggerTaskFinishedGC();
ASSERT_EQ(heap->GetRecordObjectSize(), 0);
ASSERT_EQ(heap->GetRecordNativeSize(), 0);
}
HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedSizeTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
heap->TryTriggerFullMarkBySharedSize(81579214);
ASSERT_TRUE(heap->IsFullMarkRequested());
}
HWTEST_F_L0(GCTest, DecreaseNativeBindingSizeTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->DecreaseNativeBindingSize(0);
ASSERT_EQ(heap->GetNativeBindingSize(), 0);
}
HWTEST_F_L0(GCTest, TriggerConcurrentMarkingTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->SetMarkType(MarkType::MARK_FULL);
heap->TriggerConcurrentMarking();
EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::OTHER);
}
HWTEST_F_L0(GCTest, NotifyFinishColdStartTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->NotifyFinishColdStart(true);
}
HWTEST_F_L0(GCTest, NotifyFinishColdStartSoonTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->NotifyFinishColdStartSoon();
}
HWTEST_F_L0(GCTest, NeedStopCollectionTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->SetOnSerializeEvent(true);
ASSERT_EQ(heap->NeedStopCollection(), true);
}
HWTEST_F_L0(GCTest, CollectGarbageTest003)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
heap->CollectGarbage(TriggerGCType::FULL_GC, GCReason::IDLE);
ASSERT_EQ(heap->GetJSThread()->GetMarkStatus(), MarkStatus::READY_TO_MARK);
}
HWTEST_F_L0(GCTest, NeedStopCollectionTest002)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
ASSERT_EQ(heap->NeedStopCollection(), true);
}
HWTEST_F_L0(GCTest, NeedStopCollectionTest003)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
heap->GetOldSpace()->SetInitialCapacity(1000);
ASSERT_EQ(heap->NeedStopCollection(), false);
}
HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest002)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetOldSpace()->SetInitialCapacity(100);
ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), true);
}
HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest003)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetOldSpace()->SetInitialCapacity(100);
thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), true);
}
HWTEST_F_L0(GCTest, CheckAndTriggerSharedGCTest004)
{
SharedHeap *heap = SharedHeap::GetInstance();
thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
ASSERT_EQ(heap->CheckAndTriggerSharedGC(thread), false);
}
HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest003)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetOldSpace()->SetInitialCapacity(100);
ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}
HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest004)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetOldSpace()->SetInitialCapacity(100);
thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}
HWTEST_F_L0(GCTest, CheckHugeAndTriggerSharedGCTest005)
{
SharedHeap *heap = SharedHeap::GetInstance();
thread->SetSharedMarkStatus(SharedMarkStatus::CONCURRENT_MARKING_OR_FINISHED);
ASSERT_EQ(heap->CheckHugeAndTriggerSharedGC(thread, 1), false);
}
HWTEST_F_L0(GCTest, CheckOngoingConcurrentMarkingTest001)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
ASSERT_EQ(heap->CheckOngoingConcurrentMarking(), false);
}
HWTEST_F_L0(GCTest, CheckOngoingConcurrentMarkingTest002)
{
SharedHeap *heap = SharedHeap::GetInstance();
heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
ASSERT_EQ(heap->CheckOngoingConcurrentMarking(), false);
}
HWTEST_F_L0(GCTest, SelectGCTypeTest001)
{
if constexpr (G_USE_CMS_GC) {
return;
}
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetOldSpace()->SetInitialCapacity(100);
ASSERT_EQ(heap->SelectGCType(), OLD_GC);
}
HWTEST_F_L0(GCTest, SelectGCTypeTest002)
{
if constexpr (G_USE_CMS_GC) {
return;
}
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetOldSpace()->SetMaximumCapacity(1000);
heap->GetOldSpace()->SetOvershootSize(1000);
heap->GetNewSpace()->IncreaseCommitted(100000);
ASSERT_EQ(heap->SelectGCType(), OLD_GC);
}
HWTEST_F_L0(GCTest, CollectGarbageTest009)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::REQUEST_DISABLE);
#ifndef PANDA_TARGET_32
ASSERT_TRUE(heap->GetConcurrentMarker()->IsRequestDisabled());
#else
ASSERT_FALSE(heap->GetConcurrentMarker()->IsRequestDisabled());
#endif
heap->CollectGarbage(TriggerGCType::YOUNG_GC, GCReason::TRIGGER_BY_TASKPOOL);
}
HWTEST_F_L0(GCTest, TryTriggerFullMarkBySharedLimitTest004)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
ASSERT_TRUE(heap->TryTriggerFullMarkBySharedLimit());
heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
ASSERT_FALSE(heap->TryTriggerFullMarkBySharedLimit());
}
HWTEST_F_L0(GCTest, TriggerConcurrentMarkingTest003)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->ConfigConcurrentMark(false);
heap->TriggerConcurrentMarking(MarkReason::ALLOCATION_LIMIT);
EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::OTHER);
ConcurrentMarker::DecreaseTaskCounts();
heap->GetConcurrentMarker()->ConfigConcurrentMark(true);
heap->TriggerConcurrentMarking(MarkReason::ALLOCATION_LIMIT);
EXPECT_EQ(heap->GetEcmaGCStats()->GetMarkReason(), MarkReason::ALLOCATION_LIMIT);
}
HWTEST_F_L0(GCTest, NotifyFinishColdStartTest002)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->NotifyPostFork();
ASSERT_EQ(heap->GetStartupStatus(), StartupStatus::ON_STARTUP);
heap->NotifyFinishColdStart(true);
ASSERT_EQ(heap->GetStartupStatus(), StartupStatus::JUST_FINISH_STARTUP);
}
HWTEST_F_L0(GCTest, NeedStopCollectionTest004)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->SetOnSerializeEvent(false);
heap->SetSensitiveStatus(AppSensitiveStatus::ENTER_HIGH_SENSITIVE);
ASSERT_EQ(heap->NeedStopCollection(), true);
}
HWTEST_F_L0(GCTest, TryToGetSuitableSweptRegionTest001)
{
if constexpr (G_USE_CMS_GC) {
return;
}
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
SparseSpace *space = static_cast<SparseSpace *>(heap->GetSweepableSpaceWithType(MemSpaceType::OLD_SPACE));
space->FinishFillSweptRegion();
ASSERT_EQ(space->TryToGetSuitableSweptRegion(100), nullptr);
}
HWTEST_F_L0(GCTest, CalculateGrowingFactorTest001)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->SetMemGrowingType(MemGrowingType::CONSERVATIVE);
MemController *memController = new MemController(heap);
ASSERT_EQ(memController->CalculateGrowingFactor(0, 0), 2.0);
}
HWTEST_F_L0(GCTest, CalculateGrowingFactorTest002)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->SetMemGrowingType(MemGrowingType::PRESSURE);
MemController *memController = new MemController(heap);
ASSERT_EQ(memController->CalculateGrowingFactor(0, 0), 1.1);
}
HWTEST_F_L0(GCTest, StartCalculationBeforeGCTest001)
{
if constexpr (G_USE_CMS_GC) {
return;
}
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
MemController *memController = new MemController(heap);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
memController->StartCalculationBeforeGC();
ASSERT_NE(memController->GetNewSpaceAllocSizeSinceGC(), 0);
ASSERT_NE(memController->GetOldSpaceAllocSizeSinceGC(), 0);
memController->StopCalculationAfterGC(TriggerGCType::YOUNG_GC);
ASSERT_EQ(memController->GetNewSpaceAllocSizeSinceGC(), 0);
ASSERT_EQ(memController->GetOldSpaceAllocSizeSinceGC(), 0);
}
HWTEST_F_L0(GCTest, StartCalculationBeforeGCTest002)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
MemController *memController = new MemController(heap);
memController->StopCalculationAfterGC(TriggerGCType::YOUNG_GC);
ASSERT_EQ(memController->GetNewSpaceAllocSizeSinceGC(), 0);
ASSERT_EQ(memController->GetOldSpaceAllocSizeSinceGC(), 0);
}
HWTEST_F_L0(GCTest, DryTrunkExpandTest001)
{
auto trunk = thread->GetEcmaVM()->GetChunk();
DynChunk *dynChunk = new DynChunk(trunk);
ASSERT_TRUE(dynChunk->GetAllocatedSize() < 1000);
dynChunk->SetError();
ASSERT_EQ(dynChunk->Expand(1000), -1);
}
HWTEST_F_L0(GCTest, DryTrunkInsertTest001)
{
auto trunk = thread->GetEcmaVM()->GetChunk();
DynChunk *dynChunk = new DynChunk(trunk);
ASSERT_EQ(dynChunk->Insert(5, 5), -1);
}
HWTEST_F_L0(GCTest, DryTrunkInsertTest002)
{
auto trunk = thread->GetEcmaVM()->GetChunk();
DynChunk *dynChunk = new DynChunk(trunk);
dynChunk->SetError();
ASSERT_EQ(dynChunk->Insert(0, 5), -1);
}
HWTEST_F_L0(GCTest, AdvanceAllocationInspectorTest001)
{
auto counter = new AllocationCounter();
counter->AdvanceAllocationInspector(100);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto profiler = new HeapSampling(thread->GetEcmaVM(), heap, 10, 3);
auto inspector = new AllocationInspector(heap, 10, profiler);
counter->AddAllocationInspector(inspector);
counter->AdvanceAllocationInspector(0);
}
HWTEST_F_L0(GCTest, InvokeAllocationInspectorTest001)
{
auto counter = new AllocationCounter();
counter->InvokeAllocationInspector(10000, 100, 100);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto profiler = new HeapSampling(thread->GetEcmaVM(), heap, 10, 3);
auto inspector = new AllocationInspector(heap, 10, profiler);
counter->AddAllocationInspector(inspector);
counter->InvokeAllocationInspector(10000, 100, 100);
}
HWTEST_F_L0(GCTest, OldSpaceValidCheck)
{
if constexpr (G_USE_CMS_GC) {
return;
}
static constexpr size_t kLength = 10 * 1024;
static constexpr size_t kCount = 2;
static constexpr size_t kLimit = 380 * 1024 * 1024;
instance->GetJSOptions().SetEnableForceGC(false);
Heap *heap = const_cast<Heap *>(instance->GetHeap());
ObjectFactory *factory = heap->GetEcmaVM()->GetFactory();
auto array = factory->NewTaggedArray(kLength, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
heap->ShouldThrowOOMError(true);
heap->GetOldSpace()->IncreaseLiveObjectSize(kLimit);
for (size_t i = 0; i < kCount; i++) {
array = factory->NewTaggedArray(kLength, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
Region *objectRegion = Region::ObjectAddressToRange(*array);
bool inHeap = false;
heap->GetOldSpace()->EnumerateRegions([objectRegion, &inHeap](Region *each) {
if (objectRegion == each) {
inHeap = true;
}
});
EXPECT_TRUE(inHeap);
}
}
HWTEST_F_L0(GCTest, DisableSharedConcurrentSweep)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
SharedHeap *sHeap = SharedHeap::GetInstance();
sHeap->GetSweeper()->ConfigConcurrentSweep(false);
{
[[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread);
[[maybe_unused]] JSHandle<EcmaString> key1(factory->NewFromASCII("error1"));
[[maybe_unused]] JSHandle<EcmaString> key2(factory->NewFromASCII("error2"));
[[maybe_unused]] JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
[[maybe_unused]] JSHandle<EcmaString> key3(factory->NewFromASCII("error3"));
[[maybe_unused]] JSHandle<EcmaString> key4(factory->NewFromASCII("error4"));
[[maybe_unused]] JSHandle<EcmaString> msg2(factory->NewFromASCII("this is error2"));
auto* newBitSetVector = new std::vector<std::bitset<JSAPIBitVector::BIT_SET_LENGTH>>();
int32_t capacity = 256;
std::bitset<JSAPIBitVector::BIT_SET_LENGTH> initBitSet;
newBitSetVector->resize(capacity, initBitSet);
auto deleter = []([[maybe_unused]] void *env, void *pointer, [[maybe_unused]] void *data) {
if (pointer == nullptr) {
return;
}
delete reinterpret_cast<std::vector<std::bitset<JSAPIBitVector::BIT_SET_LENGTH>> *>(pointer);
};
[[maybe_unused]] JSHandle<JSNativePointer> pointer = factory->NewSJSNativePointer(newBitSetVector, deleter,
newBitSetVector);
const char *filename1 = "__JSPandaFileManagerTest1.pa";
const char *filename2 = "__JSPandaFileManagerTest2.pa";
const char *data = R"(
.function void foo() {}
)";
JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
Parser parser;
auto res = parser.Parse(data);
std::unique_ptr<const File> pfPtr1 = pandasm::AsmEmitter::Emit(res.Value());
std::unique_ptr<const File> pfPtr2 = pandasm::AsmEmitter::Emit(res.Value());
std::shared_ptr<JSPandaFile> pf1 = pfManager->NewJSPandaFile(pfPtr1.release(), CString(filename1));
std::shared_ptr<JSPandaFile> pf2 = pfManager->NewJSPandaFile(pfPtr2.release(), CString(filename2));
pfManager->AddJSPandaFile(pf1);
pfManager->AddJSPandaFile(pf2);
JSHandle<ConstantPool> constpool1 = instance->GetFactory()->NewSConstantPool(1);
JSHandle<ConstantPool> constpool2 = instance->GetFactory()->NewSConstantPool(2);
constpool1 = Runtime::GetInstance()->AddOrUpdateConstpool(pf1.get(), constpool1, 0);
constpool2 = Runtime::GetInstance()->AddOrUpdateConstpool(pf2.get(), constpool2, 0);
}
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->WaitGCFinished(thread);
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->WaitGCFinished(thread);
sHeap->GetSweeper()->ConfigConcurrentSweep(true);
EXPECT_FALSE(sHeap->GetSweeper()->IsDisabled());
};
HWTEST_F_L0(GCTest, RawHeapSendSysEventDataSize)
{
const std::string fileName = "/data/log/faultlog/temp/jsheap.rawheap";
uint64_t fileSize = 256;
std::vector<std::string> filePaths;
std::vector<uint64_t> fileSizes;
filePaths.emplace_back(fileName);
fileSizes.emplace_back(fileSize);
GCKeyStats *keystats = thread->GetEcmaVM()->GetEcmaGCKeyStats();
int32_t ret = keystats->SendSysEventDataSize(filePaths, fileSizes);
ASSERT_EQ(ret, 0);
}
HWTEST_F_L0(GCTest, ResetLargeHeapTest)
{
if constexpr (G_USE_CMS_GC) {
return;
}
static constexpr size_t heapSize = 512 * 1024 * 1024;
const Heap *heap = thread->GetEcmaVM()->GetHeap();
const_cast<Heap *>(heap)->ResetLargeCapacity(heapSize);
ASSERT_EQ(heap->GetEcmaParamConfiguration().GetMaxHeapSize(), heapSize);
auto sharedHeap = SharedHeap::GetInstance();
sharedHeap->ResetLargeCapacity(heapSize);
ASSERT_EQ(sharedHeap->GetEcmaParamConfiguration().GetMaxHeapSize(), heapSize);
}
HWTEST_F_L0(GCTest, SharedMarkingBarrier)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
SharedHeap *sHeap = SharedHeap::GetInstance();
JSHandle<TaggedArray> array(factory->NewSTaggedArray(16));
sHeap->TriggerConcurrentMarking<TriggerGCType::SHARED_GC, MarkReason::OTHER>(thread);
std::this_thread::sleep_for(std::chrono::seconds(3));
thread->CheckSafepoint();
JSHandle<TaggedArray> obj(factory->NewSTaggedArray(16));
for (int i = 0; i < 10; i++) {
array->Set(thread, i, obj);
}
}
HWTEST_F_L0(GCTest, OvershootSizeTest1)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto newSpace = heap->GetNewSpace();
newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
newSpace->SetOverShootSize(0);
thread->SetMarkStatus(MarkStatus::READY_TO_MARK);
thread->SetProcessingLocalToSharedRset(false);
for (int i = 0; i < 10; i++) {
newSpace->Allocate(50 * 1024);
}
ASSERT_TRUE(newSpace->GetOvershootSize() == 0);
}
HWTEST_F_L0(GCTest, OvershootSizeTest2)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto newSpace = heap->GetNewSpace();
newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
newSpace->SetOverShootSize(0);
thread->SetMarkStatus(MarkStatus::READY_TO_MARK);
thread->SetProcessingLocalToSharedRset(true);
for (int i = 0; i < 10; i++) {
newSpace->Allocate(50 * 1024);
}
ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}
HWTEST_F_L0(GCTest, OvershootSizeTest3)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto newSpace = heap->GetNewSpace();
newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
newSpace->SetOverShootSize(0);
thread->SetMarkStatus(MarkStatus::MARKING);
thread->SetProcessingLocalToSharedRset(false);
for (int i = 0; i < 10; i++) {
newSpace->Allocate(50 * 1024);
}
ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}
HWTEST_F_L0(GCTest, OvershootSizeTest4)
{
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
auto newSpace = heap->GetNewSpace();
newSpace->SetInitialCapacity(newSpace->GetCommittedSize());
newSpace->SetOverShootSize(0);
thread->SetMarkStatus(MarkStatus::MARKING);
thread->SetProcessingLocalToSharedRset(true);
for (int i = 0; i < 10; i++) {
newSpace->Allocate(50 * 1024);
}
ASSERT_TRUE(newSpace->GetOvershootSize() > 0);
}
void TestStringTable(JSThread *thread, EcmaStringTable *stringTable)
{
EcmaVM *vm = thread->GetEcmaVM();
ObjectFactory *factory = vm->GetFactory();
EcmaString *str = EcmaStringAccessor::CreateEmptyString(thread->GetEcmaVM());
EcmaString *result = stringTable->GetOrInternFlattenString(vm, str);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
uint8_t utf8Data[] = {0x74, 0x65, 0x73, 0x74};
str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
result = stringTable->GetOrInternFlattenStringNoGC(vm, str);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
JSHandle<EcmaString> strHandle =
factory->NewFromASCII("00000x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");
uint32_t offset = 4;
uint32_t utf8Len = EcmaStringAccessor(*strHandle).GetLength() - offset;
result = stringTable->GetOrInternStringFromCompressedSubString(vm, strHandle, offset, utf8Len);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "0x680x650x6c0x6c0x6f0x200x770x6f0x720x6c0x64");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
str = EcmaStringAccessor::CreateFromUtf8(vm, utf8Data, sizeof(utf8Data), true);
result = stringTable->GetOrInternString(vm, str);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
JSHandle<EcmaString> first = factory->NewFromASCII("hello");
JSHandle<EcmaString> second = factory->NewFromASCII("world");
result = stringTable->GetOrInternString(vm, first, second);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "helloworld");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
result = stringTable->GetOrInternString(vm, utf8Data, sizeof(utf8Data), true);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
uint16_t utf16Data[] = {0x7F16, 0x7801, 0x89E3, 0x7801};
result = stringTable->GetOrInternString(vm, utf16Data, sizeof(utf16Data) / sizeof(uint16_t), false);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "编码解码");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
strHandle = factory->NewFromASCII("test");
stringTable->GetOrInternString(vm, *strHandle);
result = stringTable->TryGetInternString(thread, strHandle);
ASSERT_STREQ(EcmaStringAccessor(result).ToCString(thread).c_str(), "test");
ASSERT_TRUE(EcmaStringAccessor(result).IsInternString());
ASSERT_TRUE(stringTable->CheckStringTableValidity(thread));
}
HWTEST_F_L0(GCTest, stringTableConcurrentSweepTest1)
{
EcmaStringTable *stringTable = Runtime::GetInstance()->GetEcmaStringTable();
typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *hashTrieMap =
reinterpret_cast<typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *>(stringTable->GetHashTrieMap());
stringTable->GetCleaner()->SetEnableConcurrentSweep(true);
{
RuntimeLockHolder(thread, SharedHeap::GetInstance()->GetSuspensionRequestMutex());
hashTrieMap->StartSweeping();
ASSERT_TRUE(hashTrieMap->IsSweeping());
}
TestStringTable(thread, stringTable);
{
RuntimeLockHolder(thread, SharedHeap::GetInstance()->GetSuspensionRequestMutex());
hashTrieMap->FinishSweeping();
ASSERT_FALSE(hashTrieMap->IsSweeping());
}
TestStringTable(thread, stringTable);
}
HWTEST_F_L0(GCTest, stringTableConcurrentSweepTest2)
{
SharedHeap *sHeap = SharedHeap::GetInstance();
EcmaStringTable *stringTable = Runtime::GetInstance()->GetEcmaStringTable();
typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *hashTrieMap =
reinterpret_cast<typename DisableCMCGCConcurrentSweepTrait::HashTrieMapType *>(stringTable->GetHashTrieMap());
stringTable->GetCleaner()->SetEnableConcurrentSweep(true);
hashTrieMap->IncreaseInuseCount();
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
sHeap->PrepareByJSThread(thread, true);
sHeap->GetOldSpace()->SelectCSets();
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
hashTrieMap->DecreaseInuseCount();
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
sHeap->PrepareByJSThread(thread, true);
sHeap->GetOldSpace()->SelectCSets();
sHeap->CollectGarbage<TriggerGCType::SHARED_GC, GCReason::OTHER>(thread);
sHeap->CollectGarbage<TriggerGCType::SHARED_FULL_GC, GCReason::OTHER>(thread);
RuntimeLockHolder(thread, sHeap->GetSuspensionRequestMutex());
ASSERT_FALSE(hashTrieMap->IsSweeping());
}
#ifndef USE_CMC_GC
HWTEST_F_L0(GCTest, stringTableReadBarrierTest)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSTaggedType value = reinterpret_cast<JSTaggedType>(nullptr);
JSTaggedType result = Barriers::ReadBarrierForStringTableSlot(value);
ASSERT_TRUE(result == reinterpret_cast<JSTaggedType>(nullptr));
value = thread->GlobalConstants()->GetPrototypeString().GetRawData();
result = Barriers::ReadBarrierForStringTableSlot(value);
ASSERT_TRUE(result == value);
JSHandle<EcmaString> str = factory->NewFromASCIISkippingStringTable("test");
SharedHeap::GetInstance()->PrepareByJSThread(thread, false);
value = str.GetTaggedType();
Region::ObjectAddressToRange(value)->AtomicMark(reinterpret_cast<void *>(value));
result = Barriers::ReadBarrierForStringTableSlot(value);
ASSERT_TRUE(result == value);
Region::ObjectAddressToRange(value)->ClearMark(reinterpret_cast<void *>(value));
result = Barriers::ReadBarrierForStringTableSlot(value);
ASSERT_TRUE(result == reinterpret_cast<JSTaggedType>(nullptr));
}
#endif
}