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
34namespace OHOS {
35namespace SmartPerf {
36std::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
78void ProfilerFPS::SetTraceCatch()
79{
80    isCatchTrace = 1;
81}
82
83void ProfilerFPS::SetPackageName(std::string pName)
84{
85    pkgName = std::move(pName);
86}
87
88void ProfilerFPS::SetProcessId(const std::string &pid)
89{
90    processId = pid;
91}
92
93void 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
136void 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
167void 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
177void 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
202void 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
243void 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
265std::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
276FpsInfoProfiler 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
312FpsInfoProfiler 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
363void 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
391void 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
415void ProfilerFPS::SetGameLayer(std::string isGameView)
416{
417    isGameLayer = std::move(isGameView);
418}
419
420FpsInfoProfiler ProfilerFPS::selfRerderLayers(const std::string &gameLayer)
421{
422    OHOS::SmartPerf::SPUtils::GetCurrentTime(fifty, lastFpsInfoResult.curTime);
423    fpsInfoMax = GetSurfaceFrame(gameLayer);
424    return fpsInfoMax;
425}
426
427std::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}
466void ProfilerFPS::SetRkFlag()
467{
468    rkFlag = true;
469}
470}
471}