755 lines · c
1//===-- PerfReader.h - 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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_TOOLS_LLVM_PROFGEN_PERFREADER_H10#define LLVM_TOOLS_LLVM_PROFGEN_PERFREADER_H11#include "ErrorHandling.h"12#include "ProfiledBinary.h"13#include "llvm/Support/Casting.h"14#include "llvm/Support/CommandLine.h"15#include "llvm/Support/Error.h"16#include "llvm/Support/Regex.h"17#include <cstdint>18#include <fstream>19#include <map>20 21namespace llvm {22 23class CleanupInstaller;24 25namespace sampleprof {26 27// Stream based trace line iterator28class TraceStream {29 std::string CurrentLine;30 std::ifstream Fin;31 bool IsAtEoF = false;32 uint64_t LineNumber = 0;33 34public:35 TraceStream(StringRef Filename) : Fin(Filename.str()) {36 if (!Fin.good())37 exitWithError("Error read input perf script file", Filename);38 advance();39 }40 41 StringRef getCurrentLine() {42 assert(!IsAtEoF && "Line iterator reaches the End-of-File!");43 return CurrentLine;44 }45 46 uint64_t getLineNumber() { return LineNumber; }47 48 bool isAtEoF() { return IsAtEoF; }49 50 // Read the next line51 void advance() {52 if (!std::getline(Fin, CurrentLine)) {53 IsAtEoF = true;54 return;55 }56 LineNumber++;57 }58};59 60// The type of input format.61enum PerfFormat {62 UnknownFormat = 0,63 PerfData = 1, // Raw linux perf.data.64 PerfScript = 2, // Perf script create by `perf script` command.65 UnsymbolizedProfile = 3, // Unsymbolized profile generated by llvm-profgen.66 67};68 69// The type of perfscript content.70enum PerfContent {71 UnknownContent = 0,72 LBR = 1, // Only LBR sample.73 LBRStack = 2, // Hybrid sample including call stack and LBR stack.74};75 76struct PerfInputFile {77 std::string InputFile;78 PerfFormat Format = PerfFormat::UnknownFormat;79 PerfContent Content = PerfContent::UnknownContent;80};81 82// The parsed LBR sample entry.83struct LBREntry {84 uint64_t Source = 0;85 uint64_t Target = 0;86 LBREntry(uint64_t S, uint64_t T) : Source(S), Target(T) {}87 88#ifndef NDEBUG89 void print() const {90 dbgs() << "from " << format("%#010x", Source) << " to "91 << format("%#010x", Target);92 }93#endif94};95 96#ifndef NDEBUG97static inline void printLBRStack(const SmallVectorImpl<LBREntry> &LBRStack) {98 for (size_t I = 0; I < LBRStack.size(); I++) {99 dbgs() << "[" << I << "] ";100 LBRStack[I].print();101 dbgs() << "\n";102 }103}104 105static inline void printCallStack(const SmallVectorImpl<uint64_t> &CallStack) {106 for (size_t I = 0; I < CallStack.size(); I++) {107 dbgs() << "[" << I << "] " << format("%#010x", CallStack[I]) << "\n";108 }109}110#endif111 112// Hash interface for generic data of type T113// Data should implement a \fn getHashCode and a \fn isEqual114// Currently getHashCode is non-virtual to avoid the overhead of calling vtable,115// i.e we explicitly calculate hash of derived class, assign to base class's116// HashCode. This also provides the flexibility for calculating the hash code117// incrementally(like rolling hash) during frame stack unwinding since unwinding118// only changes the leaf of frame stack. \fn isEqual is a virtual function,119// which will have perf overhead. In the future, if we redesign a better hash120// function, then we can just skip this or switch to non-virtual function(like121// just ignore comparison if hash conflicts probabilities is low)122template <class T> class Hashable {123public:124 std::shared_ptr<T> Data;125 Hashable(const std::shared_ptr<T> &D) : Data(D) {}126 127 // Hash code generation128 struct Hash {129 uint64_t operator()(const Hashable<T> &Key) const {130 // Don't make it virtual for getHashCode131 uint64_t Hash = Key.Data->getHashCode();132 assert(Hash && "Should generate HashCode for it!");133 return Hash;134 }135 };136 137 // Hash equal138 struct Equal {139 bool operator()(const Hashable<T> &LHS, const Hashable<T> &RHS) const {140 // Precisely compare the data, vtable will have overhead.141 return LHS.Data->isEqual(RHS.Data.get());142 }143 };144 145 T *getPtr() const { return Data.get(); }146};147 148struct PerfSample {149 // LBR stack recorded in FIFO order.150 SmallVector<LBREntry, 16> LBRStack;151 // Call stack recorded in FILO(leaf to root) order, it's used for CS-profile152 // generation153 SmallVector<uint64_t, 16> CallStack;154 155 virtual ~PerfSample() = default;156 uint64_t getHashCode() const {157 // Use simple DJB2 hash158 auto HashCombine = [](uint64_t H, uint64_t V) {159 return ((H << 5) + H) + V;160 };161 uint64_t Hash = 5381;162 for (const auto &Value : CallStack) {163 Hash = HashCombine(Hash, Value);164 }165 for (const auto &Entry : LBRStack) {166 Hash = HashCombine(Hash, Entry.Source);167 Hash = HashCombine(Hash, Entry.Target);168 }169 return Hash;170 }171 172 bool isEqual(const PerfSample *Other) const {173 const SmallVector<uint64_t, 16> &OtherCallStack = Other->CallStack;174 const SmallVector<LBREntry, 16> &OtherLBRStack = Other->LBRStack;175 176 if (CallStack.size() != OtherCallStack.size() ||177 LBRStack.size() != OtherLBRStack.size())178 return false;179 180 if (!std::equal(CallStack.begin(), CallStack.end(), OtherCallStack.begin()))181 return false;182 183 for (size_t I = 0; I < OtherLBRStack.size(); I++) {184 if (LBRStack[I].Source != OtherLBRStack[I].Source ||185 LBRStack[I].Target != OtherLBRStack[I].Target)186 return false;187 }188 return true;189 }190 191#ifndef NDEBUG192 uint64_t Linenum = 0;193 194 void print() const {195 dbgs() << "Line " << Linenum << "\n";196 dbgs() << "LBR stack\n";197 printLBRStack(LBRStack);198 dbgs() << "Call stack\n";199 printCallStack(CallStack);200 }201#endif202};203// After parsing the sample, we record the samples by aggregating them204// into this counter. The key stores the sample data and the value is205// the sample repeat times.206using AggregatedCounter =207 std::unordered_map<Hashable<PerfSample>, uint64_t,208 Hashable<PerfSample>::Hash, Hashable<PerfSample>::Equal>;209 210using SampleVector = SmallVector<std::tuple<uint64_t, uint64_t, uint64_t>, 16>;211 212inline bool isValidFallThroughRange(uint64_t Start, uint64_t End,213 ProfiledBinary *Binary) {214 // Start bigger than End is considered invalid.215 // LBR ranges cross the unconditional jmp are also assumed invalid.216 // It's found that perf data may contain duplicate LBR entries that could form217 // a range that does not reflect real execution flow on some Intel targets,218 // e.g. Skylake. Such ranges are ususally very long. Exclude them since there219 // cannot be a linear execution range that spans over unconditional jmp.220 return Start <= End && !Binary->rangeCrossUncondBranch(Start, End);221}222 223// The state for the unwinder, it doesn't hold the data but only keep the224// pointer/index of the data, While unwinding, the CallStack is changed225// dynamicially and will be recorded as the context of the sample226struct UnwindState {227 // Profiled binary that current frame address belongs to228 const ProfiledBinary *Binary;229 // Call stack trie node230 struct ProfiledFrame {231 const uint64_t Address = DummyRoot;232 ProfiledFrame *Parent;233 SampleVector RangeSamples;234 SampleVector BranchSamples;235 std::unordered_map<uint64_t, std::unique_ptr<ProfiledFrame>> Children;236 237 ProfiledFrame(uint64_t Addr = 0, ProfiledFrame *P = nullptr)238 : Address(Addr), Parent(P) {}239 ProfiledFrame *getOrCreateChildFrame(uint64_t Address) {240 assert(Address && "Address can't be zero!");241 auto Ret = Children.emplace(242 Address, std::make_unique<ProfiledFrame>(Address, this));243 return Ret.first->second.get();244 }245 void recordRangeCount(uint64_t Start, uint64_t End, uint64_t Count) {246 RangeSamples.emplace_back(std::make_tuple(Start, End, Count));247 }248 void recordBranchCount(uint64_t Source, uint64_t Target, uint64_t Count) {249 BranchSamples.emplace_back(std::make_tuple(Source, Target, Count));250 }251 bool isDummyRoot() { return Address == DummyRoot; }252 bool isExternalFrame() { return Address == ExternalAddr; }253 bool isLeafFrame() { return Children.empty(); }254 };255 256 ProfiledFrame DummyTrieRoot;257 ProfiledFrame *CurrentLeafFrame;258 // Used to fall through the LBR stack259 uint32_t LBRIndex = 0;260 // Reference to PerfSample.LBRStack261 const SmallVector<LBREntry, 16> &LBRStack;262 // Used to iterate the address range263 InstructionPointer InstPtr;264 // Indicate whether unwinding is currently in a bad state which requires to265 // skip all subsequent unwinding.266 bool Invalid = false;267 UnwindState(const PerfSample *Sample, const ProfiledBinary *Binary)268 : Binary(Binary), LBRStack(Sample->LBRStack),269 InstPtr(Binary, Sample->CallStack.front()) {270 initFrameTrie(Sample->CallStack);271 }272 273 bool validateInitialState() {274 uint64_t LBRLeaf = LBRStack[LBRIndex].Target;275 uint64_t LeafAddr = CurrentLeafFrame->Address;276 assert((LBRLeaf != ExternalAddr || LBRLeaf == LeafAddr) &&277 "External leading LBR should match the leaf frame.");278 279 // When we take a stack sample, ideally the sampling distance between the280 // leaf IP of stack and the last LBR target shouldn't be very large.281 // Use a heuristic size (0x100) to filter out broken records.282 if (LeafAddr < LBRLeaf || LeafAddr - LBRLeaf >= 0x100) {283 WithColor::warning() << "Bogus trace: stack tip = "284 << format("%#010x", LeafAddr)285 << ", LBR tip = " << format("%#010x\n", LBRLeaf);286 return false;287 }288 return true;289 }290 291 void checkStateConsistency() {292 assert(InstPtr.Address == CurrentLeafFrame->Address &&293 "IP should align with context leaf");294 }295 296 void setInvalid() { Invalid = true; }297 bool hasNextLBR() const { return LBRIndex < LBRStack.size(); }298 uint64_t getCurrentLBRSource() const { return LBRStack[LBRIndex].Source; }299 uint64_t getCurrentLBRTarget() const { return LBRStack[LBRIndex].Target; }300 const LBREntry &getCurrentLBR() const { return LBRStack[LBRIndex]; }301 bool IsLastLBR() const { return LBRIndex == 0; }302 bool getLBRStackSize() const { return LBRStack.size(); }303 void advanceLBR() { LBRIndex++; }304 ProfiledFrame *getParentFrame() { return CurrentLeafFrame->Parent; }305 306 void pushFrame(uint64_t Address) {307 CurrentLeafFrame = CurrentLeafFrame->getOrCreateChildFrame(Address);308 }309 310 void switchToFrame(uint64_t Address) {311 if (CurrentLeafFrame->Address == Address)312 return;313 CurrentLeafFrame = CurrentLeafFrame->Parent->getOrCreateChildFrame(Address);314 }315 316 void popFrame() { CurrentLeafFrame = CurrentLeafFrame->Parent; }317 318 void clearCallStack() { CurrentLeafFrame = &DummyTrieRoot; }319 320 void initFrameTrie(const SmallVectorImpl<uint64_t> &CallStack) {321 ProfiledFrame *Cur = &DummyTrieRoot;322 for (auto Address : reverse(CallStack)) {323 Cur = Cur->getOrCreateChildFrame(Address);324 }325 CurrentLeafFrame = Cur;326 }327 328 ProfiledFrame *getDummyRootPtr() { return &DummyTrieRoot; }329};330 331// Base class for sample counter key with context332struct ContextKey {333 uint64_t HashCode = 0;334 virtual ~ContextKey() = default;335 uint64_t getHashCode() {336 if (HashCode == 0)337 genHashCode();338 return HashCode;339 }340 virtual void genHashCode() = 0;341 virtual bool isEqual(const ContextKey *K) const {342 return HashCode == K->HashCode;343 };344 345 // Utilities for LLVM-style RTTI346 enum ContextKind { CK_StringBased, CK_AddrBased };347 const ContextKind Kind;348 ContextKind getKind() const { return Kind; }349 ContextKey(ContextKind K) : Kind(K){};350};351 352// String based context id353struct StringBasedCtxKey : public ContextKey {354 SampleContextFrameVector Context;355 356 bool WasLeafInlined;357 StringBasedCtxKey() : ContextKey(CK_StringBased), WasLeafInlined(false){};358 static bool classof(const ContextKey *K) {359 return K->getKind() == CK_StringBased;360 }361 362 bool isEqual(const ContextKey *K) const override {363 const StringBasedCtxKey *Other = dyn_cast<StringBasedCtxKey>(K);364 return Context == Other->Context;365 }366 367 void genHashCode() override {368 HashCode = hash_value(SampleContextFrames(Context));369 }370};371 372// Address-based context id373struct AddrBasedCtxKey : public ContextKey {374 SmallVector<uint64_t, 16> Context;375 376 bool WasLeafInlined;377 AddrBasedCtxKey() : ContextKey(CK_AddrBased), WasLeafInlined(false){};378 static bool classof(const ContextKey *K) {379 return K->getKind() == CK_AddrBased;380 }381 382 bool isEqual(const ContextKey *K) const override {383 const AddrBasedCtxKey *Other = dyn_cast<AddrBasedCtxKey>(K);384 return Context == Other->Context;385 }386 387 void genHashCode() override { HashCode = hash_combine_range(Context); }388};389 390// The counter of branch samples for one function indexed by the branch,391// which is represented as the source and target offset pair.392using BranchSample = std::map<std::pair<uint64_t, uint64_t>, uint64_t>;393// The counter of range samples for one function indexed by the range,394// which is represented as the start and end offset pair.395using RangeSample = std::map<std::pair<uint64_t, uint64_t>, uint64_t>;396// <<inst-addr, vtable-data-symbol>, count> map for data access samples.397// The instruction address is the virtual address in the binary.398using DataAccessSample = std::map<std::pair<uint64_t, StringRef>, uint64_t>;399// Wrapper for sample counters including range counter and branch counter400struct SampleCounter {401 RangeSample RangeCounter;402 BranchSample BranchCounter;403 DataAccessSample DataAccessCounter;404 405 void recordRangeCount(uint64_t Start, uint64_t End, uint64_t Repeat) {406 assert(Start <= End && "Invalid instruction range");407 RangeCounter[{Start, End}] += Repeat;408 }409 void recordBranchCount(uint64_t Source, uint64_t Target, uint64_t Repeat) {410 BranchCounter[{Source, Target}] += Repeat;411 }412 void recordDataAccessCount(uint64_t InstAddr, StringRef DataSymbol,413 uint64_t Repeat) {414 DataAccessCounter[{InstAddr, DataSymbol}] += Repeat;415 }416};417 418// Sample counter with context to support context-sensitive profile419using ContextSampleCounterMap =420 std::unordered_map<Hashable<ContextKey>, SampleCounter,421 Hashable<ContextKey>::Hash, Hashable<ContextKey>::Equal>;422 423struct FrameStack {424 SmallVector<uint64_t, 16> Stack;425 ProfiledBinary *Binary;426 FrameStack(ProfiledBinary *B) : Binary(B) {}427 bool pushFrame(UnwindState::ProfiledFrame *Cur) {428 assert(!Cur->isExternalFrame() &&429 "External frame's not expected for context stack.");430 Stack.push_back(Cur->Address);431 return true;432 }433 434 void popFrame() {435 if (!Stack.empty())436 Stack.pop_back();437 }438 std::shared_ptr<StringBasedCtxKey> getContextKey();439};440 441struct AddressStack {442 SmallVector<uint64_t, 16> Stack;443 ProfiledBinary *Binary;444 AddressStack(ProfiledBinary *B) : Binary(B) {}445 bool pushFrame(UnwindState::ProfiledFrame *Cur) {446 assert(!Cur->isExternalFrame() &&447 "External frame's not expected for context stack.");448 Stack.push_back(Cur->Address);449 return true;450 }451 452 void popFrame() {453 if (!Stack.empty())454 Stack.pop_back();455 }456 std::shared_ptr<AddrBasedCtxKey> getContextKey();457};458 459/*460As in hybrid sample we have a group of LBRs and the most recent sampling call461stack, we can walk through those LBRs to infer more call stacks which would be462used as context for profile. VirtualUnwinder is the class to do the call stack463unwinding based on LBR state. Two types of unwinding are processd here:4641) LBR unwinding and 2) linear range unwinding.465Specifically, for each LBR entry(can be classified into call, return, regular466branch), LBR unwinding will replay the operation by pushing, popping or467switching leaf frame towards the call stack and since the initial call stack468is most recently sampled, the replay should be in anti-execution order, i.e. for469the regular case, pop the call stack when LBR is call, push frame on call stack470when LBR is return. After each LBR processed, it also needs to align with the471next LBR by going through instructions from previous LBR's target to current472LBR's source, which is the linear unwinding. As instruction from linear range473can come from different function by inlining, linear unwinding will do the range474splitting and record counters by the range with same inline context. Over those475unwinding process we will record each call stack as context id and LBR/linear476range as sample counter for further CS profile generation.477*/478class VirtualUnwinder {479public:480 VirtualUnwinder(ContextSampleCounterMap *Counter, ProfiledBinary *B)481 : CtxCounterMap(Counter), Binary(B) {}482 bool unwind(const PerfSample *Sample, uint64_t Repeat);483 std::set<uint64_t> &getUntrackedCallsites() { return UntrackedCallsites; }484 485 uint64_t NumTotalBranches = 0;486 uint64_t NumExtCallBranch = 0;487 uint64_t NumMissingExternalFrame = 0;488 uint64_t NumMismatchedProEpiBranch = 0;489 uint64_t NumMismatchedExtCallBranch = 0;490 uint64_t NumUnpairedExtAddr = 0;491 uint64_t NumPairedExtAddr = 0;492 493private:494 bool isSourceExternal(UnwindState &State) const {495 return State.getCurrentLBRSource() == ExternalAddr;496 }497 498 bool isTargetExternal(UnwindState &State) const {499 return State.getCurrentLBRTarget() == ExternalAddr;500 }501 502 // Determine whether the return source is from external code by checking if503 // the target's the next inst is a call inst.504 bool isReturnFromExternal(UnwindState &State) const {505 return isSourceExternal(State) &&506 (Binary->getCallAddrFromFrameAddr(State.getCurrentLBRTarget()) != 0);507 }508 509 // If the source is external address but it's not the `return` case, treat it510 // as a call from external.511 bool isCallFromExternal(UnwindState &State) const {512 return isSourceExternal(State) &&513 Binary->getCallAddrFromFrameAddr(State.getCurrentLBRTarget()) == 0;514 }515 516 bool isCallState(UnwindState &State) const {517 // The tail call frame is always missing here in stack sample, we will518 // use a specific tail call tracker to infer it.519 if (!isValidState(State))520 return false;521 522 if (Binary->addressIsCall(State.getCurrentLBRSource()))523 return true;524 525 return isCallFromExternal(State);526 }527 528 bool isReturnState(UnwindState &State) const {529 if (!isValidState(State))530 return false;531 532 // Simply check addressIsReturn, as ret is always reliable, both for533 // regular call and tail call.534 if (Binary->addressIsReturn(State.getCurrentLBRSource()))535 return true;536 537 return isReturnFromExternal(State);538 }539 540 bool isValidState(UnwindState &State) const { return !State.Invalid; }541 542 void unwindCall(UnwindState &State);543 void unwindLinear(UnwindState &State, uint64_t Repeat);544 void unwindReturn(UnwindState &State);545 void unwindBranch(UnwindState &State);546 547 template <typename T>548 void collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur, T &Stack);549 // Collect each samples on trie node by DFS traversal550 template <typename T>551 void collectSamplesFromFrameTrie(UnwindState::ProfiledFrame *Cur, T &Stack);552 void collectSamplesFromFrameTrie(UnwindState::ProfiledFrame *Cur);553 554 void recordRangeCount(uint64_t Start, uint64_t End, UnwindState &State,555 uint64_t Repeat);556 void recordBranchCount(const LBREntry &Branch, UnwindState &State,557 uint64_t Repeat);558 559 ContextSampleCounterMap *CtxCounterMap;560 // Profiled binary that current frame address belongs to561 ProfiledBinary *Binary;562 // Keep track of all untracked callsites563 std::set<uint64_t> UntrackedCallsites;564};565 566// Read perf trace to parse the events and samples.567class PerfReaderBase {568public:569 PerfReaderBase(ProfiledBinary *B, StringRef PerfTrace)570 : Binary(B), PerfTraceFile(PerfTrace) {571 // Initialize the base address to preferred address.572 Binary->setBaseAddress(Binary->getPreferredBaseAddress());573 };574 virtual ~PerfReaderBase() = default;575 static std::unique_ptr<PerfReaderBase>576 create(ProfiledBinary *Binary, PerfInputFile &PerfInput,577 std::optional<int32_t> PIDFilter);578 579 // Entry of the reader to parse multiple perf traces580 virtual void parsePerfTraces() = 0;581 582 // Parse the <ip, vtable-data-symbol> from the data access perf trace file,583 // and accumulate the data access count for each <ip, data-symbol> pair.584 Error585 parseDataAccessPerfTraces(StringRef DataAccessPerfFile,586 std::optional<int32_t> PIDFilter = std::nullopt);587 588 const ContextSampleCounterMap &getSampleCounters() const {589 return SampleCounters;590 }591 bool profileIsCS() { return ProfileIsCS; }592 593protected:594 ProfiledBinary *Binary = nullptr;595 StringRef PerfTraceFile;596 597 ContextSampleCounterMap SampleCounters;598 bool ProfileIsCS = false;599 600 uint64_t NumTotalSample = 0;601 uint64_t NumLeafExternalFrame = 0;602 uint64_t NumLeadingOutgoingLBR = 0;603};604 605// Read perf script to parse the events and samples.606class PerfScriptReader : public PerfReaderBase {607public:608 PerfScriptReader(ProfiledBinary *B, StringRef PerfTrace,609 std::optional<int32_t> PID)610 : PerfReaderBase(B, PerfTrace), PIDFilter(PID) {};611 612 // Entry of the reader to parse multiple perf traces613 void parsePerfTraces() override;614 615 // Parse a single line of a PERF_RECORD_MMAP event looking for a616 // mapping between the binary name and its memory layout.617 // TODO: Move this static method from PerScriptReader (subclass) to618 // PerfReaderBase (superclass).619 static bool extractMMapEventForBinary(ProfiledBinary *Binary, StringRef Line,620 MMapEvent &MMap);621 622 // Generate perf script from perf data623 static PerfInputFile convertPerfDataToTrace(ProfiledBinary *Binary,624 bool SkipPID, PerfInputFile &File,625 std::optional<int32_t> PIDFilter);626 // Extract perf script type by peaking at the input627 static PerfContent checkPerfScriptType(StringRef FileName);628 629 // Cleanup installers for temporary files created by perf script command.630 // Those files will be automatically removed when running destructor or631 // receiving signals.632 static SmallVector<CleanupInstaller, 2> TempFileCleanups;633 634protected:635 // Check whether a given line is LBR sample636 static bool isLBRSample(StringRef Line);637 // Check whether a given line is MMAP event638 static bool isMMapEvent(StringRef Line);639 // Update base address based on mmap events640 void updateBinaryAddress(const MMapEvent &Event);641 // Parse mmap event and update binary address642 void parseMMapEvent(TraceStream &TraceIt);643 // Parse perf events/samples and do aggregation644 void parseAndAggregateTrace();645 // Parse either an MMAP event or a perf sample646 void parseEventOrSample(TraceStream &TraceIt);647 // Warn if the relevant mmap event is missing.648 void warnIfMissingMMap();649 // Emit accumulate warnings.650 void warnTruncatedStack();651 // Warn if range is invalid.652 void warnInvalidRange();653 // Extract call stack from the perf trace lines654 bool extractCallstack(TraceStream &TraceIt,655 SmallVectorImpl<uint64_t> &CallStack);656 // Extract LBR stack from one perf trace line657 bool extractLBRStack(TraceStream &TraceIt,658 SmallVectorImpl<LBREntry> &LBRStack);659 uint64_t parseAggregatedCount(TraceStream &TraceIt);660 // Parse one sample from multiple perf lines, override this for different661 // sample type662 void parseSample(TraceStream &TraceIt);663 // An aggregated count is given to indicate how many times the sample is664 // repeated.665 virtual void parseSample(TraceStream &TraceIt, uint64_t Count){};666 void computeCounterFromLBR(const PerfSample *Sample, uint64_t Repeat);667 // Post process the profile after trace aggregation, we will do simple range668 // overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).669 virtual void generateUnsymbolizedProfile();670 void writeUnsymbolizedProfile(StringRef Filename);671 void writeUnsymbolizedProfile(raw_fd_ostream &OS);672 673 // Samples with the repeating time generated by the perf reader674 AggregatedCounter AggregatedSamples;675 // Keep track of all invalid return addresses676 std::set<uint64_t> InvalidReturnAddresses;677 // PID for the process of interest678 std::optional<int32_t> PIDFilter;679};680 681/*682 The reader of LBR only perf script.683 A typical LBR sample is like:684 40062f 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...685 ... 0x4005c8/0x4005dc/P/-/-/0686*/687class LBRPerfReader : public PerfScriptReader {688public:689 LBRPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,690 std::optional<int32_t> PID)691 : PerfScriptReader(Binary, PerfTrace, PID) {};692 // Parse the LBR only sample.693 void parseSample(TraceStream &TraceIt, uint64_t Count) override;694};695 696/*697 Hybrid perf script includes a group of hybrid samples(LBRs + call stack),698 which is used to generate CS profile. An example of hybrid sample:699 4005dc # call stack leaf700 400634701 400684 # call stack root702 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...703 ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries704*/705class HybridPerfReader : public PerfScriptReader {706public:707 HybridPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,708 std::optional<int32_t> PID)709 : PerfScriptReader(Binary, PerfTrace, PID) {};710 // Parse the hybrid sample including the call and LBR line711 void parseSample(TraceStream &TraceIt, uint64_t Count) override;712 void generateUnsymbolizedProfile() override;713 714private:715 // Unwind the hybrid samples after aggregration716 void unwindSamples();717};718 719/*720 Format of unsymbolized profile:721 722 [frame1 @ frame2 @ ...] # If it's a CS profile723 number of entries in RangeCounter724 from_1-to_1:count_1725 from_2-to_2:count_2726 ......727 from_n-to_n:count_n728 number of entries in BranchCounter729 src_1->dst_1:count_1730 src_2->dst_2:count_2731 ......732 src_n->dst_n:count_n733 [frame1 @ frame2 @ ...] # Next context734 ......735 736Note that non-CS profile doesn't have the empty `[]` context.737*/738class UnsymbolizedProfileReader : public PerfReaderBase {739public:740 UnsymbolizedProfileReader(ProfiledBinary *Binary, StringRef PerfTrace)741 : PerfReaderBase(Binary, PerfTrace){};742 void parsePerfTraces() override;743 744private:745 void readSampleCounters(TraceStream &TraceIt, SampleCounter &SCounters);746 void readUnsymbolizedProfile(StringRef Filename);747 748 std::unordered_set<std::string> ContextStrSet;749};750 751} // end namespace sampleprof752} // end namespace llvm753 754#endif755