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
40namespace OHOS {
41using namespace OHOS::HiviewDFX;
42namespace {
43#undef LOG_DOMAIN
44#define LOG_DOMAIN 0xD001400
45#undef LOG_TAG
46#define LOG_TAG "SyncFence"
47
48constexpr int FRAME_SET_BLUR_SIZE_ID = 100007;
49constexpr int FRAME_SET_CONTAINER_NODE_ID = 100008;
50
51#ifdef FENCE_SCHED_ENABLE
52constexpr 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
59typedef 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
69struct 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
79static 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
89void 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
110SyncFenceTracker::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
133void 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_.fetch_add(1);
147        fencesSignaled_.fetch_add(1);
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_.fetch_add(1);
161    }
162}
163
164bool 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
186inline 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
194int32_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
207void 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
222void 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_.fetch_add(1);
252}
253
254int32_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
266void 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
275void SyncFenceTracker::SetContainerNodeNum(int containerNodeNum)
276{
277    if (isGpuEnable_) {
278        processedNodeNum_ += containerNodeNum;
279    }
280}
281} // namespace OHOS