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 }