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 }