1 /*
2  * Copyright (c) 2023 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 "base/log/jank_frame_report.h"
17 
18 #include "render_service_client/core/transaction/rs_interfaces.h"
19 #include "base/log/ace_trace.h"
20 #include "base/log/log_wrapper.h"
21 #include "base/log/event_report.h"
22 
23 namespace OHOS::Ace {
24 namespace {
25 constexpr uint32_t JANK_FRAME_6_FREQ = 0;
26 constexpr uint32_t JANK_FRAME_15_FREQ = 1;
27 constexpr uint32_t JANK_FRAME_20_FREQ = 2;
28 constexpr uint32_t JANK_FRAME_36_FREQ = 3;
29 constexpr uint32_t JANK_FRAME_48_FREQ = 4;
30 constexpr uint32_t JANK_FRAME_60_FREQ = 5;
31 constexpr uint32_t JANK_FRAME_120_FREQ = 6;
32 constexpr uint32_t JANK_FRAME_180_FREQ = 7;
33 constexpr uint32_t JANK_SIZE = 8;
34 
35 using namespace std;
36 using namespace std::chrono;
37 
38 template<class T>
GetSystemTimestamp()39 int64_t GetSystemTimestamp()
40 {
41     return duration_cast<T>(system_clock::now().time_since_epoch()).count();
42 }
43 
44 template<class T>
GetSteadyTimestamp()45 int64_t GetSteadyTimestamp()
46 {
47     return duration_cast<T>(steady_clock::now().time_since_epoch()).count();
48 }
49 
GetJankRange(double jank)50 uint32_t GetJankRange(double jank)
51 {
52     if (jank < 6.0f) {
53         return JANK_FRAME_6_FREQ;
54     }
55     if (jank < 15.0f) {
56         return JANK_FRAME_15_FREQ;
57     }
58     if (jank < 20.0f) {
59         return JANK_FRAME_20_FREQ;
60     }
61     if (jank < 36.0f) {
62         return JANK_FRAME_36_FREQ;
63     }
64     if (jank < 48.0f) {
65         return JANK_FRAME_48_FREQ;
66     }
67     if (jank < 60.0f) {
68         return JANK_FRAME_60_FREQ;
69     }
70     if (jank < 120.0f) {
71         return JANK_FRAME_120_FREQ;
72     }
73     return JANK_FRAME_180_FREQ;
74 }
75 
76 class SteadyTimeRecorder {
77 public:
78     static steady_clock::time_point begin;
Begin()79     static void Begin()
80     {
81         begin = steady_clock::now();
82     }
End()83     static int64_t End()
84     {
85         auto end = steady_clock::now();
86         return duration_cast<milliseconds>(end - begin).count();
87     }
88 };
89 
90 steady_clock::time_point SteadyTimeRecorder::begin {};
91 } // namespace
92 
GetInstance()93 JankFrameReport& JankFrameReport::GetInstance()
94 {
95     static thread_local JankFrameReport instance;
96     return instance;
97 }
98 
JankFrameReport()99 JankFrameReport::JankFrameReport()
100 {
101     frameJankRecord_ = std::vector<uint16_t>(JANK_SIZE, 0);
102     jankFrameCount_ = 0;
103     prevFrameUpdateCount_ = 0;
104     currentFrameUpdateCount_ = 0;
105     recordStatus_ = JANK_IDLE;
106     startTime_ = 0;
107     prevEndTimeStamp_ = 0;
108     refreshPeriod_ = 16666666;
109     needReport_ = false;
110     hasJsAnimation_ = false;
111     animatorEndTime_ = 0;
112     jsAnimationDelayJank_ = 0;
113 }
114 
JankFrameRecord(int64_t timeStampNanos,const std::string & windowName)115 void JankFrameReport::JankFrameRecord(int64_t timeStampNanos, const std::string& windowName)
116 {
117     if (refreshPeriod_ <= 0) {
118         return;
119     }
120     int64_t now = GetSteadyTimestamp<std::chrono::nanoseconds>();
121     int64_t durationTmp = now - std::max(timeStampNanos, prevEndTimeStamp_);
122     int64_t duration = (now <= timeStampNanos) ? 0 : durationTmp;
123     double jank = double(duration) / refreshPeriod_;
124     // perf monitor jank frame
125     PerfMonitor::GetPerfMonitor()->SetFrameTime(timeStampNanos, duration, jank, windowName);
126     RecordJankStatus(jank);
127     prevFrameUpdateCount_ = currentFrameUpdateCount_;
128     RecordPreviousEnd();
129 }
130 
JsAnimationToRsRecord()131 void JankFrameReport::JsAnimationToRsRecord()
132 {
133     int64_t now = GetSteadyTimestamp<std::chrono::nanoseconds>();
134     if (hasJsAnimation_ && animatorEndTime_ != 0) {
135         int64_t jsAnimationDuration = now - animatorEndTime_;
136         jsAnimationDelayJank_ = double(jsAnimationDuration) / refreshPeriod_;
137     }
138 }
139 
RecordJankStatus(double jank)140 void JankFrameReport::RecordJankStatus(double jank)
141 {
142     if (recordStatus_ == JANK_IDLE && animatorEndTime_ == 0) {
143         return;
144     }
145     if (animatorEndTime_ != 0) {
146         hasJsAnimation_ = false;
147         animatorEndTime_ = 0;
148         jsAnimationDelayJank_ = 0;
149     }
150     // on need to record
151     if (jank <= 1.0f) {
152         return;
153     }
154     // skip first frame
155     if (prevFrameUpdateCount_ == 0 && currentFrameUpdateCount_ >= 0) {
156         return;
157     };
158     needReport_ = true;
159     frameJankRecord_[GetJankRange(jank)]++;
160     if (jank >= 6.0f) {
161         jankFrameCount_++;
162         ACE_SCOPED_TRACE("JANK_STATS_APP skippedTime=%lld(ms)",
163             static_cast<long long>(jank * refreshPeriod_ / NS_TO_MS));
164         ACE_COUNT_TRACE(jankFrameCount_, "JANK FRAME %s", pageUrl_.c_str());
165     }
166     PerfMonitor::GetPerfMonitor()->ReportJankFrameApp(jank);
167 }
168 
RecordPreviousEnd()169 void JankFrameReport::RecordPreviousEnd()
170 {
171     prevEndTimeStamp_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
172 }
173 
ClearFrameJankRecord()174 void JankFrameReport::ClearFrameJankRecord()
175 {
176     std::fill(frameJankRecord_.begin(), frameJankRecord_.end(), 0);
177     jankFrameCount_ = 0;
178     recordStatus_ = JANK_IDLE;
179     currentFrameUpdateCount_ = 0;
180     needReport_ = false;
181     hasJsAnimation_ = false;
182     jsAnimationDelayJank_ = 0;
183     animatorEndTime_ = 0;
184 }
185 
SetFrameJankFlag(JankFrameFlag flag)186 void JankFrameReport::SetFrameJankFlag(JankFrameFlag flag)
187 {
188     recordStatus_++;
189     if (recordStatus_ == 1) {
190         animatorEndTime_ = 0;
191         hasJsAnimation_ = false;
192     }
193 }
194 
ClearFrameJankFlag(JankFrameFlag flag)195 void JankFrameReport::ClearFrameJankFlag(JankFrameFlag flag)
196 {
197     if (recordStatus_ > 0) {
198         if (recordStatus_ == 1) {
199             animatorEndTime_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
200         }
201         recordStatus_--;
202     }
203     if (recordStatus_ == JANK_IDLE) {
204         currentFrameUpdateCount_ = 0;
205     }
206 }
207 
RecordFrameUpdate()208 void JankFrameReport::RecordFrameUpdate()
209 {
210     currentFrameUpdateCount_++;
211 }
212 
ResetFrameJankClock()213 void JankFrameReport::ResetFrameJankClock()
214 {
215     startTime_ = GetSystemTimestamp<std::chrono::milliseconds>();
216     SteadyTimeRecorder::Begin();
217 }
218 
StartRecord(const std::string & pageUrl)219 void JankFrameReport::StartRecord(const std::string& pageUrl)
220 {
221     ResetFrameJankClock();
222     pageUrl_ = ParsePageUrl(pageUrl);
223 }
224 
FlushRecord()225 void JankFrameReport::FlushRecord()
226 {
227     Rosen::RSInterfaces::GetInstance().ReportJankStats();
228     if (needReport_) {
229         LOGI("%{public}s", std::string("jank report,pageUrl:")
230                                .append(pageUrl_)
231                                .append(",startTime:")
232                                .append(std::to_string(startTime_))
233                                .append("duration:")
234                                .append(std::to_string(SteadyTimeRecorder::End()))
235                                .c_str());
236         EventReport::JankFrameReport(startTime_, SteadyTimeRecorder::End(), frameJankRecord_, pageUrl_);
237     }
238     ClearFrameJankRecord();
239 }
240 
ReportJSAnimation()241 void JankFrameReport::ReportJSAnimation()
242 {
243     if (animatorEndTime_ != 0) {
244         hasJsAnimation_ = true;
245     }
246 }
247 
RecordAnimateEnd()248 void JankFrameReport::RecordAnimateEnd()
249 {
250     prevEndTimeStamp_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
251 }
252 } // namespace OHOS::Ace
253