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