1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 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 16#define HILOG_TAG "Stat" 17 18#include "subcommand_stat.h" 19 20#include <csignal> 21#include <iostream> 22#include <memory> 23 24#include "debug_logger.h" 25#include "hiperf_hilog.h" 26#include "utilities.h" 27 28const uint16_t ONE_HUNDRED = 100; 29const uint16_t THOUSNADS_SEPARATOR = 3; 30namespace OHOS { 31namespace Developtools { 32namespace HiPerf { 33static std::map<pid_t, ThreadInfos> thread_map_; 34static bool g_reportCpuFlag = false; 35static bool g_reportThreadFlag = false; 36static VirtualRuntime g_runtimeInstance; 37void SubCommandStat::DumpOptions() const 38{ 39 printf("DumpOptions:\n"); 40 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false"); 41 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str()); 42 printf(" timeStopSec:\t%f sec\n", timeStopSec_); 43 printf(" timeReportMs:\t%d ms\n", timeReportMs_); 44 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str()); 45 printf(" selectGroups:\t%s\n", VectorToString(selectGroups_).c_str()); 46 printf(" noCreateNew:\t%s\n", noCreateNew_ ? "true" : "false"); 47 printf(" appPackage:\t%s\n", appPackage_.c_str()); 48 printf(" checkAppMs_:\t%d\n", checkAppMs_); 49 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str()); 50 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str()); 51 printf(" restart:\t%s\n", restart_ ? "true" : "false"); 52 printf(" perCore:\t%s\n", perCpus_ ? "true" : "false"); 53 printf(" perTread:\t%s\n", perThreads_ ? "true" : "false"); 54 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false"); 55} 56 57bool SubCommandStat::ParseOption(std::vector<std::string> &args) 58{ 59 if (args.size() == 1 and args[0] == "-h") { 60 args.clear(); 61 helpOption_ = true; 62 PrintUsage(); 63 return true; 64 } 65 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) { 66 HLOGD("get option -a failed"); 67 return false; 68 } 69 if (targetSystemWide_ && !IsSupportNonDebuggableApp()) { 70 HLOGD("-a option needs root privilege for system wide profiling."); 71 printf("-a option needs root privilege for system wide profiling.\n"); 72 return false; 73 } 74 if (!Option::GetOptionValue(args, "-c", selectCpus_)) { 75 HLOGD("get option -c failed"); 76 return false; 77 } 78 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) { 79 HLOGD("get option -d failed"); 80 return false; 81 } 82 if (!Option::GetOptionValue(args, "-i", timeReportMs_)) { 83 HLOGD("get option -i failed"); 84 return false; 85 } 86 if (!Option::GetOptionValue(args, "-e", selectEvents_)) { 87 HLOGD("get option -e failed"); 88 return false; 89 } 90 if (!Option::GetOptionValue(args, "-g", selectGroups_)) { 91 HLOGD("get option -g failed"); 92 return false; 93 } 94 if (!Option::GetOptionValue(args, "--no-inherit", noCreateNew_)) { 95 HLOGD("get option --no-inherit failed"); 96 return false; 97 } 98 if (!Option::GetOptionValue(args, "--app", appPackage_)) { 99 HLOGD("get option --app failed"); 100 return false; 101 } 102 if (!IsExistDebugByApp(appPackage_)) { 103 return false; 104 } 105 if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) { 106 return false; 107 } 108 if (!Option::GetOptionValue(args, "-p", selectPids_)) { 109 HLOGD("get option -p failed"); 110 return false; 111 } 112 if (!IsExistDebugByPid(selectPids_)) { 113 return false; 114 } 115 if (!Option::GetOptionValue(args, "-t", selectTids_)) { 116 HLOGD("get option -t failed"); 117 return false; 118 } 119 if (!Option::GetOptionValue(args, "--restart", restart_)) { 120 HLOGD("get option --restart failed"); 121 return false; 122 } 123 if (!Option::GetOptionValue(args, "--per-core", perCpus_)) { 124 HLOGD("get option --per-core failed"); 125 return false; 126 } 127 if (!Option::GetOptionValue(args, "--per-thread", perThreads_)) { 128 HLOGD("get option --per-thread failed"); 129 return false; 130 } 131 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) { 132 HLOGD("get option --verbose failed"); 133 return false; 134 } 135 return ParseSpecialOption(args); 136} 137 138bool SubCommandStat::ParseSpecialOption(std::vector<std::string> &args) 139{ 140 if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) { 141 HLOGD("get cmd failed"); 142 return false; 143 } 144 if (!args.empty()) { 145 HLOGD("redundant option(s)"); 146 return false; 147 } 148 return true; 149} 150 151void SubCommandStat::PrintUsage() 152{ 153 printf("%s\n", Help().c_str()); 154} 155 156void SubCommandStat::SetReportFlags(bool cpuFlag, bool threadFlag) 157{ 158 g_reportCpuFlag = cpuFlag; 159 g_reportThreadFlag = threadFlag; 160} 161 162void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents) 163{ 164 bool isNeedPerCpuTid = false; 165 for (auto it = countEvents.begin(); it != countEvents.end(); ++it) { 166 if (!(it->second->summaries.empty())) { 167 isNeedPerCpuTid = true; 168 break; 169 } 170 } 171 if (isNeedPerCpuTid) { 172 PrintPerHead(); 173 ReportDetailInfos(countEvents); 174 } else { 175 ReportNormal(countEvents); 176 } 177} 178 179void SubCommandStat::PrintPerHead() 180{ 181 // print head 182 if (g_reportCpuFlag && g_reportThreadFlag) { 183 printf(" %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", 184 "pid", "tid", "coreid", "comment", "coverage"); 185 return; 186 } 187 if (g_reportCpuFlag) { 188 printf(" %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage"); 189 return; 190 } 191 printf(" %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid", 192 "comment", "coverage"); 193 return; 194} 195 196void SubCommandStat::PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio, 197 std::string &configName) 198{ 199 if (reportSum == nullptr) { 200 return; 201 } 202 // print value 203 std::string strEventCount = std::to_string(reportSum->eventCountSum); 204 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) { 205 if (j == THOUSNADS_SEPARATOR) { 206 j = 0; 207 strEventCount.insert(strEventCount.begin() + i, ','); 208 } 209 } 210 211 std::string commentStr; 212 MakeComments(reportSum, commentStr); 213 214 if (g_reportCpuFlag && g_reportThreadFlag) { 215 printf(" %24s %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), 216 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu, commentStr.c_str(), 217 reportSum->scaleSum * ratio); 218 } else if (g_reportCpuFlag) { 219 printf(" %24s %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), 220 reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio); 221 } else { 222 printf(" %24s %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), 223 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(), 224 reportSum->scaleSum * ratio); 225 } 226 fflush(stdout); 227} 228 229void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap, 230 const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance) 231{ 232 CHECK_TRUE(newPerMap == nullptr, NO_RETVAL, 0, ""); 233 newPerMap->cpu = summary.cpu; 234 if (g_reportCpuFlag && !g_reportThreadFlag) { 235 return; 236 } 237 newPerMap->tid = summary.tid; 238 newPerMap->pid = thread_map_.find(summary.tid)->second.pid; 239 bool isTid = true; 240 if (newPerMap->pid == newPerMap->tid) { 241 isTid = false; 242 } 243 newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid); 244} 245 246void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary) 247{ 248 perKey = ""; 249 if (g_reportCpuFlag) { 250 perKey += std::to_string(summary.cpu); 251 perKey += "|"; 252 } 253 if (g_reportThreadFlag) { 254 perKey += std::to_string(summary.tid); 255 } 256 return; 257} 258 259void SubCommandStat::ReportDetailInfos( 260 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents) 261{ 262 std::string perKey = ""; 263 std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps; 264 for (auto event = countEvents.begin(); event != countEvents.end(); ++event) { 265 if (event->second == nullptr || event->second->eventCount == 0) { 266 continue; 267 } 268 constexpr float ratio {100.0}; 269 std::string configName = event->first; 270 perMaps.clear(); 271 for (auto &it : event->second->summaries) { 272 GetPerKey(perKey, it); 273 if (perMaps.count(perKey) == 0) { 274 auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {}); 275 InitPerMap(perMap, it, g_runtimeInstance); 276 perMaps[perKey] = std::move(perMap); 277 } 278 if (perMaps[perKey] == nullptr) { 279 continue; 280 } 281 perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum, 282 it, configName); 283 perMaps[perKey]->eventCountSum += it.eventCount; 284 if (it.timeRunning < it.timeEnabled && it.timeRunning != 0) { 285 perMaps[perKey]->scaleSum = 1 / (static_cast<double>(it.timeEnabled) / it.timeRunning); 286 } 287 } 288 for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) { 289 PrintPerValue(iper->second, ratio, configName); 290 } 291 } 292} 293 294void SubCommandStat::ReportNormal( 295 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents) 296{ 297 // print head 298 printf(" %24s %-30s | %-32s | %s\n", "count", "name", "comment", "coverage"); 299 std::map<std::string, std::string> comments; 300 GetComments(countEvents, comments); 301 for (auto it = countEvents.begin(); it != countEvents.end(); ++it) { 302 double scale = 1.0; 303 constexpr float ratio {100.0}; 304 std::string configName = it->first; 305 std::string comment = comments[configName]; 306 std::string strEventCount = std::to_string(it->second->eventCount); 307 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) { 308 if (j == THOUSNADS_SEPARATOR) { 309 strEventCount.insert(strEventCount.begin() + i, ','); 310 j = 0; 311 } 312 } 313 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) { 314 scale = 1 / (static_cast<double>(it->second->timeEnabled) / it->second->timeRunning); 315 } 316 printf(" %24s %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(), 317 comment.c_str(), scale * ratio); 318 319 fflush(stdout); 320 } 321} 322 323bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, 324 const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale) 325{ 326 auto itr = countEvents.find(configName); 327 if (itr != countEvents.end()) { 328 eventCount = itr->second->eventCount; 329 if (itr->second->id == group_id 330 && itr->second->timeRunning < itr->second->timeEnabled 331 && itr->second->timeRunning != 0) { 332 scale = static_cast<double>(itr->second->timeEnabled) / itr->second->timeRunning; 333 return true; 334 } 335 } 336 return false; 337} 338 339bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale) 340{ 341 eventCount = summary.eventCount; 342 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) { 343 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning; 344 return true; 345 } 346 return false; 347} 348 349std::string SubCommandStat::GetCommentConfigName( 350 const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName) 351{ 352 std::string commentConfigName = ""; 353 CHECK_TRUE(countEvent == nullptr || eventName.length() == 0, commentConfigName, 0, ""); 354 if (countEvent->userOnly) { 355 commentConfigName = eventName + ":u"; 356 } else if (countEvent->kernelOnly) { 357 commentConfigName = eventName + ":k"; 358 } else { 359 commentConfigName = eventName; 360 } 361 return commentConfigName; 362} 363 364void SubCommandStat::MakeComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr) 365{ 366 CHECK_TRUE(reportSum == nullptr || reportSum->commentSum == 0, NO_RETVAL, 0, ""); 367 if (reportSum->configName == "sw-task-clock") { 368 commentStr = StringPrintf("%lf cpus used", reportSum->commentSum); 369 return; 370 } 371 if (reportSum->configName == "hw-cpu-cycles") { 372 commentStr = StringPrintf("%lf GHz", reportSum->commentSum); 373 return; 374 } 375 if (reportSum->configName == "hw-instructions") { 376 commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum); 377 return; 378 } 379 if (reportSum->configName == "hw-branch-misses") { 380 commentStr = StringPrintf("%lf miss rate", reportSum->commentSum); 381 return; 382 } 383 384 if (reportSum->commentSum > 1e9) { 385 commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9); 386 return; 387 } 388 if (reportSum->commentSum > 1e6) { 389 commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6); 390 return; 391 } 392 if (reportSum->commentSum > 1e3) { 393 commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3); 394 return; 395 } 396 commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum); 397} 398 399std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent, 400 double &comment, PerfEvents::Summary &summary, std::string &configName) 401{ 402 double running_time_in_sec = 0; 403 double main_scale = 1.0; 404 bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale); 405 if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) { 406 comment = 0; 407 return "sw-cpu-clock"; 408 } 409 double scale = 1.0; 410 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) { 411 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning; 412 } 413 if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) { 414 comment += countEvent->usedCpus * scale; 415 return "sw-task-clock"; 416 } 417 if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) { 418 if (findRunningTime) { 419 double hz = 0; 420 if (abs(running_time_in_sec) > ALMOST_ZERO) { 421 hz = summary.eventCount / (running_time_in_sec / scale); 422 } 423 comment += hz / 1e9; 424 } else { 425 comment += 0; 426 } 427 return "hw-cpu-cycles"; 428 } 429 if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) { 430 double otherScale = 1.0; 431 __u64 cpuCyclesCount = 0; 432 bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale); 433 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) { 434 comment += static_cast<double>(cpuCyclesCount) / summary.eventCount; 435 return "hw-instructions"; 436 } 437 } 438 if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) { 439 double otherScale = 1.0; 440 __u64 branchInstructionsCount = 0; 441 bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale); 442 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) && 443 branchInstructionsCount != 0) { 444 comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED; 445 return "hw-branch-misses"; 446 } 447 } 448 return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime); 449} 450 451std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec, 452 double scale, bool findRunningTime) 453{ 454 comment = 0; 455 if (findRunningTime) { 456 double rate = 0; 457 if (scale != 0) { 458 rate = summary.eventCount / (running_time_in_sec / scale); 459 } 460 comment += rate; 461 } 462 return ""; 463} 464 465bool SubCommandStat::IsMonitoredAtAllTime(const double &scale) 466{ 467 constexpr double SCALE_ERROR_LIMIT = 1e-5; 468 return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT); 469} 470 471void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, 472 std::map<std::string, std::string> &comments) 473{ 474 double running_time_in_sec = 0; 475 __u64 group_id = 0; 476 double main_scale = 1.0; 477 bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale); 478 for (auto it = countEvents.begin(); it != countEvents.end(); it++) { 479 std::string configName = it->first; 480 std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock"); 481 if (configName == commentConfigName) { 482 comments[configName] = ""; 483 continue; 484 } 485 double scale = 1.0; 486 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) { 487 scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning; 488 } 489 commentConfigName = GetCommentConfigName(it->second, "sw-task-clock"); 490 if (configName == commentConfigName) { 491 double usedCpus = it->second->usedCpus * scale; 492 comments[configName] = StringPrintf("%lf cpus used", usedCpus); 493 continue; 494 } 495 commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles"); 496 if (configName == commentConfigName) { 497 if (findRunningTime && 498 ((group_id == it->second->id) || 499 (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) { 500 double hz = 0; 501 if (abs(running_time_in_sec) > ALMOST_ZERO) { 502 hz = it->second->eventCount / (running_time_in_sec / scale); 503 } 504 comments[configName] = StringPrintf("%lf GHz", hz / 1e9); 505 } else { 506 comments[configName] = ""; 507 } 508 continue; 509 } 510 commentConfigName = GetCommentConfigName(it->second, "hw-instructions"); 511 if (configName == commentConfigName && it->second->eventCount != 0) { 512 std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles"); 513 double otherScale = 1.0; 514 __u64 cpuCyclesCount = 0; 515 bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount, 516 otherScale); 517 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) { 518 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount; 519 comments[configName] = StringPrintf("%lf cycles per instruction", cpi); 520 continue; 521 } 522 } 523 commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses"); 524 if (configName == commentConfigName) { 525 std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions"); 526 double otherScale = 1.0; 527 __u64 branchInstructionsCount = 0; 528 bool other = FindEventCount(countEvents, branchInsName, it->second->id, 529 branchInstructionsCount, otherScale); 530 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) && 531 branchInstructionsCount != 0) { 532 double miss_rate = 533 static_cast<double>(it->second->eventCount) / branchInstructionsCount; 534 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED); 535 continue; 536 } 537 } 538 if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) && 539 IsMonitoredAtAllTime(scale)))) { 540 double rate = it->second->eventCount / (running_time_in_sec / scale); 541 if (rate > 1e9) { 542 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9); 543 continue; 544 } 545 if (rate > 1e6) { 546 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6); 547 continue; 548 } 549 if (rate > 1e3) { 550 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3); 551 continue; 552 } 553 comments[configName] = StringPrintf("%.3lf /sec", rate); 554 } else { 555 comments[configName] = ""; 556 } 557 } 558} 559 560bool SubCommandStat::FindRunningTime( 561 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, 562 double &running_time_in_sec, __u64 &group_id, double &main_scale) 563{ 564 for (auto it = countEvents.begin(); it != countEvents.end(); it++) { 565 if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" || 566 it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" || 567 it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") && 568 it->second->eventCount != 0u) { 569 group_id = it->second->id; 570 running_time_in_sec = it->second->eventCount / 1e9; 571 if (it->second->timeRunning < it->second->timeEnabled && 572 it->second->timeRunning != 0) { 573 main_scale = 574 static_cast<double>(it->second->timeEnabled) / it->second->timeRunning; 575 } 576 return true; 577 } 578 } 579 return false; 580} 581 582bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec, 583 double &main_scale) 584{ 585 CHECK_TRUE(summary.eventCount == 0, false, 0, ""); 586 running_time_int_sec = summary.eventCount / 1e9; 587 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) { 588 main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning; 589 } 590 return true; 591} 592 593bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids) 594{ 595 if (!CheckOptionPid(pids)) { 596 printf("Problems finding threads of monitor\n\n"); 597 printf("Usage: perf stat [<options>] [<command>]\n\n"); 598 printf("-p <pid> stat events on existing process id\n"); 599 printf("-t <tid> stat events on existing thread id\n"); 600 return false; 601 } 602 return true; 603} 604 605bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids) 606{ 607 if (pids.empty()) { 608 return true; 609 } 610 611 for (auto pid : pids) { 612 if (!IsDir("/proc/" + std::to_string(pid))) { 613 printf("not exit pid %d\n", pid); 614 return false; 615 } 616 } 617 return true; 618} 619 620void SubCommandStat::SetPerfEvent() 621{ 622 SetReportFlags(perCpus_, perThreads_); 623 perfEvents_.SetSystemTarget(targetSystemWide_); 624 perfEvents_.SetTimeOut(timeStopSec_); 625 perfEvents_.SetTimeReport(timeReportMs_); 626 perfEvents_.SetPerCpu(perCpus_); 627 perfEvents_.SetPerThread(perThreads_); 628 perfEvents_.SetVerboseReport(verboseReport_); 629 perfEvents_.SetInherit(!noCreateNew_); 630 perfEvents_.SetTrackedCommand(trackedCommand_); 631 // set report handle 632 perfEvents_.SetStatCallBack(Report); 633} 634 635bool SubCommandStat::OnSubCommand(std::vector<std::string> &args) 636{ 637 CHECK_TRUE(HelpOption(), true, 0, ""); 638 if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) { 639 return false; 640 } 641 // check option 642 if (!CheckSelectCpuPidOption()) { 643 return false; 644 } 645 if (!CheckOptions(selectPids_)) { 646 HLOGV("CheckOptions() failed"); 647 return false; 648 } 649 if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) { 650 HLOGV("CheckAppIsRunning() failed"); 651 return false; 652 } 653 654 perfEvents_.SetCpu(selectCpus_); 655 std::vector<pid_t> pids; 656 for (auto selectPid : selectPids_) { 657 HLOGD("[OnSubCommand] selectPid %d\n", selectPid); 658 std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_); 659 if (!subTids.empty()) { 660 pids.insert(pids.end(), subTids.begin(), subTids.end()); 661 } else { 662 HLOGD("[OnSubCommand] subTids empty for %d\n", selectPid); 663 } 664 } 665 pids.insert(pids.end(), selectTids_.begin(), selectTids_.end()); 666 perfEvents_.SetPid(pids); 667 if (!CheckOptionPidAndApp(pids)) { 668 HLOGV("CheckOptionPidAndApp() failed"); 669 return false; 670 } 671 SetPerfEvent(); 672 if (!PrepairEvents()) { 673 HLOGV("PrepairEvents() failed"); 674 return false; 675 } 676 677 // preapare fd 678 perfEvents_.PrepareTracking(); 679 680 // start tracking 681 perfEvents_.StartTracking(); 682 683 return true; 684} 685 686bool RegisterSubCommandStat() 687{ 688 return SubCommand::RegisterSubCommand("stat", std::make_unique<SubCommandStat>()); 689} 690 691bool SubCommandStat::PrepairEvents() 692{ 693 if (selectEvents_.empty() && selectGroups_.empty()) { 694 perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE); 695 perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE); 696 } else { 697 for (auto events : selectEvents_) { 698 if (!perfEvents_.AddEvents(events)) { 699 HLOGV("add events failed"); 700 return false; 701 } 702 } 703 for (auto events : selectGroups_) { 704 if (!perfEvents_.AddEvents(events, true)) { 705 HLOGV("add groups failed"); 706 return false; 707 } 708 } 709 } 710 return true; 711} 712 713bool SubCommandStat::CheckSelectCpuPidOption() 714{ 715 if (!selectCpus_.empty()) { 716 // the only value is not -1 717 if (!(selectCpus_.size() == 1 && selectCpus_.front() == -1)) { 718 int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1; 719 for (auto cpu : selectCpus_) { 720 if (cpu < 0 || cpu > maxCpuid) { 721 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid); 722 return false; 723 } 724 } 725 } 726 } else { 727 // the cpu default -1 728 if (!targetSystemWide_) { 729 selectCpus_.push_back(-1); 730 } 731 } 732 733 if (!selectPids_.empty()) { 734 for (auto pid : selectPids_) { 735 if (pid <= 0) { 736 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid); 737 return false; 738 } 739 } 740 } 741 if (!selectTids_.empty()) { 742 for (auto tid : selectTids_) { 743 if (tid <= 0) { 744 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid); 745 return false; 746 } 747 } 748 } 749 return true; 750} 751 752bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids) 753{ 754 if (targetSystemWide_) { 755 if (!pids.empty() || !selectTids_.empty()) { 756 printf("You cannot specify -a and -t/-p at the same time\n"); 757 return false; 758 } 759 if (!appPackage_.empty()) { 760 printf("You cannot specify -a and --app at the same time\n"); 761 return false; 762 } 763 } 764 if (!appPackage_.empty() && (!pids.empty() || !selectTids_.empty())) { 765 printf("You cannot specify --app and -t/-p at the same time\n"); 766 return false; 767 } 768 if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty() 769 && selectTids_.empty()) { 770 printf("You need to set the -p option or --app option.\n"); 771 return false; 772 } 773 if (targetSystemWide_ && !trackedCommand_.empty()) { 774 printf("You cannot specify -a and a cmd at the same time\n"); 775 return false; 776 } 777 if (!trackedCommand_.empty()) { 778 if (!pids.empty() || !selectTids_.empty()) { 779 printf("You cannot specify a cmd and -t/-p at the same time\n"); 780 return false; 781 } 782 if (!appPackage_.empty()) { 783 printf("You cannot specify a cmd and --app at the same time\n"); 784 return false; 785 } 786 if (!IsRoot()) { 787 printf("%s options needs root privilege, please check usage\n", 788 VectorToString(trackedCommand_).c_str()); 789 return false; 790 } 791 } 792 if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) { 793 printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_, 794 MIN_CHECK_APP_MS, MAX_CHECK_APP_MS); 795 return false; 796 } 797 if (timeStopSec_ < 0) { 798 printf("monitoring duration should be positive but %f is given\n", timeStopSec_); 799 return false; 800 } 801 if (timeReportMs_ < 0) { 802 printf("print interval should be non-negative but %d is given\n", timeReportMs_); 803 return false; 804 } 805 return true; 806} 807} // namespace HiPerf 808} // namespace Developtools 809} // namespace OHOS 810