/*
 * 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/mem/gc_stats.h"
#include "ecmascript/mem/gc_key_stats.h"
#include "common_components/base/time_utils.h"
#include "ecmascript/mem/heap-inl.h"

constexpr int DESCRIPTION_LENGTH = 25;
constexpr int DATA_LENGTH = 8;

#define STATS_DESCRIPTION_FORMAT(description)    \
    std::left << std::setw(DESCRIPTION_LENGTH) << (description)

#define STATS_DATA_FORMAT(data)    \
    std::setw(DATA_LENGTH) << (data)

namespace panda::ecmascript {
namespace {
struct PauseRecord {
    RecordData count;
    RecordDuration minPause;
    RecordDuration maxPause;
    RecordDuration totalPause;
};

constexpr PauseRecord GC_STATISTIC_RECORDS[] = {
    {RecordData::YOUNG_COUNT, RecordDuration::YOUNG_MIN_PAUSE, RecordDuration::YOUNG_MAX_PAUSE,
        RecordDuration::YOUNG_TOTAL_PAUSE},
    {RecordData::OLD_COUNT, RecordDuration::OLD_MIN_PAUSE, RecordDuration::OLD_MAX_PAUSE,
        RecordDuration::OLD_TOTAL_PAUSE},
    {RecordData::COMPRESS_COUNT, RecordDuration::COMPRESS_MIN_PAUSE, RecordDuration::COMPRESS_MAX_PAUSE,
        RecordDuration::COMPRESS_TOTAL_PAUSE},
    {RecordData::LOCAL_CC_COUNT, RecordDuration::LOCAL_CC_MIN_PAUSE, RecordDuration::LOCAL_CC_MAX_PAUSE,
        RecordDuration::LOCAL_CC_TOTAL_PAUSE},
    {RecordData::SHARED_COUNT, RecordDuration::SHARED_MIN_PAUSE, RecordDuration::SHARED_MAX_PAUSE,
        RecordDuration::SHARED_TOTAL_PAUSE},
    {RecordData::SWEEP_COUNT, RecordDuration::SWEEP_MIN_PAUSE, RecordDuration::SWEEP_MAX_PAUSE,
        RecordDuration::SWEEP_TOTAL_PAUSE},
};
}  // namespace

const char *GCStats::GetGCStatisticType(GCType type)
{
    switch (type) {
        case GCType::SHARED_GC:
        case GCType::SHARED_PARTIAL_GC:
        case GCType::SHARED_FULL_GC:
        case GCType::GLOBAL_GC:
            return "Shared GC";
        case GCType::PARTIAL_YOUNG_GC:
        case GCType::PARTIAL_OLD_GC:
        case GCType::LOCAL_CC:
        case GCType::COMPRESS_GC:
        case GCType::CMS_GC:
        case GCType::STICKY_CMS_GC:
            return "Local GC";
        default:
            return "UnknownType";
    }
}

void GCStats::RecordGCStatisticStart()
{
    lastGCStartTime_ = common::TimeUtil::CurrentTimeInMs();
    lastGCType_ = GetGCStatisticType(gcType_);
}

void GCStats::RecordGCStatisticEnd()
{
    lastGCEndTime_ = common::TimeUtil::CurrentTimeInMs();
}

GCStatisticData GCStats::GetGCStatistic()
{
    GCStatisticData stats;
    for (const auto &record : GC_STATISTIC_RECORDS) {
        size_t count = GetRecordData(record.count);
        if (count == 0) {
            continue;
        }
        stats.count += count;
        stats.maxPause = std::max(stats.maxPause, GetRecordDuration(record.maxPause));
        stats.totalPause += GetRecordDuration(record.totalPause);
        float minPause = GetRecordDuration(record.minPause);
        if (stats.minPause == 0.0f || minPause < stats.minPause) {
            stats.minPause = minPause;
        }
    }
    stats.lastStartTime = lastGCStartTime_;
    stats.lastEndTime = lastGCEndTime_;
    stats.lastType = lastGCType_;
    return stats;
}

GCStatisticData GCStats::MergeGCStatistic(const GCStatisticData &localStats,
    const GCStatisticData &sharedStats)
{
    GCStatisticData merged;
    merged.count = localStats.count + sharedStats.count;
    merged.maxPause = std::max(localStats.maxPause, sharedStats.maxPause);
    if (localStats.count == 0) {
        merged.minPause = sharedStats.minPause;
    } else if (sharedStats.count == 0) {
        merged.minPause = localStats.minPause;
    } else {
        merged.minPause = std::min(localStats.minPause, sharedStats.minPause);
    }
    merged.totalPause = localStats.totalPause + sharedStats.totalPause;
    if (localStats.lastStartTime >= sharedStats.lastStartTime) {
        merged.lastStartTime = localStats.lastStartTime;
        merged.lastType = localStats.lastType;
    } else {
        merged.lastStartTime = sharedStats.lastStartTime;
        merged.lastType = sharedStats.lastType;
    }
    merged.lastEndTime = std::max(localStats.lastEndTime, sharedStats.lastEndTime);
    return merged;
}
void GCStats::PrintStatisticResult()
{
    LOG_GC(INFO) << "/******************* GCStats statistic: *******************/";
    PrintGCSummaryStatistic(GCType::PARTIAL_YOUNG_GC);
    PrintGCSummaryStatistic(GCType::PARTIAL_OLD_GC);
    PrintGCSummaryStatistic(GCType::COMPRESS_GC);
    PrintGCSummaryStatistic(GCType::LOCAL_CC);
    PrintGCMemoryStatistic();
}

void GCStats::PrintGCStatistic()
{
    ASSERT(heap_ != nullptr);
    ASSERT(heap_->GetEcmaVM() != nullptr);
    if (heap_->GetEcmaVM()->GetJSOptions().EnableGCTracer() || CheckIfLongTimePause()) {
        LOG_GC(INFO) << " [ " << GetGCTypeName() << " ] "
                        << sizeToMB(recordData_[(uint8_t)RecordData::START_OBJ_SIZE]) << " ("
                        << sizeToMB(recordData_[(uint8_t)RecordData::START_COMMIT_SIZE]) << ") -> "
                        << sizeToMB(recordData_[(uint8_t)RecordData::END_OBJ_SIZE]) << " ("
                        << sizeToMB(recordData_[(uint8_t)RecordData::END_COMMIT_SIZE]) << ") MB, "
                        << scopeDuration_[Scope::ScopeId::TotalGC] << "(+"
                        << GetConcurrrentMarkDuration()
                        << ")ms, GCReason: " << GCReasonToString()
                        << ", MarkReason: " << MarkReasonToString();
        // fixme: refactor?
        if constexpr (G_USE_CMS_GC) {
            LOG_GC(INFO) << "IsInBackground: " << Runtime::GetInstance()->IsInBackground() << "; "
                << "SensitiveStatus: " << static_cast<int>(heap_->GetSensitiveStatus()) << "; "
                << "StartupStatus: " << std::to_string(static_cast<int>(heap_->GetStartupStatus())) << "; "
                << "BundleName: " << heap_->GetEcmaVM()->GetBundleName()
                << "; SlotSpace: " << std::to_string(heap_->GetSlotSpace()->GetCommittedSize())
                << "; TotalCommit" << std::to_string(heap_->GetCommittedSize());
        } else {
            LOG_GC(INFO) << "IsInBackground: " << Runtime::GetInstance()->IsInBackground() << "; "
                << "SensitiveStatus: " << static_cast<int>(heap_->GetSensitiveStatus()) << "; "
                << "StartupStatus: " << std::to_string(static_cast<int>(heap_->GetStartupStatus())) << "; "
                << "BundleName: " << heap_->GetEcmaVM()->GetBundleName()
                << "; Young: " << std::to_string(heap_->GetNewSpace()->GetCommittedSize())
                << "; Old: " << std::to_string(heap_->GetOldSpace()->GetCommittedSize())
                << "; TotalCommit" << std::to_string(heap_->GetCommittedSize());
        }
        // print verbose gc statsistics
        PrintVerboseGCStatistic();
    }
    GCFinishTrace();
    InitializeRecordList();
}

const char *GCStats::GCReasonToString()
{
    return GCReasonToString(gcReason_);
}

const char *GCStats::GCReasonToString(GCReason reason)
{
    switch (reason) {
        case GCReason::ALLOCATION_LIMIT:
            return "Memory reach limit";
        case GCReason::ALLOCATION_FAILED:
            return "Allocate object failed";
        case GCReason::IDLE:
            return "Idle time task";
        case GCReason::SWITCH_BACKGROUND:
            return "Switch to background";
        case GCReason::EXTERNAL_TRIGGER:
            return "Externally triggered";
        case GCReason::WORKER_DESTRUCTION:
            return "Worker Destruction";
        case GCReason::TRIGGER_BY_JS:
            return "Trigger by JS";
        case GCReason::HINT_GC:
            return "Trigger by hint";
        case GCReason::NATIVE_LIMIT:
            return "Native reach limit";
        case GCReason::SHARED_LIMIT:
            return "Shared reach limit";
        case GCReason::IDLE_NATIVE:
            return "Idle time task by native";
        case GCReason::HANDLE_MARKING_FINISHED:
            return "ConcurrentMark finished";
        case GCReason::TRIGGER_BY_ARKUI:
            return "Trigger by ArkUI";
        case GCReason::TRIGGER_BY_ABILITY:
            return "Trigger by AbilityRuntime";
        case GCReason::TRIGGER_BY_MEM_TOOLS:
            return "Trigger by Mem tools";
        case GCReason::TRIGGER_BY_TASKPOOL:
            return "Trigger by taskPool";
        default: // LCOV_EXCL_BR_LINE
            return "Other";
    }
}

const char *GCStats::MarkReasonToString()
{
    return concurrentMark_ ? MarkReasonToString(markReason_) : "Not ConcurrentMark";
}

const char *GCStats::MarkReasonToString(MarkReason reason)
{
    switch (reason) {
        case MarkReason::ALLOCATION_LIMIT:
            return "Memory reach limit";
        case MarkReason::OLD_GC_WITHOUT_FULLMARK:
            return "OldGC without concurrent full mark";
        case MarkReason::IDLE:
            return "Idle time task";
        case MarkReason::EXIT_HIGH_SENSITIVE:
            return "Exit high sensitive";
        case MarkReason::EXTERNAL_TRIGGER:
            return "Externally triggered";
        case MarkReason::WORKER_DESTRUCTION:
            return "Worker Destruction";
        case MarkReason::TRIGGER_BY_JS:
            return "Trigger by JS";
        case MarkReason::HINT_GC:
            return "Trigger by hint";
        case MarkReason::NATIVE_LIMIT:
            return "Native reach limit";
        case MarkReason::SHARED_LIMIT:
            return "Shared reach limit";
        case MarkReason::EXIT_SERIALIZE:
            return "Exit serialize";
        default: // LCOV_EXCL_BR_LINE
            return "Other";
    }
}

float GCStats::GetConcurrrentMarkDuration()
{
    return concurrentMark_ ? scopeDuration_[Scope::ScopeId::ConcurrentMark] : 0;
}

void GCStats::PrintVerboseGCStatistic()
{
    PrintGCDurationStatistic();
    PrintGCMemoryStatistic();
    PrintGCSummaryStatistic();
}

void GCStats::GCFinishTrace()
{
    ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK,
        ("PartialGC::Finish" + std::to_string(heap_->IsConcurrentFullMark())
        + ";Reason" + std::to_string(static_cast<int>(gcReason_))
        + ";Sensitive" + std::to_string(static_cast<int>(heap_->GetSensitiveStatus()))
        + ";IsInBackground" + std::to_string(Runtime::GetInstance()->IsInBackground())
        + ";Startup" + std::to_string(heap_->OnStartupEvent())
        + ";ConMark" + std::to_string(static_cast<int>(heap_->GetJSThread()->GetMarkStatus()))
#if USE_CMS_GC  // fixme: refactor?
        + ";Slot" + std::to_string(heap_->GetSlotSpace()->GetCommittedSize())
#else
        + ";Young" + std::to_string(heap_->GetNewSpace()->GetCommittedSize())
        + ";Old" + std::to_string(heap_->GetOldSpace()->GetCommittedSize())
#endif
        + ";TotalCommit" + std::to_string(heap_->GetCommittedSize())
        + ";NativeBindingSize" + std::to_string(heap_->GetNativeBindingSize())
        + ";NativeLimitSize" + std::to_string(heap_->GetGlobalSpaceNativeLimit())).c_str(), "");
}

void GCStats::PrintGCMemoryStatistic()
{
    NativeAreaAllocator *nativeAreaAllocator = heap_->GetNativeAreaAllocator();
    HeapRegionAllocator *heapRegionAllocator = heap_->GetHeapRegionAllocator();
    LOG_GC(INFO) << "/****************** GC Memory statistic: *****************/";
    LOG_GC(INFO) << "AllSpaces        used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetCommittedSize())) << "KB\n"
#if USE_CMS_GC      // fixme: refactor?
                    << "SlotSpace  used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetSlotSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetSlotSpace()->GetCommittedSize())) << "KB\n"
#else
                    << "ActiveSemiSpace  used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNewSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNewSpace()->GetCommittedSize())) << "KB\n"
                    << "OldSpace         used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetOldSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetOldSpace()->GetCommittedSize())) << "KB\n"
#endif
                    << "HugeObjectSpace  used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetHugeObjectSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetHugeObjectSpace()->GetCommittedSize())) << "KB\n"
                    << "NonMovableSpace  used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNonMovableSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNonMovableSpace()->GetCommittedSize())) << "KB\n"
                    << "AppSpawnSpace    used:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetAppSpawnSpace()->GetHeapObjectSize())) << "KB"
                    << "     committed:"
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetAppSpawnSpace()->GetCommittedSize())) << "KB";

    LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Anno memory usage size:")
                    << STATS_DATA_FORMAT(sizeToMB(heapRegionAllocator->GetAnnoMemoryUsage())) << "MB\n"
                    << STATS_DESCRIPTION_FORMAT("Native memory usage size:")
                    << STATS_DATA_FORMAT(sizeToMB(nativeAreaAllocator->GetNativeMemoryUsage())) << "MB\n"
                    << STATS_DESCRIPTION_FORMAT("NativeBindingSize:")
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNativeBindingSize())) << "KB\n"
                    << STATS_DESCRIPTION_FORMAT("NativeLimitSize:")
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetGlobalSpaceNativeLimit())) << "KB\n"
                    << STATS_DESCRIPTION_FORMAT("ArrayBufferNativeSize:")
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNativeAreaAllocator()->GetArrayBufferNativeSize()))
                    << "KB\n"
                    << STATS_DESCRIPTION_FORMAT("ChunkNativeSize:")
                    << STATS_DATA_FORMAT(sizeToKB(heap_->GetNativeAreaAllocator()->GetChunkNativeSize())) << "KB";
    switch (gcType_) {
        case GCType::PARTIAL_YOUNG_GC: {
            double copiedRate = double(GetRecordData(RecordData::YOUNG_ALIVE_SIZE)) /
                                GetRecordData(RecordData::YOUNG_COMMIT_SIZE);
            double premotedRate = double(GetRecordData(RecordData::YOUNG_PROMOTE_SIZE)) /
                                  GetRecordData(RecordData::YOUNG_COMMIT_SIZE);
            double survivalRate = std::min(copiedRate + premotedRate, 1.0);
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Young copied rate:") << STATS_DATA_FORMAT(copiedRate) << "\n"
                << STATS_DESCRIPTION_FORMAT("Young promoted rate:") << STATS_DATA_FORMAT(premotedRate) << "\n"
                << STATS_DESCRIPTION_FORMAT("Young survival rate:") << STATS_DATA_FORMAT(survivalRate);
            break;
        }
        case GCType::PARTIAL_OLD_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Heap alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::OLD_ALIVE_SIZE)) /
                                     GetRecordData(RecordData::OLD_COMMIT_SIZE));
            break;
        }
        case GCType::COMPRESS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Heap alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::COMPRESS_ALIVE_SIZE)) /
                                     GetRecordData(RecordData::COMPRESS_COMMIT_SIZE));
            break;
        }
        case GCType::STICKY_CMS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Heap alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SWEEP_ALIVE_SIZE)) /
                                     GetRecordData(RecordData::SWEEP_COMMIT_SIZE));
            break;
        }
        case GCType::CMS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Heap alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SWEEP_ALIVE_SIZE)) /
                                     GetRecordData(RecordData::SWEEP_COMMIT_SIZE));
            break;
        }
        default:
            break;
    }
}

void GCStats::PrintGCDurationStatistic()
{
    LOG_GC(INFO) << "/***************** GC Duration statistic: ****************/";
    switch (gcType_) {
        case GCType::PARTIAL_YOUNG_GC:
        case GCType::PARTIAL_OLD_GC:
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Initialize:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Initialize]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Mark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Mark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("MarkRoots:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::MarkRoots]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ConcurrentMark pause:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ConcurrentMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitConcurrentMarkFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitConcurrentMarkFinished]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ReMark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ReMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProcessSharedGCRSetWorkList:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProcessSharedGCRSetWorkList]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Sweep:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ClearNativeObject:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ClearNativeObject]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Evacuate:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Evacuate]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("UpdateReference:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateReference]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("UpdateWeekRef:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateWeekRef]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("UpdateRoot:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateRoot]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProceeWorkload:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProceeWorkload]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("EvacuateSpace:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::EvacuateSpace]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("EvacuateRegion:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::EvacuateRegion]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitFinish]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Finish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Finish]) << "ms";
            break;
        case GCType::COMPRESS_GC:
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProcessSharedGCRSetWorkList:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProcessSharedGCRSetWorkList]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Initialize:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Initialize]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Mark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Mark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("MarkRoots:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::MarkRoots]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Sweep:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Finish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Finish]) << "ms";
            break;
        case GCType::STICKY_CMS_GC:
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Initialize:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Initialize]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Mark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Mark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("MarkRoots:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::MarkRoots]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ConcurrentMark pause:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ConcurrentMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitConcurrentMarkFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitConcurrentMarkFinished]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ReMark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ReMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProcessSharedGCRSetWorkList:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProcessSharedGCRSetWorkList]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Sweep:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ClearNativeObject:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ClearNativeObject]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ClearDeadReferences:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ClearDeadReferences]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitFinish]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Finish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Finish]) << "ms";
        case GCType::CMS_GC:
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Initialize:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Initialize]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Mark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Mark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("MarkRoots:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::MarkRoots]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ConcurrentMark pause:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ConcurrentMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitConcurrentMarkFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitConcurrentMarkFinished]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ReMark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ReMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProcessSharedGCRSetWorkList:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProcessSharedGCRSetWorkList]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Sweep:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ClearNativeObject:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ClearNativeObject]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ClearDeadReferences:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ClearDeadReferences]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitFinish]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Finish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Finish]) << "ms";
            break;
        case GCType::LOCAL_CC:
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ConcurrentMark pause:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ConcurrentMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("WaitConcurrentMarkFinish:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::WaitConcurrentMarkFinished]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ReMark:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ReMark]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("MarkRoots:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::MarkRoots]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("ProcessSharedGCRSetWorkList:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ProcessSharedGCRSetWorkList]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("UpdateWeakRef:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateWeekRef]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("UpdateRoot:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateRoot]) << "ms\n"
                         << STATS_DESCRIPTION_FORMAT("Sweep:")
                         << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms";
            break;
        default: // LCOV_EXCL_BR_LINE
            break;
    }
}

bool GCStats::CheckIfNeedPrint(GCType type)
{
    uint32_t gcCount = 0;
    switch (type) {
        case GCType::PARTIAL_YOUNG_GC:
            gcCount = GetRecordData(RecordData::YOUNG_COUNT);
            break;
        case GCType::PARTIAL_OLD_GC:
            gcCount = GetRecordData(RecordData::OLD_COUNT);
            break;
        case GCType::COMPRESS_GC:
            gcCount = GetRecordData(RecordData::COMPRESS_COUNT);
            break;
        case GCType::LOCAL_CC:
            gcCount = GetRecordData(RecordData::LOCAL_CC_COUNT);
            break;
        default: // LCOV_EXCL_BR_LINE
            break;
    }

    if (gcCount > 0) {
        return true;
    }
    return false;
}

void GCStats::PrintGCSummaryStatistic(GCType type)
{
    if (type != GCType::START && !CheckIfNeedPrint(type)) {
        return;
    } else {
        type = type == GCType::START ? gcType_ : type;
    }
    LOG_GC(INFO) << "/***************** GC summary statistic: *****************/";
    switch (type) {
        case GCType::PARTIAL_YOUNG_GC: {
            double copiedRate = double(GetRecordData(RecordData::YOUNG_TOTAL_ALIVE)) /
                                GetRecordData(RecordData::YOUNG_TOTAL_COMMIT);
            double promotedRate = double(GetRecordData(RecordData::YOUNG_TOTAL_PROMOTE)) /
                                  GetRecordData(RecordData::YOUNG_TOTAL_COMMIT);
            double survivalRate =  std::min(copiedRate + promotedRate, 1.0);
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("YoungGC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::YOUNG_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("YoungGC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("YoungGC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("YoungGC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::YOUNG_COUNT)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("Young average copied rate:") << STATS_DATA_FORMAT(copiedRate) << "\n"
                << STATS_DESCRIPTION_FORMAT("Young average promoted rate:") << STATS_DATA_FORMAT(promotedRate) << "\n"
                << STATS_DESCRIPTION_FORMAT("Young average survival rate:") << STATS_DATA_FORMAT(survivalRate);
            break;
        }
        case GCType::PARTIAL_OLD_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("OldGC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::OLD_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("OldGC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::OLD_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("OldGC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::OLD_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("OldGC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::OLD_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::OLD_COUNT)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("Heap average alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::OLD_TOTAL_ALIVE)) /
                                     GetRecordData(RecordData::OLD_TOTAL_COMMIT));
            break;
        }
        case GCType::COMPRESS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("CompressGC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::COMPRESS_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("CompressGC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("CompressGC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("CompressGC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::COMPRESS_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::COMPRESS_COUNT)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("Heap average alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::COMPRESS_TOTAL_ALIVE)) /
                                     GetRecordData(RecordData::COMPRESS_TOTAL_COMMIT));
            break;
        }
        case GCType::STICKY_CMS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("StickySweepGC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::SWEEP_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("StickySweepGC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("StickySweepGC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("StickySweepGC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::SWEEP_COUNT)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("Heap average alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SWEEP_TOTAL_ALIVE)) /
                                     GetRecordData(RecordData::SWEEP_TOTAL_COMMIT));
            break;
        }
        case GCType::CMS_GC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("SweepGC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::SWEEP_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("SweepGC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("SweepGC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("SweepGC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::SWEEP_COUNT)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("Heap average alive rate:")
                << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SWEEP_TOTAL_ALIVE)) /
                                     GetRecordData(RecordData::SWEEP_TOTAL_COMMIT));
            break;
        }
        case GCType::LOCAL_CC: {
            LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("LOCALCC occurs count")
                << STATS_DATA_FORMAT(GetRecordData(RecordData::LOCAL_CC_COUNT)) << "\n"
                << STATS_DESCRIPTION_FORMAT("LOCALCC max pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("LOCALCC min pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE)) << "ms\n"
                << STATS_DESCRIPTION_FORMAT("LOCALCC average pause:")
                << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::LOCAL_CC_TOTAL_PAUSE) /
                                     GetRecordData(RecordData::LOCAL_CC_COUNT)) << "ms";
            break;
        }
        default: // LCOV_EXCL_BR_LINE
            break;
    }
}

size_t GCStats::GetAccumulatedAllocateSize()
{
    return accumulatedFreeSize_ + heap_->GetHeapObjectSize();
}

void GCStats::RecordStatisticBeforeGC(TriggerGCType gcType, GCReason reason)
{
    SetRecordData(RecordData::START_OBJ_SIZE, heap_->GetHeapObjectSize());
    SetRecordData(RecordData::START_COMMIT_SIZE, heap_->GetCommittedSize());
    // fixme: refactor?
    if constexpr (!G_USE_CMS_GC) {
        SetRecordData(RecordData::START_YOUNG_OBJ_SIZE, heap_->GetNewSpace()->GetHeapObjectSize());
    }
    SetRecordData(RecordData::START_NATIVE_POINTER_NUM, heap_->GetNativePointerListSize());
    gcType_ = GetGCType(gcType);
    gcReason_ = reason;

    switch (gcType_) {
        case GCType::PARTIAL_YOUNG_GC: {
            size_t youngCommitSize = heap_->GetNewSpace()->GetCommittedSize();
            SetRecordData(RecordData::YOUNG_COMMIT_SIZE, youngCommitSize);
            IncreaseRecordData(RecordData::YOUNG_TOTAL_COMMIT, youngCommitSize);
            break;
        }
        case GCType::PARTIAL_OLD_GC: {
            size_t oldCommitSize = heap_->GetCommittedSize();
            SetRecordData(RecordData::OLD_COMMIT_SIZE, oldCommitSize);
            IncreaseRecordData(RecordData::OLD_TOTAL_COMMIT, oldCommitSize);
            break;
        }
        case GCType::COMPRESS_GC: {
            size_t compressCommitSize = heap_->GetCommittedSize();
            SetRecordData(RecordData::COMPRESS_COMMIT_SIZE, compressCommitSize);
            IncreaseRecordData(RecordData::COMPRESS_TOTAL_COMMIT, compressCommitSize);
            break;
        }
        case GCType::STICKY_CMS_GC: {
            size_t sweepCommitSize = heap_->GetCommittedSize();
            SetRecordData(RecordData::SWEEP_COMMIT_SIZE, sweepCommitSize);
            IncreaseRecordData(RecordData::SWEEP_TOTAL_COMMIT, sweepCommitSize);
            break;
        }
        case GCType::CMS_GC: {
            size_t sweepCommitSize = heap_->GetCommittedSize();
            SetRecordData(RecordData::SWEEP_COMMIT_SIZE, sweepCommitSize);
            IncreaseRecordData(RecordData::SWEEP_TOTAL_COMMIT, sweepCommitSize);
            break;
        }
        default: // LCOV_EXCL_BR_LINE
            break;
    }
    ProcessBeforeLongGCStats();
    RecordGCStatisticStart();
}

void GCStats::RecordStatisticAfterGC()
{
    ASSERT(heap_ != nullptr);
    SetRecordData(RecordData::END_OBJ_SIZE, heap_->GetHeapObjectSize());
    SetRecordData(RecordData::END_COMMIT_SIZE, heap_->GetCommittedSize());

    float duration = scopeDuration_[Scope::ScopeId::TotalGC];
    switch (gcType_) {
        case GCType::PARTIAL_YOUNG_GC: {
            if (GetRecordData(RecordData::YOUNG_COUNT) == 0) {
                SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::YOUNG_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::YOUNG_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::YOUNG_COUNT);
            IncreaseRecordDuration(RecordDuration::YOUNG_TOTAL_PAUSE, duration);
            size_t youngAliveSize = heap_->GetNewSpace()->GetHeapObjectSize();
            SetRecordData(RecordData::YOUNG_ALIVE_SIZE, youngAliveSize);
            IncreaseRecordData(RecordData::YOUNG_TOTAL_ALIVE, youngAliveSize);
            size_t promotedSize = heap_->GetPromotedSize();
            SetRecordData(RecordData::YOUNG_PROMOTE_SIZE, promotedSize);
            IncreaseRecordData(RecordData::YOUNG_TOTAL_PROMOTE, promotedSize);
            break;
        }
        case GCType::PARTIAL_OLD_GC: {
            if (GetRecordData(RecordData::OLD_COUNT) == 0) {
                SetRecordDuration(RecordDuration::OLD_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::OLD_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::OLD_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::OLD_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::OLD_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::OLD_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::OLD_COUNT);
            IncreaseRecordDuration(RecordDuration::OLD_TOTAL_PAUSE, duration);
            size_t oldAliveSize = heap_->GetHeapObjectSize();
            SetRecordData(RecordData::OLD_ALIVE_SIZE, oldAliveSize);
            IncreaseRecordData(RecordData::OLD_TOTAL_ALIVE, oldAliveSize);
            break;
        }
        case GCType::COMPRESS_GC: {
            if (GetRecordData(RecordData::COMPRESS_COUNT) == 0) {
                SetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::COMPRESS_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::COMPRESS_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::COMPRESS_COUNT);
            IncreaseRecordDuration(RecordDuration::COMPRESS_TOTAL_PAUSE, duration);
            size_t compressAliveSize = heap_->GetHeapObjectSize();
            SetRecordData(RecordData::COMPRESS_ALIVE_SIZE, compressAliveSize);
            IncreaseRecordData(RecordData::COMPRESS_TOTAL_ALIVE, compressAliveSize);
            break;
        }
        case GCType::STICKY_CMS_GC: {
            if (GetRecordData(RecordData::SWEEP_COUNT) == 0) {
                SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::SWEEP_COUNT);
            IncreaseRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE, duration);
            size_t sweepAliveSize = heap_->GetHeapObjectSize();
            SetRecordData(RecordData::SWEEP_ALIVE_SIZE, sweepAliveSize);
            IncreaseRecordData(RecordData::SWEEP_TOTAL_ALIVE, sweepAliveSize);
            break;
        }
        case GCType::CMS_GC: {
            if (GetRecordData(RecordData::SWEEP_COUNT) == 0) {
                SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::SWEEP_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::SWEEP_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::SWEEP_COUNT);
            IncreaseRecordDuration(RecordDuration::SWEEP_TOTAL_PAUSE, duration);
            size_t sweepAliveSize = heap_->GetHeapObjectSize();
            SetRecordData(RecordData::SWEEP_ALIVE_SIZE, sweepAliveSize);
            IncreaseRecordData(RecordData::SWEEP_TOTAL_ALIVE, sweepAliveSize);
            break;
        }
        case GCType::LOCAL_CC: {
            if (GetRecordData(RecordData::LOCAL_CC_COUNT) == 0) {
                SetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE, duration);
                SetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE, duration);
            } else {
                SetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE,
                    std::min(GetRecordDuration(RecordDuration::LOCAL_CC_MIN_PAUSE), duration));
                SetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE,
                    std::max(GetRecordDuration(RecordDuration::LOCAL_CC_MAX_PAUSE), duration));
            }
            IncreaseRecordData(RecordData::LOCAL_CC_COUNT);
            IncreaseRecordDuration(RecordDuration::LOCAL_CC_TOTAL_PAUSE, duration);
            break;
        }
        default:
            break;
    }
    RecordGCSpeed();

    if (gcType_ == GCType::COMPRESS_GC && scopeDuration_[Scope::ScopeId::TotalGC] > longPauseTime_) {
        IncreaseFullGCLongTimeCount();
    }
    IncreaseTotalDuration(scopeDuration_[Scope::ScopeId::TotalGC]);
    IncreaseAccumulatedFreeSize(GetRecordData(RecordData::START_OBJ_SIZE) -
                                GetRecordData(RecordData::END_OBJ_SIZE));
    ProcessAfterLongGCStats();
    RecordGCStatisticEnd();
}

void GCStats::ProcessAfterLongGCStats()
{
    LongGCStats *longGCStats = GetLongGCStats();
    float gcTotalTime = GetScopeDuration(GCStats::Scope::ScopeId::TotalGC);
    if (IsLongGC(gcReason_, heap_->InSensitiveStatus(), Runtime::GetInstance()->IsInBackground(), gcTotalTime)) {
        longGCStats->SetGCType(static_cast<int>(gcType_));
        longGCStats->SetGCReason(static_cast<int>(gcReason_));
        longGCStats->SetMarkReason(static_cast<int>(markReason_));
        longGCStats->SetGCIsSensitive(heap_->InSensitiveStatus());
        longGCStats->SetGCIsInBackground(Runtime::GetInstance()->IsInBackground());
        longGCStats->SetGCTotalTime(gcTotalTime);
        longGCStats->SetGCMarkTime(GetScopeDuration(GCStats::Scope::ScopeId::Mark));
        longGCStats->SetGCEvacuateTime(GetScopeDuration(GCStats::Scope::ScopeId::Evacuate));
        longGCStats->SetGCUpdateRootTime(GetScopeDuration(GCStats::Scope::ScopeId::UpdateRoot));
        longGCStats->SetGCUpdateWeekRefTime(GetScopeDuration(GCStats::Scope::ScopeId::UpdateWeekRef));
        longGCStats->SetGCUpdateReferenceTime(GetScopeDuration(GCStats::Scope::ScopeId::UpdateReference));
        longGCStats->SetGCSweepNewToOldTime(GetScopeDuration(GCStats::Scope::ScopeId::SweepNewToOldRegions));
        longGCStats->SetGCFinalizeTime(GetScopeDuration(GCStats::Scope::ScopeId::Finalize));
        longGCStats->SetGCInvokeCallbackTime(GetScopeDuration(GCStats::Scope::ScopeId::InvokeNativeFinalizeCallbacks));
        longGCStats->SetAfterGCTotalMemUsed(heap_->GetHeapObjectSize());
        longGCStats->SetAfterGCTotalMemCommitted(heap_->GetCommittedSize());
        // fixme: refactor?
        if constexpr (G_USE_CMS_GC) {
            longGCStats->SetAfterGCSlotSpaceMemUsed(heap_->GetSlotSpace()->GetHeapObjectSize());
            longGCStats->SetAfterGCSlotSpaceMemCommitted(heap_->GetSlotSpace()->GetCommittedSize());
        } else {
            longGCStats->SetAfterGCActiveMemUsed(heap_->GetNewSpace()->GetHeapObjectSize());
            longGCStats->SetAfterGCActiveMemCommitted(heap_->GetNewSpace()->GetCommittedSize());
            longGCStats->SetAfterGCOldMemUsed(heap_->GetOldSpace()->GetHeapObjectSize());
            longGCStats->SetAfterGCOldMemCommitted(heap_->GetOldSpace()->GetCommittedSize());
        }
        longGCStats->SetAfterGCHugeMemUsed(heap_->GetHugeObjectSpace()->GetHeapObjectSize());
        longGCStats->SetAfterGCHugeMemCommitted(heap_->GetHugeObjectSpace()->GetCommittedSize());
        longGCStats->SetAfterGCNativeBindingSize(heap_->GetNativeBindingSize());
        longGCStats->SetAfterGCNativeLimit(heap_->GetGlobalSpaceNativeLimit());
    }
}

void GCStats::ProcessBeforeLongGCStats()
{
    LongGCStats *longGCStats = GetLongGCStats();
    longGCStats->SetBeforeGCTotalMemUsed(heap_->GetHeapObjectSize());
    longGCStats->SetBeforeGCTotalMemCommitted(heap_->GetCommittedSize());
    // fixme: refactor?
    if constexpr (G_USE_CMS_GC) {
        longGCStats->SetBeforeGCSlotSpaceMemUsed(heap_->GetSlotSpace()->GetHeapObjectSize());
        longGCStats->SetBeforeGCSlotSpaceMemCommitted(heap_->GetSlotSpace()->GetCommittedSize());
    } else {
        longGCStats->SetBeforeGCActiveMemUsed(heap_->GetNewSpace()->GetHeapObjectSize());
        longGCStats->SetBeforeGCActiveMemCommitted(heap_->GetNewSpace()->GetCommittedSize());
        longGCStats->SetBeforeGCOldMemUsed(heap_->GetOldSpace()->GetHeapObjectSize());
        longGCStats->SetBeforeGCOldMemCommitted(heap_->GetOldSpace()->GetCommittedSize());
    }
    longGCStats->SetBeforeGCHugeMemUsed(heap_->GetHugeObjectSpace()->GetHeapObjectSize());
    longGCStats->SetBeforeGCHugeMemCommitted(heap_->GetHugeObjectSpace()->GetCommittedSize());
    longGCStats->SetBeforeGCNativeBindingSize(heap_->GetNativeBindingSize());
    longGCStats->SetBeforeGCNativeLimit(heap_->GetGlobalSpaceNativeLimit());
}

/*
| The Judgment criteria of Long GC
|  IsInBackground  |  IsSensitive or Idle |  GCTime |
| -----------------| ---------------------|---------|
|       false      |       Sensitive      |    33   |
|       false      |  !Sensitive & !Idle  |    33   |
|       true       |         Idle         |    200  |
|       true       |        !Idle         |    200  |
|       true       |         Idle         |    500  |
*/
bool GCStats::IsLongGC(GCReason gcReason, bool gcIsSensitive, bool gcIsInBackground, float gcTotalTime)
{
    if (gcIsSensitive) {
        if (gcTotalTime > GCKeyStats::GC_SENSITIVE_LONG_TIME) {
            return true;
        }
    } else {
        if (GCKeyStats::IsIdle(gcReason)) {
            if (!gcIsInBackground && gcTotalTime > GCKeyStats::GC_IDLE_LONG_TIME) {
                return true;
            } else if (gcIsInBackground && gcTotalTime > GCKeyStats::GC_BACKGROUD_IDLE_LONG_TIME) {
                return true;
            }
        } else {
            if (!gcIsInBackground && gcTotalTime > GCKeyStats::GC_NOT_SENSITIVE_LONG_TIME) {
                return true;
            } else if (gcIsInBackground && gcTotalTime > GCKeyStats::GC_BACKGROUD_LONG_TIME) {
                return true;
            }
        }
    }
    return false;
}

void GCStats::RecordGCSpeed()
{
    double survivalRate = GetAvgSurvivalRate();
    size_t clearNativeSpeed = GetRecordData(RecordData::START_NATIVE_POINTER_NUM) /
                              scopeDuration_[Scope::ScopeId::ClearNativeObject];

    if (gcType_ == GCType::PARTIAL_YOUNG_GC) {
        size_t objSize = GetRecordData(RecordData::START_YOUNG_OBJ_SIZE);
        gcSpeed_[(uint8_t)SpeedData::MARK_SPEED] = objSize / scopeDuration_[Scope::ScopeId::Mark];
        size_t evacuateSpeed = survivalRate * objSize / scopeDuration_[Scope::ScopeId::EvacuateSpace];
        gcSpeed_[(uint8_t)SpeedData::YOUNG_EVACUATE_SPACE_SPEED] =
            (evacuateSpeed + gcSpeed_[(uint8_t)SpeedData::YOUNG_EVACUATE_SPACE_SPEED]) / 2;  // 2 means half
        gcSpeed_[(uint8_t)SpeedData::YOUNG_CLEAR_NATIVE_OBJ_SPEED] =
            (clearNativeSpeed + gcSpeed_[(uint8_t)SpeedData::YOUNG_CLEAR_NATIVE_OBJ_SPEED]) / 2;  // 2 means half
        size_t updateReferenceSpeed = GetRecordData(RecordData::START_OBJ_SIZE) /
                                      scopeDuration_[Scope::ScopeId::UpdateReference];
        gcSpeed_[(uint8_t)SpeedData::YOUNG_UPDATE_REFERENCE_SPEED] =
            (updateReferenceSpeed + gcSpeed_[(uint8_t)SpeedData::YOUNG_UPDATE_REFERENCE_SPEED]) / 2;  // 2 means half
    } else if (gcType_ == GCType::PARTIAL_OLD_GC) {
        gcSpeed_[(uint8_t)SpeedData::MARK_SPEED] =
            GetRecordData(RecordData::START_OBJ_SIZE) / scopeDuration_[Scope::ScopeId::Mark];
        size_t sweepSpeed = GetRecordData(RecordData::START_OBJ_SIZE) / scopeDuration_[Scope::ScopeId::Sweep];
        gcSpeed_[(uint8_t)SpeedData::SWEEP_SPEED] =
            (sweepSpeed + gcSpeed_[(uint8_t)SpeedData::SWEEP_SPEED]) / 2;  // 2 means half
        gcSpeed_[(uint8_t)SpeedData::OLD_CLEAR_NATIVE_OBJ_SPEED] =
            (clearNativeSpeed + gcSpeed_[(uint8_t)SpeedData::OLD_CLEAR_NATIVE_OBJ_SPEED]) / 2;  // 2 means half

        size_t evacuateSpaceSpeed = (survivalRate * GetRecordData(RecordData::START_YOUNG_OBJ_SIZE) +
            GetRecordData(RecordData::COLLECT_REGION_SET_SIZE)) / scopeDuration_[Scope::ScopeId::EvacuateSpace];
        gcSpeed_[(uint8_t)SpeedData::OLD_EVACUATE_SPACE_SPEED] =
            (evacuateSpaceSpeed + gcSpeed_[(uint8_t)SpeedData::OLD_EVACUATE_SPACE_SPEED]) / 2;  // 2 means half

        size_t updateReferenceSpeed = GetRecordData(RecordData::START_OBJ_SIZE) /
                                    scopeDuration_[Scope::ScopeId::UpdateReference];
        gcSpeed_[(uint8_t)SpeedData::UPDATE_REFERENCE_SPEED] =
            (updateReferenceSpeed + gcSpeed_[(uint8_t)SpeedData::UPDATE_REFERENCE_SPEED]) / 2;  // 2 means half
    }
}

GCType GCStats::GetGCType(TriggerGCType gcType)
{
    switch (gcType) {
        case TriggerGCType::YOUNG_GC:
            return GCType::PARTIAL_YOUNG_GC;
        case TriggerGCType::OLD_GC:
            return GCType::PARTIAL_OLD_GC;
        case TriggerGCType::FULL_GC:
            return GCType::COMPRESS_GC;
        case TriggerGCType::LOCAL_CC:
            return GCType::LOCAL_CC;
        case TriggerGCType::SHARED_GC:
            return GCType::SHARED_GC;
        case TriggerGCType::SHARED_PARTIAL_GC:
            return GCType::SHARED_PARTIAL_GC;
        case TriggerGCType::SHARED_FULL_GC:
            return GCType::SHARED_FULL_GC;
        case TriggerGCType::GLOBAL_GC:
            return GCType::GLOBAL_GC;
        case TriggerGCType::CMS_GC:
            return GCType::CMS_GC;
        case TriggerGCType::STICKY_CMS_GC:
            return GCType::STICKY_CMS_GC;
        default:
            return GCType::OTHER;
    }
}

void GCStats::InitializeRecordList()
{
    for (float &duration : scopeDuration_) {
        duration = 0.0f;
    }
    concurrentMark_ = false;
}

bool GCStats::CheckIfLongTimePause()
{
    if (scopeDuration_[Scope::ScopeId::TotalGC] > longPauseTime_) {
        LOG_GC(INFO) << "Has checked a long time gc";
        return true;
    }
    return false;
}

void SharedGCStats::PrintStatisticResult()
{
    LOG_GC(INFO) << "/******************* SharedGCStats statistic: *******************/";
    PrintSharedGCSummaryStatistic();
    PrintGCMemoryStatistic();
}

void SharedGCStats::PrintGCStatistic()
{
    if (enableGCTracer_) {
        PrintSharedGCOverview();
        PrintSharedGCDuration();
        PrintGCMemoryStatistic();
        PrintSharedGCSummaryStatistic();
    } else if (gcType_ == GCType::SHARED_PARTIAL_GC) {
        PrintSharedGCOverview();
        PrintGCMemoryStatistic();
    }
    SharedGCFinishTrace();
    InitializeRecordList();
}

void SharedGCStats::SharedGCFinishTrace()
{
    ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, ("SharedGC::Finish;Reason"
        + std::to_string(static_cast<int>(gcReason_))
        + ";Sensitive" + std::to_string(static_cast<int>(sHeap_->GetSensitiveStatus()))
        + ";IsInBackground" + std::to_string(Runtime::GetInstance()->IsInBackground())
        + ";Startup" + std::to_string(sHeap_->OnStartupEvent())
        + ";Old" + std::to_string(sHeap_->GetOldSpace()->GetCommittedSize())
        + ";huge" + std::to_string(sHeap_->GetHugeObjectSpace()->GetCommittedSize())
        + ";NonMov" + std::to_string(sHeap_->GetNonMovableSpace()->GetCommittedSize())
        + ";TotCommit" + std::to_string(sHeap_->GetCommittedSize())
        + ";NativeBindingSize" + std::to_string(sHeap_->GetNativeSizeAfterLastGC())
        + ";NativeLimitGC" + std::to_string(sHeap_->GetNativeSizeTriggerSharedGC())
        + ";NativeLimitCM" + std::to_string(sHeap_->GetNativeSizeTriggerSharedCM())).c_str(), "");
}

void SharedGCStats::PrintSharedGCOverview()
{
    LOG_GC(INFO) << " [ " << GetGCTypeName() << " ] "
                 << sizeToMB(recordData_[(uint8_t)RecordData::START_OBJ_SIZE]) << " ("
                 << sizeToMB(recordData_[(uint8_t)RecordData::START_COMMIT_SIZE]) << ") -> "
                 << sizeToMB(recordData_[(uint8_t)RecordData::END_OBJ_SIZE]) << " ("
                 << sizeToMB(recordData_[(uint8_t)RecordData::END_COMMIT_SIZE]) << ") MB, "
                 << scopeDuration_[Scope::ScopeId::TotalGC]
                 << "ms, GCReason: " << GCReasonToString()
                 << ", MarkReason: " << MarkReasonToString();
    LOG_GC(INFO) << "IsInBackground: " << Runtime::GetInstance()->IsInBackground() << "; "
                 << "SensitiveStatus: " << static_cast<int>(sHeap_->GetSensitiveStatus()) << "; "
                 << "StartupStatus: " << std::to_string(static_cast<int>(sHeap_->GetStartupStatus())) << "; "
                 << "Old: " << std::to_string(sHeap_->GetOldSpace()->GetCommittedSize()) << "; "
                 << "Huge:" << std::to_string(sHeap_->GetHugeObjectSpace()->GetCommittedSize()) << "; "
                 << "NonMov:" << std::to_string(sHeap_->GetNonMovableSpace()->GetCommittedSize()) << "; "
                 << "TotCommit:" << std::to_string(sHeap_->GetCommittedSize());
}

void SharedGCStats::PrintSharedGCSummaryStatistic()
{
    LOG_GC(INFO) << "/***************** GC summary statistic: *****************/";
    LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("SharedGC occurs count")
                 << STATS_DATA_FORMAT(GetRecordData(RecordData::SHARED_COUNT)) << "\n"
                 << STATS_DESCRIPTION_FORMAT("SharedGC max pause:")
                 << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SHARED_MAX_PAUSE)) << "ms\n"
                 << STATS_DESCRIPTION_FORMAT("SharedGC min pause:")
                 << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SHARED_MIN_PAUSE)) << "ms\n"
                 << STATS_DESCRIPTION_FORMAT("SharedGC average pause:")
                 << STATS_DATA_FORMAT(GetRecordDuration(RecordDuration::SHARED_TOTAL_PAUSE) /
                                      GetRecordData(RecordData::SHARED_COUNT)) << "ms\n"
                 << STATS_DESCRIPTION_FORMAT("SharedHeap average alive rate:")
                 << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SHARED_TOTAL_ALIVE)) /
                                      GetRecordData(RecordData::SHARED_TOTAL_COMMIT));
}

void SharedGCStats::PrintGCMemoryStatistic()
{
    NativeAreaAllocator *nativeAreaAllocator = sHeap_->GetNativeAreaAllocator();
    HeapRegionAllocator *heapRegionAllocator = sHeap_->GetHeapRegionAllocator();
    LOG_GC(INFO) << "/****************** GC Memory statistic: *****************/";
    LOG_GC(INFO) << "AllSpaces         used:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetHeapObjectSize())) << "KB"
                 << "     committed:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetCommittedSize())) << "KB\n"
                 << "SharedOldSpace         used:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetOldSpace()->GetHeapObjectSize())) << "KB"
                 << "     committed:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetOldSpace()->GetCommittedSize())) << "KB\n"
                 << "SharedNonMovableSpace  used:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetNonMovableSpace()->GetHeapObjectSize())) << "KB"
                 << "     committed:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetNonMovableSpace()->GetCommittedSize())) << "KB\n"
                 << "SharedHugeObjectSpace  used:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetHugeObjectSpace()->GetHeapObjectSize())) << "KB"
                 << "     committed:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetHugeObjectSpace()->GetCommittedSize())) << "KB\n"
                 << "SharedAppSpawnSpace    used:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetAppSpawnSpace()->GetHeapObjectSize())) << "KB"
                 << "     committed:"
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetAppSpawnSpace()->GetCommittedSize())) << "KB";

    LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Anno memory usage size:")
                 << STATS_DATA_FORMAT(sizeToMB(heapRegionAllocator->GetAnnoMemoryUsage())) << "MB\n"
                 << STATS_DESCRIPTION_FORMAT("Native memory usage size:")
                 << STATS_DATA_FORMAT(sizeToMB(nativeAreaAllocator->GetNativeMemoryUsage())) << "MB\n"
                 << STATS_DESCRIPTION_FORMAT("NativeBindingSize:")
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetNativeSizeAfterLastGC())) << "KB\n"
                 << STATS_DESCRIPTION_FORMAT("NativeLimitGC:")
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetNativeSizeTriggerSharedGC())) << "KB\n"
                 << STATS_DESCRIPTION_FORMAT("NativeLimitCM:")
                 << STATS_DATA_FORMAT(sizeToKB(sHeap_->GetNativeSizeTriggerSharedCM())) << "KB\n";

    LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("Heap alive rate:")
        << STATS_DATA_FORMAT(double(GetRecordData(RecordData::SHARED_ALIVE_SIZE)) /
                                    GetRecordData(RecordData::SHARED_COMMIT_SIZE));
}

void SharedGCStats::PrintSharedGCDuration()
{
    LOG_GC(INFO) << STATS_DESCRIPTION_FORMAT("TotalGC:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::TotalGC]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("Initialize:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Initialize]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("Mark:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Mark]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("Evacuate:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Evacuate]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("UpdateReference:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::UpdateReference]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("Sweep:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Sweep]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("Finish:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::Finish]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("SuspendAll:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::SuspendAll]) << "ms\n"
        << STATS_DESCRIPTION_FORMAT("ResumeAll:")
        << STATS_DATA_FORMAT(scopeDuration_[Scope::ScopeId::ResumeAll]) << "ms";
}

size_t SharedGCStats::GetAccumulatedAllocateSize()
{
    return accumulatedFreeSize_ + sHeap_->GetHeapObjectSize();
}

void SharedGCStats::RecordStatisticBeforeGC(TriggerGCType gcType, GCReason reason)
{
    size_t commitSize = sHeap_->GetCommittedSize();
    SetRecordData(RecordData::START_OBJ_SIZE, sHeap_->GetHeapObjectSize());
    SetRecordData(RecordData::START_COMMIT_SIZE, commitSize);
    SetRecordData(RecordData::SHARED_COMMIT_SIZE, commitSize);
    IncreaseRecordData(RecordData::SHARED_TOTAL_COMMIT, commitSize);
    gcType_ = GetGCType(gcType);
    gcReason_ = reason;
    ProcessBeforeLongGCStats();
    RecordGCStatisticStart();
}

void SharedGCStats::RecordStatisticAfterGC()
{
    SetRecordData(RecordData::END_OBJ_SIZE, sHeap_->GetHeapObjectSize());
    SetRecordData(RecordData::END_COMMIT_SIZE, sHeap_->GetCommittedSize());

    float duration = scopeDuration_[Scope::ScopeId::TotalGC];
    if (GetRecordData(RecordData::SHARED_COUNT) == 0) {
        SetRecordDuration(RecordDuration::SHARED_MIN_PAUSE, duration);
        SetRecordDuration(RecordDuration::SHARED_MAX_PAUSE, duration);
    } else {
        SetRecordDuration(RecordDuration::SHARED_MIN_PAUSE,
            std::min(GetRecordDuration(RecordDuration::SHARED_MIN_PAUSE), duration));
        SetRecordDuration(RecordDuration::SHARED_MAX_PAUSE,
            std::max(GetRecordDuration(RecordDuration::SHARED_MAX_PAUSE), duration));
    }
    IncreaseRecordData(RecordData::SHARED_COUNT);
    IncreaseRecordDuration(RecordDuration::SHARED_TOTAL_PAUSE, duration);
    size_t heapAliveSize = sHeap_->GetHeapObjectSize();
    SetRecordData(RecordData::SHARED_ALIVE_SIZE, heapAliveSize);
    IncreaseRecordData(RecordData::SHARED_TOTAL_ALIVE, heapAliveSize);

    IncreaseTotalDuration(scopeDuration_[Scope::ScopeId::TotalGC]);
    IncreaseAccumulatedFreeSize(GetRecordData(RecordData::START_OBJ_SIZE) -
                                GetRecordData(RecordData::END_OBJ_SIZE));
    ProcessAfterLongGCStats();
    RecordGCStatisticEnd();
}

void SharedGCStats::ProcessAfterLongGCStats()
{
    LongGCStats *longGCStats = GetLongGCStats();
    float gcTotalTime = GetScopeDuration(GCStats::Scope::ScopeId::TotalGC);
    if (IsLongGC(gcReason_, sHeap_->InSensitiveStatus(), Runtime::GetInstance()->IsInBackground(), gcTotalTime)) {
        longGCStats->SetGCType(static_cast<int>(gcType_));
        longGCStats->SetGCReason(static_cast<int>(gcReason_));
        longGCStats->SetMarkReason(static_cast<int>(markReason_));
        longGCStats->SetGCIsSensitive(sHeap_->InSensitiveStatus());
        longGCStats->SetGCIsInBackground(Runtime::GetInstance()->IsInBackground());
        longGCStats->SetGCTotalTime(gcTotalTime);
        longGCStats->SetGCMarkTime(GetScopeDuration(GCStats::Scope::ScopeId::Mark));
        longGCStats->SetAfterGCTotalMemUsed(sHeap_->GetHeapObjectSize());
        longGCStats->SetAfterGCTotalMemCommitted(sHeap_->GetCommittedSize());
        longGCStats->SetAfterGCOldMemUsed(sHeap_->GetOldSpace()->GetHeapObjectSize());
        longGCStats->SetAfterGCOldMemCommitted(sHeap_->GetOldSpace()->GetCommittedSize());
        longGCStats->SetAfterGCHugeMemUsed(sHeap_->GetHugeObjectSpace()->GetHeapObjectSize());
        longGCStats->SetAfterGCHugeMemCommitted(sHeap_->GetHugeObjectSpace()->GetCommittedSize());
        longGCStats->SetAfterGCNativeBindingSize(sHeap_->GetNativeSizeAfterLastGC());
        longGCStats->SetAfterGCNativeLimit(sHeap_->GetNativeSizeTriggerSharedGC());
    }
}

void SharedGCStats::ProcessBeforeLongGCStats()
{
    LongGCStats *longGCStats = GetLongGCStats();
    longGCStats->SetBeforeGCTotalMemUsed(sHeap_->GetHeapObjectSize());
    longGCStats->SetBeforeGCTotalMemCommitted(sHeap_->GetCommittedSize());
    longGCStats->SetBeforeGCOldMemUsed(sHeap_->GetOldSpace()->GetHeapObjectSize());
    longGCStats->SetBeforeGCOldMemCommitted(sHeap_->GetOldSpace()->GetCommittedSize());
    longGCStats->SetBeforeGCHugeMemUsed(sHeap_->GetHugeObjectSpace()->GetHeapObjectSize());
    longGCStats->SetBeforeGCHugeMemCommitted(sHeap_->GetHugeObjectSpace()->GetCommittedSize());
    longGCStats->SetBeforeGCNativeBindingSize(sHeap_->GetNativeSizeAfterLastGC());
    longGCStats->SetBeforeGCNativeLimit(sHeap_->GetNativeSizeTriggerSharedGC());
}
}  // namespace panda::ecmascript