1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 <cstdio>
16 #include <algorithm>
17 #include <iostream>
18 #include <sstream>
19 #include <queue>
20 #include <vector>
21 #include <map>
22 #include <string>
23 #include <ctime>
24 #include <thread>
25 #include <unistd.h>
26 #include <sys/time.h>
27 #include "include/profiler_fps.h"
28 #include "include/sp_log.h"
29 #include "include/sp_utils.h"
30 #include "include/ByTrace.h"
31 #include "include/startup_delay.h"
32 #include "include/common.h"
33
34 namespace OHOS {
35 namespace SmartPerf {
ItemData()36 std::map<std::string, std::string> ProfilerFPS::ItemData()
37 {
38 std::map<std::string, std::string> result;
39 fpsInfo.currDumpTimeStamps.clear();
40 FpsInfoProfiler finalResult = GetFpsInfo();
41 lastFpsInfoResult = finalResult;
42 if (processFlag) {
43 result["fps"] = "NA";
44 result["fpsJitters"] = "NA";
45 } else {
46 const int fullFrame = 120;
47 const int maxFullFrame = 123;
48 if (finalResult.fps > fullFrame && finalResult.fps < maxFullFrame) {
49 finalResult.fps = fullFrame;
50 }
51 result["fps"] = std::to_string(finalResult.fps);
52 LOGD("ProfilerFPS.result.fps: %s", std::to_string(finalResult.fps).c_str());
53 std::string jitterStr = "";
54 std::string split = "";
55 for (size_t i = 0; i < finalResult.jitters.size(); i++) {
56 if (i > 0) {
57 split = ";;";
58 }
59 jitterStr += split + std::to_string(finalResult.jitters[i]);
60 }
61 result["fpsJitters"] = jitterStr;
62 LOGD("ProfilerFPS.result.jitters: %s", jitterStr.c_str());
63 if (isCatchTrace > 0) {
64 ByTrace::GetInstance().CheckFpsJitters(finalResult.jitters, finalResult.fps);
65 }
66 }
67 return result;
68 }
69
SetTraceCatch()70 void ProfilerFPS::SetTraceCatch()
71 {
72 isCatchTrace = 1;
73 }
74
SetPackageName(std::string pName)75 void ProfilerFPS::SetPackageName(std::string pName)
76 {
77 pkgName = std::move(pName);
78 }
79
SetProcessId(const std::string & pid)80 void ProfilerFPS::SetProcessId(const std::string &pid)
81 {
82 processId = pid;
83 }
84
GetResultFPS(int sectionsNum)85 void ProfilerFPS::GetResultFPS(int sectionsNum)
86 {
87 struct timeval start;
88 struct timeval end;
89 gettimeofday(&start, nullptr);
90 FpsInfoProfiler fpsInfoResult;
91 unsigned long runTime;
92 fpsInfoResult = GetFpsInfo();
93 if (fpsInfoResult.fps == 0) {
94 if (lastCurrTime == 0) {
95 long long currTime = (fpsInfoResult.currTimeDump / msClear) * msClear + fpsInfoResult.currTimeDiff;
96 lastCurrTime = currTime / oneSec;
97 printf("fps:%d|%lld\n", fpsInfoResult.fps, currTime / oneSec);
98 } else {
99 printf("fps:%d|%lld\n", fpsInfoResult.fps, lastCurrTime + oneThousand);
100 lastCurrTime = lastCurrTime + oneThousand;
101 }
102 } else {
103 long long currTime = (fpsInfoResult.currTimeStamps[0] / msClear) * msClear + fpsInfoResult.currTimeDiff;
104 lastCurrTime = currTime / oneSec;
105 printf("fps:%d|%lld\n", fpsInfoResult.fps, lastCurrTime);
106 }
107 lastFpsInfoResult = fpsInfoResult;
108 if (sectionsNum != 0 && fpsInfoResult.fps != 0) {
109 GetSectionsFps(fpsInfoResult, sectionsNum);
110 }
111 time_t now = time(nullptr);
112 if (now == -1) {
113 LOGE("Failed to get current time.");
114 return;
115 }
116 char *dt = ctime(&now);
117 LOGD("printf time is: %s", dt);
118 fflush(stdout);
119 gettimeofday(&end, nullptr);
120 runTime = end.tv_sec * 1e6 - start.tv_sec * 1e6 + end.tv_usec - start.tv_usec;
121 LOGD("printf time is runTime: %s", std::to_string(runTime).c_str());
122 if (runTime < sleepTime) {
123 usleep(sleepTime - runTime);
124 }
125 OHOS::SmartPerf::SPUtils::GetCurrentTime(ten, lastFpsInfoResult.curTime);
126 }
127
GetTimeDiff()128 void ProfilerFPS::GetTimeDiff()
129 {
130 long long clockRealTime = 0;
131 long long clockMonotonicRaw = 0;
132 const int two = 2;
133 std::string strRealTime;
134 const std::string cmd = CMD_COMMAND_MAP.at(CmdCommand::TIMESTAMPS);
135 FILE *fd = popen(cmd.c_str(), "r");
136 if (fd == nullptr) {
137 return;
138 }
139 char buf[1024] = {'\0'};
140 while ((fgets(buf, sizeof(buf), fd)) != nullptr) {
141 std::string line(buf);
142 std::vector<std::string> params;
143 SPUtils::StrSplit(line, " ", params);
144 if (params[0].find("CLOCK_REALTIME") != std::string::npos && clockRealTime == 0) {
145 strRealTime = params[two];
146 strRealTime.erase(strRealTime.find('.'), 1);
147 clockRealTime = std::stoll(strRealTime);
148 currRealTime = clockRealTime;
149 } else if (params[0].find("CLOCK_MONOTONIC_RAW") != std::string::npos && clockMonotonicRaw == 0) {
150 strRealTime = params[two];
151 strRealTime.erase(strRealTime.find('.'), 1);
152 clockMonotonicRaw = std::stoll(strRealTime);
153 }
154 }
155 if (pclose(fd) == -1) {
156 LOGE("Error: Failed to close file");
157 return;
158 }
159 fpsInfo.currTimeDiff = clockRealTime - clockMonotonicRaw;
160 }
161
GetSectionsPrint(int printCount,long long msStartTime,int numb,long long harTime) const162 void ProfilerFPS::GetSectionsPrint(int printCount, long long msStartTime, int numb, long long harTime) const
163 {
164 if (printCount < numb) {
165 for (int i = 0; i < numb - printCount; i++) {
166 msStartTime += harTime;
167 printf("sectionsFps:%d|%lld\n", 0, msStartTime);
168 }
169 }
170 }
171
PrintSections(int msCount,long long currTimeLast,long long currTimeStart,long long currLastTime) const172 void ProfilerFPS::PrintSections(int msCount, long long currTimeLast,
173 long long currTimeStart, long long currLastTime) const
174 {
175 int conversionFps = 1000000;
176 int conversionTime = 1000;
177 long long times = 120;
178 int fpsNums = 0;
179 if (msCount == 0) {
180 fpsNums = 0;
181 } else {
182 fpsNums = msCount - 1;
183 }
184 double timeN = (currTimeLast - currTimeStart) * 1.0 / conversionTime;
185 if (timeN == 0) {
186 printf("sectionsFps:%d|%lld\n", 0, currLastTime);
187 return;
188 }
189 double fpsSections = (fpsNums * conversionFps) / timeN;
190 int fpsSectionsInt = round(fpsSections);
191 if (fpsSectionsInt > static_cast<int>(times)) {
192 fpsSectionsInt = static_cast<int>(times);
193 }
194 printf("sectionsFps:%d|%lld\n", fpsSectionsInt, currLastTime);
195 }
196
GetSectionsFps(FpsInfoProfiler & fpsInfoResult,int nums) const197 void ProfilerFPS::GetSectionsFps(FpsInfoProfiler &fpsInfoResult, int nums) const
198 {
199 int msCount = 0;
200 long long msJiange = 0;
201 if (nums != 0) {
202 msJiange = msClear / nums;
203 }
204 long long msStartTime = (fpsInfoResult.currTimeStamps[0] / msClear) * msClear + msJiange;
205 long long currLastTime = lastCurrTime;
206 long long harTime = msJiange / 1000000;
207 int printCount = 0;
208 long long currTimeStart = 0;
209 long long currTimeLast = 0;
210 for (size_t i = 0; i < fpsInfoResult.currTimeStamps.size(); i++) {
211 long long currTime = fpsInfoResult.currTimeStamps[i];
212 if (currTime <= msStartTime) {
213 if (msCount == 0) {
214 currTimeStart = currTime;
215 }
216 currTimeLast = currTime;
217 msCount++;
218 } else {
219 while (currTime > msStartTime) {
220 PrintSections(msCount, currTimeLast, currTimeStart, currLastTime);
221 printCount++;
222 msCount = 1;
223 msStartTime += msJiange;
224 currLastTime += harTime;
225 currTimeLast = currTime;
226 currTimeStart = currTime;
227 }
228 }
229 if (i == (static_cast<size_t>(fpsInfoResult.currTimeStamps.size()) - 1)) {
230 PrintSections(msCount, currTimeLast, currTimeStart, currLastTime);
231 currTimeLast = currTime;
232 printCount++;
233 GetSectionsPrint(printCount, currLastTime, nums, harTime);
234 }
235 }
236 }
237
GetFPS(std::vector<std::string> v)238 void ProfilerFPS::GetFPS(std::vector<std::string> v)
239 {
240 if (v[number] == "") {
241 printf("the args of num must be not-null!\n");
242 } else {
243 this->num = SPUtilesTye::StringToSometype<int>(v[number].c_str());
244 if (this->num < 0) {
245 printf("set num:%d not valid arg\n", this->num);
246 }
247 printf("set num:%d success\n", this->num);
248 int sectionsNum = (static_cast<int>(v.size()) >= four) ?
249 SPUtilesTye::StringToSometype<int>(v[four].c_str()) : 0;
250 if (sectionsNum > ten) {
251 printf("set sectionsNum:%d not valid arg \n", sectionsNum);
252 } else {
253 for (int i = 0; i < this->num; i++) {
254 GetResultFPS(sectionsNum);
255 }
256 }
257 }
258 printf("SP_daemon exec finished!\n");
259 }
260
GetSurface()261 std::string ProfilerFPS::GetSurface()
262 {
263 std::string cmdResult;
264 std::string dumperSurface = HIDUMPER_CMD_MAP.at(HidumperCmd::DUMPER_SURFACE);
265 SPUtils::LoadCmd(dumperSurface, cmdResult);
266 size_t positionLeft = cmdResult.find("[");
267 size_t positionRight = cmdResult.find("]");
268 size_t positionNum = 1;
269 return cmdResult.substr(positionLeft + positionNum, positionRight - positionLeft - positionNum);
270 }
271
GetTime()272 FpsInfoProfiler ProfilerFPS::GetTime()
273 {
274 FpsInfoProfiler curTimeFps;
275 struct timespec sysTime = { 0 };
276 clock_gettime(CLOCK_MONOTONIC, &sysTime);
277 curTimeFps.curTime = static_cast<int>(sysTime.tv_sec - 1);
278 curTimeFps.currTimeDump = (sysTime.tv_sec - 1) * mod + sysTime.tv_nsec;
279 if (curTimeFps.curTime == lastFpsInfoResult.curTime) {
280 LOGD("ProfilerFPS::The system time is not updated");
281 curTimeFps.curTime = static_cast<int>(sysTime.tv_sec);
282 curTimeFps.currTimeDump = (sysTime.tv_sec) * mod + sysTime.tv_nsec;
283 }
284 LOGD("ProfilerFPS::timeFps.curTime: %d", curTimeFps.curTime);
285 return curTimeFps;
286 }
287
GetFpsInfo()288 FpsInfoProfiler ProfilerFPS::GetFpsInfo()
289 {
290 processFlag = false;
291 fpsInfoTime.fps = 0;
292 fpsInfoTime.curTime = 0;
293 FpsInfoProfiler tmpFps = GetTime();
294
295 if (isGameApp) {
296 if (firstDump) {
297 gameLayerName = GetGameLayer();
298 if (gameLayerName.empty()) {
299 firstDump = true;
300 fpsInfoTime.Clear();
301 return fpsInfoTime;
302 } else {
303 firstDump = false;
304 }
305 }
306 OHOS::SmartPerf::SPUtils::GetCurrentTime(fifty, lastFpsInfoResult.curTime);
307 fpsInfoTime = GetSurfaceFrame(gameLayerName, tmpFps);
308 if (fpsInfoTime.fps == 0) {
309 return GetChangedLayerFps(tmpFps);
310 } else {
311 return fpsInfoTime;
312 }
313 } else {
314 std::string uniteLayer;
315 if (!rkFlag) {
316 uniteLayer = "UniRender";
317 LOGD("ProfilerFPS::uniteLayer is UniRender");
318 } else {
319 uniteLayer = GetSurface();
320 }
321 if (ohFlag) {
322 uniteLayer = GetSurface();
323 }
324 if (pkgName.empty() || pkgName.find("sceneboard") != std::string::npos) {
325 LOGD("ProfilerFPS.pkgName: %s", pkgName.c_str());
326 OHOS::SmartPerf::SPUtils::GetCurrentTime(fifty, lastFpsInfoResult.curTime);
327 fpsInfoTime = GetSurfaceFrame(uniteLayer, tmpFps);
328 } else {
329 fpsInfoTime = GetAppFps(tmpFps, uniteLayer);
330 }
331 }
332 return fpsInfoTime;
333 }
334
GetChangedLayerFps(FpsInfoProfiler & timeFps)335 FpsInfoProfiler ProfilerFPS::GetChangedLayerFps(FpsInfoProfiler &timeFps)
336 {
337 gameLayerName = GetGameLayer();
338 if (gameLayerName.empty()) {
339 if (processId.empty()) {
340 processFlag = true;
341 }
342 fpsInfoTime.Clear();
343 } else {
344 fpsInfoTime = GetSurfaceFrame(gameLayerName, timeFps);
345 }
346 return fpsInfoTime;
347 }
348
GetAppFps(FpsInfoProfiler & timeFps,std::string & uniteLayer)349 FpsInfoProfiler ProfilerFPS::GetAppFps(FpsInfoProfiler &timeFps, std::string &uniteLayer)
350 {
351 bool onTop = OHOS::SmartPerf::SPUtils::IsForeGround(pkgName);
352 if (onTop) {
353 OHOS::SmartPerf::SPUtils::GetCurrentTime(fifty, lastFpsInfoResult.curTime);
354 fpsInfoTime = GetSurfaceFrame(uniteLayer, timeFps);
355 } else {
356 LOGD("ProfilerFPS::app is in the background");
357 if (processId.empty()) {
358 processFlag = true;
359 }
360 fpsInfoTime.Clear();
361 }
362 return fpsInfoTime;
363 }
364
GetSurfaceFrame(const std::string & name,FpsInfoProfiler & timeFps)365 FpsInfoProfiler ProfilerFPS::GetSurfaceFrame(const std::string& name, FpsInfoProfiler &timeFps)
366 {
367 if (name == "") {
368 return FpsInfoProfiler();
369 }
370 return GetFrameInfoFromMap(name, timeFps);
371 }
372
GetFrameInfoFromMap(const std::string & name,FpsInfoProfiler & timeFps)373 FpsInfoProfiler ProfilerFPS::GetFrameInfoFromMap(const std::string& name, FpsInfoProfiler &timeFps)
374 {
375 FpsInfoProfiler tmpFps;
376 tmpFps.fps = 0;
377 fpsInfo = tmpFps;
378 fpsInfo.fps = 0;
379 fpsInfo.jitters.clear();
380 bool isBreak = false;
381 GetTimeDiff();
382
383 std::string cmd = "hidumper -s 10 -a \"fps " + name + "\"";
384 if (cmd.empty()) {
385 LOGE("cmd is null");
386 return fpsInfo;
387 }
388 FILE *fp = popen(cmd.c_str(), "r");
389 if (fp == nullptr) {
390 LOGE("Failed to open hidumper file");
391 return fpsInfo;
392 }
393
394 fpsNum = 0;
395 prevScreenTimestamp = -1;
396
397 fpsInfo.currTimeDump = timeFps.currTimeDump;
398 fpsInfo.curTime = timeFps.curTime;
399 char tmp[1024];
400 std::stringstream sstream;
401 while (fgets(tmp, sizeof(tmp), fp) != nullptr) {
402 LOGD("ProfilerFPS::GetFrameInfoFromMap dump time: %s", tmp);
403 curScreenTimestamp = 0;
404 sstream.clear();
405 sstream.str(tmp);
406 sstream >> curScreenTimestamp;
407 if (curScreenTimestamp == 0) {
408 continue;
409 }
410 if (CalcFpsAndJitters(isBreak)) {
411 break;
412 }
413 }
414 CalcJitters();
415 if (pclose(fp) == -1) {
416 LOGE("Error: Failed to close file");
417 return fpsInfo;
418 }
419 LOGD("ProfilerFPS fpsNum: %d", fpsNum);
420 return fpsInfo;
421 }
CalcFpsAndJitters(bool isBreak)422 bool ProfilerFPS::CalcFpsAndJitters(bool isBreak)
423 {
424 long long onScreenTime = curScreenTimestamp / mod;
425 bool findFpsCurTime = (onScreenTime == fpsInfo.curTime);
426 if (findFpsCurTime) {
427 isBreak = true;
428 fpsNum++;
429 if (isLowCurFps) {
430 fpsInfo.fps = fpsNum;
431 } else {
432 fpsInfo.fps = fpsNum;
433 fpsInfo.currDumpTimeStamps.push_back(curScreenTimestamp);
434 }
435 } else {
436 findFpsCurTime = false;
437 }
438 return isBreak && !findFpsCurTime;
439 }
CalcJitters()440 void ProfilerFPS::CalcJitters()
441 {
442 bool isOrder = true;
443 if (fpsInfo.currDumpTimeStamps.size() > 1) {
444 isOrder = fpsInfo.currDumpTimeStamps[1] - fpsInfo.currDumpTimeStamps[0] > 0;
445 }
446 if (isOrder) {
447 for (size_t i = 0; i < fpsInfo.currDumpTimeStamps.size(); i++) {
448 curScreenTimestamp = fpsInfo.currDumpTimeStamps[i];
449 fpsInfo.currTimeStamps.push_back(curScreenTimestamp);
450 long long jitter = CalculateJitter();
451 fpsInfo.jitters.push_back(jitter);
452 prevlastScreenTimestamp = curScreenTimestamp;
453 prevScreenTimestamp = curScreenTimestamp;
454 }
455 } else {
456 for (size_t i = fpsInfo.currDumpTimeStamps.size(); i > 0; i--) {
457 curScreenTimestamp = fpsInfo.currDumpTimeStamps[i - 1];
458 fpsInfo.currTimeStamps.push_back(curScreenTimestamp);
459 long long jitter = CalculateJitter();
460 fpsInfo.jitters.push_back(jitter);
461 prevlastScreenTimestamp = curScreenTimestamp;
462 prevScreenTimestamp = curScreenTimestamp;
463 }
464 }
465 }
466
CalculateJitter()467 long long ProfilerFPS::CalculateJitter()
468 {
469 long long jitter;
470 if (prevScreenTimestamp == -1) {
471 if (prevlastScreenTimestamp != 0 && (curScreenTimestamp - prevlastScreenTimestamp) < mod) {
472 jitter = curScreenTimestamp - prevlastScreenTimestamp;
473 } else {
474 jitter = curScreenTimestamp % mod;
475 }
476 } else {
477 jitter = curScreenTimestamp - prevScreenTimestamp;
478 }
479 return jitter;
480 }
481
GetOhFps(std::vector<std::string> v)482 void ProfilerFPS::GetOhFps(std::vector<std::string> v)
483 {
484 if (v[number] == "") {
485 printf("the args of num must be not-null!\n");
486 } else {
487 this->num = SPUtilesTye::StringToSometype<int>(v[number].c_str());
488 if (this->num < 0) {
489 printf("set num:%d not vaild arg\n", this->num);
490 }
491 printf("set num:%d success\n", this->num);
492 ohFlag = true;
493 int sectionsNum;
494 if (static_cast<int>(v.size()) < four) {
495 sectionsNum = 0;
496 } else {
497 sectionsNum = SPUtilesTye::StringToSometype<int>(v[four].c_str());
498 }
499 for (int i = 0; i < this->num; i++) {
500 GetResultFPS(sectionsNum);
501 }
502 }
503 printf("SP_daemon exec finished!\n");
504 }
505
SetGameLayer(std::string isGameView)506 void ProfilerFPS::SetGameLayer(std::string isGameView)
507 {
508 isGameLayer = std::move(isGameView);
509 }
510
GetGameLayer()511 std::string ProfilerFPS::GetGameLayer()
512 {
513 std::string gameLayer = "";
514 if (processId.empty()) {
515 return gameLayer;
516 }
517 std::string cmdResult;
518 const std::string dumperSurface = HIDUMPER_CMD_MAP.at(HidumperCmd::DUMPER_SURFACE);
519 char buf[1024] = {'\0'};
520 std::string start = "NodeId[";
521 std::string end = "] LayerId";
522 std::string nodeIdStr;
523 uint64_t nodeId;
524 if (dumperSurface.empty()) {
525 LOGE("ProfilerFPS::DUMPER_SURFACE failed");
526 return gameLayer;
527 }
528 FILE *fd = popen(dumperSurface.c_str(), "r");
529 if (fd == nullptr) {
530 return gameLayer;
531 }
532 while (fgets(buf, sizeof(buf), fd) != nullptr) {
533 std::string line = buf;
534 size_t startPos = line.find(start);
535 size_t endPos = line.find(end);
536 if (startPos != std::string::npos && endPos != std::string::npos) {
537 nodeIdStr = line.substr(startPos + start.length(), endPos - startPos - start.length());
538 LOGD("ProfilerFPS::nodeIdStr: (%s)", nodeIdStr.c_str());
539 }
540 const int kShiftAmount = 32;
541 if (!nodeIdStr.empty()) {
542 std::stringstream ss(nodeIdStr);
543 ss >> nodeId;
544 if (ss.fail() || !ss.eof()) {
545 return gameLayer;
546 }
547 nodeId = nodeId >> kShiftAmount;
548 LOGD("ProfilerFPS::nodeId: (%d)", nodeId);
549 GetLayerName(gameLayer, nodeId, line, endPos);
550 }
551 }
552 if (pclose(fd) == -1) {
553 LOGE("Error: Failed to close file");
554 return gameLayer;
555 }
556 LOGD("ProfilerFPS::gameLayer: (%s)", gameLayer.c_str());
557 return gameLayer;
558 }
559
GetLayerName(std::string & gameLayer,uint64_t & nodeId,std::string & line,size_t & endPos)560 std::string ProfilerFPS::GetLayerName(std::string &gameLayer, uint64_t &nodeId, std::string &line, size_t &endPos)
561 {
562 if (std::to_string(nodeId) == processId) {
563 size_t layerStartPos = line.find("[");
564 size_t layerEndPos = line.find("]");
565 if (layerEndPos - layerStartPos <= 1 && layerEndPos > endPos) {
566 return gameLayer;
567 }
568 layerStartPos += 1;
569 gameLayer = line.substr(layerStartPos, layerEndPos - layerStartPos);
570 }
571 return gameLayer;
572 }
573
SetRkFlag()574 void ProfilerFPS::SetRkFlag()
575 {
576 rkFlag = true;
577 }
578 }
579 }