1 //===-- PerfReader.cpp - perfscript reader ---------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "PerfReader.h" 9 #include "ProfileGenerator.h" 10 11 static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::ReallyHidden, 12 cl::init(false), cl::ZeroOrMore, 13 cl::desc("Print binary load events.")); 14 15 static cl::opt<bool> ShowUnwinderOutput("show-unwinder-output", 16 cl::ReallyHidden, cl::init(false), 17 cl::ZeroOrMore, 18 cl::desc("Print unwinder output")); 19 20 namespace llvm { 21 namespace sampleprof { 22 23 void VirtualUnwinder::unwindCall(UnwindState &State) { 24 // The 2nd frame after leaf could be missing if stack sample is 25 // taken when IP is within prolog/epilog, as frame chain isn't 26 // setup yet. Fill in the missing frame in that case. 27 // TODO: Currently we just assume all the addr that can't match the 28 // 2nd frame is in prolog/epilog. In the future, we will switch to 29 // pro/epi tracker(Dwarf CFI) for the precise check. 30 uint64_t Source = State.getCurrentLBRSource(); 31 auto *ParentFrame = State.getParentFrame(); 32 if (ParentFrame == State.getDummyRootPtr() || 33 ParentFrame->Address != Source) { 34 State.switchToFrame(Source); 35 } else { 36 State.popFrame(); 37 } 38 State.InstPtr.update(Source); 39 } 40 41 void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) { 42 InstructionPointer &IP = State.InstPtr; 43 uint64_t Target = State.getCurrentLBRTarget(); 44 uint64_t End = IP.Address; 45 if (Binary->usePseudoProbes()) { 46 // We don't need to top frame probe since it should be extracted 47 // from the range. 48 // The outcome of the virtual unwinding with pseudo probes is a 49 // map from a context key to the address range being unwound. 50 // This means basically linear unwinding is not needed for pseudo 51 // probes. The range will be simply recorded here and will be 52 // converted to a list of pseudo probes to report in ProfileGenerator. 53 State.getParentFrame()->recordRangeCount(Target, End, Repeat); 54 } else { 55 // Unwind linear execution part 56 uint64_t LeafAddr = State.CurrentLeafFrame->Address; 57 while (IP.Address >= Target) { 58 uint64_t PrevIP = IP.Address; 59 IP.backward(); 60 // Break into segments for implicit call/return due to inlining 61 bool SameInlinee = Binary->inlineContextEqual(PrevIP, IP.Address); 62 if (!SameInlinee || PrevIP == Target) { 63 State.switchToFrame(LeafAddr); 64 State.CurrentLeafFrame->recordRangeCount(PrevIP, End, Repeat); 65 End = IP.Address; 66 } 67 LeafAddr = IP.Address; 68 } 69 } 70 } 71 72 void VirtualUnwinder::unwindReturn(UnwindState &State) { 73 // Add extra frame as we unwind through the return 74 const LBREntry &LBR = State.getCurrentLBR(); 75 uint64_t CallAddr = Binary->getCallAddrFromFrameAddr(LBR.Target); 76 State.switchToFrame(CallAddr); 77 State.pushFrame(LBR.Source); 78 State.InstPtr.update(LBR.Source); 79 } 80 81 void VirtualUnwinder::unwindBranchWithinFrame(UnwindState &State) { 82 // TODO: Tolerate tail call for now, as we may see tail call from libraries. 83 // This is only for intra function branches, excluding tail calls. 84 uint64_t Source = State.getCurrentLBRSource(); 85 State.switchToFrame(Source); 86 State.InstPtr.update(Source); 87 } 88 89 std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() { 90 std::shared_ptr<StringBasedCtxKey> KeyStr = 91 std::make_shared<StringBasedCtxKey>(); 92 KeyStr->Context = Binary->getExpandedContextStr(Stack); 93 KeyStr->genHashCode(); 94 return KeyStr; 95 } 96 97 std::shared_ptr<ProbeBasedCtxKey> ProbeStack::getContextKey() { 98 std::shared_ptr<ProbeBasedCtxKey> ProbeBasedKey = 99 std::make_shared<ProbeBasedCtxKey>(); 100 for (auto CallProbe : Stack) { 101 ProbeBasedKey->Probes.emplace_back(CallProbe); 102 } 103 CSProfileGenerator::compressRecursionContext<const PseudoProbe *>( 104 ProbeBasedKey->Probes); 105 ProbeBasedKey->genHashCode(); 106 return ProbeBasedKey; 107 } 108 109 template <typename T> 110 void VirtualUnwinder::collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur, 111 T &Stack) { 112 if (Cur->RangeSamples.empty() && Cur->BranchSamples.empty()) 113 return; 114 115 std::shared_ptr<ContextKey> Key = Stack.getContextKey(); 116 auto Ret = CtxCounterMap->emplace(Hashable<ContextKey>(Key), SampleCounter()); 117 SampleCounter &SCounter = Ret.first->second; 118 for (auto &Item : Cur->RangeSamples) { 119 uint64_t StartOffset = Binary->virtualAddrToOffset(std::get<0>(Item)); 120 uint64_t EndOffset = Binary->virtualAddrToOffset(std::get<1>(Item)); 121 SCounter.recordRangeCount(StartOffset, EndOffset, std::get<2>(Item)); 122 } 123 124 for (auto &Item : Cur->BranchSamples) { 125 uint64_t SourceOffset = Binary->virtualAddrToOffset(std::get<0>(Item)); 126 uint64_t TargetOffset = Binary->virtualAddrToOffset(std::get<1>(Item)); 127 SCounter.recordBranchCount(SourceOffset, TargetOffset, std::get<2>(Item)); 128 } 129 } 130 131 template <typename T> 132 void VirtualUnwinder::collectSamplesFromFrameTrie( 133 UnwindState::ProfiledFrame *Cur, T &Stack) { 134 if (!Cur->isDummyRoot()) { 135 if (!Stack.pushFrame(Cur)) { 136 // Process truncated context 137 for (const auto &Item : Cur->Children) { 138 // Start a new traversal ignoring its bottom context 139 collectSamplesFromFrameTrie(Item.second.get()); 140 } 141 return; 142 } 143 } 144 145 collectSamplesFromFrame(Cur, Stack); 146 // Process children frame 147 for (const auto &Item : Cur->Children) { 148 collectSamplesFromFrameTrie(Item.second.get(), Stack); 149 } 150 // Recover the call stack 151 Stack.popFrame(); 152 } 153 154 void VirtualUnwinder::collectSamplesFromFrameTrie( 155 UnwindState::ProfiledFrame *Cur) { 156 if (Binary->usePseudoProbes()) { 157 ProbeStack Stack(Binary); 158 collectSamplesFromFrameTrie<ProbeStack>(Cur, Stack); 159 } else { 160 FrameStack Stack(Binary); 161 collectSamplesFromFrameTrie<FrameStack>(Cur, Stack); 162 } 163 } 164 165 void VirtualUnwinder::recordBranchCount(const LBREntry &Branch, 166 UnwindState &State, uint64_t Repeat) { 167 if (Branch.IsArtificial) 168 return; 169 170 if (Binary->usePseudoProbes()) { 171 // Same as recordRangeCount, We don't need to top frame probe since we will 172 // extract it from branch's source address 173 State.getParentFrame()->recordBranchCount(Branch.Source, Branch.Target, 174 Repeat); 175 } else { 176 State.CurrentLeafFrame->recordBranchCount(Branch.Source, Branch.Target, 177 Repeat); 178 } 179 } 180 181 bool VirtualUnwinder::unwind(const HybridSample *Sample, uint64_t Repeat) { 182 // Capture initial state as starting point for unwinding. 183 UnwindState State(Sample); 184 185 // Sanity check - making sure leaf of LBR aligns with leaf of stack sample 186 // Stack sample sometimes can be unreliable, so filter out bogus ones. 187 if (!State.validateInitialState()) 188 return false; 189 190 // Also do not attempt linear unwind for the leaf range as it's incomplete. 191 bool IsLeaf = true; 192 193 // Now process the LBR samples in parrallel with stack sample 194 // Note that we do not reverse the LBR entry order so we can 195 // unwind the sample stack as we walk through LBR entries. 196 while (State.hasNextLBR()) { 197 State.checkStateConsistency(); 198 199 // Unwind implicit calls/returns from inlining, along the linear path, 200 // break into smaller sub section each with its own calling context. 201 if (!IsLeaf) { 202 unwindLinear(State, Repeat); 203 } 204 IsLeaf = false; 205 206 // Save the LBR branch before it gets unwound. 207 const LBREntry &Branch = State.getCurrentLBR(); 208 209 if (isCallState(State)) { 210 // Unwind calls - we know we encountered call if LBR overlaps with 211 // transition between leaf the 2nd frame. Note that for calls that 212 // were not in the original stack sample, we should have added the 213 // extra frame when processing the return paired with this call. 214 unwindCall(State); 215 } else if (isReturnState(State)) { 216 // Unwind returns - check whether the IP is indeed at a return instruction 217 unwindReturn(State); 218 } else { 219 // Unwind branches - for regular intra function branches, we only 220 // need to record branch with context. 221 unwindBranchWithinFrame(State); 222 } 223 State.advanceLBR(); 224 // Record `branch` with calling context after unwinding. 225 recordBranchCount(Branch, State, Repeat); 226 } 227 // As samples are aggregated on trie, record them into counter map 228 collectSamplesFromFrameTrie(State.getDummyRootPtr()); 229 230 return true; 231 } 232 233 PerfReader::PerfReader(cl::list<std::string> &BinaryFilenames) { 234 // Load the binaries. 235 for (auto Filename : BinaryFilenames) 236 loadBinary(Filename, /*AllowNameConflict*/ false); 237 } 238 239 ProfiledBinary &PerfReader::loadBinary(const StringRef BinaryPath, 240 bool AllowNameConflict) { 241 // The binary table is currently indexed by the binary name not the full 242 // binary path. This is because the user-given path may not match the one 243 // that was actually executed. 244 StringRef BinaryName = llvm::sys::path::filename(BinaryPath); 245 246 // Call to load the binary in the ctor of ProfiledBinary. 247 auto Ret = BinaryTable.insert({BinaryName, ProfiledBinary(BinaryPath)}); 248 249 if (!Ret.second && !AllowNameConflict) { 250 std::string ErrorMsg = "Binary name conflict: " + BinaryPath.str() + 251 " and " + Ret.first->second.getPath().str() + " \n"; 252 exitWithError(ErrorMsg); 253 } 254 255 return Ret.first->second; 256 } 257 258 void PerfReader::updateBinaryAddress(const MMapEvent &Event) { 259 // Load the binary. 260 StringRef BinaryPath = Event.BinaryPath; 261 StringRef BinaryName = llvm::sys::path::filename(BinaryPath); 262 263 auto I = BinaryTable.find(BinaryName); 264 // Drop the event which doesn't belong to user-provided binaries 265 // or if its image is loaded at the same address 266 if (I == BinaryTable.end() || Event.BaseAddress == I->second.getBaseAddress()) 267 return; 268 269 ProfiledBinary &Binary = I->second; 270 271 // A binary image could be uploaded and then reloaded at different 272 // place, so update the address map here 273 AddrToBinaryMap.erase(Binary.getBaseAddress()); 274 AddrToBinaryMap[Event.BaseAddress] = &Binary; 275 276 // Update binary load address. 277 Binary.setBaseAddress(Event.BaseAddress); 278 } 279 280 ProfiledBinary *PerfReader::getBinary(uint64_t Address) { 281 auto Iter = AddrToBinaryMap.lower_bound(Address); 282 if (Iter == AddrToBinaryMap.end() || Iter->first != Address) { 283 if (Iter == AddrToBinaryMap.begin()) 284 return nullptr; 285 Iter--; 286 } 287 return Iter->second; 288 } 289 290 // Use ordered map to make the output deterministic 291 using OrderedCounterForPrint = std::map<std::string, RangeSample>; 292 293 static void printSampleCounter(OrderedCounterForPrint &OrderedCounter) { 294 for (auto Range : OrderedCounter) { 295 outs() << Range.first << "\n"; 296 for (auto I : Range.second) { 297 outs() << " (" << format("%" PRIx64, I.first.first) << ", " 298 << format("%" PRIx64, I.first.second) << "): " << I.second << "\n"; 299 } 300 } 301 } 302 303 static std::string getContextKeyStr(ContextKey *K, 304 const ProfiledBinary *Binary) { 305 std::string ContextStr; 306 if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(K)) { 307 return CtxKey->Context; 308 } else if (const auto *CtxKey = dyn_cast<ProbeBasedCtxKey>(K)) { 309 SmallVector<std::string, 16> ContextStack; 310 for (const auto *Probe : CtxKey->Probes) { 311 Binary->getInlineContextForProbe(Probe, ContextStack, true); 312 } 313 for (const auto &Context : ContextStack) { 314 if (ContextStr.size()) 315 ContextStr += " @ "; 316 ContextStr += Context; 317 } 318 } 319 return ContextStr; 320 } 321 322 static void printRangeCounter(ContextSampleCounterMap &Counter, 323 const ProfiledBinary *Binary) { 324 OrderedCounterForPrint OrderedCounter; 325 for (auto &CI : Counter) { 326 OrderedCounter[getContextKeyStr(CI.first.getPtr(), Binary)] = 327 CI.second.RangeCounter; 328 } 329 printSampleCounter(OrderedCounter); 330 } 331 332 static void printBranchCounter(ContextSampleCounterMap &Counter, 333 const ProfiledBinary *Binary) { 334 OrderedCounterForPrint OrderedCounter; 335 for (auto &CI : Counter) { 336 OrderedCounter[getContextKeyStr(CI.first.getPtr(), Binary)] = 337 CI.second.BranchCounter; 338 } 339 printSampleCounter(OrderedCounter); 340 } 341 342 void PerfReader::printUnwinderOutput() { 343 for (auto I : BinarySampleCounters) { 344 const ProfiledBinary *Binary = I.first; 345 outs() << "Binary(" << Binary->getName().str() << ")'s Range Counter:\n"; 346 printRangeCounter(I.second, Binary); 347 outs() << "\nBinary(" << Binary->getName().str() << ")'s Branch Counter:\n"; 348 printBranchCounter(I.second, Binary); 349 } 350 } 351 352 void PerfReader::unwindSamples() { 353 for (const auto &Item : AggregatedSamples) { 354 const HybridSample *Sample = dyn_cast<HybridSample>(Item.first.getPtr()); 355 VirtualUnwinder Unwinder(&BinarySampleCounters[Sample->Binary], 356 Sample->Binary); 357 Unwinder.unwind(Sample, Item.second); 358 } 359 360 if (ShowUnwinderOutput) 361 printUnwinderOutput(); 362 } 363 364 bool PerfReader::extractLBRStack(TraceStream &TraceIt, 365 SmallVectorImpl<LBREntry> &LBRStack, 366 ProfiledBinary *Binary) { 367 // The raw format of LBR stack is like: 368 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... 369 // ... 0x4005c8/0x4005dc/P/-/-/0 370 // It's in FIFO order and seperated by whitespace. 371 SmallVector<StringRef, 32> Records; 372 TraceIt.getCurrentLine().split(Records, " "); 373 374 // Extract leading instruction pointer if present, use single 375 // list to pass out as reference. 376 size_t Index = 0; 377 if (!Records.empty() && Records[0].find('/') == StringRef::npos) { 378 Index = 1; 379 } 380 // Now extract LBR samples - note that we do not reverse the 381 // LBR entry order so we can unwind the sample stack as we walk 382 // through LBR entries. 383 uint64_t PrevTrDst = 0; 384 385 while (Index < Records.size()) { 386 auto &Token = Records[Index++]; 387 if (Token.size() == 0) 388 continue; 389 390 SmallVector<StringRef, 8> Addresses; 391 Token.split(Addresses, "/"); 392 uint64_t Src; 393 uint64_t Dst; 394 Addresses[0].substr(2).getAsInteger(16, Src); 395 Addresses[1].substr(2).getAsInteger(16, Dst); 396 397 bool SrcIsInternal = Binary->addressIsCode(Src); 398 bool DstIsInternal = Binary->addressIsCode(Dst); 399 bool IsArtificial = false; 400 // Ignore branches outside the current binary. 401 if (!SrcIsInternal && !DstIsInternal) 402 continue; 403 if (!SrcIsInternal && DstIsInternal) { 404 // For transition from external code (such as dynamic libraries) to 405 // the current binary, keep track of the branch target which will be 406 // grouped with the Source of the last transition from the current 407 // binary. 408 PrevTrDst = Dst; 409 continue; 410 } 411 if (SrcIsInternal && !DstIsInternal) { 412 // For transition to external code, group the Source with the next 413 // availabe transition target. 414 if (!PrevTrDst) 415 continue; 416 Dst = PrevTrDst; 417 PrevTrDst = 0; 418 IsArtificial = true; 419 } 420 // TODO: filter out buggy duplicate branches on Skylake 421 422 LBRStack.emplace_back(LBREntry(Src, Dst, IsArtificial)); 423 } 424 TraceIt.advance(); 425 return !LBRStack.empty(); 426 } 427 428 bool PerfReader::extractCallstack(TraceStream &TraceIt, 429 SmallVectorImpl<uint64_t> &CallStack) { 430 // The raw format of call stack is like: 431 // 4005dc # leaf frame 432 // 400634 433 // 400684 # root frame 434 // It's in bottom-up order with each frame in one line. 435 436 // Extract stack frames from sample 437 ProfiledBinary *Binary = nullptr; 438 while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) { 439 StringRef FrameStr = TraceIt.getCurrentLine().ltrim(); 440 uint64_t FrameAddr = 0; 441 if (FrameStr.getAsInteger(16, FrameAddr)) { 442 // We might parse a non-perf sample line like empty line and comments, 443 // skip it 444 TraceIt.advance(); 445 return false; 446 } 447 TraceIt.advance(); 448 if (!Binary) { 449 Binary = getBinary(FrameAddr); 450 // we might have addr not match the MMAP, skip it 451 if (!Binary) { 452 if (AddrToBinaryMap.size() == 0) 453 WithColor::warning() << "No MMAP event in the perfscript, create it " 454 "with '--show-mmap-events'\n"; 455 break; 456 } 457 } 458 // Currently intermixed frame from different binaries is not supported. 459 // Ignore bottom frames not from binary of interest. 460 if (!Binary->addressIsCode(FrameAddr)) 461 break; 462 463 // We need to translate return address to call address 464 // for non-leaf frames 465 if (!CallStack.empty()) { 466 FrameAddr = Binary->getCallAddrFromFrameAddr(FrameAddr); 467 } 468 469 CallStack.emplace_back(FrameAddr); 470 } 471 472 // Skip other unrelated line, find the next valid LBR line 473 // Note that even for empty call stack, we should skip the address at the 474 // bottom, otherwise the following pass may generate a truncated callstack 475 while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) { 476 TraceIt.advance(); 477 } 478 // Filter out broken stack sample. We may not have complete frame info 479 // if sample end up in prolog/epilog, the result is dangling context not 480 // connected to entry point. This should be relatively rare thus not much 481 // impact on overall profile quality. However we do want to filter them 482 // out to reduce the number of different calling contexts. One instance 483 // of such case - when sample landed in prolog/epilog, somehow stack 484 // walking will be broken in an unexpected way that higher frames will be 485 // missing. 486 return !CallStack.empty() && 487 !Binary->addressInPrologEpilog(CallStack.front()); 488 } 489 490 void PerfReader::parseHybridSample(TraceStream &TraceIt) { 491 // The raw hybird sample started with call stack in FILO order and followed 492 // intermediately by LBR sample 493 // e.g. 494 // 4005dc # call stack leaf 495 // 400634 496 // 400684 # call stack root 497 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... 498 // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries 499 // 500 std::shared_ptr<HybridSample> Sample = std::make_shared<HybridSample>(); 501 502 // Parsing call stack and populate into HybridSample.CallStack 503 if (!extractCallstack(TraceIt, Sample->CallStack)) { 504 // Skip the next LBR line matched current call stack 505 if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) 506 TraceIt.advance(); 507 return; 508 } 509 // Set the binary current sample belongs to 510 Sample->Binary = getBinary(Sample->CallStack.front()); 511 512 if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) { 513 // Parsing LBR stack and populate into HybridSample.LBRStack 514 if (extractLBRStack(TraceIt, Sample->LBRStack, Sample->Binary)) { 515 // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR 516 // ranges 517 Sample->CallStack.front() = Sample->LBRStack[0].Target; 518 // Record samples by aggregation 519 Sample->genHashCode(); 520 AggregatedSamples[Hashable<PerfSample>(Sample)]++; 521 } 522 } else { 523 // LBR sample is encoded in single line after stack sample 524 exitWithError("'Hybrid perf sample is corrupted, No LBR sample line"); 525 } 526 } 527 528 void PerfReader::parseMMap2Event(TraceStream &TraceIt) { 529 // Parse a line like: 530 // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0 531 // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so 532 constexpr static const char *const Pattern = 533 "PERF_RECORD_MMAP2 ([0-9]+)/[0-9]+: " 534 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ " 535 "(0x[a-f0-9]+|0) .*\\]: [-a-z]+ (.*)"; 536 // Field 0 - whole line 537 // Field 1 - PID 538 // Field 2 - base address 539 // Field 3 - mmapped size 540 // Field 4 - page offset 541 // Field 5 - binary path 542 enum EventIndex { 543 WHOLE_LINE = 0, 544 PID = 1, 545 BASE_ADDRESS = 2, 546 MMAPPED_SIZE = 3, 547 PAGE_OFFSET = 4, 548 BINARY_PATH = 5 549 }; 550 551 Regex RegMmap2(Pattern); 552 SmallVector<StringRef, 6> Fields; 553 bool R = RegMmap2.match(TraceIt.getCurrentLine(), &Fields); 554 if (!R) { 555 std::string ErrorMsg = "Cannot parse mmap event: Line" + 556 Twine(TraceIt.getLineNumber()).str() + ": " + 557 TraceIt.getCurrentLine().str() + " \n"; 558 exitWithError(ErrorMsg); 559 } 560 MMapEvent Event; 561 Fields[PID].getAsInteger(10, Event.PID); 562 Fields[BASE_ADDRESS].getAsInteger(0, Event.BaseAddress); 563 Fields[MMAPPED_SIZE].getAsInteger(0, Event.Size); 564 Fields[PAGE_OFFSET].getAsInteger(0, Event.Offset); 565 Event.BinaryPath = Fields[BINARY_PATH]; 566 updateBinaryAddress(Event); 567 if (ShowMmapEvents) { 568 outs() << "Mmap: Binary " << Event.BinaryPath << " loaded at " 569 << format("0x%" PRIx64 ":", Event.BaseAddress) << " \n"; 570 } 571 TraceIt.advance(); 572 } 573 574 void PerfReader::parseEventOrSample(TraceStream &TraceIt) { 575 if (TraceIt.getCurrentLine().startswith("PERF_RECORD_MMAP2")) 576 parseMMap2Event(TraceIt); 577 else if (getPerfScriptType() == PERF_LBR_STACK) 578 parseHybridSample(TraceIt); 579 else { 580 // TODO: parse other type sample 581 TraceIt.advance(); 582 } 583 } 584 585 void PerfReader::parseAndAggregateTrace(StringRef Filename) { 586 // Trace line iterator 587 TraceStream TraceIt(Filename); 588 while (!TraceIt.isAtEoF()) 589 parseEventOrSample(TraceIt); 590 } 591 592 void PerfReader::checkAndSetPerfType( 593 cl::list<std::string> &PerfTraceFilenames) { 594 bool HasHybridPerf = true; 595 for (auto FileName : PerfTraceFilenames) { 596 if (!isHybridPerfScript(FileName)) { 597 HasHybridPerf = false; 598 break; 599 } 600 } 601 602 if (HasHybridPerf) { 603 PerfType = PERF_LBR_STACK; 604 } else { 605 // TODO: Support other type of perf script 606 PerfType = PERF_INVILID; 607 } 608 609 if (BinaryTable.size() > 1) { 610 // TODO: remove this if everything is ready to support multiple binaries. 611 exitWithError("Currently only support one input binary, multiple binaries' " 612 "profile will be merged in one profile and make profile " 613 "summary info inaccurate. Please use `perfdata` to merge " 614 "profiles from multiple binaries."); 615 } 616 } 617 618 void PerfReader::generateRawProfile() { 619 if (getPerfScriptType() == PERF_LBR_STACK) { 620 // Unwind samples if it's hybird sample 621 unwindSamples(); 622 } else if (getPerfScriptType() == PERF_LBR) { 623 // TODO: range overlap computation for regular AutoFDO 624 } 625 } 626 627 void PerfReader::parsePerfTraces(cl::list<std::string> &PerfTraceFilenames) { 628 // Check and set current perfscript type 629 checkAndSetPerfType(PerfTraceFilenames); 630 // Parse perf traces and do aggregation. 631 for (auto Filename : PerfTraceFilenames) 632 parseAndAggregateTrace(Filename); 633 634 generateRawProfile(); 635 } 636 637 } // end namespace sampleprof 638 } // end namespace llvm 639