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