123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- //===- InlineInfo.cpp -------------------------------------------*- 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 "llvm/DebugInfo/GSYM/FileEntry.h"
- #include "llvm/DebugInfo/GSYM/FileWriter.h"
- #include "llvm/DebugInfo/GSYM/GsymReader.h"
- #include "llvm/DebugInfo/GSYM/InlineInfo.h"
- #include "llvm/Support/DataExtractor.h"
- #include <algorithm>
- #include <inttypes.h>
- using namespace llvm;
- using namespace gsym;
- raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) {
- if (!II.isValid())
- return OS;
- bool First = true;
- for (auto Range : II.Ranges) {
- if (First)
- First = false;
- else
- OS << ' ';
- OS << Range;
- }
- OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile
- << ", CallLine = " << II.CallFile << '\n';
- for (const auto &Child : II.Children)
- OS << Child;
- return OS;
- }
- static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr,
- std::vector<const InlineInfo *> &InlineStack) {
- if (II.Ranges.contains(Addr)) {
- // If this is the top level that represents the concrete function,
- // there will be no name and we shoud clear the inline stack. Otherwise
- // we have found an inline call stack that we need to insert.
- if (II.Name != 0)
- InlineStack.insert(InlineStack.begin(), &II);
- for (const auto &Child : II.Children) {
- if (::getInlineStackHelper(Child, Addr, InlineStack))
- break;
- }
- return !InlineStack.empty();
- }
- return false;
- }
- llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr) const {
- InlineArray Result;
- if (getInlineStackHelper(*this, Addr, Result))
- return Result;
- return llvm::None;
- }
- /// Skip an InlineInfo object in the specified data at the specified offset.
- ///
- /// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo
- /// objects where the addres ranges isn't contained in the InlineInfo object
- /// or its children. This avoids allocations by not appending child InlineInfo
- /// objects to the InlineInfo::Children array.
- ///
- /// \param Data The binary stream to read the data from.
- ///
- /// \param Offset The byte offset within \a Data.
- ///
- /// \param SkippedRanges If true, address ranges have already been skipped.
- static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
- if (!SkippedRanges) {
- if (AddressRanges::skip(Data, Offset) == 0)
- return false;
- }
- bool HasChildren = Data.getU8(&Offset) != 0;
- Data.getU32(&Offset); // Skip Inline.Name.
- Data.getULEB128(&Offset); // Skip Inline.CallFile.
- Data.getULEB128(&Offset); // Skip Inline.CallLine.
- if (HasChildren) {
- while (skip(Data, Offset, false /* SkippedRanges */))
- /* Do nothing */;
- }
- // We skipped a valid InlineInfo.
- return true;
- }
- /// A Lookup helper functions.
- ///
- /// Used during the InlineInfo::lookup() call to quickly only parse an
- /// InlineInfo object if the address falls within this object. This avoids
- /// allocations by not appending child InlineInfo objects to the
- /// InlineInfo::Children array and also skips any InlineInfo objects that do
- /// not contain the address we are looking up.
- ///
- /// \param Data The binary stream to read the data from.
- ///
- /// \param Offset The byte offset within \a Data.
- ///
- /// \param BaseAddr The address that the relative address range offsets are
- /// relative to.
- static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
- uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,
- llvm::Error &Err) {
- InlineInfo Inline;
- Inline.Ranges.decode(Data, BaseAddr, Offset);
- if (Inline.Ranges.empty())
- return true;
- // Check if the address is contained within the inline information, and if
- // not, quickly skip this InlineInfo object and all its children.
- if (!Inline.Ranges.contains(Addr)) {
- skip(Data, Offset, true /* SkippedRanges */);
- return false;
- }
- // The address range is contained within this InlineInfo, add the source
- // location for this InlineInfo and any children that contain the address.
- bool HasChildren = Data.getU8(&Offset) != 0;
- Inline.Name = Data.getU32(&Offset);
- Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
- Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
- if (HasChildren) {
- // Child address ranges are encoded relative to the first address in the
- // parent InlineInfo object.
- const auto ChildBaseAddr = Inline.Ranges[0].Start;
- bool Done = false;
- while (!Done)
- Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err);
- }
- Optional<FileEntry> CallFile = GR.getFile(Inline.CallFile);
- if (!CallFile) {
- Err = createStringError(std::errc::invalid_argument,
- "failed to extract file[%" PRIu32 "]",
- Inline.CallFile);
- return false;
- }
- if (CallFile->Dir || CallFile->Base) {
- SourceLocation SrcLoc;
- SrcLoc.Name = SrcLocs.back().Name;
- SrcLoc.Offset = SrcLocs.back().Offset;
- SrcLoc.Dir = GR.getString(CallFile->Dir);
- SrcLoc.Base = GR.getString(CallFile->Base);
- SrcLoc.Line = Inline.CallLine;
- SrcLocs.back().Name = GR.getString(Inline.Name);
- SrcLocs.back().Offset = Addr - Inline.Ranges[0].Start;
- SrcLocs.push_back(SrcLoc);
- }
- return true;
- }
- llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
- uint64_t BaseAddr, uint64_t Addr,
- SourceLocations &SrcLocs) {
- // Call our recursive helper function starting at offset zero.
- uint64_t Offset = 0;
- llvm::Error Err = Error::success();
- ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err);
- return Err;
- }
- /// Decode an InlineInfo in Data at the specified offset.
- ///
- /// A local helper function to decode InlineInfo objects. This function is
- /// called recursively when parsing child InlineInfo objects.
- ///
- /// \param Data The data extractor to decode from.
- /// \param Offset The offset within \a Data to decode from.
- /// \param BaseAddr The base address to use when decoding address ranges.
- /// \returns An InlineInfo or an error describing the issue that was
- /// encountered during decoding.
- static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
- uint64_t BaseAddr) {
- InlineInfo Inline;
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);
- Inline.Ranges.decode(Data, BaseAddr, Offset);
- if (Inline.Ranges.empty())
- return Inline;
- if (!Data.isValidOffsetForDataOfSize(Offset, 1))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
- Offset);
- bool HasChildren = Data.getU8(&Offset) != 0;
- if (!Data.isValidOffsetForDataOfSize(Offset, 4))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
- Inline.Name = Data.getU32(&Offset);
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
- Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
- if (!Data.isValidOffset(Offset))
- return createStringError(std::errc::io_error,
- "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);
- Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
- if (HasChildren) {
- // Child address ranges are encoded relative to the first address in the
- // parent InlineInfo object.
- const auto ChildBaseAddr = Inline.Ranges[0].Start;
- while (true) {
- llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);
- if (!Child)
- return Child.takeError();
- // InlineInfo with empty Ranges termintes a child sibling chain.
- if (Child.get().Ranges.empty())
- break;
- Inline.Children.emplace_back(std::move(*Child));
- }
- }
- return Inline;
- }
- llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
- uint64_t BaseAddr) {
- uint64_t Offset = 0;
- return ::decode(Data, Offset, BaseAddr);
- }
- llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
- // Users must verify the InlineInfo is valid prior to calling this funtion.
- // We don't want to emit any InlineInfo objects if they are not valid since
- // it will waste space in the GSYM file.
- if (!isValid())
- return createStringError(std::errc::invalid_argument,
- "attempted to encode invalid InlineInfo object");
- Ranges.encode(O, BaseAddr);
- bool HasChildren = !Children.empty();
- O.writeU8(HasChildren);
- O.writeU32(Name);
- O.writeULEB(CallFile);
- O.writeULEB(CallLine);
- if (HasChildren) {
- // Child address ranges are encoded as relative to the first
- // address in the Ranges for this object. This keeps the offsets
- // small and allows for efficient encoding using ULEB offsets.
- const uint64_t ChildBaseAddr = Ranges[0].Start;
- for (const auto &Child : Children) {
- // Make sure all child address ranges are contained in the parent address
- // ranges.
- for (const auto &ChildRange: Child.Ranges) {
- if (!Ranges.contains(ChildRange))
- return createStringError(std::errc::invalid_argument,
- "child range not contained in parent");
- }
- llvm::Error Err = Child.encode(O, ChildBaseAddr);
- if (Err)
- return Err;
- }
- // Terminate child sibling chain by emitting a zero. This zero will cause
- // the decodeAll() function above to return false and stop the decoding
- // of child InlineInfo objects that are siblings.
- O.writeULEB(0);
- }
- return Error::success();
- }
|