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/DebugInfo/Symbolize/SymbolizableModule.h" 11 #include "llvm/Support/FileSystem.h" 12 #include "llvm/Support/Process.h" 13 14 #define DEBUG_TYPE "perf-reader" 15 16 cl::opt<bool> SkipSymbolization("skip-symbolization", 17 cl::desc("Dump the unsymbolized profile to the " 18 "output file. It will show unwinder " 19 "output for CS profile generation.")); 20 21 static cl::opt<bool> ShowMmapEvents("show-mmap-events", 22 cl::desc("Print binary load events.")); 23 24 static cl::opt<bool> 25 UseOffset("use-offset", cl::init(true), 26 cl::desc("Work with `--skip-symbolization` or " 27 "`--unsymbolized-profile` to write/read the " 28 "offset instead of virtual address.")); 29 30 static cl::opt<bool> UseLoadableSegmentAsBase( 31 "use-first-loadable-segment-as-base", 32 cl::desc("Use first loadable segment address as base address " 33 "for offsets in unsymbolized profile. By default " 34 "first executable segment address is used")); 35 36 static cl::opt<bool> 37 IgnoreStackSamples("ignore-stack-samples", 38 cl::desc("Ignore call stack samples for hybrid samples " 39 "and produce context-insensitive profile.")); 40 cl::opt<bool> ShowDetailedWarning("show-detailed-warning", 41 cl::desc("Show detailed warning message.")); 42 43 extern cl::opt<std::string> PerfTraceFilename; 44 extern cl::opt<bool> ShowDisassemblyOnly; 45 extern cl::opt<bool> ShowSourceLocations; 46 extern cl::opt<std::string> OutputFilename; 47 48 namespace llvm { 49 namespace sampleprof { 50 51 void VirtualUnwinder::unwindCall(UnwindState &State) { 52 uint64_t Source = State.getCurrentLBRSource(); 53 auto *ParentFrame = State.getParentFrame(); 54 // The 2nd frame after leaf could be missing if stack sample is 55 // taken when IP is within prolog/epilog, as frame chain isn't 56 // setup yet. Fill in the missing frame in that case. 57 // TODO: Currently we just assume all the addr that can't match the 58 // 2nd frame is in prolog/epilog. In the future, we will switch to 59 // pro/epi tracker(Dwarf CFI) for the precise check. 60 if (ParentFrame == State.getDummyRootPtr() || 61 ParentFrame->Address != Source) { 62 State.switchToFrame(Source); 63 if (ParentFrame != State.getDummyRootPtr()) { 64 if (Source == ExternalAddr) 65 NumMismatchedExtCallBranch++; 66 else 67 NumMismatchedProEpiBranch++; 68 } 69 } else { 70 State.popFrame(); 71 } 72 State.InstPtr.update(Source); 73 } 74 75 void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) { 76 InstructionPointer &IP = State.InstPtr; 77 uint64_t Target = State.getCurrentLBRTarget(); 78 uint64_t End = IP.Address; 79 80 if (End == ExternalAddr && Target == ExternalAddr) { 81 // Filter out the case when leaf external frame matches the external LBR 82 // target, this is a valid state, it happens that the code run into external 83 // address then return back. The call frame under the external frame 84 // remains valid and can be unwound later, just skip recording this range. 85 NumPairedExtAddr++; 86 return; 87 } 88 89 if (End == ExternalAddr || Target == ExternalAddr) { 90 // Range is invalid if only one point is external address. This means LBR 91 // traces contains a standalone external address failing to pair another 92 // one, likely due to interrupt jmp or broken perf script. Set the 93 // state to invalid. 94 NumUnpairedExtAddr++; 95 State.setInvalid(); 96 return; 97 } 98 99 if (!isValidFallThroughRange(Binary->virtualAddrToOffset(Target), 100 Binary->virtualAddrToOffset(End), Binary)) { 101 // Skip unwinding the rest of LBR trace when a bogus range is seen. 102 State.setInvalid(); 103 return; 104 } 105 106 if (Binary->usePseudoProbes()) { 107 // We don't need to top frame probe since it should be extracted 108 // from the range. 109 // The outcome of the virtual unwinding with pseudo probes is a 110 // map from a context key to the address range being unwound. 111 // This means basically linear unwinding is not needed for pseudo 112 // probes. The range will be simply recorded here and will be 113 // converted to a list of pseudo probes to report in ProfileGenerator. 114 State.getParentFrame()->recordRangeCount(Target, End, Repeat); 115 } else { 116 // Unwind linear execution part. 117 // Split and record the range by different inline context. For example: 118 // [0x01] ... main:1 # Target 119 // [0x02] ... main:2 120 // [0x03] ... main:3 @ foo:1 121 // [0x04] ... main:3 @ foo:2 122 // [0x05] ... main:3 @ foo:3 123 // [0x06] ... main:4 124 // [0x07] ... main:5 # End 125 // It will be recorded: 126 // [main:*] : [0x06, 0x07], [0x01, 0x02] 127 // [main:3 @ foo:*] : [0x03, 0x05] 128 while (IP.Address > Target) { 129 uint64_t PrevIP = IP.Address; 130 IP.backward(); 131 // Break into segments for implicit call/return due to inlining 132 bool SameInlinee = Binary->inlineContextEqual(PrevIP, IP.Address); 133 if (!SameInlinee) { 134 State.switchToFrame(PrevIP); 135 State.CurrentLeafFrame->recordRangeCount(PrevIP, End, Repeat); 136 End = IP.Address; 137 } 138 } 139 assert(IP.Address == Target && "The last one must be the target address."); 140 // Record the remaining range, [0x01, 0x02] in the example 141 State.switchToFrame(IP.Address); 142 State.CurrentLeafFrame->recordRangeCount(IP.Address, End, Repeat); 143 } 144 } 145 146 void VirtualUnwinder::unwindReturn(UnwindState &State) { 147 // Add extra frame as we unwind through the return 148 const LBREntry &LBR = State.getCurrentLBR(); 149 uint64_t CallAddr = Binary->getCallAddrFromFrameAddr(LBR.Target); 150 State.switchToFrame(CallAddr); 151 State.pushFrame(LBR.Source); 152 State.InstPtr.update(LBR.Source); 153 } 154 155 void VirtualUnwinder::unwindBranch(UnwindState &State) { 156 // TODO: Tolerate tail call for now, as we may see tail call from libraries. 157 // This is only for intra function branches, excluding tail calls. 158 uint64_t Source = State.getCurrentLBRSource(); 159 State.switchToFrame(Source); 160 State.InstPtr.update(Source); 161 } 162 163 std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() { 164 std::shared_ptr<StringBasedCtxKey> KeyStr = 165 std::make_shared<StringBasedCtxKey>(); 166 KeyStr->Context = Binary->getExpandedContext(Stack, KeyStr->WasLeafInlined); 167 return KeyStr; 168 } 169 170 std::shared_ptr<AddrBasedCtxKey> AddressStack::getContextKey() { 171 std::shared_ptr<AddrBasedCtxKey> KeyStr = std::make_shared<AddrBasedCtxKey>(); 172 KeyStr->Context = Stack; 173 CSProfileGenerator::compressRecursionContext<uint64_t>(KeyStr->Context); 174 CSProfileGenerator::trimContext<uint64_t>(KeyStr->Context); 175 return KeyStr; 176 } 177 178 template <typename T> 179 void VirtualUnwinder::collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur, 180 T &Stack) { 181 if (Cur->RangeSamples.empty() && Cur->BranchSamples.empty()) 182 return; 183 184 std::shared_ptr<ContextKey> Key = Stack.getContextKey(); 185 if (Key == nullptr) 186 return; 187 auto Ret = CtxCounterMap->emplace(Hashable<ContextKey>(Key), SampleCounter()); 188 SampleCounter &SCounter = Ret.first->second; 189 for (auto &Item : Cur->RangeSamples) { 190 uint64_t StartOffset = Binary->virtualAddrToOffset(std::get<0>(Item)); 191 uint64_t EndOffset = Binary->virtualAddrToOffset(std::get<1>(Item)); 192 SCounter.recordRangeCount(StartOffset, EndOffset, std::get<2>(Item)); 193 } 194 195 for (auto &Item : Cur->BranchSamples) { 196 uint64_t SourceOffset = Binary->virtualAddrToOffset(std::get<0>(Item)); 197 uint64_t TargetOffset = Binary->virtualAddrToOffset(std::get<1>(Item)); 198 SCounter.recordBranchCount(SourceOffset, TargetOffset, std::get<2>(Item)); 199 } 200 } 201 202 template <typename T> 203 void VirtualUnwinder::collectSamplesFromFrameTrie( 204 UnwindState::ProfiledFrame *Cur, T &Stack) { 205 if (!Cur->isDummyRoot()) { 206 // Truncate the context for external frame since this isn't a real call 207 // context the compiler will see. 208 if (Cur->isExternalFrame() || !Stack.pushFrame(Cur)) { 209 // Process truncated context 210 // Start a new traversal ignoring its bottom context 211 T EmptyStack(Binary); 212 collectSamplesFromFrame(Cur, EmptyStack); 213 for (const auto &Item : Cur->Children) { 214 collectSamplesFromFrameTrie(Item.second.get(), EmptyStack); 215 } 216 217 // Keep note of untracked call site and deduplicate them 218 // for warning later. 219 if (!Cur->isLeafFrame()) 220 UntrackedCallsites.insert(Cur->Address); 221 222 return; 223 } 224 } 225 226 collectSamplesFromFrame(Cur, Stack); 227 // Process children frame 228 for (const auto &Item : Cur->Children) { 229 collectSamplesFromFrameTrie(Item.second.get(), Stack); 230 } 231 // Recover the call stack 232 Stack.popFrame(); 233 } 234 235 void VirtualUnwinder::collectSamplesFromFrameTrie( 236 UnwindState::ProfiledFrame *Cur) { 237 if (Binary->usePseudoProbes()) { 238 AddressStack Stack(Binary); 239 collectSamplesFromFrameTrie<AddressStack>(Cur, Stack); 240 } else { 241 FrameStack Stack(Binary); 242 collectSamplesFromFrameTrie<FrameStack>(Cur, Stack); 243 } 244 } 245 246 void VirtualUnwinder::recordBranchCount(const LBREntry &Branch, 247 UnwindState &State, uint64_t Repeat) { 248 if (Branch.Target == ExternalAddr) 249 return; 250 251 // Record external-to-internal pattern on the trie root, it later can be 252 // used for generating head samples. 253 if (Branch.Source == ExternalAddr) { 254 State.getDummyRootPtr()->recordBranchCount(Branch.Source, Branch.Target, 255 Repeat); 256 return; 257 } 258 259 if (Binary->usePseudoProbes()) { 260 // Same as recordRangeCount, We don't need to top frame probe since we will 261 // extract it from branch's source address 262 State.getParentFrame()->recordBranchCount(Branch.Source, Branch.Target, 263 Repeat); 264 } else { 265 State.CurrentLeafFrame->recordBranchCount(Branch.Source, Branch.Target, 266 Repeat); 267 } 268 } 269 270 bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) { 271 // Capture initial state as starting point for unwinding. 272 UnwindState State(Sample, Binary); 273 274 // Sanity check - making sure leaf of LBR aligns with leaf of stack sample 275 // Stack sample sometimes can be unreliable, so filter out bogus ones. 276 if (!State.validateInitialState()) 277 return false; 278 279 NumTotalBranches += State.LBRStack.size(); 280 // Now process the LBR samples in parrallel with stack sample 281 // Note that we do not reverse the LBR entry order so we can 282 // unwind the sample stack as we walk through LBR entries. 283 while (State.hasNextLBR()) { 284 State.checkStateConsistency(); 285 286 // Do not attempt linear unwind for the leaf range as it's incomplete. 287 if (!State.IsLastLBR()) { 288 // Unwind implicit calls/returns from inlining, along the linear path, 289 // break into smaller sub section each with its own calling context. 290 unwindLinear(State, Repeat); 291 } 292 293 // Save the LBR branch before it gets unwound. 294 const LBREntry &Branch = State.getCurrentLBR(); 295 if (isCallState(State)) { 296 // Unwind calls - we know we encountered call if LBR overlaps with 297 // transition between leaf the 2nd frame. Note that for calls that 298 // were not in the original stack sample, we should have added the 299 // extra frame when processing the return paired with this call. 300 unwindCall(State); 301 } else if (isReturnState(State)) { 302 // Unwind returns - check whether the IP is indeed at a return 303 // instruction 304 unwindReturn(State); 305 } else if (isValidState(State)) { 306 // Unwind branches 307 unwindBranch(State); 308 } else { 309 // Skip unwinding the rest of LBR trace. Reset the stack and update the 310 // state so that the rest of the trace can still be processed as if they 311 // do not have stack samples. 312 State.clearCallStack(); 313 State.InstPtr.update(State.getCurrentLBRSource()); 314 State.pushFrame(State.InstPtr.Address); 315 } 316 317 State.advanceLBR(); 318 // Record `branch` with calling context after unwinding. 319 recordBranchCount(Branch, State, Repeat); 320 } 321 // As samples are aggregated on trie, record them into counter map 322 collectSamplesFromFrameTrie(State.getDummyRootPtr()); 323 324 return true; 325 } 326 327 std::unique_ptr<PerfReaderBase> 328 PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput, 329 Optional<uint32_t> PIDFilter) { 330 std::unique_ptr<PerfReaderBase> PerfReader; 331 332 if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) { 333 PerfReader.reset( 334 new UnsymbolizedProfileReader(Binary, PerfInput.InputFile)); 335 return PerfReader; 336 } 337 338 // For perf data input, we need to convert them into perf script first. 339 if (PerfInput.Format == PerfFormat::PerfData) 340 PerfInput = 341 PerfScriptReader::convertPerfDataToTrace(Binary, PerfInput, PIDFilter); 342 343 assert((PerfInput.Format == PerfFormat::PerfScript) && 344 "Should be a perfscript!"); 345 346 PerfInput.Content = 347 PerfScriptReader::checkPerfScriptType(PerfInput.InputFile); 348 if (PerfInput.Content == PerfContent::LBRStack) { 349 PerfReader.reset( 350 new HybridPerfReader(Binary, PerfInput.InputFile, PIDFilter)); 351 } else if (PerfInput.Content == PerfContent::LBR) { 352 PerfReader.reset(new LBRPerfReader(Binary, PerfInput.InputFile, PIDFilter)); 353 } else { 354 exitWithError("Unsupported perfscript!"); 355 } 356 357 return PerfReader; 358 } 359 360 PerfInputFile PerfScriptReader::convertPerfDataToTrace( 361 ProfiledBinary *Binary, PerfInputFile &File, Optional<uint32_t> PIDFilter) { 362 StringRef PerfData = File.InputFile; 363 // Run perf script to retrieve PIDs matching binary we're interested in. 364 auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf"); 365 if (!PerfExecutable) { 366 exitWithError("Perf not found."); 367 } 368 std::string PerfPath = *PerfExecutable; 369 std::string PerfTraceFile = PerfData.str() + ".script.tmp"; 370 StringRef ScriptMMapArgs[] = {PerfPath, "script", "--show-mmap-events", 371 "-F", "comm,pid", "-i", 372 PerfData}; 373 Optional<StringRef> Redirects[] = {llvm::None, // Stdin 374 StringRef(PerfTraceFile), // Stdout 375 StringRef(PerfTraceFile)}; // Stderr 376 sys::ExecuteAndWait(PerfPath, ScriptMMapArgs, llvm::None, Redirects); 377 378 // Collect the PIDs 379 TraceStream TraceIt(PerfTraceFile); 380 std::string PIDs; 381 std::unordered_set<uint32_t> PIDSet; 382 while (!TraceIt.isAtEoF()) { 383 MMapEvent MMap; 384 if (isMMap2Event(TraceIt.getCurrentLine()) && 385 extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) { 386 auto It = PIDSet.emplace(MMap.PID); 387 if (It.second && (!PIDFilter || MMap.PID == *PIDFilter)) { 388 if (!PIDs.empty()) { 389 PIDs.append(","); 390 } 391 PIDs.append(utostr(MMap.PID)); 392 } 393 } 394 TraceIt.advance(); 395 } 396 397 if (PIDs.empty()) { 398 exitWithError("No relevant mmap event is found in perf data."); 399 } 400 401 // Run perf script again to retrieve events for PIDs collected above 402 StringRef ScriptSampleArgs[] = {PerfPath, "script", "--show-mmap-events", 403 "-F", "ip,brstack", "--pid", 404 PIDs, "-i", PerfData}; 405 sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, llvm::None, Redirects); 406 407 return {PerfTraceFile, PerfFormat::PerfScript, PerfContent::UnknownContent}; 408 } 409 410 void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) { 411 // Drop the event which doesn't belong to user-provided binary 412 StringRef BinaryName = llvm::sys::path::filename(Event.BinaryPath); 413 if (Binary->getName() != BinaryName) 414 return; 415 416 // Drop the event if process does not match pid filter 417 if (PIDFilter && Event.PID != *PIDFilter) 418 return; 419 420 // Drop the event if its image is loaded at the same address 421 if (Event.Address == Binary->getBaseAddress()) { 422 Binary->setIsLoadedByMMap(true); 423 return; 424 } 425 426 if (Event.Offset == Binary->getTextSegmentOffset()) { 427 // A binary image could be unloaded and then reloaded at different 428 // place, so update binary load address. 429 // Only update for the first executable segment and assume all other 430 // segments are loaded at consecutive memory addresses, which is the case on 431 // X64. 432 Binary->setBaseAddress(Event.Address); 433 Binary->setIsLoadedByMMap(true); 434 } else { 435 // Verify segments are loaded consecutively. 436 const auto &Offsets = Binary->getTextSegmentOffsets(); 437 auto It = std::lower_bound(Offsets.begin(), Offsets.end(), Event.Offset); 438 if (It != Offsets.end() && *It == Event.Offset) { 439 // The event is for loading a separate executable segment. 440 auto I = std::distance(Offsets.begin(), It); 441 const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses(); 442 if (PreferredAddrs[I] - Binary->getPreferredBaseAddress() != 443 Event.Address - Binary->getBaseAddress()) 444 exitWithError("Executable segments not loaded consecutively"); 445 } else { 446 if (It == Offsets.begin()) 447 exitWithError("File offset not found"); 448 else { 449 // Find the segment the event falls in. A large segment could be loaded 450 // via multiple mmap calls with consecutive memory addresses. 451 --It; 452 assert(*It < Event.Offset); 453 if (Event.Offset - *It != Event.Address - Binary->getBaseAddress()) 454 exitWithError("Segment not loaded by consecutive mmaps"); 455 } 456 } 457 } 458 } 459 460 static std::string getContextKeyStr(ContextKey *K, 461 const ProfiledBinary *Binary) { 462 if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(K)) { 463 return SampleContext::getContextString(CtxKey->Context); 464 } else if (const auto *CtxKey = dyn_cast<AddrBasedCtxKey>(K)) { 465 std::ostringstream OContextStr; 466 for (uint32_t I = 0; I < CtxKey->Context.size(); I++) { 467 if (OContextStr.str().size()) 468 OContextStr << " @ "; 469 OContextStr << "0x" 470 << to_hexString( 471 Binary->virtualAddrToOffset(CtxKey->Context[I]), 472 false); 473 } 474 return OContextStr.str(); 475 } else { 476 llvm_unreachable("unexpected key type"); 477 } 478 } 479 480 void HybridPerfReader::unwindSamples() { 481 if (Binary->useFSDiscriminator()) 482 exitWithError("FS discriminator is not supported in CS profile."); 483 VirtualUnwinder Unwinder(&SampleCounters, Binary); 484 for (const auto &Item : AggregatedSamples) { 485 const PerfSample *Sample = Item.first.getPtr(); 486 Unwinder.unwind(Sample, Item.second); 487 } 488 489 // Warn about untracked frames due to missing probes. 490 if (ShowDetailedWarning) { 491 for (auto Address : Unwinder.getUntrackedCallsites()) 492 WithColor::warning() << "Profile context truncated due to missing probe " 493 << "for call instruction at " 494 << format("0x%" PRIx64, Address) << "\n"; 495 } 496 497 emitWarningSummary(Unwinder.getUntrackedCallsites().size(), 498 SampleCounters.size(), 499 "of profiled contexts are truncated due to missing probe " 500 "for call instruction."); 501 502 emitWarningSummary( 503 Unwinder.NumMismatchedExtCallBranch, Unwinder.NumTotalBranches, 504 "of branches'source is a call instruction but doesn't match call frame " 505 "stack, likely due to unwinding error of external frame."); 506 507 emitWarningSummary(Unwinder.NumPairedExtAddr * 2, Unwinder.NumTotalBranches, 508 "of branches containing paired external address."); 509 510 emitWarningSummary(Unwinder.NumUnpairedExtAddr, Unwinder.NumTotalBranches, 511 "of branches containing external address but doesn't have " 512 "another external address to pair, likely due to " 513 "interrupt jmp or broken perf script."); 514 515 emitWarningSummary( 516 Unwinder.NumMismatchedProEpiBranch, Unwinder.NumTotalBranches, 517 "of branches'source is a call instruction but doesn't match call frame " 518 "stack, likely due to frame in prolog/epilog."); 519 520 emitWarningSummary(Unwinder.NumMissingExternalFrame, 521 Unwinder.NumExtCallBranch, 522 "of artificial call branches but doesn't have an external " 523 "frame to match."); 524 } 525 526 bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt, 527 SmallVectorImpl<LBREntry> &LBRStack) { 528 // The raw format of LBR stack is like: 529 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... 530 // ... 0x4005c8/0x4005dc/P/-/-/0 531 // It's in FIFO order and seperated by whitespace. 532 SmallVector<StringRef, 32> Records; 533 TraceIt.getCurrentLine().split(Records, " ", -1, false); 534 auto WarnInvalidLBR = [](TraceStream &TraceIt) { 535 WithColor::warning() << "Invalid address in LBR record at line " 536 << TraceIt.getLineNumber() << ": " 537 << TraceIt.getCurrentLine() << "\n"; 538 }; 539 540 // Skip the leading instruction pointer. 541 size_t Index = 0; 542 uint64_t LeadingAddr; 543 if (!Records.empty() && !Records[0].contains('/')) { 544 if (Records[0].getAsInteger(16, LeadingAddr)) { 545 WarnInvalidLBR(TraceIt); 546 TraceIt.advance(); 547 return false; 548 } 549 Index = 1; 550 } 551 552 // Now extract LBR samples - note that we do not reverse the 553 // LBR entry order so we can unwind the sample stack as we walk 554 // through LBR entries. 555 while (Index < Records.size()) { 556 auto &Token = Records[Index++]; 557 if (Token.size() == 0) 558 continue; 559 560 SmallVector<StringRef, 8> Addresses; 561 Token.split(Addresses, "/"); 562 uint64_t Src; 563 uint64_t Dst; 564 565 // Stop at broken LBR records. 566 if (Addresses.size() < 2 || Addresses[0].substr(2).getAsInteger(16, Src) || 567 Addresses[1].substr(2).getAsInteger(16, Dst)) { 568 WarnInvalidLBR(TraceIt); 569 break; 570 } 571 572 bool SrcIsInternal = Binary->addressIsCode(Src); 573 bool DstIsInternal = Binary->addressIsCode(Dst); 574 if (!SrcIsInternal) 575 Src = ExternalAddr; 576 if (!DstIsInternal) 577 Dst = ExternalAddr; 578 // Filter external-to-external case to reduce LBR trace size. 579 if (!SrcIsInternal && !DstIsInternal) 580 continue; 581 582 LBRStack.emplace_back(LBREntry(Src, Dst)); 583 } 584 TraceIt.advance(); 585 return !LBRStack.empty(); 586 } 587 588 bool PerfScriptReader::extractCallstack(TraceStream &TraceIt, 589 SmallVectorImpl<uint64_t> &CallStack) { 590 // The raw format of call stack is like: 591 // 4005dc # leaf frame 592 // 400634 593 // 400684 # root frame 594 // It's in bottom-up order with each frame in one line. 595 596 // Extract stack frames from sample 597 while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) { 598 StringRef FrameStr = TraceIt.getCurrentLine().ltrim(); 599 uint64_t FrameAddr = 0; 600 if (FrameStr.getAsInteger(16, FrameAddr)) { 601 // We might parse a non-perf sample line like empty line and comments, 602 // skip it 603 TraceIt.advance(); 604 return false; 605 } 606 TraceIt.advance(); 607 // Currently intermixed frame from different binaries is not supported. 608 if (!Binary->addressIsCode(FrameAddr)) { 609 if (CallStack.empty()) 610 NumLeafExternalFrame++; 611 // Push a special value(ExternalAddr) for the external frames so that 612 // unwinder can still work on this with artificial Call/Return branch. 613 // After unwinding, the context will be truncated for external frame. 614 // Also deduplicate the consecutive external addresses. 615 if (CallStack.empty() || CallStack.back() != ExternalAddr) 616 CallStack.emplace_back(ExternalAddr); 617 continue; 618 } 619 620 // We need to translate return address to call address for non-leaf frames. 621 if (!CallStack.empty()) { 622 auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr); 623 if (!CallAddr) { 624 // Stop at an invalid return address caused by bad unwinding. This could 625 // happen to frame-pointer-based unwinding and the callee functions that 626 // do not have the frame pointer chain set up. 627 InvalidReturnAddresses.insert(FrameAddr); 628 break; 629 } 630 FrameAddr = CallAddr; 631 } 632 633 CallStack.emplace_back(FrameAddr); 634 } 635 636 // Strip out the bottom external addr. 637 if (CallStack.size() > 1 && CallStack.back() == ExternalAddr) 638 CallStack.pop_back(); 639 640 // Skip other unrelated line, find the next valid LBR line 641 // Note that even for empty call stack, we should skip the address at the 642 // bottom, otherwise the following pass may generate a truncated callstack 643 while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) { 644 TraceIt.advance(); 645 } 646 // Filter out broken stack sample. We may not have complete frame info 647 // if sample end up in prolog/epilog, the result is dangling context not 648 // connected to entry point. This should be relatively rare thus not much 649 // impact on overall profile quality. However we do want to filter them 650 // out to reduce the number of different calling contexts. One instance 651 // of such case - when sample landed in prolog/epilog, somehow stack 652 // walking will be broken in an unexpected way that higher frames will be 653 // missing. 654 return !CallStack.empty() && 655 !Binary->addressInPrologEpilog(CallStack.front()); 656 } 657 658 void PerfScriptReader::warnIfMissingMMap() { 659 if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) { 660 WithColor::warning() << "No relevant mmap event is matched for " 661 << Binary->getName() 662 << ", will use preferred address (" 663 << format("0x%" PRIx64, 664 Binary->getPreferredBaseAddress()) 665 << ") as the base loading address!\n"; 666 // Avoid redundant warning, only warn at the first unmatched sample. 667 Binary->setMissingMMapWarned(true); 668 } 669 } 670 671 void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) { 672 // The raw hybird sample started with call stack in FILO order and followed 673 // intermediately by LBR sample 674 // e.g. 675 // 4005dc # call stack leaf 676 // 400634 677 // 400684 # call stack root 678 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... 679 // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries 680 // 681 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>(); 682 #ifndef NDEBUG 683 Sample->Linenum = TraceIt.getLineNumber(); 684 #endif 685 // Parsing call stack and populate into PerfSample.CallStack 686 if (!extractCallstack(TraceIt, Sample->CallStack)) { 687 // Skip the next LBR line matched current call stack 688 if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) 689 TraceIt.advance(); 690 return; 691 } 692 693 warnIfMissingMMap(); 694 695 if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) { 696 // Parsing LBR stack and populate into PerfSample.LBRStack 697 if (extractLBRStack(TraceIt, Sample->LBRStack)) { 698 if (IgnoreStackSamples) { 699 Sample->CallStack.clear(); 700 } else { 701 // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR 702 // ranges 703 Sample->CallStack.front() = Sample->LBRStack[0].Target; 704 } 705 // Record samples by aggregation 706 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count; 707 } 708 } else { 709 // LBR sample is encoded in single line after stack sample 710 exitWithError("'Hybrid perf sample is corrupted, No LBR sample line"); 711 } 712 } 713 714 void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) { 715 std::error_code EC; 716 raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF); 717 if (EC) 718 exitWithError(EC, Filename); 719 writeUnsymbolizedProfile(OS); 720 } 721 722 // Use ordered map to make the output deterministic 723 using OrderedCounterForPrint = std::map<std::string, SampleCounter *>; 724 725 void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) { 726 OrderedCounterForPrint OrderedCounters; 727 for (auto &CI : SampleCounters) { 728 OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second; 729 } 730 731 auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator, 732 uint32_t Indent) { 733 OS.indent(Indent); 734 OS << Counter.size() << "\n"; 735 for (auto &I : Counter) { 736 uint64_t Start = I.first.first; 737 uint64_t End = I.first.second; 738 739 if (!UseOffset || (UseOffset && UseLoadableSegmentAsBase)) { 740 Start = Binary->offsetToVirtualAddr(Start); 741 End = Binary->offsetToVirtualAddr(End); 742 } 743 744 if (UseOffset && UseLoadableSegmentAsBase) { 745 Start -= Binary->getFirstLoadableAddress(); 746 End -= Binary->getFirstLoadableAddress(); 747 } 748 749 OS.indent(Indent); 750 OS << Twine::utohexstr(Start) << Separator << Twine::utohexstr(End) << ":" 751 << I.second << "\n"; 752 } 753 }; 754 755 for (auto &CI : OrderedCounters) { 756 uint32_t Indent = 0; 757 if (ProfileIsCS) { 758 // Context string key 759 OS << "[" << CI.first << "]\n"; 760 Indent = 2; 761 } 762 763 SampleCounter &Counter = *CI.second; 764 SCounterPrinter(Counter.RangeCounter, "-", Indent); 765 SCounterPrinter(Counter.BranchCounter, "->", Indent); 766 } 767 } 768 769 // Format of input: 770 // number of entries in RangeCounter 771 // from_1-to_1:count_1 772 // from_2-to_2:count_2 773 // ...... 774 // from_n-to_n:count_n 775 // number of entries in BranchCounter 776 // src_1->dst_1:count_1 777 // src_2->dst_2:count_2 778 // ...... 779 // src_n->dst_n:count_n 780 void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt, 781 SampleCounter &SCounters) { 782 auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) { 783 std::string Msg = TraceIt.isAtEoF() 784 ? "Invalid raw profile!" 785 : "Invalid raw profile at line " + 786 Twine(TraceIt.getLineNumber()).str() + ": " + 787 TraceIt.getCurrentLine().str(); 788 exitWithError(Msg); 789 }; 790 auto ReadNumber = [&](uint64_t &Num) { 791 if (TraceIt.isAtEoF()) 792 exitWithErrorForTraceLine(TraceIt); 793 if (TraceIt.getCurrentLine().ltrim().getAsInteger(10, Num)) 794 exitWithErrorForTraceLine(TraceIt); 795 TraceIt.advance(); 796 }; 797 798 auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) { 799 uint64_t Num = 0; 800 ReadNumber(Num); 801 while (Num--) { 802 if (TraceIt.isAtEoF()) 803 exitWithErrorForTraceLine(TraceIt); 804 StringRef Line = TraceIt.getCurrentLine().ltrim(); 805 806 uint64_t Count = 0; 807 auto LineSplit = Line.split(":"); 808 if (LineSplit.second.empty() || LineSplit.second.getAsInteger(10, Count)) 809 exitWithErrorForTraceLine(TraceIt); 810 811 uint64_t Source = 0; 812 uint64_t Target = 0; 813 auto Range = LineSplit.first.split(Separator); 814 if (Range.second.empty() || Range.first.getAsInteger(16, Source) || 815 Range.second.getAsInteger(16, Target)) 816 exitWithErrorForTraceLine(TraceIt); 817 818 if (!UseOffset || (UseOffset && UseLoadableSegmentAsBase)) { 819 uint64_t BaseAddr = 0; 820 if (UseOffset && UseLoadableSegmentAsBase) 821 BaseAddr = Binary->getFirstLoadableAddress(); 822 823 Source = Binary->virtualAddrToOffset(Source + BaseAddr); 824 Target = Binary->virtualAddrToOffset(Target + BaseAddr); 825 } 826 827 Counter[{Source, Target}] += Count; 828 TraceIt.advance(); 829 } 830 }; 831 832 ReadCounter(SCounters.RangeCounter, "-"); 833 ReadCounter(SCounters.BranchCounter, "->"); 834 } 835 836 void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) { 837 TraceStream TraceIt(FileName); 838 while (!TraceIt.isAtEoF()) { 839 std::shared_ptr<StringBasedCtxKey> Key = 840 std::make_shared<StringBasedCtxKey>(); 841 StringRef Line = TraceIt.getCurrentLine(); 842 // Read context stack for CS profile. 843 if (Line.startswith("[")) { 844 ProfileIsCS = true; 845 auto I = ContextStrSet.insert(Line.str()); 846 SampleContext::createCtxVectorFromStr(*I.first, Key->Context); 847 TraceIt.advance(); 848 } 849 auto Ret = 850 SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter()); 851 readSampleCounters(TraceIt, Ret.first->second); 852 } 853 } 854 855 void UnsymbolizedProfileReader::parsePerfTraces() { 856 readUnsymbolizedProfile(PerfTraceFile); 857 } 858 859 void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample, 860 uint64_t Repeat) { 861 SampleCounter &Counter = SampleCounters.begin()->second; 862 uint64_t EndOffeset = 0; 863 for (const LBREntry &LBR : Sample->LBRStack) { 864 uint64_t SourceOffset = Binary->virtualAddrToOffset(LBR.Source); 865 uint64_t TargetOffset = Binary->virtualAddrToOffset(LBR.Target); 866 867 // Record the branch if its sourceOffset is external. It can be the case an 868 // external source call an internal function, later this branch will be used 869 // to generate the function's head sample. 870 if (Binary->offsetIsCode(TargetOffset)) { 871 Counter.recordBranchCount(SourceOffset, TargetOffset, Repeat); 872 } 873 874 // If this not the first LBR, update the range count between TO of current 875 // LBR and FROM of next LBR. 876 uint64_t StartOffset = TargetOffset; 877 if (Binary->offsetIsCode(StartOffset) && Binary->offsetIsCode(EndOffeset) && 878 isValidFallThroughRange(StartOffset, EndOffeset, Binary)) 879 Counter.recordRangeCount(StartOffset, EndOffeset, Repeat); 880 EndOffeset = SourceOffset; 881 } 882 } 883 884 void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) { 885 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>(); 886 // Parsing LBR stack and populate into PerfSample.LBRStack 887 if (extractLBRStack(TraceIt, Sample->LBRStack)) { 888 warnIfMissingMMap(); 889 // Record LBR only samples by aggregation 890 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count; 891 } 892 } 893 894 void PerfScriptReader::generateUnsymbolizedProfile() { 895 // There is no context for LBR only sample, so initialize one entry with 896 // fake "empty" context key. 897 assert(SampleCounters.empty() && 898 "Sample counter map should be empty before raw profile generation"); 899 std::shared_ptr<StringBasedCtxKey> Key = 900 std::make_shared<StringBasedCtxKey>(); 901 SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter()); 902 for (const auto &Item : AggregatedSamples) { 903 const PerfSample *Sample = Item.first.getPtr(); 904 computeCounterFromLBR(Sample, Item.second); 905 } 906 } 907 908 uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) { 909 // The aggregated count is optional, so do not skip the line and return 1 if 910 // it's unmatched 911 uint64_t Count = 1; 912 if (!TraceIt.getCurrentLine().getAsInteger(10, Count)) 913 TraceIt.advance(); 914 return Count; 915 } 916 917 void PerfScriptReader::parseSample(TraceStream &TraceIt) { 918 NumTotalSample++; 919 uint64_t Count = parseAggregatedCount(TraceIt); 920 assert(Count >= 1 && "Aggregated count should be >= 1!"); 921 parseSample(TraceIt, Count); 922 } 923 924 bool PerfScriptReader::extractMMap2EventForBinary(ProfiledBinary *Binary, 925 StringRef Line, 926 MMapEvent &MMap) { 927 // Parse a line like: 928 // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0 929 // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so 930 constexpr static const char *const Pattern = 931 "PERF_RECORD_MMAP2 ([0-9]+)/[0-9]+: " 932 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ " 933 "(0x[a-f0-9]+|0) .*\\]: [-a-z]+ (.*)"; 934 // Field 0 - whole line 935 // Field 1 - PID 936 // Field 2 - base address 937 // Field 3 - mmapped size 938 // Field 4 - page offset 939 // Field 5 - binary path 940 enum EventIndex { 941 WHOLE_LINE = 0, 942 PID = 1, 943 MMAPPED_ADDRESS = 2, 944 MMAPPED_SIZE = 3, 945 PAGE_OFFSET = 4, 946 BINARY_PATH = 5 947 }; 948 949 Regex RegMmap2(Pattern); 950 SmallVector<StringRef, 6> Fields; 951 bool R = RegMmap2.match(Line, &Fields); 952 if (!R) { 953 std::string ErrorMsg = "Cannot parse mmap event: " + Line.str() + " \n"; 954 exitWithError(ErrorMsg); 955 } 956 Fields[PID].getAsInteger(10, MMap.PID); 957 Fields[MMAPPED_ADDRESS].getAsInteger(0, MMap.Address); 958 Fields[MMAPPED_SIZE].getAsInteger(0, MMap.Size); 959 Fields[PAGE_OFFSET].getAsInteger(0, MMap.Offset); 960 MMap.BinaryPath = Fields[BINARY_PATH]; 961 if (ShowMmapEvents) { 962 outs() << "Mmap: Binary " << MMap.BinaryPath << " loaded at " 963 << format("0x%" PRIx64 ":", MMap.Address) << " \n"; 964 } 965 966 StringRef BinaryName = llvm::sys::path::filename(MMap.BinaryPath); 967 return Binary->getName() == BinaryName; 968 } 969 970 void PerfScriptReader::parseMMap2Event(TraceStream &TraceIt) { 971 MMapEvent MMap; 972 if (extractMMap2EventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) 973 updateBinaryAddress(MMap); 974 TraceIt.advance(); 975 } 976 977 void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) { 978 if (isMMap2Event(TraceIt.getCurrentLine())) 979 parseMMap2Event(TraceIt); 980 else 981 parseSample(TraceIt); 982 } 983 984 void PerfScriptReader::parseAndAggregateTrace() { 985 // Trace line iterator 986 TraceStream TraceIt(PerfTraceFile); 987 while (!TraceIt.isAtEoF()) 988 parseEventOrSample(TraceIt); 989 } 990 991 // A LBR sample is like: 992 // 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ... 993 // A heuristic for fast detection by checking whether a 994 // leading " 0x" and the '/' exist. 995 bool PerfScriptReader::isLBRSample(StringRef Line) { 996 // Skip the leading instruction pointer 997 SmallVector<StringRef, 32> Records; 998 Line.trim().split(Records, " ", 2, false); 999 if (Records.size() < 2) 1000 return false; 1001 if (Records[1].startswith("0x") && Records[1].contains('/')) 1002 return true; 1003 return false; 1004 } 1005 1006 bool PerfScriptReader::isMMap2Event(StringRef Line) { 1007 // Short cut to avoid string find is possible. 1008 if (Line.empty() || Line.size() < 50) 1009 return false; 1010 1011 if (std::isdigit(Line[0])) 1012 return false; 1013 1014 // PERF_RECORD_MMAP2 does not appear at the beginning of the line 1015 // for ` perf script --show-mmap-events -i ...` 1016 return Line.contains("PERF_RECORD_MMAP2"); 1017 } 1018 1019 // The raw hybird sample is like 1020 // e.g. 1021 // 4005dc # call stack leaf 1022 // 400634 1023 // 400684 # call stack root 1024 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ... 1025 // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries 1026 // Determine the perfscript contains hybrid samples(call stack + LBRs) by 1027 // checking whether there is a non-empty call stack immediately followed by 1028 // a LBR sample 1029 PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) { 1030 TraceStream TraceIt(FileName); 1031 uint64_t FrameAddr = 0; 1032 while (!TraceIt.isAtEoF()) { 1033 // Skip the aggregated count 1034 if (!TraceIt.getCurrentLine().getAsInteger(10, FrameAddr)) 1035 TraceIt.advance(); 1036 1037 // Detect sample with call stack 1038 int32_t Count = 0; 1039 while (!TraceIt.isAtEoF() && 1040 !TraceIt.getCurrentLine().ltrim().getAsInteger(16, FrameAddr)) { 1041 Count++; 1042 TraceIt.advance(); 1043 } 1044 if (!TraceIt.isAtEoF()) { 1045 if (isLBRSample(TraceIt.getCurrentLine())) { 1046 if (Count > 0) 1047 return PerfContent::LBRStack; 1048 else 1049 return PerfContent::LBR; 1050 } 1051 TraceIt.advance(); 1052 } 1053 } 1054 1055 exitWithError("Invalid perf script input!"); 1056 return PerfContent::UnknownContent; 1057 } 1058 1059 void HybridPerfReader::generateUnsymbolizedProfile() { 1060 ProfileIsCS = !IgnoreStackSamples; 1061 if (ProfileIsCS) 1062 unwindSamples(); 1063 else 1064 PerfScriptReader::generateUnsymbolizedProfile(); 1065 } 1066 1067 void PerfScriptReader::warnTruncatedStack() { 1068 if (ShowDetailedWarning) { 1069 for (auto Address : InvalidReturnAddresses) { 1070 WithColor::warning() 1071 << "Truncated stack sample due to invalid return address at " 1072 << format("0x%" PRIx64, Address) 1073 << ", likely caused by frame pointer omission\n"; 1074 } 1075 } 1076 emitWarningSummary( 1077 InvalidReturnAddresses.size(), AggregatedSamples.size(), 1078 "of truncated stack samples due to invalid return address, " 1079 "likely caused by frame pointer omission."); 1080 } 1081 1082 void PerfScriptReader::warnInvalidRange() { 1083 std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t, 1084 pair_hash<uint64_t, uint64_t>> 1085 Ranges; 1086 1087 for (const auto &Item : AggregatedSamples) { 1088 const PerfSample *Sample = Item.first.getPtr(); 1089 uint64_t Count = Item.second; 1090 uint64_t EndOffeset = 0; 1091 for (const LBREntry &LBR : Sample->LBRStack) { 1092 uint64_t SourceOffset = Binary->virtualAddrToOffset(LBR.Source); 1093 uint64_t StartOffset = Binary->virtualAddrToOffset(LBR.Target); 1094 if (EndOffeset != 0) 1095 Ranges[{StartOffset, EndOffeset}] += Count; 1096 EndOffeset = SourceOffset; 1097 } 1098 } 1099 1100 if (Ranges.empty()) { 1101 WithColor::warning() << "No samples in perf script!\n"; 1102 return; 1103 } 1104 1105 auto WarnInvalidRange = 1106 [&](uint64_t StartOffset, uint64_t EndOffset, StringRef Msg) { 1107 if (!ShowDetailedWarning) 1108 return; 1109 WithColor::warning() 1110 << "[" 1111 << format("%8" PRIx64, Binary->offsetToVirtualAddr(StartOffset)) 1112 << "," 1113 << format("%8" PRIx64, Binary->offsetToVirtualAddr(EndOffset)) 1114 << "]: " << Msg << "\n"; 1115 }; 1116 1117 const char *EndNotBoundaryMsg = "Range is not on instruction boundary, " 1118 "likely due to profile and binary mismatch."; 1119 const char *DanglingRangeMsg = "Range does not belong to any functions, " 1120 "likely from PLT, .init or .fini section."; 1121 const char *RangeCrossFuncMsg = 1122 "Fall through range should not cross function boundaries, likely due to " 1123 "profile and binary mismatch."; 1124 const char *BogusRangeMsg = "Range start is after or too far from range end."; 1125 1126 uint64_t TotalRangeNum = 0; 1127 uint64_t InstNotBoundary = 0; 1128 uint64_t UnmatchedRange = 0; 1129 uint64_t RangeCrossFunc = 0; 1130 uint64_t BogusRange = 0; 1131 1132 for (auto &I : Ranges) { 1133 uint64_t StartOffset = I.first.first; 1134 uint64_t EndOffset = I.first.second; 1135 TotalRangeNum += I.second; 1136 1137 if (!Binary->offsetIsCode(StartOffset) || 1138 !Binary->offsetIsTransfer(EndOffset)) { 1139 InstNotBoundary += I.second; 1140 WarnInvalidRange(StartOffset, EndOffset, EndNotBoundaryMsg); 1141 } 1142 1143 auto *FRange = Binary->findFuncRangeForOffset(StartOffset); 1144 if (!FRange) { 1145 UnmatchedRange += I.second; 1146 WarnInvalidRange(StartOffset, EndOffset, DanglingRangeMsg); 1147 continue; 1148 } 1149 1150 if (EndOffset >= FRange->EndOffset) { 1151 RangeCrossFunc += I.second; 1152 WarnInvalidRange(StartOffset, EndOffset, RangeCrossFuncMsg); 1153 } 1154 1155 if (!isValidFallThroughRange(StartOffset, EndOffset, Binary)) { 1156 BogusRange += I.second; 1157 WarnInvalidRange(StartOffset, EndOffset, BogusRangeMsg); 1158 } 1159 } 1160 1161 emitWarningSummary( 1162 InstNotBoundary, TotalRangeNum, 1163 "of samples are from ranges that are not on instruction boundary."); 1164 emitWarningSummary( 1165 UnmatchedRange, TotalRangeNum, 1166 "of samples are from ranges that do not belong to any functions."); 1167 emitWarningSummary( 1168 RangeCrossFunc, TotalRangeNum, 1169 "of samples are from ranges that do cross function boundaries."); 1170 emitWarningSummary( 1171 BogusRange, TotalRangeNum, 1172 "of samples are from ranges that have range start after or too far from " 1173 "range end acrossing the unconditinal jmp."); 1174 } 1175 1176 void PerfScriptReader::parsePerfTraces() { 1177 // Parse perf traces and do aggregation. 1178 parseAndAggregateTrace(); 1179 1180 emitWarningSummary(NumLeafExternalFrame, NumTotalSample, 1181 "of samples have leaf external frame in call stack."); 1182 emitWarningSummary(NumLeadingOutgoingLBR, NumTotalSample, 1183 "of samples have leading external LBR."); 1184 1185 // Generate unsymbolized profile. 1186 warnTruncatedStack(); 1187 warnInvalidRange(); 1188 generateUnsymbolizedProfile(); 1189 AggregatedSamples.clear(); 1190 1191 if (SkipSymbolization) 1192 writeUnsymbolizedProfile(OutputFilename); 1193 } 1194 1195 } // end namespace sampleprof 1196 } // end namespace llvm 1197