Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp
35269 views
//===- InlineInfo.cpp -------------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DebugInfo/GSYM/FileEntry.h"9#include "llvm/DebugInfo/GSYM/FileWriter.h"10#include "llvm/DebugInfo/GSYM/GsymReader.h"11#include "llvm/DebugInfo/GSYM/InlineInfo.h"12#include "llvm/Support/DataExtractor.h"13#include <algorithm>14#include <inttypes.h>1516using namespace llvm;17using namespace gsym;181920raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) {21if (!II.isValid())22return OS;23bool First = true;24for (auto Range : II.Ranges) {25if (First)26First = false;27else28OS << ' ';29OS << Range;30}31OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile32<< ", CallLine = " << II.CallFile << '\n';33for (const auto &Child : II.Children)34OS << Child;35return OS;36}3738static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr,39std::vector<const InlineInfo *> &InlineStack) {40if (II.Ranges.contains(Addr)) {41// If this is the top level that represents the concrete function,42// there will be no name and we shoud clear the inline stack. Otherwise43// we have found an inline call stack that we need to insert.44if (II.Name != 0)45InlineStack.insert(InlineStack.begin(), &II);46for (const auto &Child : II.Children) {47if (::getInlineStackHelper(Child, Addr, InlineStack))48break;49}50return !InlineStack.empty();51}52return false;53}5455std::optional<InlineInfo::InlineArray>56InlineInfo::getInlineStack(uint64_t Addr) const {57InlineArray Result;58if (getInlineStackHelper(*this, Addr, Result))59return Result;60return std::nullopt;61}6263/// Skip an InlineInfo object in the specified data at the specified offset.64///65/// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo66/// objects where the addres ranges isn't contained in the InlineInfo object67/// or its children. This avoids allocations by not appending child InlineInfo68/// objects to the InlineInfo::Children array.69///70/// \param Data The binary stream to read the data from.71///72/// \param Offset The byte offset within \a Data.73///74/// \param SkippedRanges If true, address ranges have already been skipped.7576static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {77if (!SkippedRanges) {78if (skipRanges(Data, Offset) == 0)79return false;80}81bool HasChildren = Data.getU8(&Offset) != 0;82Data.getU32(&Offset); // Skip Inline.Name.83Data.getULEB128(&Offset); // Skip Inline.CallFile.84Data.getULEB128(&Offset); // Skip Inline.CallLine.85if (HasChildren) {86while (skip(Data, Offset, false /* SkippedRanges */))87/* Do nothing */;88}89// We skipped a valid InlineInfo.90return true;91}9293/// A Lookup helper functions.94///95/// Used during the InlineInfo::lookup() call to quickly only parse an96/// InlineInfo object if the address falls within this object. This avoids97/// allocations by not appending child InlineInfo objects to the98/// InlineInfo::Children array and also skips any InlineInfo objects that do99/// not contain the address we are looking up.100///101/// \param Data The binary stream to read the data from.102///103/// \param Offset The byte offset within \a Data.104///105/// \param BaseAddr The address that the relative address range offsets are106/// relative to.107108static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,109uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,110llvm::Error &Err) {111InlineInfo Inline;112decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);113if (Inline.Ranges.empty())114return true;115// Check if the address is contained within the inline information, and if116// not, quickly skip this InlineInfo object and all its children.117if (!Inline.Ranges.contains(Addr)) {118skip(Data, Offset, true /* SkippedRanges */);119return false;120}121122// The address range is contained within this InlineInfo, add the source123// location for this InlineInfo and any children that contain the address.124bool HasChildren = Data.getU8(&Offset) != 0;125Inline.Name = Data.getU32(&Offset);126Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);127Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);128if (HasChildren) {129// Child address ranges are encoded relative to the first address in the130// parent InlineInfo object.131const auto ChildBaseAddr = Inline.Ranges[0].start();132bool Done = false;133while (!Done)134Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err);135}136137std::optional<FileEntry> CallFile = GR.getFile(Inline.CallFile);138if (!CallFile) {139Err = createStringError(std::errc::invalid_argument,140"failed to extract file[%" PRIu32 "]",141Inline.CallFile);142return false;143}144145if (CallFile->Dir || CallFile->Base) {146SourceLocation SrcLoc;147SrcLoc.Name = SrcLocs.back().Name;148SrcLoc.Offset = SrcLocs.back().Offset;149SrcLoc.Dir = GR.getString(CallFile->Dir);150SrcLoc.Base = GR.getString(CallFile->Base);151SrcLoc.Line = Inline.CallLine;152SrcLocs.back().Name = GR.getString(Inline.Name);153SrcLocs.back().Offset = Addr - Inline.Ranges[0].start();154SrcLocs.push_back(SrcLoc);155}156return true;157}158159llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,160uint64_t BaseAddr, uint64_t Addr,161SourceLocations &SrcLocs) {162// Call our recursive helper function starting at offset zero.163uint64_t Offset = 0;164llvm::Error Err = Error::success();165::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err);166return Err;167}168169/// Decode an InlineInfo in Data at the specified offset.170///171/// A local helper function to decode InlineInfo objects. This function is172/// called recursively when parsing child InlineInfo objects.173///174/// \param Data The data extractor to decode from.175/// \param Offset The offset within \a Data to decode from.176/// \param BaseAddr The base address to use when decoding address ranges.177/// \returns An InlineInfo or an error describing the issue that was178/// encountered during decoding.179static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,180uint64_t BaseAddr) {181InlineInfo Inline;182if (!Data.isValidOffset(Offset))183return createStringError(std::errc::io_error,184"0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);185decodeRanges(Inline.Ranges, Data, BaseAddr, Offset);186if (Inline.Ranges.empty())187return Inline;188if (!Data.isValidOffsetForDataOfSize(Offset, 1))189return createStringError(std::errc::io_error,190"0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",191Offset);192bool HasChildren = Data.getU8(&Offset) != 0;193if (!Data.isValidOffsetForDataOfSize(Offset, 4))194return createStringError(std::errc::io_error,195"0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);196Inline.Name = Data.getU32(&Offset);197if (!Data.isValidOffset(Offset))198return createStringError(std::errc::io_error,199"0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);200Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);201if (!Data.isValidOffset(Offset))202return createStringError(std::errc::io_error,203"0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);204Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);205if (HasChildren) {206// Child address ranges are encoded relative to the first address in the207// parent InlineInfo object.208const auto ChildBaseAddr = Inline.Ranges[0].start();209while (true) {210llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);211if (!Child)212return Child.takeError();213// InlineInfo with empty Ranges termintes a child sibling chain.214if (Child.get().Ranges.empty())215break;216Inline.Children.emplace_back(std::move(*Child));217}218}219return Inline;220}221222llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,223uint64_t BaseAddr) {224uint64_t Offset = 0;225return ::decode(Data, Offset, BaseAddr);226}227228llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {229// Users must verify the InlineInfo is valid prior to calling this funtion.230// We don't want to emit any InlineInfo objects if they are not valid since231// it will waste space in the GSYM file.232if (!isValid())233return createStringError(std::errc::invalid_argument,234"attempted to encode invalid InlineInfo object");235encodeRanges(Ranges, O, BaseAddr);236bool HasChildren = !Children.empty();237O.writeU8(HasChildren);238O.writeU32(Name);239O.writeULEB(CallFile);240O.writeULEB(CallLine);241if (HasChildren) {242// Child address ranges are encoded as relative to the first243// address in the Ranges for this object. This keeps the offsets244// small and allows for efficient encoding using ULEB offsets.245const uint64_t ChildBaseAddr = Ranges[0].start();246for (const auto &Child : Children) {247// Make sure all child address ranges are contained in the parent address248// ranges.249for (const auto &ChildRange: Child.Ranges) {250if (!Ranges.contains(ChildRange))251return createStringError(std::errc::invalid_argument,252"child range not contained in parent");253}254llvm::Error Err = Child.encode(O, ChildBaseAddr);255if (Err)256return Err;257}258259// Terminate child sibling chain by emitting a zero. This zero will cause260// the decodeAll() function above to return false and stop the decoding261// of child InlineInfo objects that are siblings.262O.writeULEB(0);263}264return Error::success();265}266267static uint64_t GetTotalNumChildren(const InlineInfo &II) {268uint64_t NumChildren = II.Children.size();269for (const auto &Child : II.Children)270NumChildren += GetTotalNumChildren(Child);271return NumChildren;272}273274bool InlineInfo::operator<(const InlineInfo &RHS) const {275return GetTotalNumChildren(*this) < GetTotalNumChildren(RHS);276}277278279