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