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