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