1 /*
2  * Copyright (c) 2024 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 "ark_idle_monitor.h"
17 
18 #include <chrono>
19 
20 #include "utils/log.h"
21 #if defined(ENABLE_FFRT)
22 #include "ffrt.h"
23 #include "c/executor_task.h"
24 #endif
25 #ifdef ENABLE_UCOLLECTION
26 #include "cpu_collector.h"
27 #endif
28 
29 namespace panda::ecmascript {
30 
NotifyLooperIdleStart(int64_t timestamp,int idleTime)31 void ArkIdleMonitor::NotifyLooperIdleStart(int64_t timestamp, int idleTime)
32 {
33     SetIdleState(true);
34     AddIdleNotifyCount();
35     if (idleTime < MIN_TRIGGER_GC_IDLE_INTERVAL || !ShouldTryTriggerGC(timestamp)) {
36         return;
37     }
38     JSNApi::NotifyLooperIdleStart(vm_, timestamp, idleTime);
39 }
40 
ShouldTryTriggerGC(int64_t timestamp)41 bool ArkIdleMonitor::ShouldTryTriggerGC(int64_t timestamp)
42 {
43     int64_t notifyInterval = timestamp - GetNotifyTimestamp();
44     recordedIdleNotifyInterval_.Push(notifyInterval);
45     SetNotifyTimestamp(timestamp);
46     if (notifyInterval < MIN_TRIGGER_GC_IDLE_INTERVAL ||
47         recordedIdleNotifyInterval_.Count() != IDLE_CHECK_INTERVAL_LENGTH) {
48         return false;
49     }
50     int64_t sumInterval = recordedIdleNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
51     int64_t averageInterval = sumInterval / recordedIdleNotifyInterval_.Count();
52     if (averageInterval > MIN_TRIGGER_GC_IDLE_INTERVAL && notifyInterval < averageInterval * DOUBLE_INTERVAL_CHECK) {
53         return true;
54     }
55     return false;
56 }
57 
NotifyLooperIdleEnd(int64_t timestamp)58 void ArkIdleMonitor::NotifyLooperIdleEnd(int64_t timestamp)
59 {
60     SetIdleState(false);
61     AddIdleDuration(timestamp - GetNotifyTimestamp());
62     JSNApi::NotifyLooperIdleEnd(vm_, timestamp);
63 }
64 
CheckLowNotifyState() const65 bool ArkIdleMonitor::CheckLowNotifyState() const
66 {
67     int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
68     HILOG_DEBUG("ArkIdleMonitor: low Notify checkCounts '%{public}d', result '%{public}d' ",
69         checkCounts, static_cast<int>(numberOfLowIdleNotifyCycles_));
70     return numberOfLowIdleNotifyCycles_ >= static_cast<int64_t>(checkCounts);
71 }
72 
CheckLowRunningDurationState() const73 bool ArkIdleMonitor::CheckLowRunningDurationState() const
74 {
75     int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
76     HILOG_DEBUG("ArkIdleMonitor: low Duration checkCounts '%{public}d', result '%{public}d' ",
77         checkCounts, static_cast<int>(numberOfHighIdleTimeRatio_));
78     return numberOfHighIdleTimeRatio_ >= static_cast<int64_t>(checkCounts);
79 }
80 
IntervalMonitor()81 void ArkIdleMonitor::IntervalMonitor()
82 {
83     auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
84         std::chrono::high_resolution_clock::now()).time_since_epoch().count();
85     if (IsIdleState()) {
86         AddIdleDuration(nowTimestamp - GetNotifyTimestamp());
87         SetNotifyTimestamp(nowTimestamp);
88     }
89     if (GetIdleNotifyCount() <= LOW_IDLE_NOTIFY_THRESHOLD) {
90         numberOfLowIdleNotifyCycles_++;
91     } else {
92         numberOfLowIdleNotifyCycles_ = 0;
93     }
94     ResetIdleNotifyCount();
95     int64_t recordTotalDuration = nowTimestamp - startRecordTimestamp_;
96     if (recordTotalDuration <= 0) {
97         numberOfHighIdleTimeRatio_ = 0;
98         HILOG_ERROR("ArkIdleMonitor: recordTotalDuration <= 0");
99     } else {
100         double idleTimeRatio = static_cast<double>(GetTotalIdleDuration()) / recordTotalDuration;
101         idleTimeRatio >= IDLE_RATIO ? numberOfHighIdleTimeRatio_++ : (numberOfHighIdleTimeRatio_ = 0);
102     }
103     startRecordTimestamp_ = nowTimestamp;
104     ResetTotalIdleDuration();
105 
106     if (CheckLowNotifyState() && CheckLowRunningDurationState()) {
107         NotifyTryCompressGC();
108         PostMonitorTask(SLEEP_MONITORING_INTERVAL);
109         ClearIdleStats();
110     } else {
111         PostMonitorTask(IDLE_MONITORING_INTERVAL);
112     }
113 }
114 
PostMonitorTask(uint64_t delayMs)115 void ArkIdleMonitor::PostMonitorTask(uint64_t delayMs)
116 {
117 #if defined(ENABLE_FFRT)
118     auto task = [](void* idleMonitorPtr) {
119         if (idleMonitorPtr != nullptr) {
120             ArkIdleMonitor* arkIdleMonitor = reinterpret_cast<ArkIdleMonitor *>(idleMonitorPtr);
121             arkIdleMonitor->IntervalMonitor();
122         }
123     };
124     if (waitForStopTimerHandler_ != -1) {
125         int ret = ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
126         if (ret != 0) {
127             HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
128                 waitForStopTimerHandler_, ret);
129         }
130     }
131     waitForStopTimerHandler_ = currentTimerHandler_;
132     currentTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, delayMs, this, task, false);
133 #endif
134 }
135 
~ArkIdleMonitor()136 ArkIdleMonitor::~ArkIdleMonitor()
137 {
138 #if defined(ENABLE_FFRT)
139     if (waitForStopTimerHandler_ != -1) {
140         ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
141     }
142     if (currentTimerHandler_ != -1) {
143         ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
144     }
145 #endif
146 }
147 
ClearIdleStats()148 void ArkIdleMonitor::ClearIdleStats()
149 {
150     ResetIdleNotifyCount();
151     ResetTotalIdleDuration();
152     startRecordTimestamp_ = 0;
153     numberOfLowIdleNotifyCycles_ = 0;
154     numberOfHighIdleTimeRatio_ = 0;
155 }
156 
NotifyTryCompressGC()157 void ArkIdleMonitor::NotifyTryCompressGC()
158 {
159 #if defined(ENABLE_EVENT_HANDLER)
160     double cpuUsage = GetCpuUsage();
161     if (cpuUsage >= IDLE_CPU_USAGE) {
162         HILOG_INFO("ArkIdleMonitor: Sending a quiet notification is canceled due to high CPU usage: %{public}.2f",
163             cpuUsage);
164         return;
165     }
166     if (mainThreadHandler_ == nullptr) {
167         mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
168             OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
169     };
170     auto task = [this]() {
171         JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
172         JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
173     };
174     mainThreadHandler_->PostTask(task, "ARKTS_IDLE_COMPRESS::usaged:" + std::to_string(cpuUsage),
175         0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
176 #endif
177 }
178 
SetStartTimerCallback()179 void ArkIdleMonitor::SetStartTimerCallback()
180 {
181     JSNApi::SetStartIdleMonitorCallback([this]() {
182         this->PostMonitorTask();
183     });
184 }
185 
NotifyChangeBackgroundState(bool inBackground)186 void ArkIdleMonitor::NotifyChangeBackgroundState(bool inBackground)
187 {
188     inBackground_.store(inBackground, std::memory_order_relaxed);
189     ClearIdleStats();
190 }
191 
GetCpuUsage() const192 double ArkIdleMonitor::GetCpuUsage() const
193 {
194 #ifdef ENABLE_UCOLLECTION
195     auto collector = OHOS::HiviewDFX::UCollectClient::CpuCollector::Create();
196     auto collectResult = collector->GetSysCpuUsage();
197     if (collectResult.retCode == OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
198         HILOG_DEBUG("ArkIdleMonitor cpu usage: %{public}.2f", collectResult.data);
199         return collectResult.data;
200     }
201     HILOG_ERROR("ArkIdleMonitor get cpu usage failed, error code:%{public}d", collectResult.retCode);
202 #endif
203     return 0.0f;
204 }
205 }
206 
207