1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/mem/incremental_marker.h"
17
18#include <ctime>
19#include <regex>
20#include <sys/time.h>
21
22#include "ecmascript/mem/concurrent_marker.h"
23#include "ecmascript/mem/parallel_marker-inl.h"
24#include "ecmascript/runtime_call_id.h"
25
26namespace panda::ecmascript {
27IncrementalMarker::IncrementalMarker(Heap *heap)
28    : heap_(heap),
29    vm_(heap->GetEcmaVM()),
30    workManager_(heap->GetWorkManager()) {}
31
32void IncrementalMarker::TriggerIncrementalMark(int64_t idleMicroSec)
33{
34    ASSERT(!heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark());
35    double startTime = GetCurrentTimeInMs();
36    switch (states_) {
37        case IncrementalGCStates::ROOT_SCAN:
38            Mark();
39            ProcessIncrementalMark(idleMicroSec - (GetCurrentTimeInMs() - startTime));
40            RecordIdleTime(idleMicroSec, startTime, true);
41            break;
42        case IncrementalGCStates::INCREMENTAL_MARK:
43            ProcessIncrementalMark(idleMicroSec);
44            RecordIdleTime(idleMicroSec, startTime);
45            break;
46        case IncrementalGCStates::REMARK:
47            UpdateIncrementalMarkingSpeed(GetCurrentTimeInMs() - startTime_);
48            heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::IDLE);
49            RecordIdleTime(idleMicroSec, startTime);
50            PrintGCIdleUsageStatistic();
51            break;
52        default: // LOCV_EXCL_BR_LINE
53            LOG_ECMA(FATAL) << "this branch is unreachable";
54            UNREACHABLE();
55    }
56}
57
58void IncrementalMarker::Mark()
59{
60    LOG_GC(DEBUG) << "IncrementalMarker: Incremental Marking Begin";
61    ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "IncrementalMarker::Mark");
62    MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarking);
63    Initialize();
64    heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX);
65    states_ = IncrementalGCStates::INCREMENTAL_MARK;
66}
67
68void IncrementalMarker::Initialize()
69{
70    MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarkingInitialize);
71    startTime_ = GetCurrentTimeInMs();
72    startObjectSize_ = heap_->GetHeapObjectSize();
73    isIncrementalMarking_ = true;
74    markingFinished_ = false;
75    heap_->SetMarkType(MarkType::MARK_FULL);
76    heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
77    heap_->Prepare();
78    heap_->GetOldSpace()->SelectCSet();
79    heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
80        current->ClearMarkGCBitset();
81        current->ClearCrossRegionRSet();
82    });
83    heap_->EnumerateNonNewSpaceRegions([](Region *current) {
84        current->ResetAliveObject();
85    });
86
87    workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK);
88}
89
90void IncrementalMarker::ProcessIncrementalMark(int64_t idleMicroSec)
91{
92    RecursionScope recurScope(this);
93    if (idleMicroSec <= 0) {
94        return;
95    }
96    LOG_GC(DEBUG) << "IncrementalMarker: Process Incremental Marking";
97    uint32_t markStepSize = static_cast<uint32_t>(idleMicroSec) * markingSpeed_;
98    heap_->GetNonMovableMarker()->ProcessIncrementalMarkStack(MAIN_THREAD_INDEX, markStepSize);
99    if (markingFinished_) {
100        states_ = IncrementalGCStates::REMARK;
101        heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARK_FINISHED);
102        return;
103    }
104}
105
106void IncrementalMarker::Finish()
107{
108    states_ = IncrementalGCStates::ROOT_SCAN;
109    isIncrementalMarking_ = false;
110    heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
111    heap_->GetOldSpace()->RevertCSet();
112    auto callback = [](Region *region) {
113        region->ClearMarkGCBitset();
114        region->ClearCrossRegionRSet();
115        region->ResetAliveObject();
116    };
117    heap_->EnumerateRegions(callback);
118}
119
120double IncrementalMarker::GetCurrentTimeInMs()
121{
122    struct timeval tv;
123    gettimeofday(&tv, nullptr);
124    return static_cast<double>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND);
125}
126
127void IncrementalMarker::Reset()
128{
129    isIncrementalMarking_ = false;
130    states_ = IncrementalGCStates::ROOT_SCAN;
131    heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
132    workManager_->Finish();
133}
134
135void IncrementalMarker::RecordIdleTime(int64_t idleMicroSec, double startTime, bool needInitialize)
136{
137    if (needInitialize) {
138        receiveIdleTime_ = 0;
139        totalUsedIdleTime_ = 0.0;
140        exceedIdleTime_ = 0.0;
141    }
142    double usedIdleTime = GetCurrentTimeInMs() - startTime;
143    receiveIdleTime_ += idleMicroSec;
144    totalUsedIdleTime_ += std::min((double)idleMicroSec, usedIdleTime);
145    exceedIdleTime_ += usedIdleTime > idleMicroSec ? usedIdleTime - idleMicroSec : 0;
146}
147
148void IncrementalMarker::PrintGCIdleUsageStatistic()
149{
150    if (!vm_->IsOptionalLogEnabled()) {
151        return;
152    }
153    LOG_GC(INFO) << "/************* GCStats Idle usage statistic: *************/";
154    LOG_GC(INFO) << "Receive idle time:   " << receiveIdleTime_ << "ms\n"
155                 << "GC in idle time:     " << totalUsedIdleTime_ << "ms\n"
156                 << "GC out of idle time: " << exceedIdleTime_ << "ms\n"
157                 << "Total duration of gc:" << GetCurrentTimeInMs() - startTime_ << "ms";
158}
159}  // namespace panda::ecmascript
160