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