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