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 "Record" 17 18 #include "subcommand_record.h" 19 20 #include <chrono> 21 #include <csignal> 22 #include <cstdlib> 23 #include <ctime> 24 #include <memory> 25 #include <poll.h> 26 #include <sys/stat.h> 27 #include <sys/utsname.h> 28 #include <unistd.h> 29 30 #include "command.h" 31 #include "debug_logger.h" 32 #include "hiperf_client.h" 33 #include "option.h" 34 #include "perf_event_record.h" 35 #include "perf_file_reader.h" 36 #include "utilities.h" 37 38 using namespace std::chrono; 39 namespace OHOS { 40 namespace Developtools { 41 namespace HiPerf { 42 const std::string CONTROL_CMD_PREPARE = "prepare"; 43 const std::string CONTROL_CMD_START = "start"; 44 const std::string CONTROL_CMD_PAUSE = "pause"; 45 const std::string CONTROL_CMD_RESUME = "resume"; 46 const std::string CONTROL_CMD_STOP = "stop"; 47 const std::string CONTROL_FIFO_FILE_C2S = "/data/local/tmp/.hiperf_record_control_c2s"; 48 const std::string CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.hiperf_record_control_s2c"; 49 50 const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT = 1000ms; 51 52 constexpr uint64_t MASK_ALIGNED_8 = 7; 53 constexpr size_t MAX_DWARF_CALL_CHAIN = 2; 54 constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL | 55 PERF_SAMPLE_BRANCH_ANY_RETURN | 56 PERF_SAMPLE_BRANCH_IND_CALL; 57 GetClockId(const std::string & name)58 int GetClockId(const std::string &name) 59 { 60 static std::map<std::string, int> mapClockid = { 61 {"realtime", CLOCK_REALTIME}, {"boottime", CLOCK_BOOTTIME}, 62 {"monotonic", CLOCK_MONOTONIC}, {"monotonic_raw", CLOCK_MONOTONIC_RAW}, 63 {"clock_tai", CLOCK_TAI}, 64 }; 65 66 auto it = mapClockid.find(name); 67 if (it == mapClockid.end()) { 68 return -1; 69 } else { 70 return it->second; 71 } 72 } 73 GetBranchSampleType(const std::string & name)74 uint64_t GetBranchSampleType(const std::string &name) 75 { 76 static std::map<std::string, uint64_t> mapBranchSampleType = { 77 {"u", PERF_SAMPLE_BRANCH_USER}, 78 {"k", PERF_SAMPLE_BRANCH_KERNEL}, 79 {"any", PERF_SAMPLE_BRANCH_ANY}, 80 {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL}, 81 {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN}, 82 {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL}, 83 }; 84 85 auto it = mapBranchSampleType.find(name); 86 if (it == mapBranchSampleType.end()) { 87 return 0; 88 } else { 89 return it->second; 90 } 91 } 92 ~SubCommandRecord()93 SubCommandRecord::~SubCommandRecord() 94 { 95 CloseClientThread(); 96 } 97 DumpOptions() const98 void SubCommandRecord::DumpOptions() const 99 { 100 HLOGV("enter"); 101 printf("DumpOptions:\n"); 102 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false"); 103 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str()); 104 printf(" timeStopSec:\t%f sec\n", timeStopSec_); 105 printf(" frequency:\t%d\n", frequency_); 106 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str()); 107 int i = 0; 108 for (auto &group : selectGroups_) { 109 i++; 110 printf(" selectGroups:\t%2d:%s\n", i, VectorToString(group).c_str()); 111 } 112 printf(" no_inherit:\t%s\n", noInherit_ ? "true" : "false"); 113 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str()); 114 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str()); 115 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false"); 116 printf(" excludePerf:\t%d\n", excludeHiperf_); 117 printf(" cpuPercent:\t%d\n", cpuPercent_); 118 printf(" offCPU_:\t%d\n", offCPU_); 119 printf(" delayUnwind_:\t%d\n", delayUnwind_); 120 printf(" disableUnwind_:\t%d\n", disableUnwind_); 121 printf(" disableCallstackExpend_:\t%d\n", disableCallstackExpend_); 122 printf(" symbolDir_:\t%s\n", VectorToString(symbolDir_).c_str()); 123 printf(" outputFilename_:\t%s\n", outputFilename_.c_str()); 124 printf(" appPackage_:\t%s\n", appPackage_.c_str()); 125 printf(" clockId_:\t%s\n", clockId_.c_str()); 126 printf(" mmapPages_:\t%d\n", mmapPages_); 127 printf(" dataLimit:\t%s\n", strLimit_.c_str()); 128 printf(" callStack:\t%s\n", VectorToString(callStackType_).c_str()); 129 printf(" branchSampleTypes:\t%s\n", VectorToString(vecBranchFilters_).c_str()); 130 printf(" trackedCommand:\t%s\n", VectorToString(trackedCommand_).c_str()); 131 printf(" pipe_input:\t%d\n", clientPipeInput_); 132 printf(" pipe_output:\t%d\n", clientPipeOutput_); 133 } 134 GetOptions(std::vector<std::string> & args)135 bool SubCommandRecord::GetOptions(std::vector<std::string> &args) 136 { 137 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) { 138 return false; 139 } 140 if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) { 141 return false; 142 } 143 if (!Option::GetOptionValue(args, "-z", compressData_)) { 144 return false; 145 } 146 if (!Option::GetOptionValue(args, "--no-inherit", noInherit_)) { 147 return false; 148 } 149 if (!Option::GetOptionValue(args, "--offcpu", offCPU_)) { 150 return false; 151 } 152 if (!Option::GetOptionValue(args, "--delay-unwind", delayUnwind_)) { 153 return false; 154 } 155 if (!Option::GetOptionValue(args, "--disable-unwind", disableUnwind_)) { 156 return false; 157 } 158 if (!Option::GetOptionValue(args, "--disable-callstack-expand", disableCallstackExpend_)) { 159 return false; 160 } 161 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) { 162 return false; 163 } 164 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) { 165 return false; 166 } 167 if (!GetOptionFrequencyAndPeriod(args)) { 168 return false; 169 } 170 if (!Option::GetOptionValue(args, "--cpu-limit", cpuPercent_)) { 171 return false; 172 } 173 if (!Option::GetOptionValue(args, "-m", mmapPages_)) { 174 return false; 175 } 176 if (!Option::GetOptionValue(args, "--symbol-dir", symbolDir_)) { 177 return false; 178 } 179 if (!Option::GetOptionValue(args, "-o", outputFilename_)) { 180 return false; 181 } 182 if (!Option::GetOptionValue(args, "--app", appPackage_)) { 183 return false; 184 } 185 if (!Option::GetOptionValue(args, "--clockid", clockId_)) { 186 return false; 187 } 188 if (!Option::GetOptionValue(args, "-c", selectCpus_)) { 189 return false; 190 } 191 if (!Option::GetOptionValue(args, "-p", selectPids_)) { 192 return false; 193 } 194 if (!Option::GetOptionValue(args, "-t", selectTids_)) { 195 return false; 196 } 197 if (!Option::GetOptionValue(args, "-e", selectEvents_)) { 198 return false; 199 } 200 if (!Option::GetOptionValue(args, "-g", selectGroups_)) { 201 return false; 202 } 203 if (!Option::GetOptionValue(args, "-s", callStackType_)) { 204 return false; 205 } 206 std::vector<std::string> callStackType = {}; 207 if (!Option::GetOptionValue(args, "--call-stack", callStackType)) { 208 return false; 209 } 210 if (!callStackType_.empty()) { 211 if (!callStackType.empty()) { 212 printf("'-s %s --call-stack %s' option usage error, please check usage.\n", 213 VectorToString(callStackType_).c_str(), VectorToString(callStackType).c_str()); 214 return false; 215 } 216 } else { 217 callStackType_ = callStackType; 218 } 219 220 if (!Option::GetOptionValue(args, "--data-limit", strLimit_)) { 221 return false; 222 } 223 if (!Option::GetOptionValue(args, "-j", vecBranchFilters_)) { 224 return false; 225 } 226 if (!Option::GetOptionValue(args, "--pipe_input", clientPipeInput_)) { 227 return false; 228 } 229 if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) { 230 return false; 231 } 232 if (!Option::GetOptionValue(args, "--control", controlCmd_)) { 233 return false; 234 } 235 236 if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) { 237 return false; 238 } 239 if (!args.empty()) { 240 printf("'%s' option usage error, please check usage.\n", VectorToString(args).c_str()); 241 return false; 242 } 243 return true; 244 } 245 GetOptionFrequencyAndPeriod(std::vector<std::string> & args)246 bool SubCommandRecord::GetOptionFrequencyAndPeriod(std::vector<std::string> &args) 247 { 248 if (Option::FindOption(args, "-f") != args.end()) { 249 if (!Option::GetOptionValue(args, "-f", frequency_)) { 250 return false; 251 } 252 if (frequency_ < MIN_SAMPLE_FREQUENCY || frequency_ > MAX_SAMPLE_FREQUENCY) { 253 printf("Invalid -f value '%d', frequency should be in %d~%d \n", frequency_, 254 MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY); 255 return false; 256 } 257 } 258 if (Option::FindOption(args, "--period") != args.end()) { 259 if (frequency_ != 0) { 260 printf("option -f and --period is conflict, please check usage\n"); 261 return false; 262 } 263 if (!Option::GetOptionValue(args, "--period", period_)) { 264 return false; 265 } 266 if (period_ <= 0) { 267 printf("Invalid --period value '%d', period should be greater than 0\n", period_); 268 return false; 269 } 270 } 271 return true; 272 } 273 CheckDataLimitOption()274 bool SubCommandRecord::CheckDataLimitOption() 275 { 276 if (!strLimit_.empty()) { 277 if (!ParseDataLimitOption(strLimit_)) { 278 printf("Invalid --data-limit value %s\n", strLimit_.c_str()); 279 return false; 280 } 281 } 282 return true; 283 } 284 CheckSelectCpuPidOption()285 bool SubCommandRecord::CheckSelectCpuPidOption() 286 { 287 if (!selectCpus_.empty()) { 288 int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1; 289 for (auto cpu : selectCpus_) { 290 if (cpu < 0 || cpu > maxCpuid) { 291 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid); 292 return false; 293 } 294 } 295 } 296 297 if (!selectPids_.empty()) { 298 for (auto pid : selectPids_) { 299 if (pid <= 0) { 300 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid); 301 return false; 302 } 303 } 304 } 305 if (!selectTids_.empty()) { 306 for (auto tid : selectTids_) { 307 if (tid <= 0) { 308 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid); 309 return false; 310 } 311 } 312 } 313 return true; 314 } 315 CheckOptions()316 bool SubCommandRecord::CheckOptions() 317 { 318 if (timeStopSec_ < MIN_STOP_SECONDS || timeStopSec_ > MAX_STOP_SECONDS) { 319 printf("Invalid -d value '%.3f', the seconds should be in %.3f~%.3f \n", timeStopSec_, 320 MIN_STOP_SECONDS, MAX_STOP_SECONDS); 321 return false; 322 } 323 if (cpuPercent_ < MIN_CPU_PERCENT || cpuPercent_ > MAX_CPU_PERCENT) { 324 printf("Invalid --cpu-limit value '%d', CPU percent should be in %d~%d \n", cpuPercent_, 325 MIN_CPU_PERCENT, MAX_CPU_PERCENT); 326 return false; 327 } 328 if (mmapPages_ < MIN_PERF_MMAP_PAGE || mmapPages_ > MAX_PERF_MMAP_PAGE || 329 !PowerOfTwo(mmapPages_)) { 330 printf("Invalid -m value '%d', value should be in %d~%d and must be a power of two \n", 331 mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE); 332 return false; 333 } 334 if (!clockId_.empty() && GetClockId(clockId_) == -1) { 335 printf("Invalid --clockid value %s\n", clockId_.c_str()); 336 return false; 337 } 338 if (excludeHiperf_ == true && targetSystemWide_ == false) { 339 printf("--exclude-hiperf must be used with -a\n"); 340 return false; 341 } 342 if (!CheckDataLimitOption()) { 343 return false; 344 } 345 if (!ParseCallStackOption(callStackType_)) { 346 return false; 347 } 348 if (!ParseBranchSampleType(vecBranchFilters_)) { 349 return false; 350 } 351 if (!CheckSelectCpuPidOption()) { 352 return false; 353 } 354 if (!ParseControlCmd(controlCmd_)) { 355 return false; 356 } 357 if (!CheckTargetProcessOptions()) { 358 return false; 359 } 360 return true; 361 } 362 ParseOption(std::vector<std::string> & args)363 bool SubCommandRecord::ParseOption(std::vector<std::string> &args) 364 { 365 HLOGV("enter"); 366 if (!GetOptions(args)) { 367 return false; 368 } 369 if (!args.empty()) { 370 printf("unknown option %s\n", args.begin()->c_str()); 371 return false; 372 } 373 return CheckOptions(); 374 } 375 GetAppPackagePid(const std::string & appPackage)376 pid_t SubCommandRecord::GetAppPackagePid(const std::string &appPackage) 377 { 378 pid_t res {-1}; 379 const std::string basePath {"/proc/"}; 380 const auto startTime = steady_clock::now(); 381 static constexpr uint64_t waitAppTimeOut = 10; 382 const auto endTime = startTime + std::chrono::seconds(waitAppTimeOut); 383 do { 384 std::vector<std::string> subDirs = GetSubDirs(basePath); 385 for (const auto &subDir : subDirs) { 386 if (IsDigits(subDir)) { 387 std::string fileName {basePath + subDir}; 388 fileName += "/cmdline"; 389 if (IsSameCommand(ReadFileToString(fileName), appPackage)) { 390 return (std::stoul(subDir, nullptr)); 391 } 392 } 393 } 394 static constexpr uint64_t waitAppSleepMs = 100; 395 std::this_thread::sleep_for(milliseconds(waitAppSleepMs)); 396 } while (steady_clock::now() < endTime); 397 398 return res; 399 } 400 CheckTargetProcessOptions()401 bool SubCommandRecord::CheckTargetProcessOptions() 402 { 403 bool hasTarget = false; 404 if (targetSystemWide_) { 405 hasTarget = true; 406 } 407 if (!selectPids_.empty() || !selectTids_.empty()) { 408 if (hasTarget) { 409 printf("-p/-t %s options conflict, please check usage\n", 410 VectorToString(selectPids_).c_str()); 411 return false; 412 } 413 hasTarget = true; 414 } 415 if (!trackedCommand_.empty()) { 416 if (hasTarget) { 417 printf("%s options conflict, please check usage\n", 418 VectorToString(trackedCommand_).c_str()); 419 return false; 420 } 421 hasTarget = true; 422 } 423 if (appPackage_ != "") { 424 if (hasTarget) { 425 printf("--app %s options conflict, please check usage\n", appPackage_.c_str()); 426 return false; 427 } 428 hasTarget = true; 429 } 430 if (!hasTarget and (controlCmd_.empty() or controlCmd_ == CONTROL_CMD_PREPARE)) { 431 printf("please select a target process\n"); 432 return false; 433 } 434 435 return CheckTargetPids(); 436 } 437 CheckTargetPids()438 bool SubCommandRecord::CheckTargetPids() 439 { 440 for (auto pid : selectPids_) { 441 int rc = kill(pid, 0); 442 if (rc == -1 || rc == ESRCH) { 443 printf("not exist pid %d\n", pid); 444 return false; 445 } 446 } 447 for (auto pid : selectTids_) { 448 int rc = kill(pid, 0); 449 if (rc == -1 || rc == ESRCH) { 450 printf("not exist tid %d\n", pid); 451 return false; 452 } 453 } 454 if (appPackage_ != "") { 455 pid_t appPid = GetAppPackagePid(appPackage_); 456 if (appPid <= 0) { 457 printf("app %s not running\n", appPackage_.c_str()); 458 return false; 459 } 460 selectPids_.push_back(appPid); 461 } 462 if (!selectPids_.empty()) { 463 for (auto pid : selectPids_) { 464 auto tids = GetSubthreadIDs(pid); 465 if (!tids.empty()) { 466 selectPids_.insert(selectPids_.end(), tids.begin(), tids.end()); 467 } 468 } 469 } 470 selectPids_.insert(selectPids_.end(), selectTids_.begin(), selectTids_.end()); 471 472 return true; 473 } 474 ParseDataLimitOption(const std::string & str)475 bool SubCommandRecord::ParseDataLimitOption(const std::string &str) 476 { 477 uint unit = 1; 478 char c = str.at(str.size() - 1); 479 if (c == 'K' or c == 'k') { 480 unit = KILO; 481 } else if (c == 'm' or c == 'M') { 482 unit = KILO * KILO; 483 } else if (c == 'g' or c == 'G') { 484 unit = KILO * KILO * KILO; 485 } else { 486 return false; 487 } 488 489 std::string num = str.substr(0, str.size() - 1); 490 int64_t size = 0; 491 try { 492 size = std::stoul(num); 493 } catch (...) { 494 return false; 495 } 496 if (size <= 0) { 497 return false; 498 } 499 500 dataSizeLimit_ = size * unit; 501 502 return true; 503 } 504 ParseCallStackOption(const std::vector<std::string> & callStackType)505 bool SubCommandRecord::ParseCallStackOption(const std::vector<std::string> &callStackType) 506 { 507 if (callStackType.empty()) { 508 return true; 509 } else if (callStackType[0] == "fp") { 510 if (callStackType.size() != 1) { 511 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); 512 return false; 513 } 514 isCallStackFp_ = true; 515 } else if (callStackType[0] == "dwarf") { 516 if (callStackType.size() > MAX_DWARF_CALL_CHAIN) { 517 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); 518 return false; 519 } else if (callStackType.size() == MAX_DWARF_CALL_CHAIN) { 520 try { 521 callStackDwarfSize_ = std::stoul(callStackType.at(1)); 522 } catch (...) { 523 printf("Invalid -s value, dwarf stack size, '%s' is illegal.\n", 524 callStackType.at(1).c_str()); 525 return false; 526 } 527 if (callStackDwarfSize_ < MIN_SAMPLE_STACK_SIZE) { 528 printf("Invalid -s value, dwarf stack size, '%s' is too small.\n", 529 callStackType.at(1).c_str()); 530 return false; 531 } 532 if (callStackDwarfSize_ > MAX_SAMPLE_STACK_SIZE) { 533 printf("Invalid -s value, dwarf stack size, '%s' is bigger than max value %d.\n", 534 callStackType.at(1).c_str(), MAX_SAMPLE_STACK_SIZE); 535 return false; 536 } 537 if ((callStackDwarfSize_ & MASK_ALIGNED_8) != 0) { 538 printf("Invalid -s value, dwarf stack size, '%s' is not 8 byte aligned.\n", 539 callStackType.at(1).c_str()); 540 return false; 541 } 542 } 543 isCallStackDwarf_ = true; 544 } else { 545 printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str()); 546 return false; 547 } 548 return true; 549 } 550 ParseBranchSampleType(const std::vector<std::string> & vecBranchSampleTypes)551 bool SubCommandRecord::ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes) 552 { 553 if (!vecBranchSampleTypes.empty()) { 554 for (auto &item : vecBranchSampleTypes) { 555 uint64_t type = GetBranchSampleType(item); 556 if (type != 0) { 557 branchSampleType_ |= type; 558 } else { 559 printf("Invalid -j value '%s'\n", item.c_str()); 560 return false; 561 } 562 } 563 if ((branchSampleType_ & TYPE_PERF_SAMPLE_BRANCH) == 0) { 564 printf( 565 "Invalid -j value, requires at least one of any, any_call, any_ret, ind_call.\n"); 566 return false; 567 } 568 } 569 return true; 570 } 571 ParseControlCmd(const std::string cmd)572 bool SubCommandRecord::ParseControlCmd(const std::string cmd) 573 { 574 if (cmd.empty() or cmd == CONTROL_CMD_PREPARE or cmd == CONTROL_CMD_START or 575 cmd == CONTROL_CMD_PAUSE or cmd == CONTROL_CMD_RESUME or cmd == CONTROL_CMD_STOP) { 576 return true; 577 } 578 579 printf("Invalid --control %s option, command should be: prepare, start, pause, resume, stop.\n", 580 cmd.c_str()); 581 return false; 582 } 583 SetPerfCpuMaxPercent()584 bool SubCommandRecord::SetPerfCpuMaxPercent() 585 { 586 int percent = 0; 587 if (ReadIntFromProcFile("/proc/sys/kernel/perf_cpu_time_max_percent", percent)) { 588 if (percent == cpuPercent_) { 589 return true; 590 } 591 if (!IsRoot()) { 592 printf("root privillege is needed to change perf_cpu_time_max_percent\n"); 593 return false; 594 } 595 return WriteIntToProcFile("/proc/sys/kernel/perf_cpu_time_max_percent", cpuPercent_); 596 } 597 return false; 598 } 599 SetPerfMaxSampleRate()600 bool SubCommandRecord::SetPerfMaxSampleRate() 601 { 602 int rate = 0; 603 if (ReadIntFromProcFile("/proc/sys/kernel/perf_event_max_sample_rate", rate)) { 604 int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY; 605 if (rate >= frequency) { 606 return true; 607 } 608 return WriteIntToProcFile("/proc/sys/kernel/perf_event_max_sample_rate", frequency); 609 } else { 610 if (!IsRoot()) { 611 printf("root privillege is needed to change perf_event_max_sample_rate\n"); 612 } else { 613 printf("please check if CONFIG_PERF_EVENTS enabed.\n"); 614 } 615 } 616 return false; 617 } 618 TraceOffCpu()619 bool SubCommandRecord::TraceOffCpu() 620 { 621 // whether system support sched_switch event 622 int enable = -1; 623 const std::string node = "/sys/kernel/tracing/events/sched/sched_switch/enable"; 624 const std::string nodeDebug = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; 625 if (!ReadIntFromProcFile(node.c_str(), enable) and 626 !ReadIntFromProcFile(nodeDebug.c_str(), enable)) { 627 printf("Cannot trace off CPU, event sched:sched_switch is not available (%s or %s)\n", 628 node.c_str(), nodeDebug.c_str()); 629 return false; 630 } 631 632 return true; 633 } 634 PreparePerfEvent()635 bool SubCommandRecord::PreparePerfEvent() 636 { 637 // we need to notify perfEvents_ sampling mode by SetRecordCallBack first 638 auto processRecord = std::bind(&SubCommandRecord::ProcessRecord, this, std::placeholders::_1); 639 perfEvents_.SetRecordCallBack(processRecord); 640 641 perfEvents_.SetCpu(selectCpus_); 642 perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions() 643 644 perfEvents_.SetSystemTarget(targetSystemWide_); 645 perfEvents_.SetTimeOut(timeStopSec_); 646 perfEvents_.SetVerboseReport(verboseReport_); 647 perfEvents_.SetMmapPages(mmapPages_); 648 if (isCallStackFp_) { 649 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP); 650 } else if (isCallStackDwarf_) { 651 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF); 652 perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_); 653 } 654 if (!perfEvents_.SetBranchSampleType(branchSampleType_)) { 655 printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str()); 656 HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_); 657 return false; 658 } 659 if (!clockId_.empty()) { 660 perfEvents_.SetClockId(GetClockId(clockId_)); 661 } 662 663 if (frequency_ > 0) { 664 perfEvents_.SetSampleFrequency(frequency_); 665 } else if (period_ > 0) { 666 perfEvents_.SetSamplePeriod(period_); 667 } 668 669 perfEvents_.SetInherit(!noInherit_); 670 perfEvents_.SetTrackedCommand(trackedCommand_); 671 672 // set default sample event 673 if (selectEvents_.empty() && selectGroups_.empty()) { 674 selectEvents_.push_back("hw-cpu-cycles"); 675 } 676 677 if (!perfEvents_.AddEvents(selectEvents_)) { 678 HLOGE("Fail to AddEvents events"); 679 return false; 680 } 681 for (auto &group : selectGroups_) { 682 if (!perfEvents_.AddEvents(group, true)) { 683 HLOGE("Fail to AddEvents groups"); 684 return false; 685 } 686 } 687 // cpu off add after default event (we need both sched_switch and user selected events) 688 if (offCPU_) { 689 if (std::find(selectEvents_.begin(), selectEvents_.end(), "sched_switch") != 690 selectEvents_.end()) { 691 printf("--offcpu is not supported event sched_switch\n"); 692 return false; 693 } 694 // insert a sched_switch event to trace offcpu event 695 if (!perfEvents_.AddOffCpuEvent()) { 696 HLOGE("Fail to AddEOffCpuvent"); 697 return false; 698 } 699 } 700 701 return true; 702 } 703 PrepareSysKernel()704 bool SubCommandRecord::PrepareSysKernel() 705 { 706 if (!SetPerfMaxSampleRate()) { 707 HLOGE("Fail to call SetPerfMaxSampleRate(%d)", frequency_); 708 return false; 709 } 710 if (!SetPerfCpuMaxPercent()) { 711 HLOGE("Fail to set perf event cpu limit to %d\n", cpuPercent_); 712 return false; 713 } 714 if (offCPU_ && !TraceOffCpu()) { 715 HLOGE("Fail to TraceOffCpu"); 716 return false; 717 } 718 719 return true; 720 } 721 PrepareVirtualRuntime()722 bool SubCommandRecord::PrepareVirtualRuntime() 723 { 724 auto saveRecord = std::bind(&SubCommandRecord::SaveRecord, this, std::placeholders::_1); 725 virtualRuntime_.SetRecordMode(saveRecord); 726 727 // do some config for virtualRuntime_ 728 virtualRuntime_.SetCallStackExpend(disableCallstackExpend_ ? 0 : 1); 729 // these is same for virtual runtime 730 virtualRuntime_.SetDisableUnwind(disableUnwind_ or delayUnwind_); 731 if (!symbolDir_.empty()) { 732 if (!virtualRuntime_.SetSymbolsPaths(symbolDir_)) { 733 printf("Failed to set symbol path(%s)\n", VectorToString(symbolDir_).c_str()); 734 return false; 735 } 736 } 737 738 // load vsdo first 739 virtualRuntime_.LoadVdso(); 740 741 // prepare from kernel and ko 742 virtualRuntime_.UpdateKernelSpaceMaps(); 743 virtualRuntime_.UpdateKernelModulesSpaceMaps(); 744 return true; 745 } 746 ClientCommandResponse(bool OK)747 bool SubCommandRecord::ClientCommandResponse(bool OK) 748 { 749 using namespace HiperfClient; 750 if (OK) { 751 size_t size = write(clientPipeOutput_, ReplyOK.c_str(), ReplyOK.size()); 752 if (size != ReplyOK.size()) { 753 char errInfo[ERRINFOLEN] = { 0 }; 754 strerror_r(errno, errInfo, ERRINFOLEN); 755 HLOGD("Server:%s -> %d : %zd %d:%s", ReplyOK.c_str(), clientPipeOutput_, size, errno, 756 errInfo); 757 return false; 758 } 759 return true; 760 } else { 761 size_t size = write(clientPipeOutput_, ReplyFAIL.c_str(), ReplyFAIL.size()); 762 if (size != ReplyFAIL.size()) { 763 char errInfo[ERRINFOLEN] = { 0 }; 764 strerror_r(errno, errInfo, ERRINFOLEN); 765 HLOGD("Server:%s -> %d : %zd %d:%s", ReplyFAIL.c_str(), clientPipeOutput_, size, errno, 766 errInfo); 767 return false; 768 } 769 return true; 770 } 771 } 772 IsSamplingRunning()773 bool SubCommandRecord::IsSamplingRunning() 774 { 775 constexpr int maxWaitTrackingCount = 1000 / 100; // wait 1 second 776 int waitTrackingCount = maxWaitTrackingCount; 777 while (!perfEvents_.IsTrackRunning()) { 778 waitTrackingCount--; 779 if (waitTrackingCount <= 0) { 780 return false; 781 } 782 constexpr uint64_t waitTrackingSleepMs = 100; 783 std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs)); 784 } 785 return true; 786 } 787 ClientCommandHandle()788 void SubCommandRecord::ClientCommandHandle() 789 { 790 using namespace HiperfClient; 791 if (!IsSamplingRunning()) { 792 return; 793 } 794 // tell the caller if Exist 795 ClientCommandResponse(true); 796 797 bool hasRead = true; 798 while (!clientExit_) { 799 if (isFifoServer_ && hasRead) { 800 if (clientPipeInput_ != -1) { 801 // after read(), block is disabled, the poll will be waked neven if no data 802 close(clientPipeInput_); 803 } 804 clientPipeInput_ = open(CONTROL_FIFO_FILE_C2S.c_str(), O_RDONLY | O_NONBLOCK); 805 } 806 struct pollfd pollFd { 807 clientPipeInput_, POLLIN, 0 808 }; 809 int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TOMEOUT.count()); 810 if (polled <= 0) { 811 hasRead = false; 812 continue; 813 } 814 hasRead = true; 815 std::string command; 816 while (true) { 817 char c; 818 ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1)); 819 if (result <= 0) { 820 HLOGD("server :read from pipe file failed"); 821 break; 822 } 823 command.push_back(c); 824 if (c == '\n') { 825 break; 826 } 827 } 828 HLOGD("server:new command %s", command.c_str()); 829 if (command == ReplyStart) { 830 ClientCommandResponse(perfEvents_.EnableTracking()); 831 } else if (command == ReplyCheck) { 832 ClientCommandResponse(true); 833 } else if (command == ReplyStop) { 834 ClientCommandResponse(perfEvents_.StopTracking()); 835 } else if (command == ReplyPause) { 836 ClientCommandResponse(perfEvents_.PauseTracking()); 837 } else if (command == ReplyResume) { 838 ClientCommandResponse(perfEvents_.ResumeTracking()); 839 } 840 } 841 } 842 ProcessControl()843 bool SubCommandRecord::ProcessControl() 844 { 845 if (controlCmd_.empty()) { 846 return true; 847 } 848 849 if (controlCmd_ == CONTROL_CMD_PREPARE) { 850 if (!CreateFifoServer()) { 851 return false; 852 } 853 return true; 854 } 855 856 isFifoClient_ = true; 857 bool ret = false; 858 if (controlCmd_ == CONTROL_CMD_START) { 859 ret = SendFifoAndWaitReply(HiperfClient::ReplyStart); 860 } else if (controlCmd_ == CONTROL_CMD_RESUME) { 861 ret = SendFifoAndWaitReply(HiperfClient::ReplyResume); 862 } else if (controlCmd_ == CONTROL_CMD_PAUSE) { 863 ret = SendFifoAndWaitReply(HiperfClient::ReplyPause); 864 } else if (controlCmd_ == CONTROL_CMD_STOP) { 865 ret = SendFifoAndWaitReply(HiperfClient::ReplyStop); 866 if (ret) { 867 // wait sampling process exit really 868 while (SendFifoAndWaitReply(HiperfClient::ReplyCheck)) { 869 std::this_thread::sleep_for(1s); 870 } 871 } 872 remove(CONTROL_FIFO_FILE_C2S.c_str()); 873 remove(CONTROL_FIFO_FILE_S2C.c_str()); 874 } 875 876 if (ret) { 877 printf("%s sampling success.\n", controlCmd_.c_str()); 878 } else { 879 printf("%s sampling failed.\n", controlCmd_.c_str()); 880 } 881 return ret; 882 } 883 CreateFifoServer()884 bool SubCommandRecord::CreateFifoServer() 885 { 886 const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 887 if (mkfifo(CONTROL_FIFO_FILE_S2C.c_str(), fifoMode) != 0 or 888 mkfifo(CONTROL_FIFO_FILE_C2S.c_str(), fifoMode) != 0) { 889 if (errno == EEXIST) { 890 printf("another sampling service is running.\n"); 891 } else { 892 remove(CONTROL_FIFO_FILE_S2C.c_str()); 893 remove(CONTROL_FIFO_FILE_C2S.c_str()); 894 } 895 char errInfo[ERRINFOLEN] = { 0 }; 896 strerror_r(errno, errInfo, ERRINFOLEN); 897 HLOGE("create fifo file failed. %d:%s", errno, errInfo); 898 return false; 899 } 900 901 pid_t pid = fork(); 902 if (pid == -1) { 903 char errInfo[ERRINFOLEN] = { 0 }; 904 strerror_r(errno, errInfo, ERRINFOLEN); 905 HLOGE("fork failed. %d:%s", errno, errInfo); 906 return false; 907 } else if (pid == 0) { // child process 908 isFifoServer_ = true; 909 clientPipeOutput_ = open(CONTROL_FIFO_FILE_S2C.c_str(), O_WRONLY); 910 if (clientPipeOutput_ == -1) { 911 char errInfo[ERRINFOLEN] = { 0 }; 912 strerror_r(errno, errInfo, ERRINFOLEN); 913 HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, 914 errInfo); 915 return false; 916 } 917 fclose(stdout); // for XTS, because popen in CmdRun 918 } else { // parent process 919 isFifoClient_ = true; 920 int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); 921 if (fd == -1 or !WaitFifoReply(fd)) { 922 close(fd); 923 kill(pid, SIGINT); 924 remove(CONTROL_FIFO_FILE_C2S.c_str()); 925 remove(CONTROL_FIFO_FILE_S2C.c_str()); 926 char errInfo[ERRINFOLEN] = { 0 }; 927 strerror_r(errno, errInfo, ERRINFOLEN); 928 printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo); 929 return false; 930 } 931 close(fd); 932 printf("create control hiperf sampling success.\n"); 933 } 934 return true; 935 } 936 SendFifoAndWaitReply(const std::string & cmd)937 bool SubCommandRecord::SendFifoAndWaitReply(const std::string &cmd) 938 { 939 // need open for read first, because server maybe send reply before client wait to read 940 int fdRead = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); 941 if (fdRead == -1) { 942 HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str()); 943 return false; 944 } 945 int fdWrite = open(CONTROL_FIFO_FILE_C2S.c_str(), O_WRONLY | O_NONBLOCK); 946 if (fdWrite == -1) { 947 HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str()); 948 close(fdRead); 949 return false; 950 } 951 size_t size = write(fdWrite, cmd.c_str(), cmd.size()); 952 if (size != cmd.size()) { 953 HLOGE("failed to write fifo file(%s) command(%s)", CONTROL_FIFO_FILE_C2S.c_str(), 954 cmd.c_str()); 955 close(fdWrite); 956 close(fdRead); 957 return false; 958 } 959 close(fdWrite); 960 961 bool ret = WaitFifoReply(fdRead); 962 close(fdRead); 963 return ret; 964 } 965 WaitFifoReply(int fd)966 bool SubCommandRecord::WaitFifoReply(int fd) 967 { 968 struct pollfd pollFd { 969 fd, POLLIN, 0 970 }; 971 int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TOMEOUT.count()); 972 std::string reply; 973 if (polled > 0) { 974 while (true) { 975 char c; 976 ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); 977 if (result <= 0) { 978 HLOGD("read from fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); 979 break; 980 } 981 reply.push_back(c); 982 if (c == '\n') { 983 break; 984 } 985 } 986 } else if (polled == 0) { 987 HLOGD("wait fifo file(%s) timeout", CONTROL_FIFO_FILE_S2C.c_str()); 988 } else { 989 HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); 990 } 991 992 if (reply == HiperfClient::ReplyOK) { 993 return true; 994 } 995 return false; 996 } 997 OnSubCommand(std::vector<std::string> & args)998 bool SubCommandRecord::OnSubCommand(std::vector<std::string> &args) 999 { 1000 if (!ProcessControl()) { 1001 return false; 1002 } else if (isFifoClient_) { 1003 return true; 1004 } 1005 1006 // prepare PerfEvents 1007 if (!PrepareSysKernel() or !PreparePerfEvent()) { 1008 return false; 1009 } 1010 1011 // prepar some attr before CreateInitRecordFile 1012 if (!perfEvents_.PrepareTracking()) { 1013 HLOGE("Fail to prepare tracking "); 1014 return false; 1015 } 1016 1017 if (!CreateInitRecordFile(delayUnwind_ ? false : compressData_)) { 1018 HLOGE("Fail to create record file %s", outputFilename_.c_str()); 1019 return false; 1020 } 1021 1022 if (!PrepareVirtualRuntime()) { 1023 return false; 1024 } 1025 1026 // make a thread wait the other command 1027 if (clientPipeOutput_ != -1) { 1028 clientCommandHanle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this); 1029 } 1030 1031 // start tracking 1032 if (!perfEvents_.StartTracking(!isFifoServer_)) { 1033 return false; 1034 } 1035 1036 startSaveFileTimes_ = steady_clock::now(); 1037 if (!FinishWriteRecordFile()) { 1038 HLOGE("Fail to finish record file %s", outputFilename_.c_str()); 1039 return false; 1040 } else if (!PostProcessRecordFile()) { 1041 HLOGE("Fail to post process record file"); 1042 return false; 1043 } 1044 1045 // finial report 1046 RecordCompleted(); 1047 1048 CloseClientThread(); 1049 return true; 1050 } 1051 CloseClientThread()1052 void SubCommandRecord::CloseClientThread() 1053 { 1054 if (clientCommandHanle_.joinable()) { 1055 clientExit_ = true; 1056 close(clientPipeInput_); 1057 close(clientPipeOutput_); 1058 clientCommandHanle_.join(); 1059 if (isFifoServer_) { 1060 remove(CONTROL_FIFO_FILE_C2S.c_str()); 1061 remove(CONTROL_FIFO_FILE_S2C.c_str()); 1062 } 1063 } 1064 } 1065 ProcessRecord(std::unique_ptr<PerfEventRecord> record)1066 bool SubCommandRecord::ProcessRecord(std::unique_ptr<PerfEventRecord> record) 1067 { 1068 #if HIDEBUG_RECORD_NOT_PROCESS 1069 // some times we want to check performance 1070 // but we still want to see the record number 1071 if (record->GetType() == PERF_RECORD_SAMPLE) { 1072 recordSamples_++; 1073 } else { 1074 recordNoSamples_++; 1075 } 1076 return true; 1077 #else 1078 #ifdef HIPERF_DEBUG_TIME 1079 const auto startTime = steady_clock::now(); 1080 #endif 1081 if (excludeHiperf_) { 1082 static pid_t pid = getpid(); 1083 if (record->GetPid() == pid) { 1084 // discard record 1085 return true; 1086 } 1087 } 1088 1089 // May create some simulated events 1090 // it will call ProcessRecord before next line 1091 #if !HIDEBUG_RECORD_NOT_PROCESS_VM 1092 virtualRuntime_.UpdateFromRecord(*record); 1093 #endif 1094 #ifdef HIPERF_DEBUG_TIME 1095 prcessRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 1096 #endif 1097 return SaveRecord(std::move(record)); 1098 #endif 1099 } 1100 SaveRecord(std::unique_ptr<PerfEventRecord> record)1101 bool SubCommandRecord::SaveRecord(std::unique_ptr<PerfEventRecord> record) 1102 { 1103 #if HIDEBUG_RECORD_NOT_SAVE 1104 return true; 1105 #endif 1106 if (dataSizeLimit_ > 0u) { 1107 if (dataSizeLimit_ <= fileWriter_->GetDataSize()) { 1108 if (isDataSizeLimitStop_) { 1109 return false; 1110 } 1111 printf("record size %" PRIu64 " is large than limit %" PRIu64 ". stop sampling.\n", 1112 fileWriter_->GetDataSize(), dataSizeLimit_); 1113 perfEvents_.StopTracking(); 1114 isDataSizeLimitStop_ = true; 1115 return false; 1116 } 1117 } 1118 1119 if (record) { 1120 #ifdef HIPERF_DEBUG_TIME 1121 const auto saveTime = steady_clock::now(); 1122 #endif 1123 if (!fileWriter_->WriteRecord(*record)) { 1124 // write file failed, need stop record 1125 perfEvents_.StopTracking(); 1126 HLOGV("fail to write record %s", record->GetName().c_str()); 1127 return false; 1128 } 1129 if (record->GetType() == PERF_RECORD_SAMPLE) { 1130 recordSamples_++; 1131 } else { 1132 recordNoSamples_++; 1133 } 1134 HLOGV(" write done. size=%zu name=%s", record->GetSize(), record->GetName().c_str()); 1135 #ifdef HIPERF_DEBUG_TIME 1136 saveRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - saveTime); 1137 #endif 1138 return true; 1139 } 1140 return false; 1141 } 1142 GetCountFromFile(const std::string & fileName)1143 uint32_t SubCommandRecord::GetCountFromFile(const std::string &fileName) 1144 { 1145 uint32_t ret = 0; 1146 std::string str = ReadFileToString(fileName); 1147 std::vector<std::string> subStrs = StringSplit(str); 1148 for (auto subStr : subStrs) { 1149 ret++; 1150 std::vector<std::string> vSubstr = StringSplit(subStr, "-"); 1151 static const size_t BEGIN_END = 2; 1152 if (vSubstr.size() == BEGIN_END) { 1153 ret += (std::stoi(vSubstr[1]) - std::stoi(vSubstr[0])); 1154 } 1155 } 1156 return ret; 1157 } 1158 GetCpuDescFromFile()1159 std::string SubCommandRecord::GetCpuDescFromFile() 1160 { 1161 std::string str = ReadFileToString("/proc/cpuinfo"); 1162 std::vector<std::string> subStrs = StringSplit(str, "\n"); 1163 for (auto subStr : subStrs) { 1164 if (subStr.find("model name") == std::string::npos) { 1165 continue; 1166 } 1167 1168 std::vector<std::string> vSubstr = StringSplit(subStr, ": "); 1169 static const size_t NAME_VALUE = 2; 1170 if (vSubstr.size() == NAME_VALUE) { 1171 return vSubstr[1]; 1172 } else { 1173 return ""; 1174 } 1175 } 1176 return ""; 1177 } 1178 AddCpuFeature()1179 bool SubCommandRecord::AddCpuFeature() 1180 { 1181 utsname unameBuf; 1182 if ((uname(&unameBuf)) != 0) { 1183 perror("uname() failed"); 1184 return false; 1185 } 1186 1187 fileWriter_->AddStringFeature(FEATURE::OSRELEASE, unameBuf.release); 1188 fileWriter_->AddStringFeature(FEATURE::HOSTNAME, unameBuf.nodename); 1189 fileWriter_->AddStringFeature(FEATURE::ARCH, unameBuf.machine); 1190 1191 try { 1192 uint32_t cpuPresent = GetCountFromFile("/sys/devices/system/cpu/present"); 1193 uint32_t cpuOnline = GetCountFromFile("/sys/devices/system/cpu/online"); 1194 fileWriter_->AddNrCpusFeature(FEATURE::NRCPUS, cpuPresent - cpuOnline, cpuOnline); 1195 } catch (...) { 1196 HLOGD("get NRCPUS failed"); 1197 return false; 1198 } 1199 std::string cpuDesc = GetCpuDescFromFile(); 1200 if (!fileWriter_->AddStringFeature(FEATURE::CPUDESC, cpuDesc)) { 1201 return false; 1202 } 1203 1204 // CPUID(vendor,family,model,stepping in /proc/cpuinfo) isn't supported on Hi3516DV300 1205 // CPU_TOPOLOGY(sockets,dies,threads), isn't supported on Hi3516DV300 1206 // NUMA_TOPOLOGY 1207 // HEADER_PMU_MAPPINGS(/sys/bus/event_source/devices/cpu/type) isn't supported on Hi3516DV300 1208 1209 return true; 1210 } 1211 AddMemTotalFeature()1212 void SubCommandRecord::AddMemTotalFeature() 1213 { 1214 std::string str = ReadFileToString("/proc/meminfo"); 1215 std::vector<std::string> subStrs = StringSplit(str, " "); 1216 for (auto it = subStrs.begin(); it != subStrs.end(); it++) { 1217 if (it->find("MemTotal:") == std::string::npos) { 1218 continue; 1219 } 1220 1221 if ((it + 1) != subStrs.end()) { 1222 uint64_t memTotal = std::stoul(*(it + 1)); 1223 fileWriter_->AddU64Feature(FEATURE::TOTAL_MEM, memTotal); 1224 } 1225 break; 1226 } 1227 } 1228 AddEventDescFeature()1229 void SubCommandRecord::AddEventDescFeature() 1230 { 1231 fileWriter_->AddEventDescFeature(FEATURE::EVENT_DESC, perfEvents_.GetAttrWithId()); 1232 } 1233 AddRecordTimeFeature()1234 void SubCommandRecord::AddRecordTimeFeature() 1235 { 1236 // create time 1237 std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 1238 // clang-format off 1239 char buf[256] = { 0 }; 1240 ctime_r(&time, buf); 1241 fileWriter_->AddStringFeature(FEATURE::HIPERF_RECORD_TIME, 1242 StringReplace(buf, "\n", "")); 1243 // clang-format on 1244 return; 1245 } 1246 AddWorkloadCmdFeature()1247 void SubCommandRecord::AddWorkloadCmdFeature() 1248 { 1249 if (trackedCommand_.size() > 0) { 1250 fileWriter_->AddStringFeature(FEATURE::HIPERF_WORKLOAD_CMD, trackedCommand_.at(0)); 1251 } else { 1252 HLOGD("no trackedCommand"); 1253 } 1254 } 1255 AddCommandLineFeature()1256 void SubCommandRecord::AddCommandLineFeature() 1257 { 1258 // cmdline may end with some no ascii code 1259 // so we cp it with c_str again 1260 std::string fullCommandline = 1261 ReadFileToString("/proc/self/cmdline").c_str() + Command::fullArgument; 1262 fileWriter_->AddStringFeature(FEATURE::CMDLINE, fullCommandline); 1263 } 1264 AddCpuOffFeature()1265 void SubCommandRecord::AddCpuOffFeature() 1266 { 1267 if (offCPU_) { 1268 fileWriter_->AddBoolFeature(FEATURE::HIPERF_CPU_OFF); 1269 } 1270 } 1271 AddFeatureRecordFile()1272 bool SubCommandRecord::AddFeatureRecordFile() 1273 { 1274 // VERSION 1275 1276 if (!AddCpuFeature()) { 1277 return false; 1278 } 1279 1280 AddMemTotalFeature(); 1281 1282 AddCommandLineFeature(); 1283 1284 AddEventDescFeature(); 1285 1286 AddRecordTimeFeature(); 1287 1288 AddWorkloadCmdFeature(); 1289 1290 AddCpuOffFeature(); 1291 1292 return true; 1293 } 1294 CreateInitRecordFile(bool compressData)1295 bool SubCommandRecord::CreateInitRecordFile(bool compressData) 1296 { 1297 if (fileWriter_ == nullptr) { 1298 fileWriter_ = std::make_unique<PerfFileWriter>(); 1299 } 1300 1301 if (!fileWriter_->Open(outputFilename_, compressData)) { 1302 return false; 1303 } 1304 1305 if (!fileWriter_->WriteAttrAndId(perfEvents_.GetAttrWithId())) { 1306 return false; 1307 } 1308 1309 if (!AddFeatureRecordFile()) { 1310 return false; 1311 } 1312 1313 HLOGD("create new record file %s", outputFilename_.c_str()); 1314 return true; 1315 } 1316 PostProcessRecordFile()1317 bool SubCommandRecord::PostProcessRecordFile() 1318 { 1319 if (delayUnwind_) { 1320 // 1. prepare file to rewrite 1321 std::string tempFileName = outputFilename_ + ".tmp"; 1322 if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) { 1323 HLOGE("rename failed. unabel to do delay unwind"); 1324 perror("Fail to rename data file"); 1325 return false; 1326 } else { 1327 HLOGD("use temp file '%s' for delay unwind", tempFileName.c_str()); 1328 } 1329 1330 // renew record file 1331 // release the old one 1332 fileWriter_.reset(); 1333 if (!CreateInitRecordFile(compressData_)) { 1334 // create again 1335 HLOGEP("Fail to open data file %s ", outputFilename_.c_str()); 1336 return false; 1337 } 1338 1339 // read temp file 1340 auto fileReader = PerfFileReader::Instance(tempFileName); 1341 if (fileReader == nullptr) { 1342 HLOGEP("Fail to open data file %s ", tempFileName.c_str()); 1343 return false; 1344 } 1345 1346 // 2. read out the file and unwind 1347 auto record_callback = [&](std::unique_ptr<PerfEventRecord> record) { 1348 if (record == nullptr) { 1349 // return false in callback can stop the read process 1350 return false; 1351 } else if (record->GetType() == PERF_RECORD_SAMPLE) { 1352 HLOGM("readback record for unwind"); 1353 virtualRuntime_.UnwindFromRecord(static_cast<PerfRecordSample &>(*record)); 1354 } 1355 SaveRecord(std::move(record)); 1356 return true; 1357 }; 1358 fileReader->ReadDataSection(record_callback); 1359 1360 // 3. close again 1361 1362 // lte FinishWriteRecordFile write matched only symbols 1363 delayUnwind_ = false; 1364 if (!FinishWriteRecordFile()) { 1365 HLOGE("Fail to finish record file %s", outputFilename_.c_str()); 1366 return false; 1367 } 1368 1369 remove(tempFileName.c_str()); 1370 } 1371 return true; 1372 } 1373 1374 #if USE_COLLECT_SYMBOLIC SymbolicHits()1375 void SubCommandRecord::SymbolicHits() 1376 { 1377 for (auto &vaddr : kernelSymbolsHits_) { 1378 virtualRuntime_.GetSymbol(vaddr, 0, 0, PERF_CONTEXT_KERNEL); 1379 } 1380 1381 for (auto &processPair : userSymbolsHits_) { 1382 for (auto &vaddr : processPair.second) { 1383 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first, 1384 PERF_CONTEXT_USER); 1385 } 1386 } 1387 } 1388 #endif 1389 CollectionSymbol(std::unique_ptr<PerfEventRecord> record)1390 bool SubCommandRecord::CollectionSymbol(std::unique_ptr<PerfEventRecord> record) 1391 { 1392 if (record->GetType() == PERF_RECORD_SAMPLE) { 1393 PerfRecordSample *sample = static_cast<PerfRecordSample *>(record.get()); 1394 #if USE_COLLECT_SYMBOLIC 1395 perf_callchain_context context = record->inKernel() ? PERF_CONTEXT_KERNEL 1396 : PERF_CONTEXT_USER; 1397 // if no nr use ip 1398 if (sample->data_.nr == 0) { 1399 if (context == PERF_CONTEXT_KERNEL) { 1400 kernelSymbolsHits_.insert(sample->data_.ip); 1401 } else { 1402 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip); 1403 } 1404 } else { 1405 for (u64 i = 0; i < sample->data_.nr; i++) { 1406 if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { 1407 if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { 1408 context = PERF_CONTEXT_KERNEL; 1409 } else { 1410 context = PERF_CONTEXT_USER; 1411 } 1412 } else { 1413 if (context == PERF_CONTEXT_KERNEL) { 1414 kernelSymbolsHits_.insert(sample->data_.ips[i]); 1415 } else { 1416 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); 1417 } 1418 } 1419 } 1420 } 1421 #else 1422 virtualRuntime_.SymbolicRecord(*sample); 1423 #endif 1424 } 1425 return true; 1426 } 1427 1428 // finish writing data file, then close file FinishWriteRecordFile()1429 bool SubCommandRecord::FinishWriteRecordFile() 1430 { 1431 #ifdef HIPERF_DEBUG_TIME 1432 const auto startTime = steady_clock::now(); 1433 #endif 1434 #if !HIDEBUG_SKIP_PROCESS_SYMBOLS 1435 if (!delayUnwind_) { 1436 #if !HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS 1437 HLOGD("Load kernel symbols"); 1438 virtualRuntime_.UpdateKernelSymbols(); 1439 virtualRuntime_.UpdateKernelModulesSymbols(); 1440 #endif 1441 HLOGD("Load user symbols"); 1442 fileWriter_->ReadDataSection( 1443 std::bind(&SubCommandRecord::CollectionSymbol, this, std::placeholders::_1)); 1444 #if USE_COLLECT_SYMBOLIC 1445 SymbolicHits(); 1446 #endif 1447 HLOGD("Write the symbols to perf.data"); 1448 #if HIDEBUG_SKIP_MATCH_SYMBOLS 1449 disableUnwind_ = true; 1450 #endif 1451 #if !HIDEBUG_SKIP_SAVE_SYMBOLS 1452 if (!fileWriter_->AddSymbolsFeature(virtualRuntime_.GetSymbolsFiles())) { 1453 HLOGE("Fail to AddSymbolsFeature"); 1454 return false; 1455 } 1456 #endif 1457 } 1458 #endif 1459 1460 if (!fileWriter_->Close()) { 1461 HLOGE("Fail to close record file %s", outputFilename_.c_str()); 1462 return false; 1463 } 1464 #ifdef HIPERF_DEBUG_TIME 1465 saveFeatureTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 1466 #endif 1467 return true; 1468 } 1469 1470 #ifdef HIPERF_DEBUG_TIME ReportTime()1471 void SubCommandRecord::ReportTime() 1472 { 1473 printf("updateSymbolsTimes: %0.3f ms\n", 1474 virtualRuntime_.updateSymbolsTimes_.count() / MS_DUARTION); 1475 printf("saveFeatureTimes: %0.3f ms\n", saveFeatureTimes_.count() / MS_DUARTION); 1476 1477 printf("prcessRecordTimes: %0.3f ms\n", prcessRecordTimes_.count() / MS_DUARTION); 1478 printf("-prcessSampleRecordTimes: %0.3f ms\n", 1479 virtualRuntime_.prcessSampleRecordTimes_.count() / MS_DUARTION); 1480 printf("--unwindFromRecordTimes: %0.3f ms\n", 1481 virtualRuntime_.unwindFromRecordTimes_.count() / MS_DUARTION); 1482 printf("-prcessMmapRecordTimes: %0.3f ms\n", 1483 virtualRuntime_.prcessMmapRecordTimes_.count() / MS_DUARTION); 1484 printf("-prcessMmap2RecordTimes: %0.3f ms\n", 1485 virtualRuntime_.prcessMmap2RecordTimes_.count() / MS_DUARTION); 1486 printf("-prcessCommRecordTimes: %0.3f ms\n", 1487 virtualRuntime_.prcessCommRecordTimes_.count() / MS_DUARTION); 1488 printf("-prcessMmap2RecordTimes: %0.3f ms\n", 1489 virtualRuntime_.prcessMmap2RecordTimes_.count() / MS_DUARTION); 1490 printf("--updateThreadTimes: %0.3f ms\n", 1491 virtualRuntime_.updateThreadTimes_.count() / MS_DUARTION); 1492 printf("---threadParseMapsTimes: %0.3f ms\n", 1493 virtualRuntime_.threadParseMapsTimes_.count() / MS_DUARTION); 1494 printf("---threadCreateMmapTimes: %0.3f ms\n", 1495 virtualRuntime_.threadCreateMmapTimes_.count() / MS_DUARTION); 1496 printf("--unwindCallStackTimes: %0.3f ms\n", 1497 virtualRuntime_.unwindCallStackTimes_.count() / MS_DUARTION); 1498 printf("-symbolicRecordTimes: %0.3f ms\n", 1499 virtualRuntime_.symbolicRecordTimes_.count() / MS_DUARTION); 1500 printf("saveRecordTimes: %0.3f ms\n", saveRecordTimes_.count() / MS_DUARTION); 1501 printf("-writeTimes: %0.3f ms\n", fileWriter_->writeTimes_.count() / MS_DUARTION); 1502 1503 printf("logTimes: %0.3f ms\n", DebugLogger::GetInstance()->logTimes_.count() / MS_DUARTION); 1504 printf("-logSprintfTimes: %0.3f ms\n", 1505 DebugLogger::GetInstance()->logSprintfTimes_.count() / MS_DUARTION); 1506 printf("-logWriteTimes: %0.3f ms\n", 1507 DebugLogger::GetInstance()->logWriteTimes_.count() / MS_DUARTION); 1508 printf("logCount: %zu (%4.2f ms/log)\n", DebugLogger::GetInstance()->logCount_, 1509 DebugLogger::GetInstance()->logTimes_.count() / 1510 static_cast<double>(DebugLogger::GetInstance()->logCount_) / MS_DUARTION); 1511 } 1512 #endif 1513 RecordCompleted()1514 bool SubCommandRecord::RecordCompleted() 1515 { 1516 if (verboseReport_) { 1517 printf("Save Record used %0.3f ms.\n", 1518 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() / 1519 MS_DUARTION); 1520 } 1521 HLOGV("Save Record used %0.3f ms.\n", 1522 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() / 1523 MS_DUARTION); 1524 1525 // print brief file info 1526 double mb = static_cast<double>(fileWriter_->GetDataSize()) / (KILO * KILO); 1527 if (compressData_) { 1528 printf("[ hiperf record: Captured and compressed %.3f MB perf data. ]\n", mb); 1529 } else { 1530 printf("[ hiperf record: Captured %.3f MB perf data. ]\n", mb); 1531 } 1532 printf("[ Sample records: %zu, Non sample records: %zu ]\n", recordSamples_, recordNoSamples_); 1533 // Show brief sample lost. 1534 size_t lostSamples = 0; 1535 size_t lostNonSamples = 0; 1536 perfEvents_.GetLostSamples(lostSamples, lostNonSamples); 1537 printf("[ Sample lost: %zu, Non sample lost: %zu ]\n", lostSamples, lostNonSamples); 1538 1539 #ifdef HIPERF_DEBUG_TIME 1540 ReportTime(); 1541 #endif 1542 return true; 1543 } 1544 RegisterSubCommandRecord(void)1545 bool SubCommandRecord::RegisterSubCommandRecord(void) 1546 { 1547 HLOGV("enter"); 1548 return SubCommand::RegisterSubCommand("record", std::make_unique<SubCommandRecord>()); 1549 } 1550 } // namespace HiPerf 1551 } // namespace Developtools 1552 } // namespace OHOS 1553