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 #include <sys/time.h>
16
17 #include "base/perfmonitor/perf_monitor.h"
18
19 #include "base/log/ace_trace.h"
20 #include "base/log/event_report.h"
21 #include "base/log/jank_frame_report.h"
22 #include "base/log/log.h"
23 #include "base/perfmonitor/perf_constants.h"
24 #include "base/utils/system_properties.h"
25 #include "core/common/ace_application_info.h"
26 #include "render_service_client/core/transaction/rs_interfaces.h"
27
28 namespace OHOS::Ace {
29 using namespace std;
30 PerfMonitor* PerfMonitor::pMonitor = nullptr;
31 constexpr int64_t SCENE_TIMEOUT = 10000000000;
32 constexpr int64_t RESPONSE_TIMEOUT = 60000000;
33 constexpr int64_t STARTAPP_FRAME_TIMEOUT = 100000000;
34 constexpr float SINGLE_FRAME_TIME = 16600000;
35 const int32_t JANK_SKIPPED_THRESHOLD = SystemProperties::GetJankFrameThreshold();
36 const int32_t DEFAULT_JANK_REPORT_THRESHOLD = 3;
37
GetCurrentRealTimeNs()38 static int64_t GetCurrentRealTimeNs()
39 {
40 struct timespec ts = { 0, 0 };
41 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
42 return 0;
43 }
44 return (ts.tv_sec * NS_TO_S + ts.tv_nsec);
45 }
46
GetCurrentSystimeMs()47 static int64_t GetCurrentSystimeMs()
48 {
49 auto timeNow = std::chrono::system_clock::now();
50 auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow.time_since_epoch());
51 int64_t curSystime = tmp.count();
52 return curSystime;
53 }
54
ConvertRealtimeToSystime(int64_t realTime,int64_t & sysTime)55 void ConvertRealtimeToSystime(int64_t realTime, int64_t& sysTime)
56 {
57 int64_t curRealTime = GetCurrentRealTimeNs();
58 if (curRealTime == 0) {
59 sysTime = 0;
60 return;
61 }
62 int64_t curSysTime = GetCurrentSystimeMs();
63 sysTime = curSysTime - (curRealTime - realTime) / NS_TO_MS;
64 }
65
GetSourceTypeName(PerfSourceType sourceType)66 std::string GetSourceTypeName(PerfSourceType sourceType)
67 {
68 std::string type = "";
69 switch (sourceType) {
70 case PERF_TOUCH_EVENT:
71 type = "TOUCHSCREEN";
72 break;
73 case PERF_MOUSE_EVENT:
74 type = "MOUSE";
75 break;
76 case PERF_TOUCH_PAD:
77 type = "TOUCHPAD";
78 break;
79 case PERF_JOY_STICK:
80 type = "JOYSTICK";
81 break;
82 case PERF_KEY_EVENT:
83 type = "KEY_EVENT";
84 break;
85 default :
86 type = "UNKNOWN_SOURCE";
87 break;
88 }
89 return type;
90 }
91
ConvertToRsData(OHOS::Rosen::DataBaseRs & dataRs,DataBase & data)92 void ConvertToRsData(OHOS::Rosen::DataBaseRs &dataRs, DataBase& data)
93 {
94 dataRs.eventType = static_cast<int32_t>(data.eventType);
95 dataRs.sceneId = data.sceneId;
96 dataRs.appPid = data.baseInfo.pid;
97 dataRs.uniqueId = data.beginVsyncTime / NS_TO_MS;
98 dataRs.inputTime = data.inputTime;
99 dataRs.beginVsyncTime = data.beginVsyncTime;
100 dataRs.endVsyncTime = data.endVsyncTime;
101 dataRs.versionCode = data.baseInfo.versionCode;
102 dataRs.versionName = data.baseInfo.versionName;
103 dataRs.bundleName = data.baseInfo.bundleName;
104 dataRs.processName = data.baseInfo.processName;
105 dataRs.abilityName = data.baseInfo.abilityName;
106 dataRs.pageUrl = data.baseInfo.pageUrl;
107 dataRs.sourceType = GetSourceTypeName(data.sourceType);
108 dataRs.note = data.baseInfo.note;
109 dataRs.isDisplayAnimator = data.isDisplayAnimator;
110 }
111
ReportPerfEventToRS(DataBase & data)112 void ReportPerfEventToRS(DataBase& data)
113 {
114 OHOS::Rosen::DataBaseRs dataRs;
115 ConvertToRsData(dataRs, data);
116 switch (dataRs.eventType) {
117 case EVENT_RESPONSE:
118 {
119 ACE_SCOPED_TRACE("EVENT_REPORT_RESPONSE_RS sceneId = %s, uniqueId = %lld",
120 dataRs.sceneId.c_str(), static_cast<long long> (dataRs.uniqueId));
121 Rosen::RSInterfaces::GetInstance().ReportEventResponse(dataRs);
122 break;
123 }
124 case EVENT_COMPLETE:
125 {
126 if (data.isDisplayAnimator) {
127 ACE_SCOPED_TRACE("EVENT_REPORT_COMPLETE_RS sceneId = %s, uniqueId = %lld",
128 dataRs.sceneId.c_str(), static_cast<long long> (dataRs.uniqueId));
129 Rosen::RSInterfaces::GetInstance().ReportEventComplete(dataRs);
130 }
131 break;
132 }
133 case EVENT_JANK_FRAME:
134 {
135 ACE_SCOPED_TRACE("EVENT_REPORT_JANK_RS sceneId = %s, uniqueId = %lld",
136 dataRs.sceneId.c_str(), static_cast<long long> (dataRs.uniqueId));
137 Rosen::RSInterfaces::GetInstance().ReportEventJankFrame(dataRs);
138 break;
139 }
140 default :
141 break;
142 }
143 }
144
ReportPerfEventToUI(DataBase data)145 void ReportPerfEventToUI(DataBase data)
146 {
147 switch (data.eventType) {
148 case EVENT_COMPLETE:
149 if (!data.needReportRs) {
150 EventReport::ReportEventComplete(data);
151 }
152 break;
153 case EVENT_JANK_FRAME:
154 if (data.totalFrames > 0) {
155 EventReport::ReportEventJankFrame(data);
156 }
157 break;
158 default :
159 break;
160 }
161 }
162
InitRecord(const std::string & sId,PerfActionType aType,PerfSourceType sType,const std::string & nt,int64_t time)163 void SceneRecord::InitRecord(const std::string& sId, PerfActionType aType, PerfSourceType sType, const std::string& nt,
164 int64_t time)
165 {
166 sceneId = sId;
167 actionType = aType;
168 sourceType = sType;
169 note = nt;
170 inputTime = time;
171 beginVsyncTime = GetCurrentRealTimeNs();
172 isDisplayAnimator = IsDisplayAnimator(sceneId);
173 }
174
IsTimeOut(int64_t nowTime)175 bool SceneRecord::IsTimeOut(int64_t nowTime)
176 {
177 if (nowTime - beginVsyncTime > SCENE_TIMEOUT) {
178 return true;
179 }
180 return false;
181 }
182
RecordFrame(int64_t vsyncTime,int64_t duration,int32_t skippedFrames)183 void SceneRecord::RecordFrame(int64_t vsyncTime, int64_t duration, int32_t skippedFrames)
184 {
185 if (totalFrames == 0) {
186 beginVsyncTime = GetCurrentRealTimeNs();
187 isFirstFrame = true;
188 } else {
189 isFirstFrame = false;
190 }
191 skippedFrames = static_cast<int32_t>(duration / SINGLE_FRAME_TIME);
192 if (!isFirstFrame && skippedFrames >= 1) {
193 if (isSuccessive) {
194 seqMissFrames = seqMissFrames + skippedFrames;
195 } else {
196 seqMissFrames = skippedFrames;
197 isSuccessive = true;
198 }
199 if (maxSuccessiveFrames < seqMissFrames) {
200 maxSuccessiveFrames = seqMissFrames;
201 }
202 totalMissed += skippedFrames;
203 } else {
204 isSuccessive = false;
205 seqMissFrames = 0;
206 }
207 if (!isFirstFrame && duration > maxFrameTime) {
208 maxFrameTime = duration;
209 }
210 totalFrames++;
211 }
212
Report(const std::string & sceneId,int64_t vsyncTime,bool isRsRender)213 void SceneRecord::Report(const std::string& sceneId, int64_t vsyncTime, bool isRsRender)
214 {
215 if (isRsRender || vsyncTime <= beginVsyncTime) {
216 endVsyncTime = GetCurrentRealTimeNs();
217 } else {
218 endVsyncTime = vsyncTime;
219 }
220 needReportRs = !isRsRender;
221 }
222
IsFirstFrame()223 bool SceneRecord::IsFirstFrame()
224 {
225 return isFirstFrame;
226 }
227
IsDisplayAnimator(const std::string & sceneId)228 bool SceneRecord::IsDisplayAnimator(const std::string& sceneId)
229 {
230 if (sceneId == PerfConstants::APP_LIST_FLING || sceneId == PerfConstants::APP_SWIPER_SCROLL
231 || sceneId == PerfConstants::SNAP_RECENT_ANI) {
232 return true;
233 }
234 return false;
235 }
236
Reset()237 void SceneRecord::Reset()
238 {
239 beginVsyncTime = 0;
240 endVsyncTime = 0;
241 maxFrameTime = 0;
242 maxSuccessiveFrames = 0;
243 seqMissFrames = 0;
244 totalMissed = 0;
245 totalFrames = 0;
246 isSuccessive = false;
247 isFirstFrame = false;
248 sceneId = "";
249 actionType = UNKNOWN_ACTION;
250 sourceType = UNKNOWN_SOURCE;
251 note = "";
252 }
253
GetPerfMonitor()254 PerfMonitor* PerfMonitor::GetPerfMonitor()
255 {
256 if (pMonitor == nullptr) {
257 pMonitor = new PerfMonitor();
258 }
259 return pMonitor;
260 }
261
Start(const std::string & sceneId,PerfActionType type,const std::string & note)262 void PerfMonitor::Start(const std::string& sceneId, PerfActionType type, const std::string& note)
263 {
264 std::lock_guard<std::mutex> Lock(mMutex);
265 int64_t inputTime = GetInputTime(sceneId, type, note);
266 SceneRecord* record = GetRecord(sceneId);
267 if (record == nullptr) {
268 record = new SceneRecord();
269 record->InitRecord(sceneId, type, mSourceType, note, inputTime);
270 mRecords.insert(std::pair<std::string, SceneRecord*> (sceneId, record));
271 RecordBaseInfo(record);
272 AceAsyncTraceBegin(0, sceneId.c_str());
273 }
274 }
275
End(const std::string & sceneId,bool isRsRender)276 void PerfMonitor::End(const std::string& sceneId, bool isRsRender)
277 {
278 std::lock_guard<std::mutex> Lock(mMutex);
279 SceneRecord* record = GetRecord(sceneId);
280 if (record != nullptr) {
281 RecordBaseInfo(record);
282 record->Report(sceneId, mVsyncTime, isRsRender);
283 ReportAnimateEnd(sceneId, record);
284 RemoveRecord(sceneId);
285 AceAsyncTraceEnd(0, sceneId.c_str());
286 }
287 }
288
RecordInputEvent(PerfActionType type,PerfSourceType sourceType,int64_t time)289 void PerfMonitor::RecordInputEvent(PerfActionType type, PerfSourceType sourceType, int64_t time)
290 {
291 mSourceType = sourceType;
292 if (time <= 0) {
293 time = GetCurrentRealTimeNs();
294 }
295 switch (type) {
296 case LAST_DOWN:
297 {
298 ACE_SCOPED_TRACE("RecordInputEvent: last_down=%lld(ns)", static_cast<long long>(time));
299 mInputTime[LAST_DOWN] = time;
300 break;
301 }
302 case LAST_UP:
303 {
304 ACE_SCOPED_TRACE("RecordInputEvent: last_up=%lld(ns)", static_cast<long long>(time));
305 mInputTime[LAST_UP] = time;
306 isResponseExclusion = true;
307 break;
308 }
309 case FIRST_MOVE:
310 {
311 ACE_SCOPED_TRACE("RecordInputEvent: first_move=%lld(ns)", static_cast<long long>(time));
312 mInputTime[FIRST_MOVE] = time;
313 break;
314 }
315 default:
316 break;
317 }
318 }
319
SetFrameTime(int64_t vsyncTime,int64_t duration,double jank,const std::string & windowName)320 void PerfMonitor::SetFrameTime(int64_t vsyncTime, int64_t duration, double jank, const std::string& windowName)
321 {
322 std::lock_guard<std::mutex> Lock(mMutex);
323 mVsyncTime = vsyncTime;
324 int32_t skippedFrames = static_cast<int32_t> (jank);
325 for (auto it = mRecords.begin(); it != mRecords.end();) {
326 if (it->second != nullptr) {
327 (it->second)->RecordFrame(vsyncTime, duration, skippedFrames);
328 if ((it->second)->IsTimeOut(vsyncTime + duration)) {
329 delete it->second;
330 mRecords.erase(it++);
331 continue;
332 }
333 if ((it->second)->IsFirstFrame()) {
334 ReportAnimateStart(it->first, it->second);
335 }
336 }
337 it++;
338 }
339 ProcessJank(jank, windowName);
340 }
341
ReportJankFrameApp(double jank)342 void PerfMonitor::ReportJankFrameApp(double jank)
343 {
344 if (jank >= static_cast<double>(JANK_SKIPPED_THRESHOLD)) {
345 JankInfo jankInfo;
346 jankInfo.skippedFrameTime = static_cast<int64_t>(jank * SINGLE_FRAME_TIME);
347 RecordBaseInfo(nullptr);
348 jankInfo.baseInfo = baseInfo;
349 EventReport::ReportJankFrameApp(jankInfo);
350 }
351 }
352
SetPageUrl(const std::string & pageUrl)353 void PerfMonitor::SetPageUrl(const std::string& pageUrl)
354 {
355 baseInfo.pageUrl = pageUrl;
356 }
357
GetPageUrl()358 std::string PerfMonitor::GetPageUrl()
359 {
360 return baseInfo.pageUrl;
361 }
362
RecordBaseInfo(SceneRecord * record)363 void PerfMonitor::RecordBaseInfo(SceneRecord* record)
364 {
365 baseInfo.pid = AceApplicationInfo::GetInstance().GetPid();
366 baseInfo.bundleName = AceApplicationInfo::GetInstance().GetPackageName();
367 baseInfo.versionCode = AceApplicationInfo::GetInstance().GetAppVersionCode();
368 baseInfo.versionName = AceApplicationInfo::GetInstance().GetAppVersionName();
369 baseInfo.processName = AceApplicationInfo::GetInstance().GetProcessName();
370 baseInfo.abilityName = AceApplicationInfo::GetInstance().GetAbilityName();
371 if (record != nullptr) {
372 baseInfo.note = record->note;
373 }
374 }
375
GetRecord(const std::string & sceneId)376 SceneRecord* PerfMonitor::GetRecord(const std::string& sceneId)
377 {
378 if (mRecords.find(sceneId) != mRecords.end()) {
379 return mRecords[sceneId];
380 }
381 return nullptr;
382 }
383
RemoveRecord(const std::string & sceneId)384 void PerfMonitor::RemoveRecord(const std::string& sceneId)
385 {
386 std::map <std::string, SceneRecord*>::iterator iter = mRecords.find(sceneId);
387 if (iter != mRecords.end()) {
388 if (iter->second != nullptr) {
389 delete iter->second;
390 }
391 mRecords.erase(iter);
392 }
393 }
394
GetInputTime(const std::string & sceneId,PerfActionType type,const std::string & note)395 int64_t PerfMonitor::GetInputTime(const std::string& sceneId, PerfActionType type, const std::string& note)
396 {
397 int64_t inputTime = 0;
398 switch (type) {
399 case LAST_DOWN:
400 inputTime = mInputTime[LAST_DOWN];
401 break;
402 case LAST_UP:
403 inputTime = mInputTime[LAST_UP];
404 break;
405 case FIRST_MOVE:
406 inputTime = mInputTime[FIRST_MOVE];
407 break;
408 default:
409 break;
410 }
411 if (inputTime <= 0 || IsExceptResponseTime(inputTime, sceneId)) {
412 ACE_SCOPED_TRACE("GetInputTime: now time");
413 inputTime = GetCurrentRealTimeNs();
414 }
415 return inputTime;
416 }
417
ReportAnimateStart(const std::string & sceneId,SceneRecord * record)418 void PerfMonitor::ReportAnimateStart(const std::string& sceneId, SceneRecord* record)
419 {
420 if (record == nullptr) {
421 return;
422 }
423 DataBase data;
424 FlushDataBase(record, data);
425 ReportPerfEvent(EVENT_RESPONSE, data);
426 }
427
ReportAnimateEnd(const std::string & sceneId,SceneRecord * record)428 void PerfMonitor::ReportAnimateEnd(const std::string& sceneId, SceneRecord* record)
429 {
430 if (record == nullptr) {
431 return;
432 }
433 DataBase data;
434 FlushDataBase(record, data);
435 ReportPerfEvent(EVENT_JANK_FRAME, data);
436 ReportPerfEvent(EVENT_COMPLETE, data);
437 }
438
FlushDataBase(SceneRecord * record,DataBase & data)439 void PerfMonitor::FlushDataBase(SceneRecord* record, DataBase& data)
440 {
441 if (record == nullptr) {
442 return;
443 }
444 data.sceneId = record->sceneId;
445 data.inputTime = record->inputTime;
446 data.beginVsyncTime = record->beginVsyncTime;
447 if (data.beginVsyncTime < data.inputTime) {
448 data.inputTime = data.beginVsyncTime;
449 }
450 data.endVsyncTime = record->endVsyncTime;
451 if (data.beginVsyncTime > data.endVsyncTime) {
452 data.endVsyncTime = data.beginVsyncTime;
453 }
454 data.maxFrameTime = record->maxFrameTime;
455 data.maxSuccessiveFrames = record->maxSuccessiveFrames;
456 data.totalMissed = record->totalMissed;
457 data.totalFrames = record->totalFrames;
458 data.needReportRs = record->needReportRs;
459 data.isDisplayAnimator = record->isDisplayAnimator;
460 data.sourceType = record->sourceType;
461 data.actionType = record->actionType;
462 data.baseInfo = baseInfo;
463 }
464
ReportPerfEvent(PerfEventType type,DataBase & data)465 void PerfMonitor::ReportPerfEvent(PerfEventType type, DataBase& data)
466 {
467 switch (type) {
468 case EVENT_RESPONSE:
469 data.eventType = EVENT_RESPONSE;
470 break;
471 case EVENT_COMPLETE:
472 data.eventType = EVENT_COMPLETE;
473 break;
474 case EVENT_JANK_FRAME:
475 data.eventType = EVENT_JANK_FRAME;
476 break;
477 default :
478 break;
479 }
480 ReportPerfEventToUI(data);
481 ReportPerfEventToRS(data);
482 }
483
IsExceptResponseTime(int64_t time,const std::string & sceneId)484 bool PerfMonitor::IsExceptResponseTime(int64_t time, const std::string& sceneId)
485 {
486 if ((sceneId == PerfConstants::ABILITY_OR_PAGE_SWITCH
487 && GetCurrentRealTimeNs() - time > RESPONSE_TIMEOUT)
488 || sceneId == PerfConstants::VOLUME_BAR_SHOW
489 || sceneId == PerfConstants::APP_TRANSITION_TO_OTHER_APP
490 || sceneId == PerfConstants::APP_TRANSITION_FROM_OTHER_APP) {
491 return true;
492 }
493 if (sceneId == PerfConstants::PC_APP_CENTER_GESTURE_OPERATION ||
494 sceneId == PerfConstants::PC_GESTURE_TO_RECENT ||
495 sceneId == PerfConstants::PC_SHORTCUT_SHOW_DESKTOP ||
496 sceneId == PerfConstants::PC_SHORTCUT_RESTORE_DESKTOP ||
497 sceneId == PerfConstants::PC_SHOW_DESKTOP_GESTURE_OPERATION ||
498 sceneId == PerfConstants::PC_ALT_TAB_TO_RECENT ||
499 sceneId == PerfConstants::PC_SHORTCUT_TO_RECENT ||
500 sceneId == PerfConstants::PC_EXIT_RECENT ||
501 sceneId == PerfConstants::PC_SHORTCUT_TO_APP_CENTER ||
502 sceneId == PerfConstants::PC_SHORTCUT_TO_APP_CENTER_ON_RECENT ||
503 sceneId == PerfConstants::PC_SHORTCUT_EXIT_APP_CENTER) {
504 return true;
505 }
506 return false;
507 }
508
509 // for jank frame app
IsExclusionFrame()510 bool PerfMonitor::IsExclusionFrame()
511 {
512 return isResponseExclusion || isStartAppFrame || isBackgroundApp;
513 }
514
SetAppStartStatus()515 void PerfMonitor::SetAppStartStatus()
516 {
517 isStartAppFrame = true;
518 startAppTime = GetCurrentRealTimeNs();
519 }
520
CheckInStartAppStatus()521 void PerfMonitor::CheckInStartAppStatus()
522 {
523 if (isStartAppFrame) {
524 int64_t curTime = GetCurrentRealTimeNs();
525 if (curTime - startAppTime >= STARTAPP_FRAME_TIMEOUT) {
526 isStartAppFrame = false;
527 startAppTime = curTime;
528 }
529 }
530 }
531
SetAppForeground(bool isShow)532 void PerfMonitor::SetAppForeground(bool isShow)
533 {
534 isBackgroundApp = !isShow;
535 }
536
CheckExclusionWindow(const std::string & windowName)537 void PerfMonitor::CheckExclusionWindow(const std::string& windowName)
538 {
539 isExclusionWindow = false;
540 if (windowName == "softKeyboard1" ||
541 windowName == "SCBWallpaper1" ||
542 windowName == "SCBStatusBar15") {
543 isExclusionWindow = true;
544 }
545 }
546
CheckResponseStatus()547 void PerfMonitor::CheckResponseStatus()
548 {
549 if (isResponseExclusion) {
550 isResponseExclusion = false;
551 }
552 }
553
ProcessJank(double jank,const std::string & windowName)554 void PerfMonitor::ProcessJank(double jank, const std::string& windowName)
555 {
556 // single frame behavior report
557 CheckExclusionWindow(windowName);
558 ReportJankFrame(jank, windowName);
559 CheckInStartAppStatus();
560 CheckResponseStatus();
561 }
562
ReportJankFrame(double jank,const std::string & windowName)563 void PerfMonitor::ReportJankFrame(double jank, const std::string& windowName)
564 {
565 if (jank >= static_cast<double>(DEFAULT_JANK_REPORT_THRESHOLD) && !IsExclusionFrame()) {
566 JankInfo jankInfo;
567 jankInfo.skippedFrameTime = static_cast<int64_t>(jank * SINGLE_FRAME_TIME);
568 jankInfo.windowName = windowName;
569 RecordBaseInfo(nullptr);
570 jankInfo.baseInfo = baseInfo;
571 EventReport::ReportJankFrameFiltered(jankInfo);
572 }
573 }
574 } // namespace OHOS::Ace
575