//===- InlineInfo.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/GSYM/FileEntry.h" #include "llvm/DebugInfo/GSYM/FileWriter.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/Support/DataExtractor.h" #include #include 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 &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::getInlineStack(uint64_t Addr) const { InlineArray Result; if (getInlineStackHelper(*this, Addr, Result)) return Result; return llvm::None; } /// 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 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 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::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(); }