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}