//===- MCPseudoProbe.h - Pseudo probe encoding support ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains the declaration of the MCPseudoProbe to support the pseudo // probe encoding for AutoFDO. Pseudo probes together with their inline context // are encoded in a DFS recursive way in the .pseudoprobe sections. For each // .pseudoprobe section, the encoded binary data consist of a single or mutiple // function records each for one outlined function. A function record has the // following format : // // FUNCTION BODY (one for each outlined function present in the text section) // GUID (uint64) // GUID of the function's source name which may be different from the // actual binary linkage name. This GUID will be used to decode and // generate a profile against the source function name. // NPROBES (ULEB128) // Number of probes originating from this function. // NUM_INLINED_FUNCTIONS (ULEB128) // Number of callees inlined into this function, aka number of // first-level inlinees // PROBE RECORDS // A list of NPROBES entries. Each entry contains: // INDEX (ULEB128) // TYPE (uint4) // 0 - block probe, 1 - indirect call, 2 - direct call // ATTRIBUTE (uint3) // 1 - reserved // 2 - Sentinel // 4 - HasDiscriminator // ADDRESS_TYPE (uint1) // 0 - code address for regular probes (for downwards compatibility) // - GUID of linkage name for sentinel probes // 1 - address delta // CODE_ADDRESS (uint64 or ULEB128) // code address or address delta, depending on ADDRESS_TYPE // DISCRIMINATOR (ULEB128) if HasDiscriminator // INLINED FUNCTION RECORDS // A list of NUM_INLINED_FUNCTIONS entries describing each of the inlined // callees. Each record contains: // INLINE SITE // ID of the callsite probe (ULEB128) // FUNCTION BODY // A FUNCTION BODY entry describing the inlined function. // // TODO: retire the ADDRESS_TYPE encoding for code addresses once compatibility // is no longer an issue. //===----------------------------------------------------------------------===// #ifndef LLVM_MC_MCPSEUDOPROBE_H #define LLVM_MC_MCPSEUDOPROBE_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/IR/PseudoProbe.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorOr.h" #include #include #include #include #include #include #include namespace llvm { class MCSymbol; class MCObjectStreamer; class raw_ostream; enum class MCPseudoProbeFlag { // If set, indicates that the probe is encoded as an address delta // instead of a real code address. AddressDelta = 0x1, }; // Function descriptor decoded from .pseudo_probe_desc section struct MCPseudoProbeFuncDesc { uint64_t FuncGUID = 0; uint64_t FuncHash = 0; StringRef FuncName; MCPseudoProbeFuncDesc(uint64_t GUID, uint64_t Hash, StringRef Name) : FuncGUID(GUID), FuncHash(Hash), FuncName(Name){}; LLVM_ABI void print(raw_ostream &OS); }; class MCDecodedPseudoProbe; // An inline frame has the form using InlineSite = std::tuple; using MCPseudoProbeInlineStack = SmallVector; // GUID to PseudoProbeFuncDesc map class GUIDProbeFunctionMap : public std::vector { public: auto find(uint64_t GUID) const { auto CompareDesc = [](const MCPseudoProbeFuncDesc &Desc, uint64_t GUID) { return Desc.FuncGUID < GUID; }; auto It = llvm::lower_bound(*this, GUID, CompareDesc); if (It->FuncGUID != GUID) return end(); return It; } }; class MCDecodedPseudoProbeInlineTree; class MCPseudoProbeBase { protected: uint32_t Index; uint32_t Discriminator; uint8_t Attributes; uint8_t Type; // The value should be equal to PseudoProbeReservedId::Last + 1 which is // defined in SampleProfileProbe.h. The header file is not included here to // reduce the dependency from MC to IPO. const static uint32_t PseudoProbeFirstId = 1; public: MCPseudoProbeBase(uint64_t I, uint64_t At, uint8_t T, uint32_t D) : Index(I), Discriminator(D), Attributes(At), Type(T) {} bool isEntry() const { return Index == PseudoProbeFirstId; } uint32_t getIndex() const { return Index; } uint32_t getDiscriminator() const { return Discriminator; } uint8_t getAttributes() const { return Attributes; } uint8_t getType() const { return Type; } bool isBlock() const { return Type == static_cast(PseudoProbeType::Block); } bool isIndirectCall() const { return Type == static_cast(PseudoProbeType::IndirectCall); } bool isDirectCall() const { return Type == static_cast(PseudoProbeType::DirectCall); } bool isCall() const { return isIndirectCall() || isDirectCall(); } void setAttributes(uint8_t Attr) { Attributes = Attr; } }; /// Instances of this class represent a pseudo probe instance for a pseudo probe /// table entry, which is created during a machine instruction is assembled and /// uses an address from a temporary label created at the current address in the /// current section. class MCPseudoProbe : public MCPseudoProbeBase { uint64_t Guid; MCSymbol *Label; public: MCPseudoProbe(MCSymbol *Label, uint64_t Guid, uint64_t Index, uint64_t Type, uint64_t Attributes, uint32_t Discriminator) : MCPseudoProbeBase(Index, Attributes, Type, Discriminator), Guid(Guid), Label(Label) { assert(Type <= 0xFF && "Probe type too big to encode, exceeding 2^8"); assert(Attributes <= 0xFF && "Probe attributes too big to encode, exceeding 2^16"); } uint64_t getGuid() const { return Guid; }; MCSymbol *getLabel() const { return Label; } LLVM_ABI void emit(MCObjectStreamer *MCOS, const MCPseudoProbe *LastProbe) const; }; // Represents a callsite with caller function name and probe id using MCPseudoProbeFrameLocation = std::pair; class MCDecodedPseudoProbe : public MCPseudoProbeBase { uint64_t Address; MCDecodedPseudoProbeInlineTree *InlineTree; public: MCDecodedPseudoProbe(uint64_t Ad, uint32_t I, PseudoProbeType K, uint8_t At, uint32_t D, MCDecodedPseudoProbeInlineTree *Tree) : MCPseudoProbeBase(I, At, static_cast(K), D), Address(Ad), InlineTree(Tree){}; LLVM_ABI uint64_t getGuid() const; uint64_t getAddress() const { return Address; } void setAddress(uint64_t Addr) { Address = Addr; } MCDecodedPseudoProbeInlineTree *getInlineTreeNode() const { return InlineTree; } // Get the inlined context by traversing current inline tree backwards, // each tree node has its InlineSite which is taken as the context. // \p ContextStack is populated in root to leaf order LLVM_ABI void getInlineContext(SmallVectorImpl &ContextStack, const GUIDProbeFunctionMap &GUID2FuncMAP) const; // Helper function to get the string from context stack LLVM_ABI std::string getInlineContextStr(const GUIDProbeFunctionMap &GUID2FuncMAP) const; // Print pseudo probe while disassembling LLVM_ABI void print(raw_ostream &OS, const GUIDProbeFunctionMap &GUID2FuncMAP, bool ShowName) const; }; // Address to pseudo probes map. class AddressProbesMap : public std::vector> { auto getIt(uint64_t Addr) const { auto CompareProbe = [](const MCDecodedPseudoProbe &Probe, uint64_t Addr) { return Probe.getAddress() < Addr; }; return llvm::lower_bound(*this, Addr, CompareProbe); } public: // Returns range of probes within [\p From, \p To) address range. auto find(uint64_t From, uint64_t To) const { return llvm::make_range(getIt(From), getIt(To)); } // Returns range of probes with given \p Address. auto find(uint64_t Address) const { auto FromIt = getIt(Address); if (FromIt == end() || FromIt->get().getAddress() != Address) return llvm::make_range(end(), end()); auto ToIt = getIt(Address + 1); return llvm::make_range(FromIt, ToIt); } }; template class MCPseudoProbeInlineTreeBase { protected: // Track children (e.g. inlinees) of current context InlinedProbeTreeMap Children; // Set of probes that come with the function. ProbesType Probes; MCPseudoProbeInlineTreeBase() { static_assert(std::is_base_of::value, "DerivedProbeInlineTreeType must be subclass of " "MCPseudoProbeInlineTreeBase"); } public: uint64_t Guid = 0; // Root node has a GUID 0. bool isRoot() const { return Guid == 0; } InlinedProbeTreeMap &getChildren() { return Children; } const InlinedProbeTreeMap &getChildren() const { return Children; } const ProbesType &getProbes() const { return Probes; } // Caller node of the inline site MCPseudoProbeInlineTreeBase *Parent = nullptr; DerivedProbeInlineTreeType *getOrAddNode(const InlineSite &Site) { auto Ret = Children.emplace( Site, std::make_unique(Site)); Ret.first->second->Parent = this; return Ret.first->second.get(); }; }; // A Tri-tree based data structure to group probes by inline stack. // A tree is allocated for a standalone .text section. A fake // instance is created as the root of a tree. // A real instance of this class is created for each function, either a // not inlined function that has code in .text section or an inlined function. struct InlineSiteHash { uint64_t operator()(const InlineSite &Site) const { return std::get<0>(Site) ^ std::get<1>(Site); } }; class MCPseudoProbeInlineTree : public MCPseudoProbeInlineTreeBase< std::vector, MCPseudoProbeInlineTree, std::unordered_map, InlineSiteHash>> { public: MCPseudoProbeInlineTree() = default; MCPseudoProbeInlineTree(uint64_t Guid) { this->Guid = Guid; } MCPseudoProbeInlineTree(const InlineSite &Site) { this->Guid = std::get<0>(Site); } // MCPseudoProbeInlineTree method based on Inlinees LLVM_ABI void addPseudoProbe(const MCPseudoProbe &Probe, const MCPseudoProbeInlineStack &InlineStack); LLVM_ABI void emit(MCObjectStreamer *MCOS, const MCPseudoProbe *&LastProbe); }; // inline tree node for the decoded pseudo probe class MCDecodedPseudoProbeInlineTree : public MCPseudoProbeInlineTreeBase< MCDecodedPseudoProbe *, MCDecodedPseudoProbeInlineTree, MutableArrayRef> { uint32_t NumProbes = 0; uint32_t ProbeId = 0; public: MCDecodedPseudoProbeInlineTree() = default; MCDecodedPseudoProbeInlineTree(const InlineSite &Site, MCDecodedPseudoProbeInlineTree *Parent) : ProbeId(std::get<1>(Site)) { this->Guid = std::get<0>(Site); this->Parent = Parent; } // Return false if it's a dummy inline site bool hasInlineSite() const { return !isRoot() && !Parent->isRoot(); } bool isTopLevelFunc() const { return !isRoot() && Parent->isRoot(); } InlineSite getInlineSite() const { return InlineSite(Guid, ProbeId); } void setProbes(MutableArrayRef ProbesRef) { Probes = ProbesRef.data(); NumProbes = ProbesRef.size(); } auto getProbes() const { return MutableArrayRef(Probes, NumProbes); } }; /// Instances of this class represent the pseudo probes inserted into a compile /// unit. class MCPseudoProbeSections { public: void addPseudoProbe(MCSymbol *FuncSym, const MCPseudoProbe &Probe, const MCPseudoProbeInlineStack &InlineStack) { MCProbeDivisions[FuncSym].addPseudoProbe(Probe, InlineStack); } // The addresses of MCPseudoProbeInlineTree are used by the tree structure and // need to be stable. using MCProbeDivisionMap = std::unordered_map; private: // A collection of MCPseudoProbe for each function. The MCPseudoProbes are // grouped by GUIDs due to inlining that can bring probes from different // functions into one function. MCProbeDivisionMap MCProbeDivisions; public: const MCProbeDivisionMap &getMCProbes() const { return MCProbeDivisions; } bool empty() const { return MCProbeDivisions.empty(); } LLVM_ABI void emit(MCObjectStreamer *MCOS); }; class MCPseudoProbeTable { // A collection of MCPseudoProbe in the current module grouped by // functions. MCPseudoProbes will be encoded into a corresponding // .pseudoprobe section. With functions emitted as separate comdats, // a text section really only contains the code of a function solely, and the // probes associated with the text section will be emitted into a standalone // .pseudoprobe section that shares the same comdat group with the function. MCPseudoProbeSections MCProbeSections; public: LLVM_ABI static void emit(MCObjectStreamer *MCOS); MCPseudoProbeSections &getProbeSections() { return MCProbeSections; } #ifndef NDEBUG static int DdgPrintIndent; #endif }; class MCPseudoProbeDecoder { // Decoded pseudo probes vector. std::vector PseudoProbeVec; // Injected pseudo probes, identified by the containing inline tree node. // Need to keep injected probes separately for two reasons: // 1) Probes cannot be added to the PseudoProbeVec: appending may cause // reallocation so that pointers to its elements will become invalid. // 2) Probes belonging to function record must be contiguous in PseudoProbeVec // as owning InlineTree references them with an ArrayRef to save space. std::unordered_map> InjectedProbeMap; // Decoded inline records vector. std::vector InlineTreeVec; // GUID to PseudoProbeFuncDesc map. GUIDProbeFunctionMap GUID2FuncDescMap; BumpPtrAllocator FuncNameAllocator; // Address to probes map. AddressProbesMap Address2ProbesMap; // The dummy root of the inline trie, all the outlined function will directly // be the children of the dummy root, all the inlined function will be the // children of its inlineer. So the relation would be like: // DummyRoot --> OutlinedFunc --> InlinedFunc1 --> InlinedFunc2 MCDecodedPseudoProbeInlineTree DummyInlineRoot; /// Points to the current location in the buffer. const uint8_t *Data = nullptr; /// Points to the end of the buffer. const uint8_t *End = nullptr; /// Whether encoding is based on a starting probe with absolute code address. bool EncodingIsAddrBased = false; // Decoding helper function template ErrorOr readUnencodedNumber(); template ErrorOr readUnsignedNumber(); template ErrorOr readSignedNumber(); ErrorOr readString(uint32_t Size); public: using Uint64Set = DenseSet; using Uint64Map = DenseMap; // Decode pseudo_probe_desc section to build GUID to PseudoProbeFuncDesc map. // If pseudo_probe_desc section is mapped to memory and \p IsMMapped is true, // uses StringRefs pointing to the section. LLVM_ABI bool buildGUID2FuncDescMap(const uint8_t *Start, std::size_t Size, bool IsMMapped = false); // Decode pseudo_probe section to count the number of probes and inlined // function records for each function record. template bool countRecords(bool &Discard, uint32_t &ProbeCount, uint32_t &InlinedCount, const Uint64Set &GuidFilter); // Decode pseudo_probe section to build address to probes map for specifed // functions only. LLVM_ABI bool buildAddress2ProbeMap(const uint8_t *Start, std::size_t Size, const Uint64Set &GuildFilter, const Uint64Map &FuncStartAddrs); // Print pseudo_probe_desc section info LLVM_ABI void printGUID2FuncDescMap(raw_ostream &OS); // Print pseudo_probe section info, used along with show-disassembly LLVM_ABI void printProbeForAddress(raw_ostream &OS, uint64_t Address); // do printProbeForAddress for all addresses LLVM_ABI void printProbesForAllAddresses(raw_ostream &OS); // Look up the probe of a call for the input address LLVM_ABI const MCDecodedPseudoProbe * getCallProbeForAddr(uint64_t Address) const; LLVM_ABI const MCPseudoProbeFuncDesc *getFuncDescForGUID(uint64_t GUID) const; // Helper function to populate one probe's inline stack into // \p InlineContextStack. // Current leaf location info will be added if IncludeLeaf is true // Example: // Current probe(bar:3) inlined at foo:2 then inlined at main:1 // IncludeLeaf = true, Output: [main:1, foo:2, bar:3] // IncludeLeaf = false, Output: [main:1, foo:2] LLVM_ABI void getInlineContextForProbe( const MCDecodedPseudoProbe *Probe, SmallVectorImpl &InlineContextStack, bool IncludeLeaf) const; const AddressProbesMap &getAddress2ProbesMap() const { return Address2ProbesMap; } AddressProbesMap &getAddress2ProbesMap() { return Address2ProbesMap; } const GUIDProbeFunctionMap &getGUID2FuncDescMap() const { return GUID2FuncDescMap; } LLVM_ABI const MCPseudoProbeFuncDesc * getInlinerDescForProbe(const MCDecodedPseudoProbe *Probe) const; const MCDecodedPseudoProbeInlineTree &getDummyInlineRoot() const { return DummyInlineRoot; } void addInjectedProbe(const MCDecodedPseudoProbe &Probe, uint64_t Address) { const MCDecodedPseudoProbeInlineTree *Parent = Probe.getInlineTreeNode(); InjectedProbeMap[Parent].emplace_back(Probe).setAddress(Address); } size_t getNumInjectedProbes(const MCDecodedPseudoProbeInlineTree *Parent) const { auto It = InjectedProbeMap.find(Parent); if (It == InjectedProbeMap.end()) return 0; return It->second.size(); } auto getInjectedProbes(MCDecodedPseudoProbeInlineTree *Parent) { auto It = InjectedProbeMap.find(Parent); assert(It != InjectedProbeMap.end()); return iterator_range(It->second); } ArrayRef getInlineTreeVec() const { return InlineTreeVec; } private: // Recursively parse an inlining tree encoded in pseudo_probe section. Returns // whether the the top-level node should be skipped. template bool buildAddress2ProbeMap(MCDecodedPseudoProbeInlineTree *Cur, uint64_t &LastAddr, const Uint64Set &GuildFilter, const Uint64Map &FuncStartAddrs, const uint32_t CurChildIndex); }; } // end namespace llvm #endif // LLVM_MC_MCPSEUDOPROBE_H