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 
26 namespace panda::ecmascript {
IncrementalMarker(Heap *heap)27 IncrementalMarker::IncrementalMarker(Heap *heap)
28     : heap_(heap),
29     vm_(heap->GetEcmaVM()),
30     workManager_(heap->GetWorkManager()) {}
31 
TriggerIncrementalMark(int64_t idleMicroSec)32 void 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 
Mark()58 void 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 
Initialize()68 void 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 
ProcessIncrementalMark(int64_t idleMicroSec)90 void 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 
Finish()106 void 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 
GetCurrentTimeInMs()120 double 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 
Reset()127 void IncrementalMarker::Reset()
128 {
129     isIncrementalMarking_ = false;
130     states_ = IncrementalGCStates::ROOT_SCAN;
131     heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
132     workManager_->Finish();
133 }
134 
RecordIdleTime(int64_t idleMicroSec, double startTime, bool needInitialize)135 void 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 
PrintGCIdleUsageStatistic()148 void 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