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
GetCurrentRealTimeNs()76 int64_t GetCurrentRealTimeNs()
77 {
78 struct timespec ts = { 0, 0 };
79 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
80 return 0;
81 }
82 return (ts.tv_sec * NS_TO_S + ts.tv_nsec);
83 }
84
ClockTimeCalibration(int64_t & now)85 void ClockTimeCalibration(int64_t &now)
86 {
87 int64_t realTime = GetCurrentRealTimeNs();
88 if (now - realTime >= NS_TO_MS || realTime - now >= NS_TO_MS) {
89 ACE_SCOPED_TRACE("ClockTimeCalibration realTime = %ld, now = %ld",
90 static_cast<long>(realTime), static_cast<long>(now));
91 now = realTime;
92 }
93 }
94
95 class SteadyTimeRecorder {
96 public:
97 static steady_clock::time_point begin;
Begin()98 static void Begin()
99 {
100 begin = steady_clock::now();
101 }
End()102 static int64_t End()
103 {
104 auto end = steady_clock::now();
105 return duration_cast<milliseconds>(end - begin).count();
106 }
107 };
108
109 steady_clock::time_point SteadyTimeRecorder::begin {};
110 } // namespace
111
GetInstance()112 JankFrameReport& JankFrameReport::GetInstance()
113 {
114 static thread_local JankFrameReport instance;
115 return instance;
116 }
117
JankFrameReport()118 JankFrameReport::JankFrameReport()
119 {
120 frameJankRecord_ = std::vector<uint16_t>(JANK_SIZE, 0);
121 jankFrameCount_ = 0;
122 prevFrameUpdateCount_ = 0;
123 currentFrameUpdateCount_ = 0;
124 recordStatus_ = JANK_IDLE;
125 startTime_ = 0;
126 prevEndTimeStamp_ = 0;
127 refreshPeriod_ = 16666666;
128 needReport_ = false;
129 hasJsAnimation_ = false;
130 animatorEndTime_ = 0;
131 jsAnimationDelayJank_ = 0;
132 }
133
JankFrameRecord(int64_t timeStampNanos,const std::string & windowName)134 void JankFrameReport::JankFrameRecord(int64_t timeStampNanos, const std::string& windowName)
135 {
136 if (refreshPeriod_ <= 0) {
137 return;
138 }
139 int64_t now = GetSteadyTimestamp<std::chrono::nanoseconds>();
140 ClockTimeCalibration(now);
141 int64_t durationTmp = now - std::max(timeStampNanos, prevEndTimeStamp_);
142 int64_t duration = (now <= timeStampNanos) ? 0 : durationTmp;
143 double jank = double(duration) / refreshPeriod_;
144 // perf monitor jank frame
145 PerfMonitor::GetPerfMonitor()->SetFrameTime(timeStampNanos, duration, jank, windowName);
146 RecordJankStatus(jank);
147 prevFrameUpdateCount_ = currentFrameUpdateCount_;
148 RecordPreviousEnd();
149 }
150
JsAnimationToRsRecord()151 void JankFrameReport::JsAnimationToRsRecord()
152 {
153 int64_t now = GetSteadyTimestamp<std::chrono::nanoseconds>();
154 if (hasJsAnimation_ && animatorEndTime_ != 0) {
155 int64_t jsAnimationDuration = now - animatorEndTime_;
156 jsAnimationDelayJank_ = double(jsAnimationDuration) / refreshPeriod_;
157 }
158 }
159
RecordJankStatus(double jank)160 void JankFrameReport::RecordJankStatus(double jank)
161 {
162 if (recordStatus_ == JANK_IDLE && animatorEndTime_ == 0) {
163 return;
164 }
165 if (animatorEndTime_ != 0) {
166 hasJsAnimation_ = false;
167 animatorEndTime_ = 0;
168 jsAnimationDelayJank_ = 0;
169 }
170 // on need to record
171 if (jank <= 1.0f) {
172 return;
173 }
174 // skip first frame
175 if (prevFrameUpdateCount_ == 0 && (currentFrameUpdateCount_ >= 0)) {
176 return;
177 };
178 needReport_ = true;
179 frameJankRecord_[GetJankRange(jank)]++;
180 if (jank >= 6.0f) {
181 jankFrameCount_++;
182 ACE_SCOPED_TRACE("JANK_STATS_APP skippedTime=%lld(ms)",
183 static_cast<long long>(jank * refreshPeriod_ / NS_TO_MS));
184 ACE_COUNT_TRACE(jankFrameCount_, "JANK FRAME %s", pageUrl_.c_str());
185 }
186 PerfMonitor::GetPerfMonitor()->ReportJankFrameApp(jank);
187 }
188
RecordPreviousEnd()189 void JankFrameReport::RecordPreviousEnd()
190 {
191 prevEndTimeStamp_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
192 }
193
ClearFrameJankRecord()194 void JankFrameReport::ClearFrameJankRecord()
195 {
196 std::fill(frameJankRecord_.begin(), frameJankRecord_.end(), 0);
197 jankFrameCount_ = 0;
198 recordStatus_ = JANK_IDLE;
199 currentFrameUpdateCount_ = 0;
200 needReport_ = false;
201 hasJsAnimation_ = false;
202 jsAnimationDelayJank_ = 0;
203 animatorEndTime_ = 0;
204 }
205
SetFrameJankFlag(JankFrameFlag flag)206 void JankFrameReport::SetFrameJankFlag(JankFrameFlag flag)
207 {
208 recordStatus_++;
209 if (recordStatus_ == 1) {
210 animatorEndTime_ = 0;
211 hasJsAnimation_ = false;
212 }
213 }
214
ClearFrameJankFlag(JankFrameFlag flag)215 void JankFrameReport::ClearFrameJankFlag(JankFrameFlag flag)
216 {
217 if (recordStatus_ > 0) {
218 if (recordStatus_ == 1) {
219 animatorEndTime_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
220 }
221 recordStatus_--;
222 }
223 if (recordStatus_ == JANK_IDLE) {
224 currentFrameUpdateCount_ = 0;
225 }
226 }
227
RecordFrameUpdate()228 void JankFrameReport::RecordFrameUpdate()
229 {
230 currentFrameUpdateCount_++;
231 }
232
ResetFrameJankClock()233 void JankFrameReport::ResetFrameJankClock()
234 {
235 startTime_ = GetSystemTimestamp<std::chrono::milliseconds>();
236 SteadyTimeRecorder::Begin();
237 }
238
StartRecord(const std::string & pageUrl)239 void JankFrameReport::StartRecord(const std::string& pageUrl)
240 {
241 ResetFrameJankClock();
242 pageUrl_ = ParsePageUrl(pageUrl);
243 }
244
FlushRecord()245 void JankFrameReport::FlushRecord()
246 {
247 Rosen::RSInterfaces::GetInstance().ReportJankStats();
248 if (needReport_) {
249 LOGI("%{public}s", std::string("jank report,pageUrl:")
250 .append(pageUrl_)
251 .append(",startTime:")
252 .append(std::to_string(startTime_))
253 .append("duration:")
254 .append(std::to_string(SteadyTimeRecorder::End()))
255 .c_str());
256 EventReport::JankFrameReport(startTime_, SteadyTimeRecorder::End(), frameJankRecord_, pageUrl_);
257 }
258 ClearFrameJankRecord();
259 }
260
ReportJSAnimation()261 void JankFrameReport::ReportJSAnimation()
262 {
263 if (animatorEndTime_ != 0) {
264 hasJsAnimation_ = true;
265 }
266 }
267
RecordAnimateEnd()268 void JankFrameReport::RecordAnimateEnd()
269 {
270 prevEndTimeStamp_ = GetSteadyTimestamp<std::chrono::nanoseconds>();
271 }
272 } // namespace OHOS::Ace
273