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 "ecmascript/mem/concurrent_marker.h"
19 #include "ecmascript/mem/parallel_marker.h"
20 #include "ecmascript/mem/old_gc_visitor-inl.h"
21 #include "ecmascript/runtime_call_id.h"
22
23 namespace panda::ecmascript {
IncrementalMarker(Heap * heap)24 IncrementalMarker::IncrementalMarker(Heap *heap)
25 : heap_(heap),
26 vm_(heap->GetEcmaVM()),
27 workManager_(heap->GetWorkManager()) {}
28
TriggerIncrementalMark(int64_t idleMicroSec)29 void IncrementalMarker::TriggerIncrementalMark(int64_t idleMicroSec)
30 {
31 ASSERT(!heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark());
32 double startTime = GetCurrentTimeInMs();
33 switch (states_) {
34 case IncrementalGCStates::ROOT_SCAN:
35 Mark();
36 ProcessIncrementalMark(idleMicroSec - (GetCurrentTimeInMs() - startTime));
37 RecordIdleTime(idleMicroSec, startTime, true);
38 break;
39 case IncrementalGCStates::INCREMENTAL_MARK:
40 ProcessIncrementalMark(idleMicroSec);
41 RecordIdleTime(idleMicroSec, startTime);
42 break;
43 case IncrementalGCStates::REMARK:
44 UpdateIncrementalMarkingSpeed(GetCurrentTimeInMs() - startTime_);
45 heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::IDLE);
46 RecordIdleTime(idleMicroSec, startTime);
47 PrintGCIdleUsageStatistic();
48 break;
49 default: // LOCV_EXCL_BR_LINE
50 LOG_ECMA(FATAL) << "this branch is unreachable";
51 UNREACHABLE();
52 }
53 }
54
MarkRoots()55 void IncrementalMarker::MarkRoots()
56 {
57 ASSERT(heap_->IsFullMark());
58 OldGCMarkRootVisitor oldGCMarkRootVisitor(workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX));
59 heap_->GetNonMovableMarker()->MarkRoots(oldGCMarkRootVisitor);
60 }
61
Mark()62 void IncrementalMarker::Mark()
63 {
64 LOG_GC(DEBUG) << "IncrementalMarker: Incremental Marking Begin";
65 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "IncrementalMarker::Mark");
66 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarking);
67 Initialize();
68 MarkRoots();
69 workManager_->GetWorkNodeHolder(MAIN_THREAD_INDEX)->PushWorkNodeToGlobal(false);
70 states_ = IncrementalGCStates::INCREMENTAL_MARK;
71 }
72
Initialize()73 void IncrementalMarker::Initialize()
74 {
75 MEM_ALLOCATE_AND_GC_TRACE(vm_, IncrementalMarkingInitialize);
76 startTime_ = GetCurrentTimeInMs();
77 startObjectSize_ = heap_->GetHeapObjectSize();
78 isIncrementalMarking_ = true;
79 markingFinished_ = false;
80 heap_->SetMarkType(MarkType::MARK_FULL);
81 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARKING);
82 heap_->Prepare();
83 heap_->GetOldSpace()->SelectCSet();
84 heap_->GetAppSpawnSpace()->EnumerateRegions([](Region *current) {
85 current->ClearMarkGCBitset();
86 current->ClearCrossRegionRSet();
87 });
88 heap_->EnumerateNonNewSpaceRegions([](Region *current) {
89 current->ResetAliveObject();
90 });
91
92 workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::OLD_HANDLE_GLOBAL_POOL_TASK);
93 }
94
ProcessIncrementalMark(int64_t idleMicroSec)95 void IncrementalMarker::ProcessIncrementalMark(int64_t idleMicroSec)
96 {
97 RecursionScope recurScope(this);
98 if (idleMicroSec <= 0) {
99 return;
100 }
101 LOG_GC(DEBUG) << "IncrementalMarker: Process Incremental Marking";
102 uint32_t markStepSize = static_cast<uint32_t>(idleMicroSec) * markingSpeed_;
103 heap_->GetNonMovableMarker()->ProcessIncrementalMarkStack(MAIN_THREAD_INDEX, markStepSize);
104 if (markingFinished_) {
105 states_ = IncrementalGCStates::REMARK;
106 heap_->GetJSThread()->SetMarkStatus(MarkStatus::MARK_FINISHED);
107 return;
108 }
109 }
110
Finish()111 void IncrementalMarker::Finish()
112 {
113 states_ = IncrementalGCStates::ROOT_SCAN;
114 isIncrementalMarking_ = false;
115 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
116 heap_->GetOldSpace()->RevertCSet();
117 auto callback = [](Region *region) {
118 region->ClearMarkGCBitset();
119 region->ClearCrossRegionRSet();
120 region->ResetAliveObject();
121 };
122 heap_->EnumerateRegions(callback);
123 }
124
GetCurrentTimeInMs()125 double IncrementalMarker::GetCurrentTimeInMs()
126 {
127 struct timeval tv;
128 gettimeofday(&tv, nullptr);
129 return static_cast<double>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND);
130 }
131
Reset()132 void IncrementalMarker::Reset()
133 {
134 isIncrementalMarking_ = false;
135 states_ = IncrementalGCStates::ROOT_SCAN;
136 heap_->GetJSThread()->SetMarkStatus(MarkStatus::READY_TO_MARK);
137 workManager_->Finish();
138 }
139
RecordIdleTime(int64_t idleMicroSec,double startTime,bool needInitialize)140 void IncrementalMarker::RecordIdleTime(int64_t idleMicroSec, double startTime, bool needInitialize)
141 {
142 if (needInitialize) {
143 receiveIdleTime_ = 0;
144 totalUsedIdleTime_ = 0.0;
145 exceedIdleTime_ = 0.0;
146 }
147 double usedIdleTime = GetCurrentTimeInMs() - startTime;
148 receiveIdleTime_ += idleMicroSec;
149 totalUsedIdleTime_ += std::min((double)idleMicroSec, usedIdleTime);
150 exceedIdleTime_ += usedIdleTime > idleMicroSec ? usedIdleTime - idleMicroSec : 0;
151 }
152
PrintGCIdleUsageStatistic()153 void IncrementalMarker::PrintGCIdleUsageStatistic()
154 {
155 if (!vm_->IsOptionalLogEnabled()) {
156 return;
157 }
158 LOG_GC(INFO) << "/************* GCStats Idle usage statistic: *************/";
159 LOG_GC(INFO) << "Receive idle time: " << receiveIdleTime_ << "ms\n"
160 << "GC in idle time: " << totalUsedIdleTime_ << "ms\n"
161 << "GC out of idle time: " << exceedIdleTime_ << "ms\n"
162 << "Total duration of gc:" << GetCurrentTimeInMs() - startTime_ << "ms";
163 }
164 } // namespace panda::ecmascript
165