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 <csignal> 21#include <cstdlib> 22#include <ctime> 23#include <memory> 24#include <poll.h> 25#if defined(CONFIG_HAS_SYSPARA) 26#include <parameters.h> 27#endif 28#include <sys/stat.h> 29#include <sys/utsname.h> 30#include <unistd.h> 31 32#include "command.h" 33#include "debug_logger.h" 34#include "hiperf_client.h" 35#if defined(is_ohos) && is_ohos 36#include "hiperf_hilog.h" 37#endif 38#include "option.h" 39#include "perf_event_record.h" 40#include "perf_file_reader.h" 41#include "utilities.h" 42#include "subcommand_report.h" 43 44using namespace std::chrono; 45namespace OHOS { 46namespace Developtools { 47namespace HiPerf { 48const std::string CONTROL_CMD_PREPARE = "prepare"; 49const std::string CONTROL_CMD_START = "start"; 50const std::string CONTROL_CMD_PAUSE = "pause"; 51const std::string CONTROL_CMD_RESUME = "resume"; 52const std::string CONTROL_CMD_STOP = "stop"; 53const std::string CONTROL_FIFO_FILE_C2S = "/data/local/tmp/.hiperf_record_control_c2s"; 54const std::string CONTROL_FIFO_FILE_S2C = "/data/local/tmp/.hiperf_record_control_s2c"; 55 56const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent"; 57const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate"; 58const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb"; 59const std::string SCHED_SWITCH = "/sys/kernel/tracing/events/sched/sched_switch/enable"; 60const std::string SCHED_SWITCH_DEBUG = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; 61const std::string PROC_VERSION = "/proc/version"; 62const std::string SAVED_CMDLINES_SIZE = "/sys/kernel/tracing/saved_cmdlines_size"; 63 64// when there are many events, start record will take more time. 65const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT = 2000ms; 66const std::chrono::milliseconds CONTROL_WAITREPY_TOMEOUT_CHECK = 1000ms; 67 68constexpr uint64_t MASK_ALIGNED_8 = 7; 69constexpr size_t MAX_DWARF_CALL_CHAIN = 2; 70constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL | 71 PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_JUMP | 72 PERF_SAMPLE_BRANCH_IND_CALL | PERF_SAMPLE_BRANCH_COND | 73 PERF_SAMPLE_BRANCH_CALL; 74 75int GetClockId(const std::string &name) 76{ 77 static std::map<std::string, int> mapClockid = { 78 {"realtime", CLOCK_REALTIME}, {"boottime", CLOCK_BOOTTIME}, 79 {"monotonic", CLOCK_MONOTONIC}, {"monotonic_raw", CLOCK_MONOTONIC_RAW}, 80 {"clock_tai", CLOCK_TAI}, 81 }; 82 83 auto it = mapClockid.find(name); 84 if (it == mapClockid.end()) { 85 return -1; 86 } else { 87 return it->second; 88 } 89} 90 91uint64_t GetBranchSampleType(const std::string &name) 92{ 93 static std::map<std::string, uint64_t> mapBranchSampleType = { 94 {"u", PERF_SAMPLE_BRANCH_USER}, 95 {"k", PERF_SAMPLE_BRANCH_KERNEL}, 96 {"any", PERF_SAMPLE_BRANCH_ANY}, 97 {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL}, 98 {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN}, 99 {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL}, 100 {"ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP}, 101 {"cond", PERF_SAMPLE_BRANCH_COND}, 102 {"call", PERF_SAMPLE_BRANCH_CALL}, 103 }; 104 105 auto it = mapBranchSampleType.find(name); 106 if (it == mapBranchSampleType.end()) { 107 return 0; 108 } else { 109 return it->second; 110 } 111} 112 113SubCommandRecord::~SubCommandRecord() 114{ 115 CloseClientThread(); 116} 117 118void SubCommandRecord::DumpOptions() const 119{ 120 printf("DumpOptions:\n"); 121 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false"); 122 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str()); 123 printf(" timeStopSec:\t%f sec\n", timeStopSec_); 124 printf(" frequency:\t%d\n", frequency_); 125 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str()); 126 int i = 0; 127 for (auto &group : selectGroups_) { 128 i++; 129 printf(" selectGroups:\t%2d:%s\n", i, VectorToString(group).c_str()); 130 } 131 printf(" no_inherit:\t%s\n", noInherit_ ? "true" : "false"); 132 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str()); 133 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str()); 134 printf(" excludeTids:\t%s\n", VectorToString(excludeTids_).c_str()); 135 printf(" excludeThreads:\t%s\n", VectorToString(excludeThreadNames_).c_str()); 136 printf(" kernelCallChain:\t%s\n", kernelCallChain_ ? "true" : "false"); 137 printf(" callChainUserOnly_:\t%s\n", callChainUserOnly_ ? "true" : "false"); 138 printf(" restart:\t%s\n", restart_ ? "true" : "false"); 139 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false"); 140 printf(" excludePerf:\t%d\n", excludeHiperf_); 141 printf(" cpuPercent:\t%d\n", cpuPercent_); 142 printf(" offCPU_:\t%d\n", offCPU_); 143 printf(" delayUnwind_:\t%d\n", delayUnwind_); 144 printf(" disableUnwind_:\t%d\n", disableUnwind_); 145 printf(" disableCallstackExpend_:\t%d\n", disableCallstackExpend_); 146 printf(" enableDebugInfoSymbolic:\t%d\n", enableDebugInfoSymbolic_); 147 printf(" symbolDir_:\t%s\n", VectorToString(symbolDir_).c_str()); 148 printf(" outputFilename_:\t%s\n", outputFilename_.c_str()); 149 printf(" appPackage_:\t%s\n", appPackage_.c_str()); 150 printf(" checkAppMs_:\t%d\n", checkAppMs_); 151 printf(" clockId_:\t%s\n", clockId_.c_str()); 152 printf(" mmapPages_:\t%d\n", mmapPages_); 153 printf(" dataLimit:\t%s\n", strLimit_.c_str()); 154 printf(" callStack:\t%s\n", VectorToString(callStackType_).c_str()); 155 printf(" branchSampleTypes:\t%s\n", VectorToString(vecBranchFilters_).c_str()); 156 printf(" trackedCommand:\t%s\n", VectorToString(trackedCommand_).c_str()); 157 printf(" pipe_input:\t%d\n", clientPipeInput_); 158 printf(" pipe_output:\t%d\n", clientPipeOutput_); 159 printf(" cmdlinesSize_:\t%d\n", cmdlinesSize_); 160 printf(" report_:\t%s\n", report_ ? "true" : "false"); 161} 162 163bool SubCommandRecord::GetSpeOptions() 164{ 165 std::string speName; 166 167 for (size_t i = 0; i < selectEvents_.size(); i++) { 168 std::string optionValue = selectEvents_[i]; 169 170 std::vector<std::string> valueExpressions = StringSplit(optionValue, "/"); 171 if (i == 0) { 172 if (valueExpressions.size() > 1) { 173 speName = valueExpressions[0]; 174 valueExpressions.erase(valueExpressions.begin()); 175 } else { 176 break; 177 } 178 } 179 180 for (auto item: valueExpressions) { 181 std::vector<std::string> expressions = StringSplit(item, "="); 182 size_t itemNum = 2; 183 if (expressions.size() == itemNum) { 184 std::string name = expressions[0]; 185 unsigned long long num = std::stoull(expressions[1]); 186 if (speOptMap_.find(name) != speOptMap_.end()) { 187 speOptMap_[name] = num; 188 } 189 if (num != 0) { 190 speOptions_.emplace_back(name); 191 } 192 } 193 } 194 } 195 if (speName.size() > 0) { 196 selectEvents_.clear(); 197 selectEvents_.emplace_back(speName); 198 } 199 return true; 200} 201 202bool SubCommandRecord::GetOptions(std::vector<std::string> &args) 203{ 204 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) { 205 return false; 206 } 207 if (targetSystemWide_ && !IsSupportNonDebuggableApp()) { 208 HLOGD("-a option needs root privilege for system wide profiling."); 209 printf("-a option needs root privilege for system wide profiling.\n"); 210 return false; 211 } 212 if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) { 213 return false; 214 } 215 if (!Option::GetOptionValue(args, "-z", compressData_)) { 216 return false; 217 } 218 if (!Option::GetOptionValue(args, "--no-inherit", noInherit_)) { 219 return false; 220 } 221 if (!Option::GetOptionValue(args, "--offcpu", offCPU_)) { 222 return false; 223 } 224 if (!Option::GetOptionValue(args, "--delay-unwind", delayUnwind_)) { 225 return false; 226 } 227 if (!Option::GetOptionValue(args, "--disable-unwind", disableUnwind_)) { 228 return false; 229 } 230 if (!Option::GetOptionValue(args, "--disable-callstack-expand", disableCallstackExpend_)) { 231 return false; 232 } 233 if (!Option::GetOptionValue(args, "--enable-debuginfo-symbolic", enableDebugInfoSymbolic_)) { 234 return false; 235 } 236 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) { 237 return false; 238 } 239 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) { 240 return false; 241 } 242 if (!GetOptionFrequencyAndPeriod(args)) { 243 return false; 244 } 245 if (!Option::GetOptionValue(args, "--cpu-limit", cpuPercent_)) { 246 return false; 247 } 248 if (!Option::GetOptionValue(args, "-m", mmapPages_)) { 249 return false; 250 } 251 if (!Option::GetOptionValue(args, "--symbol-dir", symbolDir_)) { 252 return false; 253 } 254 if (!Option::GetOptionValue(args, "-o", outputFilename_)) { 255 return false; 256 } 257 if (!Option::GetOptionValue(args, "--app", appPackage_)) { 258 return false; 259 } 260 if (!IsExistDebugByApp(appPackage_)) { 261 return false; 262 } 263 if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) { 264 return false; 265 } 266 if (!Option::GetOptionValue(args, "--clockid", clockId_)) { 267 return false; 268 } 269 if (!Option::GetOptionValue(args, "-c", selectCpus_)) { 270 return false; 271 } 272 if (!Option::GetOptionValue(args, "-p", selectPids_)) { 273 return false; 274 } 275 if (!IsExistDebugByPid(selectPids_)) { 276 return false; 277 } 278 if (!Option::GetOptionValue(args, "-t", selectTids_)) { 279 return false; 280 } 281 if (!Option::GetOptionValue(args, "-e", selectEvents_)) { 282 return false; 283 } 284 if (!Option::GetOptionValue(args, "-g", selectGroups_)) { 285 return false; 286 } 287 if (!GetSpeOptions()) { 288 return false; 289 } 290 if (!Option::GetOptionValue(args, "-s", callStackType_)) { 291 return false; 292 } 293 if (!Option::GetOptionValue(args, "--kernel-callchain", kernelCallChain_)) { 294 return false; 295 } 296 if (!Option::GetOptionValue(args, "--callchain-useronly", callChainUserOnly_)) { 297 return false; 298 } 299 if (!Option::GetOptionValue(args, "--exclude-tid", excludeTids_)) { 300 return false; 301 } 302 if (!Option::GetOptionValue(args, "--exclude-thread", excludeThreadNames_)) { 303 return false; 304 } 305 if (!Option::GetOptionValue(args, "--restart", restart_)) { 306 return false; 307 } 308 std::vector<std::string> callStackType = {}; 309 if (!Option::GetOptionValue(args, "--call-stack", callStackType)) { 310 return false; 311 } 312 if (!callStackType_.empty()) { 313 if (!callStackType.empty()) { 314 printf("'-s %s --call-stack %s' option usage error, please check usage.\n", 315 VectorToString(callStackType_).c_str(), VectorToString(callStackType).c_str()); 316 return false; 317 } 318 } else { 319 callStackType_ = callStackType; 320 } 321 if (!Option::GetOptionValue(args, "--data-limit", strLimit_)) { 322 return false; 323 } 324 if (!Option::GetOptionValue(args, "-j", vecBranchFilters_)) { 325 return false; 326 } 327 if (!Option::GetOptionValue(args, "--pipe_input", clientPipeInput_)) { 328 return false; 329 } 330 if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) { 331 return false; 332 } 333 if (!Option::GetOptionValue(args, "--control", controlCmd_)) { 334 return false; 335 } 336 if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) { 337 return false; 338 } 339 if (!Option::GetOptionValue(args, "--cmdline-size", cmdlinesSize_)) { 340 return false; 341 } 342 if (!Option::GetOptionValue(args, "--report", report_)) { 343 return false; 344 } 345 if (targetSystemWide_ && dedupStack_) { 346 printf("-a option is conflict with --dedup_stack.\n"); 347 return false; 348 } 349 CHECK_TRUE(!Option::GetOptionTrackedCommand(args, trackedCommand_), false, 0, ""); 350 CHECK_TRUE(!args.empty(), false, LOG_TYPE_PRINTF, 351 "'%s' option usage error, please check usage.\n", VectorToString(args).c_str()); 352 return true; 353} 354 355bool SubCommandRecord::GetOptionFrequencyAndPeriod(std::vector<std::string> &args) 356{ 357 if (Option::FindOption(args, "-f") != args.end()) { 358 if (!Option::GetOptionValue(args, "-f", frequency_)) { 359 return false; 360 } 361 if (frequency_ < MIN_SAMPLE_FREQUENCY || frequency_ > MAX_SAMPLE_FREQUENCY) { 362 printf("Invalid -f value '%d', frequency should be in %d~%d \n", frequency_, 363 MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY); 364 return false; 365 } 366 } 367 if (Option::FindOption(args, "--period") != args.end()) { 368 if (frequency_ != 0) { 369 printf("option -f and --period is conflict, please check usage\n"); 370 return false; 371 } 372 if (!Option::GetOptionValue(args, "--period", period_)) { 373 return false; 374 } 375 if (period_ <= 0) { 376 printf("Invalid --period value '%d', period should be greater than 0\n", period_); 377 return false; 378 } 379 } 380 return true; 381} 382 383bool SubCommandRecord::CheckDataLimitOption() 384{ 385 if (!strLimit_.empty()) { 386 if (!ParseDataLimitOption(strLimit_)) { 387 printf("Invalid --data-limit value %s\n", strLimit_.c_str()); 388 return false; 389 } 390 } 391 return true; 392} 393 394bool SubCommandRecord::CheckSelectCpuPidOption() 395{ 396 if (!selectCpus_.empty()) { 397 int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1; 398 for (auto cpu : selectCpus_) { 399 if (cpu < 0 || cpu > maxCpuid) { 400 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid); 401 return false; 402 } 403 } 404 } 405 406 if (!selectPids_.empty()) { 407 for (auto pid : selectPids_) { 408 if (pid <= 0) { 409 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid); 410 return false; 411 } 412 } 413 } 414 if (!selectTids_.empty()) { 415 for (auto tid : selectTids_) { 416 if (tid <= 0) { 417 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid); 418 return false; 419 } 420 } 421 } 422 return true; 423} 424 425bool SubCommandRecord::CheckArgsRange() 426{ 427 if (timeStopSec_ < MIN_STOP_SECONDS || timeStopSec_ > MAX_STOP_SECONDS) { 428 printf("Invalid -d value '%.3f', the seconds should be in %.3f~%.3f \n", timeStopSec_, 429 MIN_STOP_SECONDS, MAX_STOP_SECONDS); 430 return false; 431 } 432 if (cpuPercent_ < MIN_CPU_PERCENT || cpuPercent_ > MAX_CPU_PERCENT) { 433 printf("Invalid --cpu-limit value '%d', CPU percent should be in %d~%d \n", cpuPercent_, 434 MIN_CPU_PERCENT, MAX_CPU_PERCENT); 435 return false; 436 } 437 if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) { 438 printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_, 439 MIN_CHECK_APP_MS, MAX_CHECK_APP_MS); 440 return false; 441 } 442 if (mmapPages_ < MIN_PERF_MMAP_PAGE || mmapPages_ > MAX_PERF_MMAP_PAGE || 443 !PowerOfTwo(mmapPages_)) { 444 printf("Invalid -m value '%d', value should be in %d~%d and must be a power of two \n", 445 mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE); 446 return false; 447 } 448 if (cmdlinesSize_ < MIN_SAVED_CMDLINES_SIZE || cmdlinesSize_ > MAX_SAVED_CMDLINES_SIZE || 449 !PowerOfTwo(cmdlinesSize_)) { 450 printf("Invalid --cmdline-size value '%d', value should be in %d~%d and must be a power of two \n", 451 cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE); 452 return false; 453 } 454 if (!clockId_.empty() && GetClockId(clockId_) == -1) { 455 printf("Invalid --clockid value %s\n", clockId_.c_str()); 456 return false; 457 } 458 if (!targetSystemWide_ && excludeHiperf_) { 459 printf("--exclude-hiperf must be used with -a\n"); 460 return false; 461 } 462 return true; 463} 464 465bool SubCommandRecord::CheckOptions() 466{ 467 if (!CheckArgsRange()) { 468 return false; 469 } 470 if (!CheckDataLimitOption()) { 471 return false; 472 } 473 if (!ParseCallStackOption(callStackType_)) { 474 return false; 475 } 476 if (!ParseBranchSampleType(vecBranchFilters_)) { 477 return false; 478 } 479 if (!CheckSelectCpuPidOption()) { 480 return false; 481 } 482 if (!ParseControlCmd(controlCmd_)) { 483 return false; 484 } 485 if (!CheckTargetProcessOptions()) { 486 return false; 487 } 488 if (!CheckReportOption()) { 489 return false; 490 } 491 return true; 492} 493 494bool SubCommandRecord::ParseOption(std::vector<std::string> &args) 495{ 496 if (!GetOptions(args)) { 497 return false; 498 } 499 CHECK_TRUE(!args.empty(), false, LOG_TYPE_PRINTF, "unknown option %s\n", args.begin()->c_str()); 500 if (controlCmd_.empty()) { 501 if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) { 502 return false; 503 } 504 } 505 return CheckOptions(); 506} 507 508bool SubCommandRecord::CheckTargetProcessOptions() 509{ 510 bool hasTarget = false; 511 if (targetSystemWide_) { 512 hasTarget = true; 513 } 514 if (!selectPids_.empty() || !selectTids_.empty()) { 515 CHECK_TRUE(hasTarget, false, LOG_TYPE_PRINTF, 516 "-p/-t %s options conflict, please check usage\n", VectorToString(selectPids_).c_str()); 517 hasTarget = true; 518 } 519 if (!trackedCommand_.empty()) { 520 if (hasTarget) { 521 printf("%s options conflict, please check usage\n", 522 VectorToString(trackedCommand_).c_str()); 523 return false; 524 } 525 if (!IsRoot()) { 526 printf("%s options needs root privilege, please check usage\n", 527 VectorToString(trackedCommand_).c_str()); 528 return false; 529 } 530 hasTarget = true; 531 } 532 if (appPackage_ != "") { 533 if (hasTarget) { 534 printf("--app %s options conflict, please check usage\n", appPackage_.c_str()); 535 return false; 536 } 537 hasTarget = true; 538 } 539 if (!hasTarget and (controlCmd_.empty() or controlCmd_ == CONTROL_CMD_PREPARE)) { 540 printf("please select a target process\n"); 541 return false; 542 } 543 if (controlCmd_ == CONTROL_CMD_PREPARE) { 544 CHECK_TRUE(!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_), false, 0, ""); 545 } 546 return CheckTargetPids(); 547} 548 549bool SubCommandRecord::CheckTargetPids() 550{ 551 for (auto pid : selectPids_) { 552 if (!IsDir("/proc/" + std::to_string(pid))) { 553 printf("not exist pid %d\n", pid); 554 return false; 555 } 556 } 557 for (auto tid : selectTids_) { 558 if (!IsDir("/proc/" + std::to_string(tid))) { 559 printf("not exist tid %d\n", tid); 560 return false; 561 } 562 } 563 if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) { 564 return false; 565 } 566 if (!selectPids_.empty()) { 567 for (auto pid : selectPids_) { 568 auto tids = GetSubthreadIDs(pid); 569 if (!tids.empty()) { 570 selectTids_.insert(selectTids_.end(), tids.begin(), tids.end()); 571 mapPids_[pid] = tids; 572 } 573 } 574 } 575 if (!SubCommand::HandleSubCommandExclude(excludeTids_, excludeThreadNames_, selectTids_)) { 576 return false; 577 } 578 selectPids_.insert(selectPids_.end(), selectTids_.begin(), selectTids_.end()); 579 580 return true; 581} 582 583bool SubCommandRecord::CheckReportOption() 584{ 585 if (targetSystemWide_ && report_) { 586 printf("--report options conflict, please check usage\n"); 587 return false; 588 } 589 return true; 590} 591 592bool SubCommandRecord::ParseDataLimitOption(const std::string &str) 593{ 594 uint unit = 1; 595 char c = str.at(str.size() >= 1 ? str.size() - 1 : 0); 596 if (c == 'K' or c == 'k') { 597 unit = KILO; 598 } else if (c == 'm' or c == 'M') { 599 unit = KILO * KILO; 600 } else if (c == 'g' or c == 'G') { 601 unit = KILO * KILO * KILO; 602 } else { 603 return false; 604 } 605 606 std::string num = str.substr(0, str.size() >= 1 ? str.size() - 1 : 0); 607 int64_t size = 0; 608 try { 609 size = std::stoul(num); 610 } catch (...) { 611 return false; 612 } 613 if (size <= 0) { 614 return false; 615 } 616 617 dataSizeLimit_ = size * unit; 618 619 return true; 620} 621 622bool SubCommandRecord::ParseCallStackOption(const std::vector<std::string> &callStackType) 623{ 624 if (callStackType.empty()) { 625 return true; 626 } else if (callStackType[0] == "fp") { 627 if (callStackType.size() != 1) { 628 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); 629 return false; 630 } 631 isCallStackFp_ = true; 632 } else if (callStackType[0] == "dwarf") { 633 if (callStackType.size() > MAX_DWARF_CALL_CHAIN) { 634 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str()); 635 return false; 636 } else if (callStackType.size() == MAX_DWARF_CALL_CHAIN) { 637 try { 638 callStackDwarfSize_ = std::stoul(callStackType.at(1)); 639 } catch (...) { 640 printf("Invalid -s value, dwarf stack size, '%s' is illegal.\n", 641 callStackType.at(1).c_str()); 642 return false; 643 } 644 if (callStackDwarfSize_ < MIN_SAMPLE_STACK_SIZE) { 645 printf("Invalid -s value, dwarf stack size, '%s' is too small.\n", 646 callStackType.at(1).c_str()); 647 return false; 648 } 649 if (callStackDwarfSize_ > MAX_SAMPLE_STACK_SIZE) { 650 printf("Invalid -s value, dwarf stack size, '%s' is bigger than max value %u.\n", 651 callStackType.at(1).c_str(), MAX_SAMPLE_STACK_SIZE); 652 return false; 653 } 654 if ((callStackDwarfSize_ & MASK_ALIGNED_8) != 0) { 655 printf("Invalid -s value, dwarf stack size, '%s' is not 8 byte aligned.\n", 656 callStackType.at(1).c_str()); 657 return false; 658 } 659 } 660 isCallStackDwarf_ = true; 661 SymbolsFile::needParseJsFunc_ = true; // only in record and dwarf mode need to parse 662 } else { 663 printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str()); 664 return false; 665 } 666 return true; 667} 668 669bool SubCommandRecord::ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes) 670{ 671 if (!vecBranchSampleTypes.empty()) { 672 for (auto &item : vecBranchSampleTypes) { 673 uint64_t type = GetBranchSampleType(item); 674 if (type != 0) { 675 branchSampleType_ |= type; 676 } else { 677 printf("Invalid -j value '%s'\n", item.c_str()); 678 return false; 679 } 680 } 681 if ((branchSampleType_ & TYPE_PERF_SAMPLE_BRANCH) == 0) { 682 printf("Invalid -j value, requires at least one of " 683 "any, any_call, any_ret, ind_call, ind_jmp, cond, call.\n"); 684 return false; 685 } 686 } 687 return true; 688} 689 690bool SubCommandRecord::ParseControlCmd(const std::string cmd) 691{ 692 if (cmd.empty() or cmd == CONTROL_CMD_PREPARE or cmd == CONTROL_CMD_START or 693 cmd == CONTROL_CMD_PAUSE or cmd == CONTROL_CMD_RESUME or cmd == CONTROL_CMD_STOP) { 694 return true; 695 } 696 697 printf("Invalid --control %s option, command should be: prepare, start, pause, resume, stop.\n", 698 cmd.c_str()); 699 return false; 700} 701 702bool SubCommandRecord::SetPerfLimit(const std::string& file, int value, std::function<bool (int, int)> const& cmp, 703 const std::string& param) 704{ 705 int oldValue = 0; 706 CHECK_TRUE(!ReadIntFromProcFile(file, oldValue), false, LOG_TYPE_PRINTF, "read %s fail.\n", file.c_str()); 707 708 if (cmp(oldValue, value)) { 709 HLOGI("cmp return true."); 710 return true; 711 } 712 713 if (IsRoot()) { 714 bool ret = WriteIntToProcFile(file, value); 715 if (!ret) { 716 printf("please set %s to %d manually if perf limit is wanted.\n", file.c_str(), value); 717 } 718 } 719 720 CHECK_TRUE(!OHOS::system::SetParameter(param, std::to_string(value)), false, LOG_TYPE_PRINTF, 721 "set parameter %s fail.\n", param.c_str()); 722 isNeedSetPerfHarden_ = true; 723 return true; 724} 725 726bool SubCommandRecord::SetPerfCpuMaxPercent() 727{ 728 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; }; 729 return SetPerfLimit(PERF_CPU_TIME_MAX_PERCENT, cpuPercent_, cmp, "hiviewdfx.hiperf.perf_cpu_time_max_percent"); 730} 731 732bool SubCommandRecord::SetPerfMaxSampleRate() 733{ 734 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; }; 735 int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY; 736 int maxRate = 0; 737 CHECK_TRUE(!ReadIntFromProcFile(PERF_EVENT_MAX_SAMPLE_RATE, maxRate), false, LOG_TYPE_PRINTF, 738 "read %s fail.\n", PERF_EVENT_MAX_SAMPLE_RATE.c_str()); 739 if (maxRate > frequency) { 740 return true; 741 } 742 int newRate = frequency > static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE) ? frequency : 743 static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE); 744 return SetPerfLimit(PERF_EVENT_MAX_SAMPLE_RATE, newRate, cmp, 745 "hiviewdfx.hiperf.perf_event_max_sample_rate"); 746} 747 748bool SubCommandRecord::SetPerfEventMlock() 749{ 750 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; }; 751 int mlock_kb = sysconf(_SC_NPROCESSORS_CONF) * (mmapPages_ + 1) * 4; 752 return SetPerfLimit(PERF_EVENT_MLOCK_KB, mlock_kb, cmp, "hiviewdfx.hiperf.perf_event_mlock_kb"); 753} 754 755bool SubCommandRecord::SetPerfHarden() 756{ 757 if (!isNeedSetPerfHarden_) { 758 return true; 759 } 760 761 std::string perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, "1"); 762 if (perfHarden == "1") { 763 CHECK_TRUE(!OHOS::system::SetParameter(PERF_DISABLE_PARAM, "0"), false, LOG_TYPE_PRINTF, 764 "set parameter security.perf_harden to 0 fail."); 765 } 766 767 CHECK_TRUE(!OHOS::system::SetParameter(PERF_DISABLE_PARAM, "1"), false, LOG_TYPE_PRINTF, 768 "set parameter security.perf_harden to 1 fail."); 769 return true; 770} 771 772bool SubCommandRecord::TraceOffCpu() 773{ 774 // whether system support sched_switch event 775 int enable = -1; 776 std::string node = SCHED_SWITCH; 777 const std::string nodeDebug = SCHED_SWITCH_DEBUG; 778 CHECK_TRUE(!ReadIntFromProcFile(node.c_str(), enable) and !ReadIntFromProcFile(nodeDebug.c_str(), enable), 779 false, LOG_TYPE_PRINTF, "Cannot trace off CPU, event sched:sched_switch is not available (%s or %s)\n", 780 node.c_str(), nodeDebug.c_str()); 781 782 return true; 783} 784 785void SubCommandRecord::SetSavedCmdlinesSize() 786{ 787 if (!ReadIntFromProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) { 788 printf("Failed to read from %s.\n", SAVED_CMDLINES_SIZE.c_str()); 789 } 790 if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, cmdlinesSize_)) { 791 printf("Failed to write size:%d to %s.\n", cmdlinesSize_, SAVED_CMDLINES_SIZE.c_str()); 792 } 793} 794 795void SubCommandRecord::RecoverSavedCmdlinesSize() 796{ 797 CHECK_TRUE(oldCmdlinesSize_ == 0, NO_RETVAL, 0, ""); 798 if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) { 799 printf("Failed to recover value of %s.\n", SAVED_CMDLINES_SIZE.c_str()); 800 } 801} 802 803bool SubCommandRecord::PreparePerfEvent() 804{ 805 // we need to notify perfEvents_ sampling mode by SetRecordCallBack first 806 auto processRecord = [this](std::unique_ptr<PerfEventRecord> record) -> bool { 807 return this->ProcessRecord(std::move(record)); 808 }; 809 perfEvents_.SetRecordCallBack(processRecord); 810 811 if (selectEvents_.size() > 0 && selectEvents_[0] == "arm_spe_0") { 812 if (!IsRoot()) { 813 printf("%s options needs root privilege, please check usage\n", selectEvents_[0].c_str()); 814 return false; 815 } 816 selectEvents_.insert(selectEvents_.begin(), "sw-dummy"); 817 perfEvents_.isSpe_ = true; 818 perfEvents_.SetConfig(speOptMap_); 819 isSpe_ = true; 820 } 821 822 perfEvents_.SetCpu(selectCpus_); 823 perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions() 824 825 perfEvents_.SetSystemTarget(targetSystemWide_); 826 perfEvents_.SetTimeOut(timeStopSec_); 827 perfEvents_.SetVerboseReport(verboseReport_); 828 perfEvents_.SetMmapPages(mmapPages_); 829 if (isCallStackFp_) { 830 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP); 831 } else if (isCallStackDwarf_) { 832 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF); 833 perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_); 834 } 835 if (!perfEvents_.SetBranchSampleType(branchSampleType_)) { 836 printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str()); 837 HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_); 838 return false; 839 } 840 if (!clockId_.empty()) { 841 perfEvents_.SetClockId(GetClockId(clockId_)); 842 } 843 844 if (frequency_ > 0) { 845 perfEvents_.SetSampleFrequency(frequency_); 846 } else if (period_ > 0) { 847 perfEvents_.SetSamplePeriod(period_); 848 } 849 850 perfEvents_.SetInherit(!noInherit_); 851 perfEvents_.SetTrackedCommand(trackedCommand_); 852 853 // set default sample event 854 if (selectEvents_.empty() && selectGroups_.empty()) { 855 selectEvents_.push_back("hw-cpu-cycles"); 856 } 857 858 CHECK_TRUE(!perfEvents_.AddEvents(selectEvents_), false, 1, "Fail to AddEvents events"); 859 for (auto &group : selectGroups_) { 860 CHECK_TRUE(!perfEvents_.AddEvents(group, true), false, 1, "Fail to AddEvents groups"); 861 } 862 // cpu off add after default event (we need both sched_switch and user selected events) 863 if (offCPU_) { 864 CHECK_TRUE(std::find(selectEvents_.begin(), selectEvents_.end(), "sched_switch") != selectEvents_.end(), 865 false, LOG_TYPE_PRINTF, "--offcpu is not supported event sched_switch\n"); 866 // insert a sched_switch event to trace offcpu event 867 CHECK_TRUE(!perfEvents_.AddOffCpuEvent(), false, 1, "Fail to AddEOffCpuvent"); 868 } 869 870 return true; 871} 872 873bool SubCommandRecord::PrepareSysKernel() 874{ 875 SetHM(); 876 SetSavedCmdlinesSize(); 877 CHECK_TRUE(!SetPerfMaxSampleRate(), false, 1, "Fail to call SetPerfMaxSampleRate(%d)", frequency_); 878 879 CHECK_TRUE(!SetPerfCpuMaxPercent(), false, 1, "Fail to set perf event cpu limit to %d\n", cpuPercent_); 880 881 CHECK_TRUE(!SetPerfEventMlock(), false, 1, "Fail to set perf event mlock limit\n"); 882 883 CHECK_TRUE(!SetPerfHarden(), false, 1, "Fail to set perf event harden\n"); 884 885 CHECK_TRUE(offCPU_ && !TraceOffCpu(), false, 1, "Fail to TraceOffCpu"); 886 887 return true; 888} 889 890bool SubCommandRecord::PrepareVirtualRuntime() 891{ 892 auto saveRecord = [this](std::unique_ptr<PerfEventRecord> record) -> bool { 893 return this->SaveRecord(std::move(record), false); 894 }; 895 virtualRuntime_.SetRecordMode(saveRecord); 896 897 // do some config for virtualRuntime_ 898 virtualRuntime_.SetCallStackExpend(disableCallstackExpend_ ? 0 : 1); 899 // these is same for virtual runtime 900 virtualRuntime_.SetDisableUnwind(disableUnwind_ or delayUnwind_); 901 virtualRuntime_.EnableDebugInfoSymbolic(enableDebugInfoSymbolic_); 902 if (!symbolDir_.empty()) { 903 if (!virtualRuntime_.SetSymbolsPaths(symbolDir_)) { 904 printf("Failed to set symbol path(%s)\n", VectorToString(symbolDir_).c_str()); 905 return false; 906 } 907 } 908 909 // load vsdo first 910 virtualRuntime_.LoadVdso(); 911 912 if (!callChainUserOnly_) { 913 // prepare from kernel and ko 914 virtualRuntime_.SetNeedKernelCallChain(!callChainUserOnly_); 915 virtualRuntime_.UpdateKernelSpaceMaps(); 916 virtualRuntime_.UpdateKernelModulesSpaceMaps(); 917 if (isHM_) { 918 virtualRuntime_.UpdateServiceSpaceMaps(); 919 } 920 } 921 922 if (isHM_) { 923 virtualRuntime_.UpdateDevhostSpaceMaps(); 924 } 925 if (dedupStack_) { 926 virtualRuntime_.SetDedupStack(); 927 auto collectSymbol = [this](PerfRecordSample *sample) { 928 this->CollectSymbol(std::move(sample)); 929 }; 930 virtualRuntime_.SetCollectSymbolCallBack(collectSymbol); 931 } 932 return true; 933} 934 935void SubCommandRecord::WriteCommEventBeforeSampling() 936{ 937 CHECK_TRUE(restart_, NO_RETVAL, 0, ""); 938 for (auto it = mapPids_.begin(); it != mapPids_.end(); ++it) { 939 virtualRuntime_.GetThread(it->first, it->first); 940 for (auto tid : it->second) { 941 virtualRuntime_.GetThread(it->first, tid); 942 } 943 } 944 if (mapPids_.empty()) { 945 if (!selectPids_.empty()) { 946 for (auto pid : selectPids_) { 947 virtualRuntime_.GetThread(pid, pid); 948 } 949 } 950 } 951} 952 953bool SubCommandRecord::ClientCommandResponse(bool OK) 954{ 955 using namespace HiperfClient; 956 if (OK) { 957 size_t size = write(clientPipeOutput_, ReplyOK.c_str(), ReplyOK.size()); 958 if (size != ReplyOK.size()) { 959 char errInfo[ERRINFOLEN] = { 0 }; 960 strerror_r(errno, errInfo, ERRINFOLEN); 961 HLOGD("Server:%s -> %d : %zd %d:%s", ReplyOK.c_str(), clientPipeOutput_, size, errno, 962 errInfo); 963 return false; 964 } 965 return true; 966 } else { 967 size_t size = write(clientPipeOutput_, ReplyFAIL.c_str(), ReplyFAIL.size()); 968 if (size != ReplyFAIL.size()) { 969 char errInfo[ERRINFOLEN] = { 0 }; 970 strerror_r(errno, errInfo, ERRINFOLEN); 971 HLOGD("Server:%s -> %d : %zd %d:%s", ReplyFAIL.c_str(), clientPipeOutput_, size, errno, 972 errInfo); 973 return false; 974 } 975 return true; 976 } 977} 978 979bool SubCommandRecord::IsSamplingRunning() 980{ 981 constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second 982 int waitTrackingCount = maxWaitTrackingCount; 983 while (!perfEvents_.IsTrackRunning()) { 984 waitTrackingCount--; 985 if (waitTrackingCount <= 0) { 986 return false; 987 } 988 constexpr uint64_t waitTrackingSleepMs = 100; 989 std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs)); 990 } 991 return true; 992} 993 994void SubCommandRecord::ClientCommandHandle() 995{ 996 using namespace HiperfClient; 997 CHECK_TRUE(!IsSamplingRunning(), NO_RETVAL, 0, ""); 998 // tell the caller if Exist 999 ClientCommandResponse(true); 1000 1001 bool hasRead = true; 1002 while (!clientExit_) { 1003 if (isFifoServer_ && hasRead) { 1004 if (clientPipeInput_ != -1) { 1005 // after read(), block is disabled, the poll will be waked neven if no data 1006 close(clientPipeInput_); 1007 } 1008 clientPipeInput_ = open(CONTROL_FIFO_FILE_C2S.c_str(), O_RDONLY | O_NONBLOCK); 1009 } 1010 struct pollfd pollFd { 1011 clientPipeInput_, POLLIN, 0 1012 }; 1013 int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TOMEOUT.count()); 1014 if (polled <= 0) { 1015 hasRead = false; 1016 continue; 1017 } 1018 hasRead = true; 1019 std::string command; 1020 while (true) { 1021 char c; 1022 ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1)); 1023 if (result <= 0) { 1024 HLOGD("server :read from pipe file failed"); 1025 break; 1026 } 1027 command.push_back(c); 1028 if (c == '\n') { 1029 break; 1030 } 1031 } 1032 HLOGD("server:new command %s", command.c_str()); 1033 if (command == ReplyStart) { 1034 ClientCommandResponse(perfEvents_.EnableTracking()); 1035 } else if (command == ReplyCheck) { 1036 ClientCommandResponse(!clientExit_); 1037 } else if (command == ReplyStop) { 1038 ClientCommandResponse(perfEvents_.StopTracking()); 1039 } else if (command == ReplyPause) { 1040 ClientCommandResponse(perfEvents_.PauseTracking()); 1041 } else if (command == ReplyResume) { 1042 ClientCommandResponse(perfEvents_.ResumeTracking()); 1043 } 1044 } 1045} 1046 1047bool SubCommandRecord::ProcessControl() 1048{ 1049 if (controlCmd_.empty()) { 1050 return true; 1051 } 1052 1053 if (controlCmd_ == CONTROL_CMD_PREPARE) { 1054 CHECK_TRUE(!CreateFifoServer(), false, 0, ""); 1055 return true; 1056 } 1057 1058 isFifoClient_ = true; 1059 bool ret = false; 1060 if (controlCmd_ == CONTROL_CMD_START) { 1061 ret = SendFifoAndWaitReply(HiperfClient::ReplyStart, CONTROL_WAITREPY_TOMEOUT); 1062 } else if (controlCmd_ == CONTROL_CMD_RESUME) { 1063 ret = SendFifoAndWaitReply(HiperfClient::ReplyResume, CONTROL_WAITREPY_TOMEOUT); 1064 } else if (controlCmd_ == CONTROL_CMD_PAUSE) { 1065 ret = SendFifoAndWaitReply(HiperfClient::ReplyPause, CONTROL_WAITREPY_TOMEOUT); 1066 } else if (controlCmd_ == CONTROL_CMD_STOP) { 1067 ret = SendFifoAndWaitReply(HiperfClient::ReplyStop, CONTROL_WAITREPY_TOMEOUT); 1068 if (ret) { 1069 // wait sampling process exit really 1070 static constexpr uint64_t waitCheckSleepMs = 200; 1071 std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); 1072 while (SendFifoAndWaitReply(HiperfClient::ReplyCheck, CONTROL_WAITREPY_TOMEOUT_CHECK)) { 1073 std::this_thread::sleep_for(milliseconds(waitCheckSleepMs)); 1074 } 1075 HLOGI("wait reply check end."); 1076 } 1077 remove(CONTROL_FIFO_FILE_C2S.c_str()); 1078 remove(CONTROL_FIFO_FILE_S2C.c_str()); 1079 } 1080 1081 if (ret) { 1082 printf("%s sampling success.\n", controlCmd_.c_str()); 1083 } else { 1084 printf("%s sampling failed.\n", controlCmd_.c_str()); 1085 } 1086 return ret; 1087} 1088 1089bool SubCommandRecord::CreateFifoServer() 1090{ 1091 char errInfo[ERRINFOLEN] = { 0 }; 1092 const mode_t fifoMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 1093 if (mkfifo(CONTROL_FIFO_FILE_S2C.c_str(), fifoMode) != 0 or 1094 mkfifo(CONTROL_FIFO_FILE_C2S.c_str(), fifoMode) != 0) { 1095 if (errno == EEXIST) { 1096 printf("another sampling service is running.\n"); 1097 } else { 1098 remove(CONTROL_FIFO_FILE_S2C.c_str()); 1099 remove(CONTROL_FIFO_FILE_C2S.c_str()); 1100 } 1101 strerror_r(errno, errInfo, ERRINFOLEN); 1102 HLOGE("create fifo file failed. %d:%s", errno, errInfo); 1103 return false; 1104 } 1105 1106 pid_t pid = fork(); 1107 if (pid == -1) { 1108 strerror_r(errno, errInfo, ERRINFOLEN); 1109 HLOGE("fork failed. %d:%s", errno, errInfo); 1110 return false; 1111 } else if (pid == 0) { // child process 1112 close(STDIN_FILENO); 1113 close(STDERR_FILENO); 1114 isFifoServer_ = true; 1115 clientPipeOutput_ = open(CONTROL_FIFO_FILE_S2C.c_str(), O_WRONLY); 1116 if (clientPipeOutput_ == -1) { 1117 strerror_r(errno, errInfo, ERRINFOLEN); 1118 HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo); 1119 return false; 1120 } 1121 nullFd_ = open("/dev/null", O_WRONLY); 1122 (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null 1123 } else { // parent process 1124 isFifoClient_ = true; 1125 int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); 1126 if (fd == -1 or !WaitFifoReply(fd, CONTROL_WAITREPY_TOMEOUT)) { 1127 close(fd); 1128 kill(pid, SIGKILL); 1129 remove(CONTROL_FIFO_FILE_C2S.c_str()); 1130 remove(CONTROL_FIFO_FILE_S2C.c_str()); 1131 strerror_r(errno, errInfo, ERRINFOLEN); 1132 printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo); 1133 return false; 1134 } 1135 close(fd); 1136 printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create"); 1137 } 1138 return true; 1139} 1140 1141bool SubCommandRecord::SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut) 1142{ 1143 // need open for read first, because server maybe send reply before client wait to read 1144 int fdRead = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK); 1145 if (fdRead == -1) { 1146 HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_S2C.c_str()); 1147 return false; 1148 } 1149 int fdWrite = open(CONTROL_FIFO_FILE_C2S.c_str(), O_WRONLY | O_NONBLOCK); 1150 if (fdWrite == -1) { 1151 HLOGE("can not open fifo file(%s)", CONTROL_FIFO_FILE_C2S.c_str()); 1152 close(fdRead); 1153 return false; 1154 } 1155 size_t size = write(fdWrite, cmd.c_str(), cmd.size()); 1156 if (size != cmd.size()) { 1157 HLOGE("failed to write fifo file(%s) command(%s)", CONTROL_FIFO_FILE_C2S.c_str(), 1158 cmd.c_str()); 1159 close(fdWrite); 1160 close(fdRead); 1161 return false; 1162 } 1163 close(fdWrite); 1164 1165 bool ret = WaitFifoReply(fdRead, timeOut); 1166 close(fdRead); 1167 return ret; 1168} 1169 1170bool SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut) 1171{ 1172 struct pollfd pollFd { 1173 fd, POLLIN, 0 1174 }; 1175 int polled = poll(&pollFd, 1, timeOut.count()); 1176 std::string reply; 1177 if (polled > 0) { 1178 while (true) { 1179 char c; 1180 ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1)); 1181 if (result <= 0) { 1182 HLOGD("read from fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); 1183 break; 1184 } 1185 reply.push_back(c); 1186 if (c == '\n') { 1187 break; 1188 } 1189 } 1190 } else if (polled == 0) { 1191 HLOGD("wait fifo file(%s) timeout", CONTROL_FIFO_FILE_S2C.c_str()); 1192 } else { 1193 HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str()); 1194 } 1195 1196 if (reply == HiperfClient::ReplyOK) { 1197 return true; 1198 } 1199 return false; 1200} 1201 1202bool SubCommandRecord::OnSubCommand(std::vector<std::string> &args) 1203{ 1204 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord onSubCommand start"); 1205 if (!ProcessControl()) { 1206 return false; 1207 } else if (isFifoClient_) { 1208 return true; 1209 } 1210 1211 // prepare PerfEvents 1212 if (!PrepareSysKernel() or !PreparePerfEvent()) { 1213 return false; 1214 } 1215 1216 // prepar some attr before CreateInitRecordFile 1217 CHECK_TRUE(!perfEvents_.PrepareTracking(), false, LOG_TYPE_WITH_HILOG, "Fail to prepare tracking "); 1218 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord perfEvents prepared"); 1219 1220 if (!CreateInitRecordFile(delayUnwind_ ? false : compressData_)) { 1221 HLOGE("Fail to create record file %s", outputFilename_.c_str()); 1222 HIPERF_HILOGE(MODULE_DEFAULT, "Fail to create record file %s", outputFilename_.c_str()); 1223 return false; 1224 } 1225 1226 if (!PrepareVirtualRuntime()) { 1227 return false; 1228 } 1229 1230 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord virtualRuntime prepared"); 1231 1232 // make a thread wait the other command 1233 if (clientPipeOutput_ != -1) { 1234 clientCommandHanle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this); 1235 } 1236 1237 //write comm event 1238 WriteCommEventBeforeSampling(); 1239 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord StartTracking"); 1240 // start tracking 1241 if (isDataSizeLimitStop_) { 1242 // mmap record size has been larger than limit, dont start sampling. 1243 } else if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) { 1244 CHECK_TRUE(!perfEvents_.StartTracking(isFifoServer_), false, 0, ""); 1245 } else { 1246 if (!perfEvents_.StartTracking((!isFifoServer_) && (clientPipeInput_ == -1))) { 1247 return false; 1248 } 1249 } 1250 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord perfEvents tracking finish"); 1251 1252 startSaveFileTimes_ = steady_clock::now(); 1253 if (!FinishWriteRecordFile()) { 1254 HLOGE("Fail to finish record file %s", outputFilename_.c_str()); 1255 HIPERF_HILOGE(MODULE_DEFAULT, "Fail to finish record file %s", outputFilename_.c_str()); 1256 return false; 1257 } else if (!PostProcessRecordFile()) { 1258 HLOGE("Fail to post process record file"); 1259 HIPERF_HILOGE(MODULE_DEFAULT, "Fail to post process record file"); 1260 return false; 1261 } 1262 1263 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord final report"); 1264 // finial report 1265 RecordCompleted(); 1266 RecoverSavedCmdlinesSize(); 1267 OnlineReportData(); 1268 CloseClientThread(); 1269 RemoveVdsoTmpFile(); 1270 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord finish"); 1271 return true; 1272} 1273 1274void SubCommandRecord::CloseClientThread() 1275{ 1276 if (clientCommandHanle_.joinable()) { 1277 clientExit_ = true; 1278 HLOGI("CloseClientThread"); 1279 if (nullFd_ != -1) { 1280 close(nullFd_); 1281 } 1282 clientCommandHanle_.join(); 1283 close(clientPipeInput_); 1284 close(clientPipeOutput_); 1285 if (isFifoServer_) { 1286 remove(CONTROL_FIFO_FILE_C2S.c_str()); 1287 remove(CONTROL_FIFO_FILE_S2C.c_str()); 1288 } 1289 } 1290} 1291 1292void SubCommandRecord::RemoveVdsoTmpFile() 1293{ 1294 std::vector<std::string> fileName = {"/data/local/tmp/[shmm]", "/data/local/tmp/[vdso]"}; 1295 for (auto name : fileName) { 1296 if (access(name.c_str(), F_OK) == 0) { 1297 if (remove(name.c_str()) != 0) { 1298 char errInfo[ERRINFOLEN] = { 0 }; 1299 strerror_r(errno, errInfo, ERRINFOLEN); 1300 HLOGE("remove file %s failed,errno:%d,errinfo:%s", name.c_str(), errno, errInfo); 1301 } 1302 } 1303 } 1304} 1305 1306bool SubCommandRecord::ProcessRecord(std::unique_ptr<PerfEventRecord> record) 1307{ 1308 CHECK_TRUE(record == nullptr, false, 1, "record is null"); 1309#if HIDEBUG_RECORD_NOT_PROCESS 1310 // some times we want to check performance 1311 // but we still want to see the record number 1312 if (record->GetType() == PERF_RECORD_SAMPLE) { 1313 recordSamples_++; 1314 } else { 1315 recordNoSamples_++; 1316 } 1317 if (record->GetType() == PERF_RECORD_SAMPLE) { 1318 // when the record is allowed from a cache memory, does not free memory after use 1319 record.release(); 1320 } 1321 return true; 1322#else 1323#ifdef HIPERF_DEBUG_TIME 1324 const auto startTime = steady_clock::now(); 1325#endif 1326 if (excludeHiperf_) { 1327 static pid_t pid = getpid(); 1328 if (record->GetPid() == pid) { 1329 if (record->GetType() == PERF_RECORD_SAMPLE) { 1330 // when the record is allowed from a cache memory, does not free memory after use 1331 record.release(); 1332 } 1333 // discard record 1334 return true; 1335 } 1336 } 1337 1338 // May create some simulated events 1339 // it will call ProcessRecord before next line 1340#if !HIDEBUG_RECORD_NOT_PROCESS_VM 1341 virtualRuntime_.UpdateFromRecord(*record); 1342#endif 1343#ifdef HIPERF_DEBUG_TIME 1344 prcessRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 1345#endif 1346 return SaveRecord(std::move(record), true); 1347#endif 1348} 1349 1350bool SubCommandRecord::SaveRecord(std::unique_ptr<PerfEventRecord> record, bool ptrReleaseFlag) 1351{ 1352 ON_SCOPE_EXIT { 1353 if (ptrReleaseFlag && record->GetType() == PERF_RECORD_SAMPLE) { 1354 // when the record is allowed from a cache memory, does not free memory after use 1355 record.release(); 1356 } 1357 }; 1358#if HIDEBUG_RECORD_NOT_SAVE 1359 return true; 1360#endif 1361 if (dataSizeLimit_ > 0u) { 1362 if (dataSizeLimit_ <= fileWriter_->GetDataSize()) { 1363 CHECK_TRUE(isDataSizeLimitStop_, false, 0, ""); 1364 printf("record size %" PRIu64 " is large than limit %" PRIu64 ". stop sampling.\n", 1365 fileWriter_->GetDataSize(), dataSizeLimit_); 1366 perfEvents_.StopTracking(); 1367 isDataSizeLimitStop_ = true; 1368 return false; 1369 } 1370 } 1371 1372 if (record) { 1373#ifdef HIPERF_DEBUG_TIME 1374 const auto saveTime = steady_clock::now(); 1375#endif 1376 if (!fileWriter_->WriteRecord(*record)) { 1377 // write file failed, need stop record 1378 perfEvents_.StopTracking(); 1379 HLOGV("fail to write record %s", record->GetName().c_str()); 1380 return false; 1381 } 1382 if (record->GetType() == PERF_RECORD_SAMPLE) { 1383 recordSamples_++; 1384 } else { 1385 recordNoSamples_++; 1386 } 1387 HLOGV(" write done. size=%zu name=%s", record->GetSize(), record->GetName().c_str()); 1388#ifdef HIPERF_DEBUG_TIME 1389 saveRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - saveTime); 1390#endif 1391 return true; 1392 } 1393 return false; 1394} 1395 1396uint32_t SubCommandRecord::GetCountFromFile(const std::string &fileName) 1397{ 1398 uint32_t ret = 0; 1399 std::string str = ReadFileToString(fileName); 1400 std::vector<std::string> subStrs = StringSplit(str); 1401 for (auto subStr : subStrs) { 1402 ret++; 1403 std::vector<std::string> vSubstr = StringSplit(subStr, "-"); 1404 static const size_t BEGIN_END = 2; 1405 if (vSubstr.size() == BEGIN_END) { 1406 ret += (std::stoi(vSubstr[1]) - std::stoi(vSubstr[0])); 1407 } 1408 } 1409 return ret; 1410} 1411 1412std::string SubCommandRecord::GetCpuDescFromFile() 1413{ 1414 std::string str = ReadFileToString("/proc/cpuinfo"); 1415 std::vector<std::string> subStrs = StringSplit(str, "\n"); 1416 for (auto subStr : subStrs) { 1417 if (subStr.find("model name") == std::string::npos) { 1418 continue; 1419 } 1420 1421 std::vector<std::string> vSubstr = StringSplit(subStr, ": "); 1422 static const size_t NAME_VALUE = 2; 1423 if (vSubstr.size() == NAME_VALUE) { 1424 return vSubstr[1]; 1425 } else { 1426 return ""; 1427 } 1428 } 1429 return ""; 1430} 1431 1432bool SubCommandRecord::AddCpuFeature() 1433{ 1434 utsname unameBuf; 1435 if ((uname(&unameBuf)) != 0) { 1436 perror("uname() failed"); 1437 return false; 1438 } 1439 1440 fileWriter_->AddStringFeature(FEATURE::OSRELEASE, unameBuf.release); 1441 fileWriter_->AddStringFeature(FEATURE::HOSTNAME, unameBuf.nodename); 1442 fileWriter_->AddStringFeature(FEATURE::ARCH, unameBuf.machine); 1443 1444 try { 1445 uint32_t cpuPresent = GetCountFromFile("/sys/devices/system/cpu/present"); 1446 uint32_t cpuOnline = GetCountFromFile("/sys/devices/system/cpu/online"); 1447 fileWriter_->AddNrCpusFeature(FEATURE::NRCPUS, cpuPresent - cpuOnline, cpuOnline); 1448 } catch (...) { 1449 HLOGD("get NRCPUS failed"); 1450 return false; 1451 } 1452 std::string cpuDesc = GetCpuDescFromFile(); 1453 if (!fileWriter_->AddStringFeature(FEATURE::CPUDESC, cpuDesc)) { 1454 return false; 1455 } 1456 1457 // CPUID(vendor,family,model,stepping in /proc/cpuinfo) isn't supported on Hi3516DV300 1458 // CPU_TOPOLOGY(sockets,dies,threads), isn't supported on Hi3516DV300 1459 // NUMA_TOPOLOGY 1460 // HEADER_PMU_MAPPINGS(/sys/bus/event_source/devices/cpu/type) isn't supported on Hi3516DV300 1461 1462 return true; 1463} 1464 1465void SubCommandRecord::AddMemTotalFeature() 1466{ 1467 std::string str = ReadFileToString("/proc/meminfo"); 1468 std::vector<std::string> subStrs = StringSplit(str, " "); 1469 for (auto it = subStrs.begin(); it != subStrs.end(); it++) { 1470 if (it->find("MemTotal:") == std::string::npos) { 1471 continue; 1472 } 1473 1474 if ((it + 1) != subStrs.end()) { 1475 uint64_t memTotal = std::stoul(*(it + 1)); 1476 fileWriter_->AddU64Feature(FEATURE::TOTAL_MEM, memTotal); 1477 } 1478 break; 1479 } 1480} 1481 1482void SubCommandRecord::AddEventDescFeature() 1483{ 1484 fileWriter_->AddEventDescFeature(FEATURE::EVENT_DESC, perfEvents_.GetAttrWithId()); 1485} 1486 1487void SubCommandRecord::AddRecordTimeFeature() 1488{ 1489 // create time 1490 std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 1491 // clang-format off 1492 char buf[256] = { 0 }; 1493 ctime_r(&time, buf); 1494 fileWriter_->AddStringFeature(FEATURE::HIPERF_RECORD_TIME, 1495 StringReplace(buf, "\n", "")); 1496 // clang-format on 1497 return; 1498} 1499 1500void SubCommandRecord::AddWorkloadCmdFeature() 1501{ 1502 if (trackedCommand_.size() > 0) { 1503 fileWriter_->AddStringFeature(FEATURE::HIPERF_WORKLOAD_CMD, trackedCommand_.at(0)); 1504 } else { 1505 HLOGD("no trackedCommand"); 1506 } 1507} 1508 1509void SubCommandRecord::AddCommandLineFeature() 1510{ 1511 // cmdline may end with some no ascii code 1512 // so we cp it with c_str again 1513 std::string fullCommandline = 1514 ReadFileToString("/proc/self/cmdline").c_str() + Command::fullArgument; 1515 fileWriter_->AddStringFeature(FEATURE::CMDLINE, fullCommandline); 1516 HIPERF_HILOGI(MODULE_DEFAULT, "cmd : %{public}s", fullCommandline.c_str()); 1517} 1518 1519void SubCommandRecord::AddCpuOffFeature() 1520{ 1521 if (offCPU_) { 1522 fileWriter_->AddBoolFeature(FEATURE::HIPERF_CPU_OFF); 1523 } 1524} 1525 1526void SubCommandRecord::AddDevhostFeature() 1527{ 1528 if (isHM_) { 1529 fileWriter_->AddStringFeature(FEATURE::HIPERF_HM_DEVHOST, 1530 StringPrintf("%d", virtualRuntime_.devhostPid_)); 1531 } 1532} 1533 1534bool SubCommandRecord::AddFeatureRecordFile() 1535{ 1536 // VERSION 1537 CHECK_TRUE(!AddCpuFeature(), false, 0, ""); 1538 AddMemTotalFeature(); 1539 1540 AddCommandLineFeature(); 1541 1542 AddEventDescFeature(); 1543 1544 AddRecordTimeFeature(); 1545 1546 AddWorkloadCmdFeature(); 1547 1548 AddCpuOffFeature(); 1549 1550 AddDevhostFeature(); 1551 1552 return true; 1553} 1554 1555bool SubCommandRecord::CreateInitRecordFile(bool compressData) 1556{ 1557 if (fileWriter_ == nullptr) { 1558 fileWriter_ = std::make_unique<PerfFileWriter>(); 1559 } 1560 1561 if (!fileWriter_->Open(outputFilename_, compressData)) { 1562 return false; 1563 } 1564 1565 CHECK_TRUE(!fileWriter_->WriteAttrAndId(perfEvents_.GetAttrWithId()), false, 0, ""); 1566 1567 CHECK_TRUE(!AddFeatureRecordFile(), false, 0, ""); 1568 1569 HLOGD("create new record file %s", outputFilename_.c_str()); 1570 return true; 1571} 1572 1573bool SubCommandRecord::PostProcessRecordFile() 1574{ 1575 if (delayUnwind_) { 1576 // 1. prepare file to rewrite 1577 std::string tempFileName = outputFilename_ + ".tmp"; 1578 if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) { 1579 HLOGE("rename failed. unabel to do delay unwind"); 1580 perror("Fail to rename data file"); 1581 return false; 1582 } else { 1583 HLOGD("use temp file '%s' for delay unwind", tempFileName.c_str()); 1584 } 1585 1586 // renew record file 1587 // release the old one 1588 fileWriter_.reset(); 1589 if (!CreateInitRecordFile(compressData_)) { 1590 // create again 1591 HLOGEP("Fail to open data file %s ", outputFilename_.c_str()); 1592 return false; 1593 } 1594 1595 // read temp file 1596 auto fileReader = PerfFileReader::Instance(tempFileName); 1597 if (fileReader == nullptr) { 1598 HLOGEP("Fail to open data file %s ", tempFileName.c_str()); 1599 return false; 1600 } 1601 1602 // 2. read out the file and unwind 1603 auto record_callback = [&](std::unique_ptr<PerfEventRecord> record) { 1604 if (record == nullptr) { 1605 // return false in callback can stop the read process 1606 return false; 1607 } else if (record->GetType() == PERF_RECORD_SAMPLE) { 1608 HLOGM("readback record for unwind"); 1609 virtualRuntime_.UnwindFromRecord(static_cast<PerfRecordSample &>(*record)); 1610 } 1611 SaveRecord(std::move(record)); 1612 return true; 1613 }; 1614 fileReader->ReadDataSection(record_callback); 1615 1616 // 3. close again 1617 1618 // lte FinishWriteRecordFile write matched only symbols 1619 delayUnwind_ = false; 1620 CHECK_TRUE(!FinishWriteRecordFile(), false, 1, "Fail to finish record file %s", outputFilename_.c_str()); 1621 1622 remove(tempFileName.c_str()); 1623 } 1624 return true; 1625} 1626 1627#if USE_COLLECT_SYMBOLIC 1628void SubCommandRecord::SymbolicHits() 1629{ 1630 if (isHM_) { 1631 for (auto &processPair : kernelThreadSymbolsHits_) { 1632 for (auto &vaddr : processPair.second) { 1633 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first, 1634 PERF_CONTEXT_MAX); 1635 } 1636 } 1637 } 1638 1639 for (auto &vaddr : kernelSymbolsHits_) { 1640 virtualRuntime_.GetSymbol(vaddr, 0, 0, PERF_CONTEXT_KERNEL); 1641 } 1642 1643 for (auto &processPair : userSymbolsHits_) { 1644 for (auto &vaddr : processPair.second) { 1645 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first, 1646 PERF_CONTEXT_USER); 1647 } 1648 } 1649} 1650#endif 1651 1652bool SubCommandRecord::CollectionSymbol(std::unique_ptr<PerfEventRecord> record) 1653{ 1654 CHECK_TRUE(record == nullptr, false, 0, ""); 1655 if (record->GetType() == PERF_RECORD_SAMPLE) { 1656 PerfRecordSample *sample = static_cast<PerfRecordSample *>(record.get()); 1657#if USE_COLLECT_SYMBOLIC 1658 CollectSymbol(sample); 1659#else 1660 virtualRuntime_.SymbolicRecord(*sample); 1661#endif 1662 // the record is allowed from a cache memory, does not free memory after use 1663 record.release(); 1664 } 1665 1666 if (isSpe_ && record->GetType() == PERF_RECORD_AUXTRACE) { 1667 PerfRecordAuxtrace *sample = static_cast<PerfRecordAuxtrace *>(record.get()); 1668 virtualRuntime_.SymbolSpeRecord(*sample); 1669 } 1670 1671 return true; 1672} 1673 1674void SubCommandRecord::CollectSymbol(PerfRecordSample *sample) 1675{ 1676 CHECK_TRUE(sample == nullptr, NO_RETVAL, 0, ""); 1677 perf_callchain_context context = sample->inKernel() ? PERF_CONTEXT_KERNEL 1678 : PERF_CONTEXT_USER; 1679 pid_t serverPid; 1680 // if no nr use ip ? remove stack nr == 0? 1681 if (sample->data_.nr == 0) { 1682 serverPid = sample->GetServerPidof(0); 1683 if (virtualRuntime_.IsKernelThread(serverPid)) { 1684 kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ip); 1685 } else if (context == PERF_CONTEXT_KERNEL) { 1686 kernelSymbolsHits_.insert(sample->data_.ip); 1687 } else { 1688 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip); 1689 } 1690 } else { 1691 for (u64 i = 0; i < sample->data_.nr; i++) { 1692 if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) { 1693 if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) { 1694 context = PERF_CONTEXT_KERNEL; 1695 } else { 1696 context = PERF_CONTEXT_USER; 1697 } 1698 } else { 1699 serverPid = sample->GetServerPidof(i); 1700 if (virtualRuntime_.IsKernelThread(serverPid)) { 1701 kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]); 1702 } else if (context == PERF_CONTEXT_KERNEL) { 1703 kernelSymbolsHits_.insert(sample->data_.ips[i]); 1704 } else { 1705 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]); 1706 } 1707 } 1708 } 1709 } 1710} 1711 1712// finish writing data file, then close file 1713bool SubCommandRecord::FinishWriteRecordFile() 1714{ 1715#ifdef HIPERF_DEBUG_TIME 1716 const auto startTime = steady_clock::now(); 1717#endif 1718#if !HIDEBUG_SKIP_PROCESS_SYMBOLS 1719 if (!delayUnwind_) { 1720#if !HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS 1721 if (!callChainUserOnly_) { 1722 virtualRuntime_.UpdateKernelSymbols(); 1723 virtualRuntime_.UpdateKernelModulesSymbols(); 1724 if (isHM_) { 1725 virtualRuntime_.UpdateServiceSymbols(); 1726 } 1727 } 1728 if (isHM_) { 1729 virtualRuntime_.UpdateDevhostSymbols(); 1730 } 1731#endif 1732 HLOGD("Load user symbols"); 1733 if (dedupStack_) { 1734 virtualRuntime_.CollectDedupSymbol(kernelSymbolsHits_, userSymbolsHits_); 1735 } else { 1736 fileWriter_->ReadDataSection( 1737 [this] (std::unique_ptr<PerfEventRecord> record) -> bool { 1738 return this->CollectionSymbol(std::move(record)); 1739 }); 1740 } 1741#if USE_COLLECT_SYMBOLIC 1742 SymbolicHits(); 1743#endif 1744#if HIDEBUG_SKIP_MATCH_SYMBOLS 1745 disableUnwind_ = true; 1746#endif 1747#if !HIDEBUG_SKIP_SAVE_SYMBOLS 1748 CHECK_TRUE(!fileWriter_->AddSymbolsFeature(virtualRuntime_.GetSymbolsFiles()), 1749 false, 1, "Fail to AddSymbolsFeature"); 1750#endif 1751 } 1752#endif 1753 CHECK_TRUE(dedupStack_ && !fileWriter_->AddUniStackTableFeature(virtualRuntime_.GetUniStackTable()), false, 0, ""); 1754 CHECK_TRUE(!fileWriter_->Close(), false, 1, "Fail to close record file %s", outputFilename_.c_str()); 1755#ifdef HIPERF_DEBUG_TIME 1756 saveFeatureTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime); 1757#endif 1758 return true; 1759} 1760 1761#ifdef HIPERF_DEBUG_TIME 1762void SubCommandRecord::ReportTime() 1763{ 1764 printf("updateSymbolsTimes: %0.3f ms\n", 1765 virtualRuntime_.updateSymbolsTimes_.count() / MS_DURATION); 1766 printf("saveFeatureTimes: %0.3f ms\n", saveFeatureTimes_.count() / MS_DURATION); 1767 1768 printf("prcessRecordTimes: %0.3f ms\n", prcessRecordTimes_.count() / MS_DURATION); 1769 printf("-prcessSampleRecordTimes: %0.3f ms\n", 1770 virtualRuntime_.processSampleRecordTimes_.count() / MS_DURATION); 1771 printf("--unwindFromRecordTimes: %0.3f ms\n", 1772 virtualRuntime_.unwindFromRecordTimes_.count() / MS_DURATION); 1773 printf("-prcessMmapRecordTimes: %0.3f ms\n", 1774 virtualRuntime_.processMmapRecordTimes_.count() / MS_DURATION); 1775 printf("-prcessMmap2RecordTimes: %0.3f ms\n", 1776 virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION); 1777 printf("-prcessCommRecordTimes: %0.3f ms\n", 1778 virtualRuntime_.processCommRecordTimes_.count() / MS_DURATION); 1779 printf("-prcessMmap2RecordTimes: %0.3f ms\n", 1780 virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION); 1781 printf("--updateThreadTimes: %0.3f ms\n", 1782 virtualRuntime_.updateThreadTimes_.count() / MS_DURATION); 1783 printf("---threadParseMapsTimes: %0.3f ms\n", 1784 virtualRuntime_.threadParseMapsTimes_.count() / MS_DURATION); 1785 printf("---threadCreateMmapTimes: %0.3f ms\n", 1786 virtualRuntime_.threadCreateMmapTimes_.count() / MS_DURATION); 1787 printf("--unwindCallStackTimes: %0.3f ms\n", 1788 virtualRuntime_.unwindCallStackTimes_.count() / MS_DURATION); 1789 printf("-symbolicRecordTimes: %0.3f ms\n", 1790 virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION); 1791 printf("saveRecordTimes: %0.3f ms\n", saveRecordTimes_.count() / MS_DURATION); 1792 printf("-writeTimes: %0.3f ms\n", fileWriter_->writeTimes_.count() / MS_DURATION); 1793 1794 printf("logTimes: %0.3f ms\n", DebugLogger::GetInstance()->logTimes_.count() / MS_DURATION); 1795 printf("-logSprintfTimes: %0.3f ms\n", 1796 DebugLogger::GetInstance()->logSprintfTimes_.count() / MS_DURATION); 1797 printf("-logWriteTimes: %0.3f ms\n", 1798 DebugLogger::GetInstance()->logWriteTimes_.count() / MS_DURATION); 1799 printf("logCount: %zu (%4.2f ms/log)\n", DebugLogger::GetInstance()->logCount_, 1800 DebugLogger::GetInstance()->logTimes_.count() / 1801 static_cast<double>(DebugLogger::GetInstance()->logCount_) / MS_DURATION); 1802} 1803#endif 1804 1805bool SubCommandRecord::RecordCompleted() 1806{ 1807 if (verboseReport_) { 1808 printf("Save Record used %0.3f ms.\n", 1809 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() / 1810 MS_DURATION); 1811 } 1812 HLOGV("Save Record used %0.3f ms.\n", 1813 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() / 1814 MS_DURATION); 1815 1816 // print brief file info 1817 double mb = static_cast<double>(fileWriter_->GetDataSize()) / (KILO * KILO); 1818 if (compressData_) { 1819 printf("[ hiperf record: Captured and compressed %.3f MB perf data. ]\n", mb); 1820 } else { 1821 printf("[ hiperf record: Captured %.3f MB perf data. ]\n", mb); 1822 } 1823 printf("[ Sample records: %zu, Non sample records: %zu ]\n", recordSamples_, recordNoSamples_); 1824 // Show brief sample lost. 1825 size_t lostSamples = 0; 1826 size_t lostNonSamples = 0; 1827 perfEvents_.GetLostSamples(lostSamples, lostNonSamples); 1828 printf("[ Sample lost: %zu, Non sample lost: %zu ]\n", lostSamples, lostNonSamples); 1829 1830#ifdef HIPERF_DEBUG_TIME 1831 ReportTime(); 1832#endif 1833 return true; 1834} 1835 1836bool SubCommandRecord::RegisterSubCommandRecord(void) 1837{ 1838 return SubCommand::RegisterSubCommand("record", std::make_unique<SubCommandRecord>()); 1839} 1840 1841void SubCommandRecord::SetHM() 1842{ 1843 utsname unameBuf; 1844 if ((uname(&unameBuf)) == 0) { 1845 std::string osrelease = unameBuf.release; 1846 isHM_ = osrelease.find(HMKERNEL) != std::string::npos; 1847 } 1848 virtualRuntime_.SetHM(isHM_); 1849 perfEvents_.SetHM(isHM_); 1850 HLOGD("Set isHM_: %d", isHM_); 1851 if (isHM_) { 1852 // find devhost pid 1853 const std::string basePath {"/proc/"}; 1854 std::vector<std::string> subDirs = GetSubDirs(basePath); 1855 for (const auto &subDir : subDirs) { 1856 if (!IsDigits(subDir)) { 1857 continue; 1858 } 1859 pid_t pid = std::stoll(subDir); 1860 std::string cmdline = GetProcessName(pid); 1861 if (cmdline == "/bin/" + DEVHOST_FILE_NAME) { 1862 virtualRuntime_.SetDevhostPid(pid); 1863 break; 1864 } 1865 } 1866 } 1867} 1868 1869bool SubCommandRecord::OnlineReportData() 1870{ 1871 if (!report_) { 1872 return true; 1873 } 1874 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s begin to report file %" HILOG_PUBLIC "s", 1875 __FUNCTION__, outputFilename_.c_str()); 1876 bool ret = false; 1877 std::string tempFileName = outputFilename_ + ".tmp"; 1878 if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) { 1879 char errInfo[ERRINFOLEN] = { 0 }; 1880 strerror_r(errno, errInfo, ERRINFOLEN); 1881 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s can't rename file %" HILOG_PUBLIC "s" 1882 "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n", 1883 __FUNCTION__, outputFilename_.c_str(), errno, errInfo); 1884 return false; 1885 } 1886 1887 std::unique_ptr<SubCommandReport> reporter = std::make_unique<SubCommandReport>(); 1888 HLOGD("report the file %s to report file %s \n", tempFileName.c_str(), outputFilename_.c_str()); 1889 std::vector<std::string> args; 1890 args.emplace_back("-i"); 1891 args.emplace_back(tempFileName); 1892 args.emplace_back("-o"); 1893 args.emplace_back(outputFilename_); 1894 args.emplace_back("-s"); 1895 if (reporter->ParseOption(args)) { 1896 ret = reporter->OnSubCommand(args); 1897 } 1898 1899 if (remove(tempFileName.c_str()) != 0) { 1900 char errInfo[ERRINFOLEN] = { 0 }; 1901 strerror_r(errno, errInfo, ERRINFOLEN); 1902 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s remove file failed %" HILOG_PUBLIC "s" 1903 "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n", 1904 __FUNCTION__, tempFileName.c_str(), errno, errInfo); 1905 } 1906 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s report result %" HILOG_PUBLIC "s", 1907 __FUNCTION__, ret ? "success" : "fail"); 1908 return ret; 1909} 1910} // namespace HiPerf 1911} // namespace Developtools 1912} // namespace OHOS 1913