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