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