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