• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }