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#include "perf_events.h" 16 17#include <cassert> 18#include <cinttypes> 19#include <csignal> 20#include <cstdint> 21#include <cstdlib> 22#include <iostream> 23#include <sys/ioctl.h> 24#include <sys/mman.h> 25#include <sys/resource.h> 26#include <sys/syscall.h> 27#include <unistd.h> 28#if defined(CONFIG_HAS_SYSPARA) 29#include <parameters.h> 30#endif 31 32#include "spe_decoder.h" 33#include "debug_logger.h" 34#include "hiperf_hilog.h" 35#include "register.h" 36#include "subcommand_dump.h" 37#include "symbols_file.h" 38#include "utilities.h" 39 40using namespace std; 41using namespace std::chrono; 42namespace OHOS { 43namespace Developtools { 44namespace HiPerf { 45static std::atomic_bool g_trackRunning = false; 46 47OHOS::UniqueFd PerfEvents::Open(perf_event_attr &attr, pid_t pid, int cpu, int groupFd, 48 unsigned long flags) 49{ 50 OHOS::UniqueFd fd = UniqueFd(syscall(__NR_perf_event_open, &attr, pid, cpu, groupFd, flags)); 51 if (fd < 0) { 52 HLOGEP("syscall perf_event_open failed. "); 53 // dump when open failed. 54 SubCommandDump::DumpPrintEventAttr(attr, std::numeric_limits<int>::min()); 55 } 56 HLOGV("perf_event_open: got fd %d for pid %d cpu %d group %d flags %lu", fd.Get(), pid, cpu, groupFd, flags); 57 return fd; 58} 59 60void PerfEvents::SpeReadData(void *dataPage, u64 *dataTail, uint8_t *buf, u32 size) 61{ 62 void *src = nullptr; 63 u32 left = 0; 64 u32 offset = static_cast<u32>(*dataTail); 65 u32 copySize; 66 u32 traceSize = size; 67 CHECK_TRUE(size > (auxMmapPages_ * pageSize_ + sizeof(struct PerfRecordAuxtraceData)), 68 NO_RETVAL, 1, "buf size invalid"); 69 while (traceSize > 0) { 70 offset = CALC_OFFSET(offset, auxMmapPages_ * pageSize_); 71 left = static_cast<u32>(auxMmapPages_ * pageSize_ - offset); 72 copySize = min(traceSize, left); 73 src = PTR_ADD(dataPage, offset); 74 if (memcpy_s(buf, left, src, copySize) != 0) { 75 HLOGV("SpeReadData memcpy_s failed."); 76 } 77 78 traceSize -= copySize; 79 offset += copySize; 80 buf = reinterpret_cast<uint8_t *>(PTR_ADD(buf, copySize)); 81 } 82 83 *dataTail += size; 84} 85 86static u64 arm_spe_reference() 87{ 88 struct timespec ts; 89 clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 90 return static_cast<uint64_t>(ts.tv_sec) ^ static_cast<uint64_t>(ts.tv_nsec); 91} 92 93void PerfEvents::ReadRecordsFromSpeMmaps(MmapFd& mmapFd, u64 auxOffset, u64 auxSize, u32 pid, u32 tid) 94{ 95 if (mmapFd.mmapPage == nullptr || mmapFd.auxBuf == nullptr) { 96 printf("ReadRecordsFromSpeMmaps mmapFd.mmapPage == nullptr, mmapFd.fd: %d", mmapFd.fd); 97 return; 98 } 99 perf_event_mmap_page *userPage = reinterpret_cast<perf_event_mmap_page *>(mmapFd.mmapPage); 100 void *auxPage = mmapFd.auxBuf; 101 userPage->aux_tail = auxOffset - auxSize; 102 u64 auxHead = userPage->aux_head; 103 u64 auxTail = userPage->aux_tail; 104 HLOGD("mmap cpu %d, aux_head: %llu, aux_tail:%llu, auxOffset:%llu, auxSize:%llu", 105 mmapFd.cpu, auxHead, auxTail, auxOffset, auxSize); 106 if (auxHead <= auxTail) { 107 return; 108 } 109 if (auxSize > auxMmapPages_ * pageSize_) { 110 userPage->aux_tail += auxSize; 111 return; 112 } 113 114 int cpu = mmapFd.cpu; 115 __sync_synchronize(); 116 PerfRecordAuxtrace auxtraceRecord = PerfRecordAuxtrace(auxSize, auxTail, 117 arm_spe_reference(), cpu, tid, cpu, pid); 118 static std::vector<u8> vbuf(RECORD_SIZE_LIMIT); 119 uint8_t *buf; 120 if ((buf = recordBuf_->AllocForWrite(auxtraceRecord.header.size + auxSize)) == nullptr) { 121 HLOGD("alloc buffer failed: PerfRecordAuxtrace record, readSize: %llu", auxSize); 122 return; 123 } 124 auxtraceRecord.GetBinary1(vbuf); 125 if (memcpy_s(buf, auxtraceRecord.header.size, vbuf.data(), auxtraceRecord.header.size) != 0) { 126 HLOGE("memcpy_s return failed"); 127 return; 128 } 129 buf += auxtraceRecord.header.size; 130 131 while (auxSize > 0) { 132 u64 readSize = pageSize_; 133 if (auxSize < pageSize_) { 134 readSize = auxSize; 135 } 136 __sync_synchronize(); 137 SpeReadData(auxPage, &auxTail, buf, readSize); 138 __sync_synchronize(); 139 userPage->aux_tail += readSize; 140 auxTail = userPage->aux_tail; 141 buf += readSize; 142 auxSize -= readSize; 143 } 144 recordBuf_->EndWrite(); 145} 146 147u32 GetSpeType() 148{ 149 FILE *fd; 150 u32 speType; 151 152 fd = fopen("/sys/devices/arm_spe_0/type", "r"); 153 if (fd == nullptr) { 154 HLOGV("open sysfs file failed"); 155 return -1; 156 } 157 if (fscanf_s(fd, "%u", &speType) <= 0) { 158 HLOGV("fscanf_s file failed"); 159 (void)fclose(fd); 160 return -1; 161 } 162 163 (void)fclose(fd); 164 return speType; 165} 166 167PerfEvents::PerfEvents() : timeOut_(DEFAULT_TIMEOUT * THOUSANDS), timeReport_(0) 168{ 169 pageSize_ = sysconf(_SC_PAGESIZE); 170 HLOGI("BuildArch %s", GetArchName(BUILD_ARCH_TYPE).c_str()); 171} 172 173PerfEvents::~PerfEvents() 174{ 175 // close mmap 176 for (auto it = cpuMmap_.begin(); it != cpuMmap_.end();) { 177 const MmapFd &mmapItem = it->second; 178 if (!isSpe_) { 179 munmap(mmapItem.mmapPage, (1 + mmapPages_) * pageSize_); 180 } else { 181 munmap(mmapItem.mmapPage, (1 + auxMmapPages_) * pageSize_); 182 munmap(mmapItem.auxBuf, auxMmapPages_ * pageSize_); 183 } 184 it = cpuMmap_.erase(it); 185 } 186 187 ExitReadRecordBufThread(); 188} 189 190bool PerfEvents::IsEventSupport(perf_type_id type, __u64 config) 191{ 192 unique_ptr<perf_event_attr> attr = PerfEvents::CreateDefaultAttr(type, config); 193 CHECK_TRUE(attr == nullptr, false, 0, ""); 194 UniqueFd fd = Open(*attr.get()); 195 if (fd < 0) { 196 printf("event not support %s\n", GetStaticConfigName(type, config).c_str()); 197 return false; 198 } 199 return true; 200} 201bool PerfEvents::IsEventAttrSupport(perf_event_attr &attr) 202{ 203 UniqueFd fd = Open(attr); 204 if (fd < 0) { 205 return false; 206 } 207 return true; 208} 209 210bool PerfEvents::SetBranchSampleType(uint64_t value) 211{ 212 if (value != 0) { 213 // cpu-clcles event must be supported 214 unique_ptr<perf_event_attr> attr = 215 PerfEvents::CreateDefaultAttr(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); 216 CHECK_TRUE(attr == nullptr, false, 0, ""); 217 attr->sample_type |= PERF_SAMPLE_BRANCH_STACK; 218 attr->branch_sample_type = value; 219 if (!IsEventAttrSupport(*attr.get())) { 220 return false; 221 } 222 } 223 branchSampleType_ = value; 224 return true; 225} 226 227bool PerfEvents::AddDefaultEvent(perf_type_id type) 228{ 229 auto it = DEFAULT_TYPE_CONFIGS.find(type); 230 if (it != DEFAULT_TYPE_CONFIGS.end()) { 231 for (auto config : it->second) { 232 AddEvent(type, config); 233 } 234 } 235 return true; 236} 237 238bool PerfEvents::AddOffCpuEvent() 239{ 240 std::string eventName = "sched:sched_switch"; 241 if (eventSpaceType_ == EventSpaceType::USER) { 242 eventName += ":u"; 243 } else if (eventSpaceType_ == EventSpaceType::KERNEL) { 244 eventName += ":k"; 245 } 246 return AddEvent(eventName); 247} 248 249bool PerfEvents::AddEvents(const std::vector<std::string> &eventStrings, bool group) 250{ 251 bool followGroup = false; 252 HLOGV(" %s", VectorToString(eventStrings).c_str()); 253 254 for (std::string eventString : eventStrings) { 255 if (!AddEvent(eventString, followGroup)) { 256 return false; 257 } 258 // this is group request , Follow-up events need to follow the previous group 259 if (group) { 260 followGroup = true; 261 } 262 } 263 return true; 264} 265 266// event name can have :k or :u suffix 267// tracepoint event name is like sched:sched_switch 268// clang-format off 269bool PerfEvents::ParseEventName(const std::string &nameStr, 270 std::string &name, bool &excludeUser, bool &excludeKernel, bool &isTracePoint) 271// clang-format on 272{ 273 name = nameStr; 274 excludeUser = false; 275 excludeKernel = false; 276 isTracePoint = false; 277 if (nameStr.find(":") != std::string::npos) { 278 static constexpr size_t maxNumberTokensNoTracePoint = 2; 279 static constexpr size_t maxNumberTokensTracePoint = 3; 280 std::vector<std::string> eventTokens = StringSplit(nameStr, ":"); 281 if (eventTokens.size() == maxNumberTokensTracePoint) { 282 // tracepoint event with :u or :k 283 if (eventTokens.back() == "k") { 284 excludeUser = true; 285 HLOGV("kernelOnly event"); 286 } else if (eventTokens.back() == "u") { 287 excludeKernel = true; 288 HLOGV("userOnly event"); 289 } else { 290 HLOGV("unknown event name %s", nameStr.c_str()); 291 return false; 292 } 293 name = eventTokens[0] + ":" + eventTokens[1]; 294 isTracePoint = true; 295 } else if (eventTokens.size() == maxNumberTokensNoTracePoint) { 296 name = eventTokens[0]; 297 if (eventTokens.back() == "k") { 298 excludeUser = true; 299 HLOGV("kernelOnly event"); 300 } else if (eventTokens.back() == "u") { 301 excludeKernel = true; 302 HLOGV("userOnly event"); 303 } else { 304 name = nameStr; 305 isTracePoint = true; 306 HLOGV("tracepoint event is in form of xx:xxx"); 307 } 308 } else { 309 printf("unknown ':' format:'%s'\n", nameStr.c_str()); 310 return false; 311 } 312 if (reportCallBack_) { 313 if ((eventTokens[0] == "sw-task-clock" || eventTokens[0] == "sw-cpu-clock") && 314 (excludeUser || excludeKernel)) { 315 printf( 316 "event type %s with modifier u and modifier k is not supported by the kernel.", 317 eventTokens[0].c_str()); 318 return false; 319 } 320 } 321 } 322 return true; 323} 324 325bool PerfEvents::AddEvent(const std::string &eventString, bool followGroup) 326{ 327 std::string eventName; 328 bool excludeUser = false; 329 bool excludeKernel = false; 330 bool isTracePointEvent = false; 331 if (!ParseEventName(eventString, eventName, excludeUser, excludeKernel, isTracePointEvent)) { 332 return false; 333 } 334 if (excludeUser) { 335 eventSpaceType_ |= EventSpaceType::KERNEL; 336 } else if (excludeKernel) { 337 eventSpaceType_ |= EventSpaceType::USER; 338 } else { 339 eventSpaceType_ |= EventSpaceType::USER_KERNEL; 340 } 341 342 // find if 343 if (isTracePointEvent) { 344 if (traceConfigTable.empty()) { 345 LoadTracepointEventTypesFromSystem(); 346 } 347 for (auto traceType : traceConfigTable) { 348 if (traceType.second == eventName) { 349 return AddEvent(PERF_TYPE_TRACEPOINT, traceType.first, excludeUser, excludeKernel, 350 followGroup); 351 } 352 } 353 } else { 354 if (eventName == "arm_spe_0") { 355 u32 speType = GetSpeType(); 356 return AddSpeEvent(speType); 357 } 358 if (StringStartsWith(eventName, "0x") 359 && eventName.length() <= MAX_HEX_EVENT_NAME_LENGTH && IsHexDigits(eventName)) { 360 return AddEvent(PERF_TYPE_RAW, std::stoull(eventName, nullptr, NUMBER_FORMAT_HEX_BASE), 361 excludeUser, excludeKernel, followGroup); 362 } else { 363 auto [find, typeId, configId] = GetStaticConfigId(eventName); 364 if (find) { 365 return AddEvent(typeId, configId, excludeUser, excludeKernel, followGroup); 366 } 367 } 368 } 369 370 printf("%s event is not supported by the kernel.\n", eventName.c_str()); 371 return false; 372} 373 374bool PerfEvents::AddSpeEvent(u32 type, bool followGroup) 375{ 376 EventGroupItem &eventGroupItem = followGroup ? eventGroupItem_.back() : 377 eventGroupItem_.emplace_back(); 378 EventItem &eventItem = eventGroupItem.eventItems.emplace_back(); 379 380 if (memset_s(&eventItem.attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) { 381 HLOGE("memset_s failed in PerfEvents::AddEvent"); 382 return false; 383 } 384 eventItem.attr.type = type; 385 eventItem.attr.sample_period = MULTIPLE_SIZE; 386 eventItem.attr.size = sizeof(perf_event_attr); 387 eventItem.attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; 388 eventItem.attr.inherit = (inherit_ ? 1 : 0); 389 eventItem.attr.sample_type = SAMPLE_ID; 390 eventItem.attr.sample_id_all = 1; 391 eventItem.attr.disabled = 1; 392 eventItem.attr.config = 0x700010007; // temp type 393 return true; 394} 395 396void PerfEvents::SetConfig(std::map<const std::string, unsigned long long> &speOptMaps) 397{ 398 int jitterOffset = 16; 399 int branchOffset = 32; 400 int loadOffset = 33; 401 int storeOffset = 34; 402 config_ |= (speOptMaps["ts_enable"] & 0x1) << 0; 403 config_ |= (speOptMaps["pa_enable"] & 0x1) << 1; 404 config_ |= (speOptMaps["jitter"] & 0x1) << jitterOffset; 405 config_ |= (speOptMaps["branch_filter"] & 0x1) << branchOffset; 406 config_ |= (speOptMaps["load_filter"] & 0x1) << loadOffset; 407 config_ |= (speOptMaps["store_filter"] & 0x1) << storeOffset; 408 config1_ |= speOptMaps["event_filter"]; 409 config2_ |= speOptMaps["min_latency"] & 0xfff; 410} 411 412bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, bool excludeKernel, 413 bool followGroup) 414{ 415 HLOG_ASSERT(!excludeUser or !excludeKernel); 416 CHECK_TRUE(followGroup && eventGroupItem_.empty(), false, 1, "no group leader create before"); 417 // found the event name 418 CHECK_TRUE(!IsEventSupport(type, config), false, 0, ""); 419 HLOGV("type %d config %llu excludeUser %d excludeKernel %d followGroup %d", type, config, 420 excludeUser, excludeKernel, followGroup); 421 422 // if use follow ? 423 EventGroupItem &eventGroupItem = followGroup ? eventGroupItem_.back() 424 : eventGroupItem_.emplace_back(); 425 // always new item 426 EventItem &eventItem = eventGroupItem.eventItems.emplace_back(); 427 428 eventItem.typeName = GetTypeName(type); 429 if (type == PERF_TYPE_TRACEPOINT) { 430 eventItem.configName = GetTraceConfigName(config); 431 } else { 432 eventItem.configName = GetStaticConfigName(type, config); 433 } 434 435 // attr 436 if (memset_s(&eventItem.attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) { 437 HLOGE("memset_s failed in PerfEvents::AddEvent"); 438 return false; 439 } 440 eventItem.attr.size = sizeof(perf_event_attr); 441 eventItem.attr.type = type; 442 eventItem.attr.config = config; 443 eventItem.attr.disabled = 1; 444 eventItem.attr.read_format = 445 PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; 446 447 eventItem.attr.inherit = (inherit_ ? 1 : 0); 448 eventItem.attr.exclude_kernel = excludeKernel; 449 eventItem.attr.exclude_user = excludeUser; 450 451 // we also need mmap for record 452 if (recordCallBack_) { 453 if (samplePeriod_ > 0) { 454 eventItem.attr.freq = 0; 455 eventItem.attr.sample_freq = 0; 456 eventItem.attr.sample_period = samplePeriod_; 457 } else if (sampleFreq_ > 0) { 458 eventItem.attr.freq = 1; 459 eventItem.attr.sample_freq = sampleFreq_; 460 } else { 461 if (type == PERF_TYPE_TRACEPOINT) { 462 eventItem.attr.freq = 0; 463 eventItem.attr.sample_period = DEFAULT_SAMPLE_PERIOD; 464 } else { 465 eventItem.attr.freq = 1; 466 eventItem.attr.sample_freq = DEFAULT_SAMPLE_FREQUNCY; 467 } 468 } 469 470 eventItem.attr.watermark = 1; 471 eventItem.attr.wakeup_watermark = (mmapPages_ * pageSize_) >> 1; 472 static constexpr unsigned int maxWakeupMark = 1024 * 1024; 473 if (eventItem.attr.wakeup_watermark > maxWakeupMark) { 474 eventItem.attr.wakeup_watermark = maxWakeupMark; 475 } 476 477 // for a group of events, only enable comm/mmap on the first event 478 if (!followGroup) { 479 eventItem.attr.comm = 1; 480 eventItem.attr.mmap = 1; 481 eventItem.attr.mmap2 = 1; 482 eventItem.attr.mmap_data = 1; 483 } 484 485 if (sampleStackType_ == SampleStackType::DWARF) { 486 eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN | 487 PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER; 488 eventItem.attr.exclude_callchain_user = 1; 489 eventItem.attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch()); 490 eventItem.attr.sample_stack_user = dwarfSampleStackSize_; 491 } else if (sampleStackType_ == SampleStackType::FP) { 492 eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN; 493 } else { 494 eventItem.attr.sample_type = SAMPLE_TYPE; 495 } 496 497 if (isHM_) { 498 eventItem.attr.sample_type |= PERF_SAMPLE_SERVER_PID; 499 } 500 } 501 502 // set clock id 503 if (clockId_ != -1) { 504 eventItem.attr.use_clockid = 1; 505 eventItem.attr.clockid = clockId_; 506 } 507 if (branchSampleType_ != 0) { 508 eventItem.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; 509 eventItem.attr.branch_sample_type = branchSampleType_; 510 } 511 512 HLOGV("Add Event: '%s':'%s' %s %s %s", eventItem.typeName.c_str(), eventItem.configName.c_str(), 513 excludeUser ? "excludeUser" : "", excludeKernel ? "excludeKernel" : "", 514 followGroup ? "" : "group leader"); 515 516 return true; 517} 518 519std::unique_ptr<perf_event_attr> PerfEvents::CreateDefaultAttr(perf_type_id type, __u64 config) 520{ 521 unique_ptr<perf_event_attr> attr = make_unique<perf_event_attr>(); 522 if (memset_s(attr.get(), sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) { 523 HLOGE("memset_s failed in PerfEvents::CreateDefaultAttr"); 524 return nullptr; 525 } 526 attr->size = sizeof(perf_event_attr); 527 attr->type = type; 528 attr->config = config; 529 attr->disabled = 1; 530 return attr; 531} 532 533// should move to upper caller 534static struct sigaction g_oldSig { 535}; 536static bool CaptureSig() 537{ 538 HLOGD("capture Ctrl + C to end sampling decently"); 539 struct sigaction sig { 540 }; 541 542 sig.sa_handler = [](int sig) { 543 printf("\n Ctrl + C detected.\n"); 544 g_trackRunning = false; 545 }; 546 547 sig.sa_flags = 0; 548 if (sigaction(SIGINT, &sig, &g_oldSig) < 0) { 549 perror("Fail to call sigaction for SIGINT"); 550 return false; 551 } 552 return true; 553} 554 555static void RecoverCaptureSig() 556{ 557 if (sigaction(SIGINT, &g_oldSig, nullptr) < 0) { 558 perror("Fail to call sigaction for SIGINT"); 559 } 560} 561 562// split to two part 563// because WriteAttrAndId need fd id before start tracking 564bool PerfEvents::PrepareTracking(void) 565{ 566 // 1. prepare cpu pid 567 CHECK_TRUE(!PrepareFdEvents(), false, 1, "PrepareFdEvents() failed"); 568 569 // 2. create events 570 CHECK_TRUE(!CreateFdEvents(), false, 1, "CreateFdEvents() failed"); 571 572 HLOGV("success"); 573 prepared_ = true; 574 return true; 575} 576 577void PerfEvents::ExitReadRecordBufThread() 578{ 579 if (isLowPriorityThread_) { 580 if (setpriority(PRIO_PROCESS, gettid(), 0) != 0) { 581 HLOGW("failed to decrease priority of reading kernel"); 582 } 583 } 584 if (readRecordBufThread_.joinable()) { 585 { 586 std::lock_guard<std::mutex> lk(mtxRrecordBuf_); 587 readRecordThreadRunning_ = false; 588 __sync_synchronize(); 589 cvRecordBuf_.notify_one(); 590 } 591 readRecordBufThread_.join(); 592 } 593} 594 595bool PerfEvents::PrepareRecordThread() 596{ 597 try { 598 recordBuf_ = std::make_unique<RingBuffer>(CalcBufferSize()); 599 } catch (const std::exception &e) { 600 printf("create record buffer(size %zu) failed: %s\n", CalcBufferSize(), e.what()); 601 return false; 602 } 603 readRecordThreadRunning_ = true; 604 readRecordBufThread_ = std::thread(&PerfEvents::ReadRecordFromBuf, this); 605 606 rlimit rlim; 607 int result = getrlimit(RLIMIT_NICE, &rlim); 608 const rlim_t lowPriority = 40; 609 if (result == 0 && rlim.rlim_cur == lowPriority) { 610 const int highPriority = -20; 611 result = setpriority(PRIO_PROCESS, gettid(), highPriority); 612 if (result != 0) { 613 HLOGW("failed to increase priority of reading kernel"); 614 } else { 615 isLowPriorityThread_ = true; 616 } 617 } 618 619 return true; 620} 621 622void PerfEvents::WaitRecordThread() 623{ 624 printf("Process and Saving data...\n"); 625 ExitReadRecordBufThread(); 626 627 const auto usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - trackingEndTime_); 628 if (verboseReport_) { 629 printf("Record Process Completed (wait %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 630 } 631 HLOGV("Record Process Completed (wait %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 632#ifdef HIPERF_DEBUG_TIME 633 printf("%zu record processed, used %0.3f ms(%4.2f us/record)\n", recordEventCount_, 634 recordCallBackTime_.count() / MS_DURATION, 635 recordCallBackTime_.count() / static_cast<double>(recordEventCount_)); 636 printf("total wait sleep time %0.3f ms.\n", recordSleepTime_.count() / MS_DURATION); 637 printf("read from kernel time %0.3f ms.\n", recordKernelReadTime_.count() / MS_DURATION); 638#endif 639} 640 641bool PerfEvents::StartTracking(bool immediately) 642{ 643 if (!prepared_) { 644 HLOGD("do not prepared_"); 645 return false; 646 } 647 648 if (recordCallBack_) { 649 if (!PrepareRecordThread()) { 650 return false; 651 } 652 } 653 654 HLOGD("step: 1. enable event"); 655 HIPERF_HILOGI(MODULE_DEFAULT, "StartTracking step: 1. enable event"); 656 trackingStartTime_ = steady_clock::now(); 657 if (immediately) { 658 if (!EnableTracking()) { 659 HLOGE("PerfEvents::EnableTracking() failed"); 660 return false; 661 } 662 printf("Profiling duration is %.3f seconds.\n", float(timeOut_.count()) / THOUSANDS); 663 printf("Start Profiling...\n"); 664 } 665 666 g_trackRunning = true; 667 if (!CaptureSig()) { 668 HLOGE("captureSig() failed"); 669 g_trackRunning = false; 670 ExitReadRecordBufThread(); 671 return false; 672 } 673 674 HLOGD("step: 2. thread loop"); 675 HIPERF_HILOGI(MODULE_DEFAULT, "StartTracking step: 2. thread loop"); 676 if (recordCallBack_) { 677 RecordLoop(); 678 } else { 679 StatLoop(); 680 } 681 682 HLOGD("step: 3. disable event"); 683 HIPERF_HILOGI(MODULE_DEFAULT, "StartTracking step: 3. disable event"); 684 if (!PerfEventsEnable(false)) { 685 HLOGE("PerfEvents::PerfEventsEnable() failed"); 686 } 687 if (recordCallBack_) { 688 // read left samples after disable events 689 ReadRecordsFromMmaps(); 690 } 691 trackingEndTime_ = steady_clock::now(); 692 693 RecoverCaptureSig(); 694 695 HLOGD("step: 4. wait record thread"); 696 HIPERF_HILOGI(MODULE_DEFAULT, "StartTracking step: 4. wait record thread"); 697 if (recordCallBack_) { 698 WaitRecordThread(); 699 } 700 701 HLOGD("step: 5. exit"); 702 HIPERF_HILOGI(MODULE_DEFAULT, "StartTracking step: 5. exit"); 703 return true; 704} 705 706bool PerfEvents::StopTracking(void) 707{ 708 if (g_trackRunning) { 709 printf("some one called StopTracking\n"); 710 g_trackRunning = false; 711 if (trackedCommand_) { 712 if (trackedCommand_->GetState() == TrackedCommand::State::COMMAND_STARTED) { 713 trackedCommand_->Stop(); 714 } 715 } 716 CHECK_TRUE(!PerfEventsEnable(false), false, 1, "StopTracking : PerfEventsEnable(false) failed"); 717 } 718 return true; 719} 720 721bool PerfEvents::PauseTracking(void) 722{ 723 CHECK_TRUE(!startedTracking_, false, 0, ""); 724 return PerfEventsEnable(false); 725} 726 727bool PerfEvents::ResumeTracking(void) 728{ 729 CHECK_TRUE(!startedTracking_, false, 0, ""); 730 return PerfEventsEnable(true); 731} 732 733bool PerfEvents::EnableTracking() 734{ 735 CHECK_TRUE(startedTracking_, true, 0, ""); 736 CHECK_TRUE(!PerfEventsEnable(true), false, 1, "PerfEvents::PerfEventsEnable() failed"); 737 738 if (trackedCommand_) { 739 // start tracked Command 740 if (trackedCommand_->GetState() == TrackedCommand::State::COMMAND_WAITING) { 741 if (!trackedCommand_->StartCommand()) { 742 int wstatus; 743 if (!trackedCommand_->WaitCommand(wstatus)) { 744 trackedCommand_->Stop(); 745 } 746 std::string commandName = trackedCommand_->GetCommandName(); 747 printf("failed to execute command: %zu: %s\n", commandName.size(), commandName.c_str()); 748 return false; 749 } 750 } else if (trackedCommand_->GetState() != TrackedCommand::State::COMMAND_STARTED) { 751 return false; 752 } 753 } 754 startedTracking_ = true; 755 return true; 756} 757 758bool PerfEvents::IsTrackRunning() 759{ 760 return g_trackRunning; 761} 762 763void PerfEvents::SetSystemTarget(bool systemTarget) 764{ 765 systemTarget_ = systemTarget; 766} 767 768void PerfEvents::SetCpu(std::vector<pid_t> cpus) 769{ 770 cpus_ = cpus; 771} 772 773void PerfEvents::SetPid(std::vector<pid_t> pids) 774{ 775 pids_ = pids; 776} 777 778void PerfEvents::SetTimeOut(float timeOut) 779{ 780 if (timeOut > 0) { 781 timeOut_ = milliseconds(static_cast<int>(timeOut * THOUSANDS)); 782 } 783} 784 785void PerfEvents::SetTimeReport(int timeReport) 786{ 787 static constexpr int minMsReportInterval = 10; 788 if (timeReport < minMsReportInterval && timeReport != 0) { 789 timeReport = minMsReportInterval; 790 printf("time report min value is %d.\n", timeReport); 791 } 792 793 timeReport_ = milliseconds(timeReport); 794} 795 796std::map<__u64, std::string> PerfEvents::GetSupportEvents(perf_type_id type) 797{ 798 if (type == PERF_TYPE_TRACEPOINT) { 799 LoadTracepointEventTypesFromSystem(); 800 } 801 802 std::map<__u64, std::string> eventConfigs; 803 auto configTable = TYPE_CONFIGS.find(type); 804 if (configTable != TYPE_CONFIGS.end()) { 805 auto configs = configTable->second; 806 for (auto config : configs) { 807 if (type == PERF_TYPE_TRACEPOINT || IsEventSupport(type, (__u64)config.first)) { 808 eventConfigs.insert(config); 809 } else { 810 HLOGD("'%s' not support", config.second.c_str()); 811 } 812 } 813 } 814 return eventConfigs; 815} 816 817void PerfEvents::LoadTracepointEventTypesFromSystem() 818{ 819 if (traceConfigTable.empty()) { 820 std::string basePath {"/sys/kernel/tracing/events"}; 821 if (access(basePath.c_str(), R_OK) != 0) { 822 basePath = "/sys/kernel/debug/tracing/events"; 823 } 824 for (const auto &eventName : GetSubDirs(basePath)) { 825 std::string eventPath = basePath + "/" + eventName; 826 for (const auto &concreteEvent : GetSubDirs(eventPath)) { 827 std::string idPath = eventPath + "/" + concreteEvent + "/id"; 828 { 829 std::string resolvedPath = CanonicalizeSpecPath(idPath.c_str()); 830 std::ifstream ifs {resolvedPath}; 831 // clang-format off 832 const std::string idStr = { 833 std::istream_iterator<char>(ifs), 834 std::istream_iterator<char>() 835 }; 836 // clang-format on 837 __u64 id {0}; 838 try { 839 id = std::stoul(idStr, nullptr); 840 } catch (...) { 841 continue; 842 } 843 if (isHM_ && id < MIN_HM_TRACEPOINT_EVENT_ID) { 844 continue; 845 } 846 auto typeConfigs = TYPE_CONFIGS.find(PERF_TYPE_TRACEPOINT); 847 HLOG_ASSERT(typeConfigs != TYPE_CONFIGS.end()); 848 auto configPair = typeConfigs->second.insert( 849 std::make_pair(id, eventName + ":" + concreteEvent)); 850 traceConfigTable.insert(std::make_pair(id, eventName + ":" + concreteEvent)); 851 ConfigTable::iterator it = configPair.first; 852 HLOGV("TYPE_CONFIGS add %llu:%s in %zu", it->first, it->second.c_str(), 853 typeConfigs->second.size()); 854 } 855 } 856 } 857 } 858} 859 860void PerfEvents::SetPerCpu(bool perCpu) 861{ 862 perCpu_ = perCpu; 863} 864 865void PerfEvents::SetPerThread(bool perThread) 866{ 867 perThread_ = perThread; 868} 869 870void PerfEvents::SetVerboseReport(bool verboseReport) 871{ 872 verboseReport_ = verboseReport; 873} 874 875void PerfEvents::SetSampleFrequency(unsigned int frequency) 876{ 877 if (frequency > 0) { 878 sampleFreq_ = frequency; 879 } 880 int maxRate = 0; 881 CHECK_TRUE(!ReadIntFromProcFile("/proc/sys/kernel/perf_event_max_sample_rate", maxRate), 882 NO_RETVAL, LOG_TYPE_PRINTF, 883 "read perf_event_max_sample_rate fail.\n"); 884 if (sampleFreq_ > static_cast<unsigned int>(maxRate)) { 885 static bool printFlag = false; 886 sampleFreq_ = static_cast<unsigned int>(maxRate); 887 if (!printFlag) { 888 printf("Adjust sampling frequency to maximum allowed frequency %d.\n", maxRate); 889 printFlag = true; 890 } 891 } 892} 893 894void PerfEvents::SetSamplePeriod(unsigned int period) 895{ 896 if (period > 0) { 897 samplePeriod_ = period; 898 } 899} 900 901void PerfEvents::SetMmapPages(size_t mmapPages) 902{ 903 mmapPages_ = mmapPages; 904} 905 906void PerfEvents::SetSampleStackType(SampleStackType type) 907{ 908 sampleStackType_ = type; 909} 910 911void PerfEvents::SetDwarfSampleStackSize(uint32_t stackSize) 912{ 913 HLOGD("request stack size is %u", stackSize); 914 dwarfSampleStackSize_ = stackSize; 915} 916 917bool PerfEvents::PerfEventsEnable(bool enable) 918{ 919 HLOGV("%s", std::to_string(enable).c_str()); 920 for (const auto &eventGroupItem : eventGroupItem_) { 921 for (const auto &eventItem : eventGroupItem.eventItems) { 922 for (const auto &fdItem : eventItem.fdItems) { 923 int result = 924 ioctl(fdItem.fd, enable ? PERF_EVENT_IOC_ENABLE : PERF_EVENT_IOC_DISABLE, 0); 925 if (result < 0) { 926 printf("Cannot '%s' perf fd! type config name: '%s:%s'\n", 927 enable ? "enable" : "disable", eventItem.typeName.c_str(), 928 eventItem.configName.c_str()); 929 return false; 930 } 931 } 932 } 933 } 934 return true; 935} 936 937void PerfEvents::SetHM(bool isHM) 938{ 939 isHM_ = isHM; 940} 941 942void PerfEvents::SetStatCallBack(StatCallBack reportCallBack) 943{ 944 reportCallBack_ = reportCallBack; 945} 946void PerfEvents::SetRecordCallBack(RecordCallBack recordCallBack) 947{ 948 recordCallBack_ = recordCallBack; 949} 950 951inline void PerfEvents::PutAllCpus() 952{ 953 int cpuConfigs = sysconf(_SC_NPROCESSORS_CONF); 954 for (int i = 0; i < cpuConfigs; i++) { 955 cpus_.push_back(i); // put all cpu 956 } 957} 958 959bool PerfEvents::PrepareFdEvents(void) 960{ 961 /* 962 https://man7.org/linux/man-pages/man2/perf_event_open.2.html 963 pid == 0 and cpu == -1 964 This measures the calling process/thread on any CPU. 965 966 pid == 0 and cpu >= 0 967 This measures the calling process/thread only when running 968 on the specified CPU. 969 970 pid > 0 and cpu == -1 971 This measures the specified process/thread on any CPU. 972 973 pid > 0 and cpu >= 0 974 This measures the specified process/thread only when 975 running on the specified CPU. 976 977 pid == -1 and cpu >= 0 978 This measures all processes/threads on the specified CPU. 979 This requires CAP_PERFMON (since Linux 5.8) or 980 CAP_SYS_ADMIN capability or a 981 /proc/sys/kernel/perf_event_paranoid value of less than 1. 982 983 pid == -1 and cpu == -1 984 This setting is invalid and will return an error. 985 */ 986 if (systemTarget_) { 987 pids_.clear(); 988 pids_.push_back(-1); 989 } else { 990 if (trackedCommand_) { 991 pids_.push_back(trackedCommand_->GetChildPid()); 992 } 993 if (pids_.empty()) { 994 pids_.push_back(0); // no pid means use 0 as self pid 995 } 996 } 997 if (perCpu_ || perThread_) { 998 cpus_.clear(); 999 PutAllCpus(); 1000 } 1001 if (cpus_.empty()) { 1002 PutAllCpus(); 1003 } 1004 1005 // print info tell user which cpu and process we will select. 1006 if (pids_.size() == 1 && pids_[0] == -1) { 1007 HLOGI("target process: system scope \n"); 1008 } else { 1009 HLOGI("target process: %zu (%s)\n", pids_.size(), 1010 (pids_[0] == 0) ? std::to_string(gettid()).c_str() : VectorToString(pids_).c_str()); 1011 } 1012 if (cpus_.size() == 1 && cpus_[0] == -1) { 1013 HLOGI("target cpus: %ld \n", sysconf(_SC_NPROCESSORS_CONF)); 1014 } else { 1015 HLOGI("target cpus: %zu / %ld (%s)\n", cpus_.size(), sysconf(_SC_NPROCESSORS_CONF), 1016 VectorToString(cpus_).c_str()); 1017 } 1018 1019 return true; 1020} 1021 1022bool PerfEvents::CreateFdEvents(void) 1023{ 1024 // must be some events , or will failed 1025 CHECK_TRUE(eventGroupItem_.empty(), false, LOG_TYPE_PRINTF, "no event select.\n"); 1026 1027 // create each fd by cpu and process user select 1028 /* 1029 https://man7.org/linux/man-pages/man2/perf_event_open.2.html 1030 1031 (A single event on its own is created with group_fd = -1 and is 1032 considered to be a group with only 1 member.) 1033 */ 1034 // Even if there is only one event, it is counted as a group. 1035 1036 uint fdNumber = 0; 1037 uint eventNumber = 0; 1038 uint groupNumber = 0; 1039 for (auto &eventGroupItem : eventGroupItem_) { 1040 /* 1041 Explain what is the configuration of the group: 1042 Suppose we have 2 Event, 2 PID, and 3 CPU settings 1043 According to verification, 1044 Group's fd requires the pid to be the same as the cpu, the only difference is event 1045 In other words, if you want to bind E1 and E2 to the same group 1046 That can only be like this: 1047 1048 event E1 pid P1 cpu C1 [Group 1] 1049 event E1 pid P1 cpu C2 [Group 2] 1050 event E1 pid P1 cpu C3 [Group 3] 1051 1052 event E1 pid P2 cpu C1 [Group 4] 1053 event E1 pid P2 cpu C2 [Group 5] 1054 event E1 pid P2 cpu C3 [Group 6] 1055 1056 event E2 pid P1 cpu C1 [Group 1] 1057 event E2 pid P1 cpu C2 [Group 2] 1058 event E2 pid P1 cpu C3 [Group 3] 1059 1060 event E2 pid P2 cpu C1 [Group 4] 1061 event E2 pid P2 cpu C2 [Group 5] 1062 event E2 pid P2 cpu C3 [Group 6] 1063 */ 1064 HLOGV("group %2u. eventGroupItem leader: '%s':", groupNumber++, 1065 eventGroupItem.eventItems[0].configName.c_str()); 1066 1067 int groupFdCache[cpus_.size()][pids_.size()]; 1068 for (size_t i = 0; i < cpus_.size(); i++) { // each cpu 1069 for (size_t j = 0; j < pids_.size(); j++) { // each pid 1070 // The leader is created first, with group_fd = -1. 1071 groupFdCache[i][j] = -1; 1072 } 1073 } 1074 1075 uint eventIndex = 0; 1076 for (auto &eventItem : eventGroupItem.eventItems) { 1077 HLOGV(" - event %2u. eventName: '%s:%s'", eventIndex++, eventItem.typeName.c_str(), 1078 eventItem.configName.c_str()); 1079 1080 for (size_t icpu = 0; icpu < cpus_.size(); icpu++) { // each cpu 1081 for (size_t ipid = 0; ipid < pids_.size(); ipid++) { // each pid 1082 // one fd event group must match same cpu and same pid config (event can be 1083 // different) 1084 // clang-format off 1085 UniqueFd fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu], 1086 groupFdCache[icpu][ipid], 0); 1087 // clang-format on 1088 if (fd < 0) { 1089 if (errno == ESRCH) { 1090 if (verboseReport_) { 1091 printf("pid %d does not exist.\n", pids_[ipid]); 1092 } 1093 HLOGE("pid %d does not exist.\n", pids_[ipid]); 1094 continue; 1095 } else { 1096 // clang-format off 1097 if (verboseReport_) { 1098 char errInfo[ERRINFOLEN] = { 0 }; 1099 strerror_r(errno, errInfo, ERRINFOLEN); 1100 printf("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", 1101 eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); 1102 } 1103 char errInfo[ERRINFOLEN] = { 0 }; 1104 strerror_r(errno, errInfo, ERRINFOLEN); 1105 HLOGE("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n", 1106 eventItem.configName.c_str(), cpus_[icpu], errno, errInfo); 1107 // clang-format on 1108 break; // jump to next cpu 1109 } 1110 } 1111 // after open successed , fill the result 1112 // make a new FdItem 1113 FdItem &fdItem = eventItem.fdItems.emplace_back(); 1114 fdItem.fd = move(fd); 1115 fdItem.cpu = cpus_[icpu]; 1116 fdItem.pid = pids_[ipid]; 1117 fdNumber++; 1118 1119 // if sampling, mmap ring buffer 1120 bool createMmapSucc = true; 1121 if (recordCallBack_) { 1122 createMmapSucc = isSpe_ ? 1123 CreateSpeMmap(fdItem, eventItem.attr) : CreateMmap(fdItem, eventItem.attr); 1124 } 1125 if (!createMmapSucc) { 1126 printf("create mmap fail\n"); 1127 HIPERF_HILOGI(MODULE_DEFAULT, "create mmap fail"); 1128 return false; 1129 } 1130 // update group leader 1131 int groupFdCacheNum = groupFdCache[icpu][ipid]; 1132 if (groupFdCacheNum == -1) { 1133 groupFdCache[icpu][ipid] = fdItem.fd.Get(); 1134 } 1135 } 1136 } 1137 eventNumber++; 1138 } 1139 } 1140 1141 CHECK_TRUE(fdNumber == 0, false, 1, "open %d fd for %d events", fdNumber, eventNumber); 1142 1143 HLOGD("will try read %u events from %u fd (%zu groups):", eventNumber, fdNumber, 1144 eventGroupItem_.size()); 1145 1146 return true; 1147} 1148 1149bool PerfEvents::StatReport(const __u64 &durationInSec) 1150{ 1151 read_format_no_group readNoGroupValue; 1152 1153 // only need read when need report 1154 HLOGM("eventGroupItem_:%zu", eventGroupItem_.size()); 1155 __u64 groupId = 0; 1156 // clear countEvents data 1157 countEvents_.clear(); 1158 for (const auto &eventGroupItem : eventGroupItem_) { 1159 HLOGM("eventItems:%zu", eventGroupItem.eventItems.size()); 1160 groupId++; 1161 for (const auto &eventItem : eventGroupItem.eventItems) { 1162 // count event info together (every cpu , every pid) 1163 std::string configName = ""; 1164 if (eventItem.attr.exclude_kernel) { 1165 configName = eventItem.configName + ":u"; 1166 } else if (eventItem.attr.exclude_user) { 1167 configName = eventItem.configName + ":k"; 1168 } else { 1169 configName = eventItem.configName; 1170 } 1171 if (countEvents_.count(configName) == 0) { 1172 auto countEvent = make_unique<CountEvent>(CountEvent {}); 1173 countEvents_[configName] = std::move(countEvent); 1174 countEvents_[configName]->userOnly = eventItem.attr.exclude_kernel; 1175 countEvents_[configName]->kernelOnly = eventItem.attr.exclude_user; 1176 } 1177 const std::unique_ptr<CountEvent> &countEvent = countEvents_[configName]; 1178 HLOGM("eventItem.fdItems:%zu", eventItem.fdItems.size()); 1179 for (const auto &fditem : eventItem.fdItems) { 1180 if (read(fditem.fd, &readNoGroupValue, sizeof(readNoGroupValue)) > 0) { 1181 countEvent->eventCount += readNoGroupValue.value; 1182 countEvent->timeEnabled += readNoGroupValue.timeEnabled; 1183 countEvent->timeRunning += readNoGroupValue.timeRunning; 1184 countEvent->id = groupId; 1185 if (durationInSec != 0) { 1186 countEvent->usedCpus = (countEvent->eventCount / 1e9) / (durationInSec / THOUSANDS); 1187 } 1188 if (verboseReport_) { 1189 printf("%s id:%llu(c%d:p%d) timeEnabled:%llu timeRunning:%llu value:%llu\n", 1190 eventItem.configName.c_str(), readNoGroupValue.id, fditem.cpu, fditem.pid, 1191 readNoGroupValue.timeEnabled, readNoGroupValue.timeRunning, readNoGroupValue.value); 1192 } 1193 if ((perCpu_ || perThread_) && readNoGroupValue.value) { 1194 countEvent->summaries.emplace_back(fditem.cpu, fditem.pid, readNoGroupValue.value, 1195 readNoGroupValue.timeEnabled, readNoGroupValue.timeRunning); 1196 } 1197 } else { 1198 printf("read failed from event '%s'\n", eventItem.configName.c_str()); 1199 } 1200 } 1201 } 1202 } 1203 1204 reportCallBack_(countEvents_); 1205 1206 return true; 1207} 1208 1209bool PerfEvents::CreateSpeMmap(const FdItem &item, const perf_event_attr &attr) 1210{ 1211 auto it = cpuMmap_.find(item.cpu); 1212 if (it == cpuMmap_.end()) { 1213 void *rbuf = mmap(nullptr, (1 + auxMmapPages_) * pageSize_, (PROT_READ | PROT_WRITE), MAP_SHARED, 1214 item.fd.Get(), 0); 1215 CHECK_TRUE(rbuf == MMAP_FAILED, false, 1, ""); 1216 void *auxRbuf = mmap(nullptr, auxMmapPages_ * pageSize_, (PROT_READ | PROT_WRITE), MAP_SHARED, 1217 item.fd.Get(), 0); 1218 MmapFd mmapItem; 1219 mmapItem.fd = item.fd.Get(); 1220 mmapItem.mmapPage = reinterpret_cast<perf_event_mmap_page *>(rbuf); 1221 mmapItem.buf = reinterpret_cast<uint8_t *>(rbuf) + pageSize_; 1222 mmapItem.auxBuf = auxRbuf; 1223 mmapItem.bufSize = auxMmapPages_ * pageSize_; 1224 mmapItem.auxBufSize = auxMmapPages_ * pageSize_; 1225 mmapItem.attr = &attr; 1226 mmapItem.tid_ = item.pid; 1227 mmapItem.cpu = item.cpu; 1228 cpuMmap_[item.cpu] = mmapItem; 1229 pollFds_.emplace_back(pollfd {mmapItem.fd, POLLIN, 0}); 1230 } else { 1231 const MmapFd &mmapItem = it->second; 1232 int rc = ioctl(item.fd.Get(), PERF_EVENT_IOC_SET_OUTPUT, mmapItem.fd); 1233 if (rc != 0) { 1234 HLOGEP("ioctl PERF_EVENT_IOC_SET_OUTPUT (%d -> %d) ", item.fd.Get(), mmapItem.fd); 1235 perror("failed to share mapped buffer\n"); 1236 return false; 1237 } 1238 } 1239 return true; 1240} 1241 1242bool PerfEvents::CreateMmap(const FdItem &item, const perf_event_attr &attr) 1243{ 1244 auto it = cpuMmap_.find(item.cpu); 1245 if (it == cpuMmap_.end()) { 1246 void *rbuf = mmap(nullptr, (1 + mmapPages_) * pageSize_, PROT_READ | PROT_WRITE, MAP_SHARED, 1247 item.fd.Get(), 0); 1248 if (rbuf == MMAP_FAILED) { 1249 char errInfo[ERRINFOLEN] = {0}; 1250 strerror_r(errno, errInfo, ERRINFOLEN); 1251 perror("errno:%d, errstr:%s", errno, errInfo); 1252 perror("Fail to call mmap \n"); 1253 return false; 1254 } 1255 MmapFd mmapItem; 1256 mmapItem.fd = item.fd.Get(); 1257 mmapItem.mmapPage = reinterpret_cast<perf_event_mmap_page *>(rbuf); 1258 mmapItem.buf = reinterpret_cast<uint8_t *>(rbuf) + pageSize_; 1259 mmapItem.bufSize = mmapPages_ * pageSize_; 1260 mmapItem.attr = &attr; 1261 mmapItem.posCallChain = GetCallChainPosInSampleRecord(attr); 1262 1263 cpuMmap_[item.cpu] = mmapItem; 1264 pollFds_.emplace_back(pollfd {mmapItem.fd, POLLIN, 0}); 1265 HLOGD("CreateMmap success cpu %d fd %d", item.cpu, mmapItem.fd); 1266 } else { 1267 const MmapFd &mmapItem = it->second; 1268 int rc = ioctl(item.fd.Get(), PERF_EVENT_IOC_SET_OUTPUT, mmapItem.fd); 1269 if (rc != 0) { 1270 HLOGEP("ioctl PERF_EVENT_IOC_SET_OUTPUT (%d -> %d) ", item.fd.Get(), mmapItem.fd); 1271 perror("failed to share mapped buffer\n"); 1272 return false; 1273 } 1274 } 1275 return true; 1276} 1277 1278std::vector<AttrWithId> PerfEvents::GetAttrWithId() const 1279{ 1280 std::vector<AttrWithId> result; 1281 HLOGV("eventGroupItem_ %zu :", eventGroupItem_.size()); 1282 1283 for (const auto &eventGroupItem : eventGroupItem_) { 1284 HLOGV(" eventItems %zu eventItems:", eventGroupItem.eventItems.size()); 1285 for (const auto &eventItem : eventGroupItem.eventItems) { 1286 AttrWithId attrId; 1287 attrId.attr = eventItem.attr; 1288 attrId.name = eventItem.configName; 1289 HLOGV(" fdItems %zu fdItems:", eventItem.fdItems.size()); 1290 for (const auto &fdItem : eventItem.fdItems) { 1291 auto &id = attrId.ids.emplace_back(fdItem.GetPrefId()); 1292 HLOGV(" eventItem.fdItems GetPrefId %" PRIu64 "", id); 1293 } 1294 result.emplace_back(attrId); 1295 } 1296 } 1297 return result; 1298} 1299 1300size_t PerfEvents::CalcBufferSize() 1301{ 1302 size_t maxBufferSize; 1303 if (LittleMemory()) { 1304 maxBufferSize = MAX_BUFFER_SIZE_LITTLE; 1305 } else { 1306 maxBufferSize = MAX_BUFFER_SIZE_LARGE; 1307 } 1308 1309 size_t bufferSize = maxBufferSize; 1310 if (!systemTarget_) { 1311 // suppose ring buffer is 4 times as much as mmap 1312 static constexpr int TIMES = 4; 1313 bufferSize = cpuMmap_.size() * mmapPages_ * pageSize_ * TIMES; 1314 if (bufferSize < MIN_BUFFER_SIZE) { 1315 bufferSize = MIN_BUFFER_SIZE; 1316 } else if (bufferSize > maxBufferSize) { 1317 bufferSize = maxBufferSize; 1318 } 1319 } 1320 HLOGD("CalcBufferSize return %zu", bufferSize); 1321 return bufferSize; 1322} 1323 1324inline bool PerfEvents::IsRecordInMmap(int timeout) 1325{ 1326 HLOGV("enter"); 1327 if (pollFds_.size() > 0) { 1328 if (poll(static_cast<struct pollfd*>(pollFds_.data()), pollFds_.size(), timeout) <= 0) { 1329 // time out try again 1330 return false; 1331 } 1332 } 1333 HLOGV("poll record from mmap"); 1334 return true; 1335} 1336 1337static bool CompareRecordTime(const PerfEvents::MmapFd *left, const PerfEvents::MmapFd *right) 1338{ 1339 return left->timestamp > right->timestamp; 1340} 1341 1342void PerfEvents::ReadRecordsFromMmaps() 1343{ 1344#ifdef HIPERF_DEBUG_TIME 1345 const auto readKenelStartTime = steady_clock::now(); 1346#endif 1347 // get readable mmap at this time 1348 for (auto &it : cpuMmap_) { 1349 ssize_t dataSize = it.second.mmapPage->data_head - it.second.mmapPage->data_tail; 1350 __sync_synchronize(); // this same as rmb in gcc, after reading mmapPage->data_head 1351 if (dataSize <= 0) { 1352 continue; 1353 } 1354 it.second.dataSize = dataSize; 1355 MmapRecordHeap_.push_back(&(it.second)); 1356 } 1357 if (MmapRecordHeap_.empty()) { 1358 return; 1359 } 1360 bool enableFlag = false; 1361 if (MmapRecordHeap_.size() > 1) { 1362 for (const auto &it : MmapRecordHeap_) { 1363 GetRecordFromMmap(*it); 1364 } 1365 std::make_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.end(), CompareRecordTime); 1366 1367 size_t heapSize = MmapRecordHeap_.size(); 1368 while (heapSize > 1) { 1369 std::pop_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize, 1370 CompareRecordTime); 1371 bool auxEvent = false; 1372 u32 pid = 0; 1373 u32 tid = 0; 1374 u64 auxOffset = 0; 1375 u64 auxSize = 0; 1376 MoveRecordToBuf(*MmapRecordHeap_[heapSize - 1], auxEvent, auxOffset, auxSize, pid, tid); 1377 if (isSpe_ && auxEvent) { 1378 ReadRecordsFromSpeMmaps(*MmapRecordHeap_[heapSize - 1], auxOffset, auxSize, pid, tid); 1379 enableFlag = true; 1380 } 1381 if (GetRecordFromMmap(*MmapRecordHeap_[heapSize - 1])) { 1382 std::push_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize, 1383 CompareRecordTime); 1384 } else { 1385 heapSize--; 1386 } 1387 } 1388 } 1389 1390 while (GetRecordFromMmap(*MmapRecordHeap_.front())) { 1391 bool auxEvent = false; 1392 u32 pid = 0; 1393 u32 tid = 0; 1394 u64 auxOffset = 0; 1395 u64 auxSize = 0; 1396 MoveRecordToBuf(*MmapRecordHeap_.front(), auxEvent, auxOffset, auxSize, pid, tid); 1397 if (isSpe_ && auxEvent) { 1398 ReadRecordsFromSpeMmaps(*MmapRecordHeap_.front(), auxOffset, auxSize, pid, tid); 1399 enableFlag = true; 1400 } 1401 } 1402 if (isSpe_ && enableFlag) { 1403 PerfEventsEnable(false); 1404 PerfEventsEnable(true); 1405 } 1406 MmapRecordHeap_.clear(); 1407 { 1408 std::lock_guard<std::mutex> lk(mtxRrecordBuf_); 1409 recordBufReady_ = true; 1410 } 1411 cvRecordBuf_.notify_one(); 1412#ifdef HIPERF_DEBUG_TIME 1413 recordKernelReadTime_ += duration_cast<milliseconds>(steady_clock::now() - readKenelStartTime); 1414#endif 1415} 1416 1417bool PerfEvents::GetRecordFromMmap(MmapFd &mmap) 1418{ 1419 if (mmap.dataSize <= 0) { 1420 return false; 1421 } 1422 1423 GetRecordFieldFromMmap(mmap, &(mmap.header), mmap.mmapPage->data_tail, sizeof(mmap.header)); 1424 if (mmap.header.type != PERF_RECORD_SAMPLE) { 1425 mmap.timestamp = 0; 1426 return true; 1427 } 1428 // in PERF_RECORD_SAMPLE : header + u64 sample_id + u64 ip + u32 pid + u32 tid + u64 time 1429 constexpr size_t timePos = sizeof(perf_event_header) + sizeof(uint64_t) + sizeof(uint64_t) + 1430 sizeof(uint32_t) + sizeof(uint32_t); 1431 GetRecordFieldFromMmap(mmap, &(mmap.timestamp), mmap.mmapPage->data_tail + timePos, 1432 sizeof(mmap.timestamp)); 1433 return true; 1434} 1435 1436void PerfEvents::GetRecordFieldFromMmap(MmapFd &mmap, void *dest, size_t pos, size_t size) 1437{ 1438 CHECK_TRUE(mmap.bufSize == 0, NO_RETVAL, 0, ""); 1439 pos = pos % mmap.bufSize; 1440 size_t tailSize = mmap.bufSize - pos; 1441 size_t copySize = std::min(size, tailSize); 1442 if (memcpy_s(dest, copySize, mmap.buf + pos, copySize) != 0) { 1443 HLOGEP("memcpy_s %p to %p failed. size %zd", mmap.buf + pos, dest, copySize); 1444 } 1445 if (copySize < size) { 1446 size -= copySize; 1447 if (memcpy_s(static_cast<uint8_t *>(dest) + copySize, size, mmap.buf, size) != 0) { 1448 HLOGEP("GetRecordFieldFromMmap: memcpy_s mmap.buf to dest failed. size %zd", size); 1449 } 1450 } 1451} 1452 1453size_t PerfEvents::GetCallChainPosInSampleRecord(const perf_event_attr &attr) 1454{ 1455 // reference struct PerfRecordSampleData 1456 int fixedFieldNumber = __builtin_popcountll( 1457 attr.sample_type & (PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID | 1458 PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | 1459 PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)); 1460 size_t pos = sizeof(perf_event_header) + sizeof(uint64_t) * fixedFieldNumber; 1461 if (attr.sample_type & PERF_SAMPLE_READ) { 1462 pos += sizeof(read_format); 1463 } 1464 return pos; 1465} 1466 1467size_t PerfEvents::GetStackSizePosInSampleRecord(MmapFd &mmap) 1468{ 1469 size_t pos = mmap.posCallChain; 1470 if (mmap.attr->sample_type & PERF_SAMPLE_CALLCHAIN) { 1471 uint64_t nr = 0; 1472 GetRecordFieldFromMmap(mmap, &nr, mmap.mmapPage->data_tail + pos, sizeof(nr)); 1473 pos += (sizeof(nr) + nr * sizeof(uint64_t)); 1474 } 1475 if (mmap.attr->sample_type & PERF_SAMPLE_RAW) { 1476 uint32_t raw_size = 0; 1477 GetRecordFieldFromMmap(mmap, &raw_size, mmap.mmapPage->data_tail + pos, sizeof(raw_size)); 1478 pos += (sizeof(raw_size) + raw_size); 1479 } 1480 if (mmap.attr->sample_type & PERF_SAMPLE_BRANCH_STACK) { 1481 uint64_t bnr = 0; 1482 GetRecordFieldFromMmap(mmap, &bnr, mmap.mmapPage->data_tail + pos, sizeof(bnr)); 1483 pos += (sizeof(bnr) + bnr * sizeof(PerfBranchEntry)); 1484 } 1485 if (mmap.attr->sample_type & PERF_SAMPLE_REGS_USER) { 1486 uint64_t user_abi = 0; 1487 GetRecordFieldFromMmap(mmap, &user_abi, mmap.mmapPage->data_tail + pos, sizeof(user_abi)); 1488 pos += sizeof(user_abi); 1489 if (user_abi > 0) { 1490 uint64_t reg_nr = __builtin_popcountll(mmap.attr->sample_regs_user); 1491 pos += reg_nr * sizeof(uint64_t); 1492 } 1493 } 1494 if (mmap.attr->sample_type & PERF_SAMPLE_SERVER_PID) { 1495 uint64_t server_nr = 0; 1496 GetRecordFieldFromMmap(mmap, &server_nr, mmap.mmapPage->data_tail + pos, sizeof(server_nr)); 1497 pos += (sizeof(server_nr) + server_nr * sizeof(uint64_t)); 1498 } 1499 return pos; 1500} 1501 1502bool PerfEvents::CutStackAndMove(MmapFd &mmap) 1503{ 1504 constexpr uint32_t alignSize = 64; 1505 if (!(mmap.attr->sample_type & PERF_SAMPLE_STACK_USER)) { 1506 return false; 1507 } 1508 size_t stackSizePos = GetStackSizePosInSampleRecord(mmap); 1509 uint64_t stackSize = 0; 1510 GetRecordFieldFromMmap(mmap, &stackSize, mmap.mmapPage->data_tail + stackSizePos, 1511 sizeof(stackSize)); 1512 if (stackSize == 0) { 1513 return false; 1514 } 1515 size_t dynSizePos = stackSizePos + sizeof(uint64_t) + stackSize; 1516 uint64_t dynSize = 0; 1517 GetRecordFieldFromMmap(mmap, &dynSize, mmap.mmapPage->data_tail + dynSizePos, sizeof(dynSize)); 1518 uint64_t newStackSize = std::min((dynSize + alignSize - 1) & 1519 (~(alignSize >= 1 ? alignSize - 1 : 0)), stackSize); 1520 if (newStackSize >= stackSize) { 1521 return false; 1522 } 1523 HLOGM("stackSize %" PRIx64 " dynSize %" PRIx64 " newStackSize %" PRIx64 "\n", stackSize, dynSize, newStackSize); 1524 // move and cut stack_data 1525 // mmap: |<+++copy1+++>|<++++++copy2++++++>|<---------------cut--------------->|<+++copy3+++>| 1526 // ^ ^ ^ ^ 1527 // new_header stackSizePos <stackSize-dynSize> dynSizePos 1528 uint16_t recordSize = mmap.header.size; 1529 mmap.header.size -= stackSize - newStackSize; // reduce the stack size 1530 uint8_t *buf = recordBuf_->AllocForWrite(mmap.header.size); 1531 // copy1: new_header 1532 CHECK_TRUE(buf == nullptr, false, 0, ""); 1533 if (memcpy_s(buf, sizeof(perf_event_header), &(mmap.header), sizeof(perf_event_header)) != 0) { 1534 HLOGEP("memcpy_s %p to %p failed. size %zd", &(mmap.header), buf, 1535 sizeof(perf_event_header)); 1536 } 1537 size_t copyPos = sizeof(perf_event_header); 1538 size_t copySize = stackSizePos - sizeof(perf_event_header) + sizeof(stackSize) + newStackSize; 1539 // copy2: copy stack_size, data[stack_size], 1540 GetRecordFieldFromMmap(mmap, buf + copyPos, mmap.mmapPage->data_tail + copyPos, copySize); 1541 copyPos += copySize; 1542 // copy3: copy dyn_size 1543 GetRecordFieldFromMmap(mmap, buf + copyPos, mmap.mmapPage->data_tail + dynSizePos, 1544 recordSize - dynSizePos); 1545 // update stack_size 1546 if (memcpy_s(buf + stackSizePos, sizeof(stackSize), &(newStackSize), sizeof(newStackSize)) != 0) { 1547 HLOGEP("CutStackAndMove: memcpy_s newStack to buf stackSizePos failed. size %zd", sizeof(newStackSize)); 1548 } 1549 recordBuf_->EndWrite(); 1550 __sync_synchronize(); 1551 mmap.mmapPage->data_tail += recordSize; 1552 mmap.dataSize -= recordSize; 1553 return true; 1554} 1555 1556void PerfEvents::MoveRecordToBuf(MmapFd &mmap, bool &isAuxEvent, u64 &auxOffset, u64 &auxSize, u32 &pid, u32 &tid) 1557{ 1558 uint8_t *buf = nullptr; 1559 if (mmap.header.type == PERF_RECORD_SAMPLE) { 1560 if (recordBuf_->GetFreeSize() <= BUFFER_CRITICAL_LEVEL) { 1561 lostSamples_++; 1562 HLOGD("BUFFER_CRITICAL_LEVEL: lost sample record"); 1563 goto RETURN; 1564 } 1565 if (CutStackAndMove(mmap)) { 1566 return; 1567 } 1568 } else if (mmap.header.type == PERF_RECORD_LOST) { 1569 // in PERF_RECORD_LOST : header + u64 id + u64 lost 1570 constexpr size_t lostPos = sizeof(perf_event_header) + sizeof(uint64_t); 1571 uint64_t lost = 0; 1572 GetRecordFieldFromMmap(mmap, &lost, mmap.mmapPage->data_tail + lostPos, sizeof(lost)); 1573 lostSamples_ += lost; 1574 HLOGD("PERF_RECORD_LOST: lost sample record"); 1575 goto RETURN; 1576 } 1577 if (mmap.header.type == PERF_RECORD_AUX) { 1578 isAuxEvent = true; 1579 // in AUX : header + u64 aux_offset + u64 aux_size 1580 uint64_t auxOffsetPos = sizeof(perf_event_header); 1581 uint64_t auxSizePos = sizeof(perf_event_header) + sizeof(uint64_t); 1582 uint64_t pidPos = auxSizePos + sizeof(uint64_t) * 2; // 2 : offset 1583 uint64_t tidPos = pidPos + sizeof(uint32_t); 1584 GetRecordFieldFromMmap(mmap, &auxOffset, mmap.mmapPage->data_tail + auxOffsetPos, sizeof(auxOffset)); 1585 GetRecordFieldFromMmap(mmap, &auxSize, mmap.mmapPage->data_tail + auxSizePos, sizeof(auxSize)); 1586 GetRecordFieldFromMmap(mmap, &pid, mmap.mmapPage->data_tail + pidPos, sizeof(pid)); 1587 GetRecordFieldFromMmap(mmap, &tid, mmap.mmapPage->data_tail + tidPos, sizeof(tid)); 1588 } 1589 1590 if ((buf = recordBuf_->AllocForWrite(mmap.header.size)) == nullptr) { 1591 // this record type must be Non-Sample 1592 lostNonSamples_++; 1593 HLOGD("alloc buffer failed: lost non-sample record"); 1594 goto RETURN; 1595 } 1596 1597 GetRecordFieldFromMmap(mmap, buf, mmap.mmapPage->data_tail, mmap.header.size); 1598 recordBuf_->EndWrite(); 1599RETURN: 1600 __sync_synchronize(); 1601 mmap.mmapPage->data_tail += mmap.header.size; 1602 mmap.dataSize -= mmap.header.size; 1603} 1604 1605void PerfEvents::ReadRecordFromBuf() 1606{ 1607 const perf_event_attr *attr = GetDefaultAttr(); 1608 uint8_t *p = nullptr; 1609 1610 while (readRecordThreadRunning_) { 1611 { 1612 std::unique_lock<std::mutex> lk(mtxRrecordBuf_); 1613 cvRecordBuf_.wait(lk, [this] { 1614 if (recordBufReady_) { 1615 recordBufReady_ = false; 1616 return true; 1617 } 1618 return !readRecordThreadRunning_; 1619 }); 1620 } 1621 while ((p = recordBuf_->GetReadData()) != nullptr) { 1622 uint32_t *type = reinterpret_cast<uint32_t *>(p); 1623#ifdef HIPERF_DEBUG_TIME 1624 const auto readingStartTime_ = steady_clock::now(); 1625#endif 1626#if !HIDEBUG_SKIP_CALLBACK 1627 recordCallBack_(GetPerfSampleFromCache(*type, p, *attr)); 1628#endif 1629 recordEventCount_++; 1630#ifdef HIPERF_DEBUG_TIME 1631 recordCallBackTime_ += 1632 duration_cast<milliseconds>(steady_clock::now() - readingStartTime_); 1633#endif 1634 recordBuf_->EndRead(); 1635 } 1636 } 1637 HLOGD("exit because trackStoped"); 1638 1639 // read the data left over in buffer 1640 while ((p = recordBuf_->GetReadData()) != nullptr) { 1641 uint32_t *type = reinterpret_cast<uint32_t *>(p); 1642#ifdef HIPERF_DEBUG_TIME 1643 const auto readingStartTime_ = steady_clock::now(); 1644#endif 1645#if !HIDEBUG_SKIP_CALLBACK 1646 recordCallBack_(GetPerfSampleFromCache(*type, p, *attr)); 1647#endif 1648 recordEventCount_++; 1649#ifdef HIPERF_DEBUG_TIME 1650 recordCallBackTime_ += duration_cast<milliseconds>(steady_clock::now() - readingStartTime_); 1651#endif 1652 recordBuf_->EndRead(); 1653 } 1654 HLOGD("read all records from buffer"); 1655} 1656 1657bool PerfEvents::HaveTargetsExit(const std::chrono::steady_clock::time_point &startTime) 1658{ 1659 if (systemTarget_) { 1660 return false; 1661 } 1662 if (trackedCommand_) { 1663 if (trackedCommand_->GetState() < TrackedCommand::State::COMMAND_STARTED) { 1664 return false; // not start yet 1665 } 1666 int wstatus; 1667 if (trackedCommand_->WaitCommand(wstatus)) { 1668 milliseconds usedMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime); 1669 printf("tracked command(%s) has exited (total %" PRId64 " ms)\n", 1670 trackedCommand_->GetCommandName().c_str(), (uint64_t)usedMsTick.count()); 1671 return true; 1672 } 1673 return false; 1674 } 1675 1676 for (auto it = pids_.begin(); it != pids_.end();) { 1677 if (IsDir("/proc/" + std::to_string(*it))) { 1678 it++; 1679 } else { 1680 it = pids_.erase(it); 1681 } 1682 } 1683 if (pids_.empty()) { 1684 milliseconds usedMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime); 1685 printf("tracked processes have exited (total %" PRId64 " ms)\n", (uint64_t)usedMsTick.count()); 1686 return true; 1687 } 1688 return false; 1689} 1690 1691void PerfEvents::RecordLoop() 1692{ 1693 // calc the time 1694 const auto startTime = steady_clock::now(); 1695 const auto endTime = startTime + timeOut_; 1696 milliseconds usedTimeMsTick {}; 1697 int count = 1; 1698 1699 while (g_trackRunning) { 1700 // time check point 1701 const auto thisTime = steady_clock::now(); 1702 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime); 1703 if ((uint64_t)usedTimeMsTick.count() > (uint64_t)(count * THOUSANDS)) { 1704 if (HaveTargetsExit(startTime)) { 1705 break; 1706 } 1707 ++count; 1708 } 1709 1710 if (thisTime >= endTime) { 1711 printf("Timeout exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 1712 if (trackedCommand_) { 1713 trackedCommand_->Stop(); 1714 } 1715 break; 1716 } 1717 1718 int timeLeft = duration_cast<milliseconds>(endTime - thisTime).count(); 1719 if (IsRecordInMmap(std::min(timeLeft, pollTimeOut_))) { 1720 ReadRecordsFromMmaps(); 1721 } 1722 } 1723 1724 if (!g_trackRunning) { 1725 // for user interrupt situation, print time statistic 1726 usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime); 1727 printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 1728 } 1729} 1730 1731void PerfEvents::StatLoop() 1732{ 1733 // calc the time 1734 const auto startTime = steady_clock::now(); 1735 const auto endTime = startTime + timeOut_; 1736 auto nextReportTime = startTime + timeReport_; 1737 milliseconds usedTimeMsTick {}; 1738 __u64 durationInSec = 0; 1739 int64_t thesholdTimeInMs = 2 * HUNDREDS; 1740 1741 while (g_trackRunning) { 1742 // time check point 1743 const auto thisTime = steady_clock::now(); 1744 if (timeReport_ != milliseconds::zero()) { 1745 // stat cmd 1746 if (thisTime >= nextReportTime) { 1747 // only for log or debug? 1748 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime); 1749 durationInSec = usedTimeMsTick.count(); 1750 auto lefTimeMsTick = duration_cast<milliseconds>(endTime - thisTime); 1751 printf("\nReport at %" PRId64 " ms (%" PRId64 " ms left):\n", 1752 (uint64_t)usedTimeMsTick.count(), (uint64_t)lefTimeMsTick.count()); 1753 // end of comments 1754 nextReportTime += timeReport_; 1755 StatReport(durationInSec); 1756 } 1757 } 1758 1759 if (HaveTargetsExit(startTime)) { 1760 break; 1761 } 1762 1763 if (thisTime >= endTime) { 1764 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime); 1765 durationInSec = usedTimeMsTick.count(); 1766 printf("Timeout exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 1767 if (trackedCommand_) { 1768 trackedCommand_->Stop(); 1769 } 1770 break; 1771 } 1772 1773 // lefttime > 200ms sleep 100ms, else sleep 200us 1774 uint64_t defaultSleepUs = 2 * HUNDREDS; // 200us 1775 if (timeReport_ == milliseconds::zero() 1776 && (timeOut_.count() * THOUSANDS) > thesholdTimeInMs) { 1777 milliseconds leftTimeMsTmp = duration_cast<milliseconds>(endTime - thisTime); 1778 if (leftTimeMsTmp.count() > thesholdTimeInMs) { 1779 defaultSleepUs = HUNDREDS * THOUSANDS; // 100ms 1780 } 1781 } 1782 std::this_thread::sleep_for(microseconds(defaultSleepUs)); 1783 } 1784 1785 if (!g_trackRunning) { 1786 // for user interrupt situation, print time statistic 1787 usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime); 1788 printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count()); 1789 } 1790 1791 if (timeReport_ == milliseconds::zero()) { 1792 StatReport(durationInSec); 1793 } 1794} 1795 1796const std::string PerfEvents::GetTypeName(perf_type_id type_id) 1797{ 1798 auto it = PERF_TYPES.find(type_id); 1799 if (it != PERF_TYPES.end()) { 1800 return it->second; 1801 } else { 1802 return "<not found>"; 1803 } 1804} 1805} // namespace HiPerf 1806} // namespace Developtools 1807} // namespace OHOS 1808