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