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