1 /*
2 * Copyright (c) 2022 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 <ctime>
17 #include <cinttypes>
18
19 #include "sync_fence_tracker.h"
20 #include "frame_sched.h"
21 #include "hilog/log.h"
22 #include "parameters.h"
23 #include "hisysevent.h"
24 #include "file_ex.h"
25
26 #ifndef ROSEN_TRACE_DISABLE
27 #include "hitrace_meter.h"
28 #define RS_TRACE_NAME_FMT(fmt, ...) HITRACE_METER_FMT(HITRACE_TAG_GRAPHIC_AGP, fmt, ##__VA_ARGS__)
29 #else
30 #define RS_TRACE_NAME_FMT(fmt, ...)
31 #endif //ROSEN_TRACE_DISABLE
32
33 #ifdef FENCE_SCHED_ENABLE
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <sys/ioctl.h>
37 #include <unistd.h>
38 #endif
39
40 namespace OHOS {
41 using namespace OHOS::HiviewDFX;
42 namespace {
43 #undef LOG_DOMAIN
44 #define LOG_DOMAIN 0xD001400
45 #undef LOG_TAG
46 #define LOG_TAG "SyncFence"
47
48 constexpr int FRAME_SET_BLUR_SIZE_ID = 100007;
49 constexpr int FRAME_SET_CONTAINER_NODE_ID = 100008;
50
51 #ifdef FENCE_SCHED_ENABLE
52 constexpr unsigned int QOS_CTRL_IPC_MAGIC = 0xCC;
53
54 #define QOS_CTRL_BASIC_OPERATION \
55 _IOWR(QOS_CTRL_IPC_MAGIC, 1, struct QosCtrlData)
56
57 #define QOS_APPLY 1
58
59 typedef enum {
60 QOS_BACKGROUND = 0,
61 QOS_UTILITY,
62 QOS_DEFAULT,
63 QOS_USER_INITIATED,
64 QOS_DEADLINE_REQUEST,
65 QOS_USER_INTERACTIVE,
66 QOS_KEY_BACKGROUND,
67 } QosLevel;
68
69 struct QosCtrlData {
70 int pid;
71 unsigned int type;
72 unsigned int level;
73 int qos;
74 int staticQos;
75 int dynamicQos;
76 bool tagSchedEnable = false;
77 };
78
TrivalOpenQosCtrlNode(void)79 static int TrivalOpenQosCtrlNode(void)
80 {
81 char fileName[] = "/proc/thread-self/sched_qos_ctrl";
82 int fd = open(fileName, O_RDWR);
83 if (fd < 0) {
84 HILOG_WARN(LOG_CORE, "open qos node failed");
85 }
86 return fd;
87 }
88
QosApply(unsigned int level)89 void QosApply(unsigned int level)
90 {
91 int fd = TrivalOpenQosCtrlNode();
92 if (fd < 0) {
93 return;
94 }
95
96 int tid = gettid();
97 struct QosCtrlData data;
98 data.level = level;
99 data.type = QOS_APPLY;
100 data.pid = tid;
101 int ret = ioctl(fd, QOS_CTRL_BASIC_OPERATION, &data);
102 if (ret < 0) {
103 HILOG_WARN(LOG_CORE, "qos apply failed");
104 }
105 close(fd);
106 }
107 #endif
108 }
109
SyncFenceTracker(const std::string threadName)110 SyncFenceTracker::SyncFenceTracker(const std::string threadName)
111 : threadName_(threadName),
112 fencesQueued_(0),
113 fencesSignaled_(0)
114 {
115 runner_ = OHOS::AppExecFwk::EventRunner::Create(threadName_);
116 handler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(runner_);
117
118 #ifdef FENCE_SCHED_ENABLE
119 if (handler_) {
120 handler_->PostTask([]() {
121 QosApply(QosLevel::QOS_USER_INTERACTIVE);
122 });
123 }
124 #endif
125 if (threadName_.compare("Acquire Fence") == 0) {
126 isGpuFence_ = true;
127 }
128 if (isGpuFence_) {
129 isGpuEnable_ = OHOS::system::GetBoolParameter("persist.deadline.gpu_enable", false);
130 }
131 }
132
TrackFence(const sptr<SyncFence> & fence,bool traceTag)133 void SyncFenceTracker::TrackFence(const sptr<SyncFence>& fence, bool traceTag)
134 {
135 if (fence == nullptr) {
136 HILOG_DEBUG(LOG_CORE, "Trace fence failed, fence is null");
137 return;
138 }
139 if (isGpuFence_) {
140 if (!traceTag && !isGpuEnable_) {
141 return;
142 }
143 }
144 if (fence->SyncFileReadTimestamp() != SyncFence::FENCE_PENDING_TIMESTAMP) {
145 RS_TRACE_NAME_FMT("%s %d has signaled", threadName_.c_str(), fencesQueued_.load());
146 fencesQueued_++;
147 fencesSignaled_++;
148 return;
149 }
150
151 RS_TRACE_NAME_FMT("%s %d", threadName_.c_str(), fencesQueued_.load());
152 if (handler_) {
153 handler_->PostTask([this, fence, traceTag]() {
154 if (isGpuFence_ && isGpuEnable_) {
155 Rosen::FrameSched::GetInstance().SetFrameParam(FRAME_SET_CONTAINER_NODE_ID, 0, 0, processedNodeNum_);
156 processedNodeNum_ = 0;
157 }
158 Loop(fence, traceTag);
159 });
160 fencesQueued_++;
161 }
162 }
163
CheckGpuSubhealthEventLimit()164 bool SyncFenceTracker::CheckGpuSubhealthEventLimit()
165 {
166 auto now = std::chrono::system_clock::now();
167 std::time_t t = std::chrono::system_clock::to_time_t(now);
168 std::tm *tm = std::localtime(&t);
169 if (tm != nullptr && (gpuSubhealthEventNum_ == 0 || tm->tm_yday > gpuSubhealthEventDay_)) {
170 gpuSubhealthEventDay_ = tm->tm_yday;
171 gpuSubhealthEventNum_ = 0;
172 HILOG_DEBUG(LOG_CORE, "first event of %{public}" PRId32, gpuSubhealthEventDay_);
173 gpuSubhealthEventNum_++;
174 return true;
175 } else if (gpuSubhealthEventNum_ < GPU_SUBHEALTH_EVENT_LIMIT) {
176 gpuSubhealthEventNum_++;
177 HILOG_DEBUG(LOG_CORE, "%{public}" PRId32 "event of %{public}" PRId32 "day",
178 gpuSubhealthEventNum_, gpuSubhealthEventDay_);
179 return true;
180 }
181 HILOG_DEBUG(LOG_CORE, "%{public}" PRId32 "event exceed %{public}" PRId32 "day",
182 gpuSubhealthEventNum_, gpuSubhealthEventDay_);
183 return false;
184 }
185
UpdateFrameQueue(int32_t startTime)186 inline void SyncFenceTracker::UpdateFrameQueue(int32_t startTime)
187 {
188 if (frameStartTimes_->size() >= FRAME_QUEUE_SIZE_LIMIT) {
189 frameStartTimes_->pop();
190 }
191 frameStartTimes_->push(startTime);
192 }
193
GetFrameRate()194 int32_t SyncFenceTracker::GetFrameRate()
195 {
196 int32_t frameRate = 0;
197 int32_t frameNum = static_cast<int32_t>(frameStartTimes_->size());
198 if (frameNum > 1) {
199 int32_t interval = frameStartTimes_->back() - frameStartTimes_->front();
200 if (interval > 0) {
201 frameRate = FRAME_PERIOD * (frameNum - 1) / interval;
202 }
203 }
204 return frameRate;
205 }
206
ReportEventGpuSubhealth(int32_t duration)207 void SyncFenceTracker::ReportEventGpuSubhealth(int32_t duration)
208 {
209 if (handler_) {
210 handler_->PostTask([this, duration]() {
211 RS_TRACE_NAME_FMT("report GPU_SUBHEALTH_MONITORING");
212 auto reportName = "GPU_SUBHEALTH_MONITORING";
213 HILOG_DEBUG(LOG_CORE, "report GPU_SUBHEALTH_MONITORING. duration : %{public}"
214 PRId32, duration);
215 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::GRAPHIC, reportName,
216 OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, "WAIT_ACQUIRE_FENCE_TIME",
217 duration, "FRAME_RATE", GetFrameRate());
218 });
219 }
220 }
221
Loop(const sptr<SyncFence> & fence,bool traceTag)222 void SyncFenceTracker::Loop(const sptr<SyncFence>& fence, bool traceTag)
223 {
224 uint32_t fenceIndex = 0;
225 fenceIndex = fencesSignaled_.load();
226 {
227 RS_TRACE_NAME_FMT("Waiting for %s %d", threadName_.c_str(), fenceIndex);
228 int32_t result = 0;
229 if (isGpuFence_ && traceTag) {
230 int32_t startTimestamp = static_cast<int32_t>(
231 std::chrono::duration_cast<std::chrono::milliseconds>(
232 std::chrono::steady_clock::now().time_since_epoch()).count());
233 UpdateFrameQueue(startTimestamp);
234 result = WaitFence(fence);
235 int32_t endTimestamp = static_cast<int32_t>(
236 std::chrono::duration_cast<std::chrono::milliseconds>(
237 std::chrono::steady_clock::now().time_since_epoch()).count());
238 int32_t duration = endTimestamp - startTimestamp;
239 HILOG_DEBUG(LOG_CORE, "Waiting for Acquire Fence: %{public}" PRId32 "ms", duration);
240 if (duration > GPU_SUBHEALTH_EVENT_THRESHOLD && CheckGpuSubhealthEventLimit()) {
241 ReportEventGpuSubhealth(duration);
242 }
243 } else {
244 result = WaitFence(fence);
245 }
246
247 if (result < 0) {
248 HILOG_DEBUG(LOG_CORE, "Error waiting for SyncFence: %s", strerror(result));
249 }
250 }
251 fencesSignaled_++;
252 }
253
WaitFence(const sptr<SyncFence> & fence)254 int32_t SyncFenceTracker::WaitFence(const sptr<SyncFence>& fence)
255 {
256 if (isGpuFence_ && isGpuEnable_) {
257 Rosen::FrameSched::GetInstance().MonitorGpuStart();
258 }
259 int32_t result = fence->Wait(SYNC_TIME_OUT);
260 if (isGpuFence_ && isGpuEnable_) {
261 Rosen::FrameSched::GetInstance().MonitorGpuEnd();
262 }
263 return result;
264 }
265
SetBlurSize(int32_t blurSize)266 void SyncFenceTracker::SetBlurSize(int32_t blurSize)
267 {
268 if (handler_) {
269 handler_->PostTask([blurSize]() {
270 Rosen::FrameSched::GetInstance().SetFrameParam(FRAME_SET_BLUR_SIZE_ID, 0, 0, blurSize);
271 });
272 }
273 }
274
SetContainerNodeNum(int containerNodeNum)275 void SyncFenceTracker::SetContainerNodeNum(int containerNodeNum)
276 {
277 if (isGpuEnable_) {
278 processedNodeNum_ += containerNodeNum;
279 }
280 }
281 } // namespace OHOS