| //===--- PseudoProbe.cpp - Pseudo probe decoding utilities ------*- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PseudoProbe.h" |
| #include "ErrorHandling.h" |
| #include "llvm/Support/Endian.h" |
| #include "llvm/Support/LEB128.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <limits> |
| #include <memory> |
| |
| using namespace llvm; |
| using namespace sampleprof; |
| using namespace support; |
| |
| namespace llvm { |
| namespace sampleprof { |
| |
| static StringRef getProbeFNameForGUID(const GUIDProbeFunctionMap &GUID2FuncMAP, |
| uint64_t GUID) { |
| auto It = GUID2FuncMAP.find(GUID); |
| assert(It != GUID2FuncMAP.end() && |
| "Probe function must exist for a valid GUID"); |
| return It->second.FuncName; |
| } |
| |
| void PseudoProbeFuncDesc::print(raw_ostream &OS) { |
| OS << "GUID: " << FuncGUID << " Name: " << FuncName << "\n"; |
| OS << "Hash: " << FuncHash << "\n"; |
| } |
| |
| void PseudoProbe::getInlineContext(SmallVectorImpl<std::string> &ContextStack, |
| const GUIDProbeFunctionMap &GUID2FuncMAP, |
| bool ShowName) const { |
| uint32_t Begin = ContextStack.size(); |
| PseudoProbeInlineTree *Cur = InlineTree; |
| // It will add the string of each node's inline site during iteration. |
| // Note that it won't include the probe's belonging function(leaf location) |
| while (Cur->hasInlineSite()) { |
| std::string ContextStr; |
| if (ShowName) { |
| StringRef FuncName = |
| getProbeFNameForGUID(GUID2FuncMAP, std::get<0>(Cur->ISite)); |
| ContextStr += FuncName.str(); |
| } else { |
| ContextStr += Twine(std::get<0>(Cur->ISite)).str(); |
| } |
| ContextStr += ":"; |
| ContextStr += Twine(std::get<1>(Cur->ISite)).str(); |
| ContextStack.emplace_back(ContextStr); |
| Cur = Cur->Parent; |
| } |
| // Make the ContextStack in caller-callee order |
| std::reverse(ContextStack.begin() + Begin, ContextStack.end()); |
| } |
| |
| std::string |
| PseudoProbe::getInlineContextStr(const GUIDProbeFunctionMap &GUID2FuncMAP, |
| bool ShowName) const { |
| std::ostringstream OContextStr; |
| SmallVector<std::string, 16> ContextStack; |
| getInlineContext(ContextStack, GUID2FuncMAP, ShowName); |
| for (auto &CxtStr : ContextStack) { |
| if (OContextStr.str().size()) |
| OContextStr << " @ "; |
| OContextStr << CxtStr; |
| } |
| return OContextStr.str(); |
| } |
| |
| static const char *PseudoProbeTypeStr[3] = {"Block", "IndirectCall", |
| "DirectCall"}; |
| |
| void PseudoProbe::print(raw_ostream &OS, |
| const GUIDProbeFunctionMap &GUID2FuncMAP, |
| bool ShowName) { |
| OS << "FUNC: "; |
| if (ShowName) { |
| StringRef FuncName = getProbeFNameForGUID(GUID2FuncMAP, GUID); |
| OS << FuncName.str() << " "; |
| } else { |
| OS << GUID << " "; |
| } |
| OS << "Index: " << Index << " "; |
| OS << "Type: " << PseudoProbeTypeStr[static_cast<uint8_t>(Type)] << " "; |
| if (isDangling()) { |
| OS << "Dangling "; |
| } |
| if (isTailCall()) { |
| OS << "TailCall "; |
| } |
| std::string InlineContextStr = getInlineContextStr(GUID2FuncMAP, ShowName); |
| if (InlineContextStr.size()) { |
| OS << "Inlined: @ "; |
| OS << InlineContextStr; |
| } |
| OS << "\n"; |
| } |
| |
| template <typename T> T PseudoProbeDecoder::readUnencodedNumber() { |
| if (Data + sizeof(T) > End) { |
| exitWithError("Decode unencoded number error in " + SectionName + |
| " section"); |
| } |
| T Val = endian::readNext<T, little, unaligned>(Data); |
| return Val; |
| } |
| |
| template <typename T> T PseudoProbeDecoder::readUnsignedNumber() { |
| unsigned NumBytesRead = 0; |
| uint64_t Val = decodeULEB128(Data, &NumBytesRead); |
| if (Val > std::numeric_limits<T>::max() || (Data + NumBytesRead > End)) { |
| exitWithError("Decode number error in " + SectionName + " section"); |
| } |
| Data += NumBytesRead; |
| return static_cast<T>(Val); |
| } |
| |
| template <typename T> T PseudoProbeDecoder::readSignedNumber() { |
| unsigned NumBytesRead = 0; |
| int64_t Val = decodeSLEB128(Data, &NumBytesRead); |
| if (Val > std::numeric_limits<T>::max() || (Data + NumBytesRead > End)) { |
| exitWithError("Decode number error in " + SectionName + " section"); |
| } |
| Data += NumBytesRead; |
| return static_cast<T>(Val); |
| } |
| |
| StringRef PseudoProbeDecoder::readString(uint32_t Size) { |
| StringRef Str(reinterpret_cast<const char *>(Data), Size); |
| if (Data + Size > End) { |
| exitWithError("Decode string error in " + SectionName + " section"); |
| } |
| Data += Size; |
| return Str; |
| } |
| |
| void PseudoProbeDecoder::buildGUID2FuncDescMap(const uint8_t *Start, |
| std::size_t Size) { |
| // The pseudo_probe_desc section has a format like: |
| // .section .pseudo_probe_desc,"",@progbits |
| // .quad -5182264717993193164 // GUID |
| // .quad 4294967295 // Hash |
| // .uleb 3 // Name size |
| // .ascii "foo" // Name |
| // .quad -2624081020897602054 |
| // .quad 174696971957 |
| // .uleb 34 |
| // .ascii "main" |
| #ifndef NDEBUG |
| SectionName = "pseudo_probe_desc"; |
| #endif |
| Data = Start; |
| End = Data + Size; |
| |
| while (Data < End) { |
| uint64_t GUID = readUnencodedNumber<uint64_t>(); |
| uint64_t Hash = readUnencodedNumber<uint64_t>(); |
| uint32_t NameSize = readUnsignedNumber<uint32_t>(); |
| StringRef Name = readString(NameSize); |
| |
| // Initialize PseudoProbeFuncDesc and populate it into GUID2FuncDescMap |
| GUID2FuncDescMap.emplace(GUID, PseudoProbeFuncDesc(GUID, Hash, Name)); |
| } |
| assert(Data == End && "Have unprocessed data in pseudo_probe_desc section"); |
| } |
| |
| void PseudoProbeDecoder::buildAddress2ProbeMap(const uint8_t *Start, |
| std::size_t Size) { |
| // The pseudo_probe section encodes an inline forest and each tree has a |
| // format like: |
| // FUNCTION BODY (one for each uninlined function present in the text |
| // section) |
| // GUID (uint64) |
| // GUID of the function |
| // 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 - tail call, 2 - dangling |
| // ADDRESS_TYPE (uint1) |
| // 0 - code address, 1 - address delta |
| // CODE_ADDRESS (uint64 or ULEB128) |
| // code address or address delta, depending on Flag |
| // INLINED FUNCTION RECORDS |
| // A list of NUM_INLINED_FUNCTIONS entries describing each of the |
| // inlined callees. Each record contains: |
| // INLINE SITE |
| // GUID of the inlinee (uint64) |
| // Index of the callsite probe (ULEB128) |
| // FUNCTION BODY |
| // A FUNCTION BODY entry describing the inlined function. |
| #ifndef NDEBUG |
| SectionName = "pseudo_probe"; |
| #endif |
| Data = Start; |
| End = Data + Size; |
| |
| PseudoProbeInlineTree *Root = &DummyInlineRoot; |
| PseudoProbeInlineTree *Cur = &DummyInlineRoot; |
| uint64_t LastAddr = 0; |
| uint32_t Index = 0; |
| // A DFS-based decoding |
| while (Data < End) { |
| // Read inline site for inlinees |
| if (Root != Cur) { |
| Index = readUnsignedNumber<uint32_t>(); |
| } |
| // Switch/add to a new tree node(inlinee) |
| Cur = Cur->getOrAddNode(std::make_tuple(Cur->GUID, Index)); |
| // Read guid |
| Cur->GUID = readUnencodedNumber<uint64_t>(); |
| // Read number of probes in the current node. |
| uint32_t NodeCount = readUnsignedNumber<uint32_t>(); |
| // Read number of direct inlinees |
| Cur->ChildrenToProcess = readUnsignedNumber<uint32_t>(); |
| // Read all probes in this node |
| for (std::size_t I = 0; I < NodeCount; I++) { |
| // Read index |
| uint32_t Index = readUnsignedNumber<uint32_t>(); |
| // Read type | flag. |
| uint8_t Value = readUnencodedNumber<uint8_t>(); |
| uint8_t Kind = Value & 0xf; |
| uint8_t Attr = (Value & 0x70) >> 4; |
| // Read address |
| uint64_t Addr = 0; |
| if (Value & 0x80) { |
| int64_t Offset = readSignedNumber<int64_t>(); |
| Addr = LastAddr + Offset; |
| } else { |
| Addr = readUnencodedNumber<int64_t>(); |
| } |
| // Populate Address2ProbesMap |
| std::vector<PseudoProbe> &ProbeVec = Address2ProbesMap[Addr]; |
| ProbeVec.emplace_back(Addr, Cur->GUID, Index, PseudoProbeType(Kind), Attr, |
| Cur); |
| Cur->addProbes(&ProbeVec.back()); |
| LastAddr = Addr; |
| } |
| |
| // Look for the parent for the next node by subtracting the current |
| // node count from tree counts along the parent chain. The first node |
| // in the chain that has a non-zero tree count is the target. |
| while (Cur != Root) { |
| if (Cur->ChildrenToProcess == 0) { |
| Cur = Cur->Parent; |
| if (Cur != Root) { |
| assert(Cur->ChildrenToProcess > 0 && |
| "Should have some unprocessed nodes"); |
| Cur->ChildrenToProcess -= 1; |
| } |
| } else { |
| break; |
| } |
| } |
| } |
| |
| assert(Data == End && "Have unprocessed data in pseudo_probe section"); |
| assert(Cur == Root && |
| " Cur should point to root when the forest is fully built up"); |
| } |
| |
| void PseudoProbeDecoder::printGUID2FuncDescMap(raw_ostream &OS) { |
| OS << "Pseudo Probe Desc:\n"; |
| // Make the output deterministic |
| std::map<uint64_t, PseudoProbeFuncDesc> OrderedMap(GUID2FuncDescMap.begin(), |
| GUID2FuncDescMap.end()); |
| for (auto &I : OrderedMap) { |
| I.second.print(OS); |
| } |
| } |
| |
| void PseudoProbeDecoder::printProbeForAddress(raw_ostream &OS, |
| uint64_t Address) { |
| auto It = Address2ProbesMap.find(Address); |
| if (It != Address2ProbesMap.end()) { |
| for (auto &Probe : It->second) { |
| OS << " [Probe]:\t"; |
| Probe.print(OS, GUID2FuncDescMap, true); |
| } |
| } |
| } |
| |
| const PseudoProbe * |
| PseudoProbeDecoder::getCallProbeForAddr(uint64_t Address) const { |
| auto It = Address2ProbesMap.find(Address); |
| if (It == Address2ProbesMap.end()) |
| return nullptr; |
| const std::vector<PseudoProbe> &Probes = It->second; |
| |
| const PseudoProbe *CallProbe = nullptr; |
| for (const auto &Probe : Probes) { |
| if (Probe.isCall()) { |
| assert(!CallProbe && |
| "There should be only one call probe corresponding to address " |
| "which is a callsite."); |
| CallProbe = &Probe; |
| } |
| } |
| return CallProbe; |
| } |
| |
| const PseudoProbeFuncDesc * |
| PseudoProbeDecoder::getFuncDescForGUID(uint64_t GUID) const { |
| auto It = GUID2FuncDescMap.find(GUID); |
| assert(It != GUID2FuncDescMap.end() && "Function descriptor doesn't exist"); |
| return &It->second; |
| } |
| |
| void PseudoProbeDecoder::getInlineContextForProbe( |
| const PseudoProbe *Probe, SmallVectorImpl<std::string> &InlineContextStack, |
| bool IncludeLeaf) const { |
| Probe->getInlineContext(InlineContextStack, GUID2FuncDescMap, true); |
| if (!IncludeLeaf) |
| return; |
| // Note that the context from probe doesn't include leaf frame, |
| // hence we need to retrieve and prepend leaf if requested. |
| const auto *FuncDesc = getFuncDescForGUID(Probe->GUID); |
| InlineContextStack.emplace_back(FuncDesc->FuncName + ":" + |
| Twine(Probe->Index).str()); |
| } |
| |
| const PseudoProbeFuncDesc * |
| PseudoProbeDecoder::getInlinerDescForProbe(const PseudoProbe *Probe) const { |
| PseudoProbeInlineTree *InlinerNode = Probe->InlineTree; |
| if (!InlinerNode->hasInlineSite()) |
| return nullptr; |
| return getFuncDescForGUID(std::get<0>(InlinerNode->ISite)); |
| } |
| |
| } // end namespace sampleprof |
| } // end namespace llvm |