10b57cec5SDimitry Andric //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "llvm/DebugInfo/GSYM/FileEntry.h" 108bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/FileWriter.h" 11480093f4SDimitry Andric #include "llvm/DebugInfo/GSYM/GsymReader.h" 120b57cec5SDimitry Andric #include "llvm/DebugInfo/GSYM/InlineInfo.h" 138bcb0991SDimitry Andric #include "llvm/Support/DataExtractor.h" 140b57cec5SDimitry Andric #include <algorithm> 150b57cec5SDimitry Andric #include <inttypes.h> 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric using namespace llvm; 180b57cec5SDimitry Andric using namespace gsym; 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) { 220b57cec5SDimitry Andric if (!II.isValid()) 230b57cec5SDimitry Andric return OS; 240b57cec5SDimitry Andric bool First = true; 250b57cec5SDimitry Andric for (auto Range : II.Ranges) { 260b57cec5SDimitry Andric if (First) 270b57cec5SDimitry Andric First = false; 280b57cec5SDimitry Andric else 290b57cec5SDimitry Andric OS << ' '; 300b57cec5SDimitry Andric OS << Range; 310b57cec5SDimitry Andric } 320b57cec5SDimitry Andric OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile 330b57cec5SDimitry Andric << ", CallLine = " << II.CallFile << '\n'; 340b57cec5SDimitry Andric for (const auto &Child : II.Children) 350b57cec5SDimitry Andric OS << Child; 360b57cec5SDimitry Andric return OS; 370b57cec5SDimitry Andric } 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr, 400b57cec5SDimitry Andric std::vector<const InlineInfo *> &InlineStack) { 410b57cec5SDimitry Andric if (II.Ranges.contains(Addr)) { 420b57cec5SDimitry Andric // If this is the top level that represents the concrete function, 430b57cec5SDimitry Andric // there will be no name and we shoud clear the inline stack. Otherwise 440b57cec5SDimitry Andric // we have found an inline call stack that we need to insert. 450b57cec5SDimitry Andric if (II.Name != 0) 460b57cec5SDimitry Andric InlineStack.insert(InlineStack.begin(), &II); 470b57cec5SDimitry Andric for (const auto &Child : II.Children) { 480b57cec5SDimitry Andric if (::getInlineStackHelper(Child, Addr, InlineStack)) 490b57cec5SDimitry Andric break; 500b57cec5SDimitry Andric } 510b57cec5SDimitry Andric return !InlineStack.empty(); 520b57cec5SDimitry Andric } 530b57cec5SDimitry Andric return false; 540b57cec5SDimitry Andric } 550b57cec5SDimitry Andric 56bdd1243dSDimitry Andric std::optional<InlineInfo::InlineArray> 57bdd1243dSDimitry Andric InlineInfo::getInlineStack(uint64_t Addr) const { 580b57cec5SDimitry Andric InlineArray Result; 590b57cec5SDimitry Andric if (getInlineStackHelper(*this, Addr, Result)) 600b57cec5SDimitry Andric return Result; 61bdd1243dSDimitry Andric return std::nullopt; 620b57cec5SDimitry Andric } 638bcb0991SDimitry Andric 64480093f4SDimitry Andric /// Skip an InlineInfo object in the specified data at the specified offset. 65480093f4SDimitry Andric /// 66480093f4SDimitry Andric /// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo 67480093f4SDimitry Andric /// objects where the addres ranges isn't contained in the InlineInfo object 68480093f4SDimitry Andric /// or its children. This avoids allocations by not appending child InlineInfo 69480093f4SDimitry Andric /// objects to the InlineInfo::Children array. 70480093f4SDimitry Andric /// 71480093f4SDimitry Andric /// \param Data The binary stream to read the data from. 72480093f4SDimitry Andric /// 73480093f4SDimitry Andric /// \param Offset The byte offset within \a Data. 74480093f4SDimitry Andric /// 75480093f4SDimitry Andric /// \param SkippedRanges If true, address ranges have already been skipped. 76480093f4SDimitry Andric 77480093f4SDimitry Andric static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) { 78480093f4SDimitry Andric if (!SkippedRanges) { 7981ad6265SDimitry Andric if (skipRanges(Data, Offset) == 0) 80480093f4SDimitry Andric return false; 81480093f4SDimitry Andric } 82480093f4SDimitry Andric bool HasChildren = Data.getU8(&Offset) != 0; 83480093f4SDimitry Andric Data.getU32(&Offset); // Skip Inline.Name. 84480093f4SDimitry Andric Data.getULEB128(&Offset); // Skip Inline.CallFile. 85480093f4SDimitry Andric Data.getULEB128(&Offset); // Skip Inline.CallLine. 86480093f4SDimitry Andric if (HasChildren) { 87480093f4SDimitry Andric while (skip(Data, Offset, false /* SkippedRanges */)) 88480093f4SDimitry Andric /* Do nothing */; 89480093f4SDimitry Andric } 90480093f4SDimitry Andric // We skipped a valid InlineInfo. 91480093f4SDimitry Andric return true; 92480093f4SDimitry Andric } 93480093f4SDimitry Andric 94480093f4SDimitry Andric /// A Lookup helper functions. 95480093f4SDimitry Andric /// 96480093f4SDimitry Andric /// Used during the InlineInfo::lookup() call to quickly only parse an 97480093f4SDimitry Andric /// InlineInfo object if the address falls within this object. This avoids 98480093f4SDimitry Andric /// allocations by not appending child InlineInfo objects to the 99480093f4SDimitry Andric /// InlineInfo::Children array and also skips any InlineInfo objects that do 100480093f4SDimitry Andric /// not contain the address we are looking up. 101480093f4SDimitry Andric /// 102480093f4SDimitry Andric /// \param Data The binary stream to read the data from. 103480093f4SDimitry Andric /// 104480093f4SDimitry Andric /// \param Offset The byte offset within \a Data. 105480093f4SDimitry Andric /// 106480093f4SDimitry Andric /// \param BaseAddr The address that the relative address range offsets are 107480093f4SDimitry Andric /// relative to. 108480093f4SDimitry Andric 109480093f4SDimitry Andric static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset, 110480093f4SDimitry Andric uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, 111480093f4SDimitry Andric llvm::Error &Err) { 112480093f4SDimitry Andric InlineInfo Inline; 11381ad6265SDimitry Andric decodeRanges(Inline.Ranges, Data, BaseAddr, Offset); 114480093f4SDimitry Andric if (Inline.Ranges.empty()) 115480093f4SDimitry Andric return true; 116480093f4SDimitry Andric // Check if the address is contained within the inline information, and if 117480093f4SDimitry Andric // not, quickly skip this InlineInfo object and all its children. 118480093f4SDimitry Andric if (!Inline.Ranges.contains(Addr)) { 119480093f4SDimitry Andric skip(Data, Offset, true /* SkippedRanges */); 120480093f4SDimitry Andric return false; 121480093f4SDimitry Andric } 122480093f4SDimitry Andric 123480093f4SDimitry Andric // The address range is contained within this InlineInfo, add the source 124480093f4SDimitry Andric // location for this InlineInfo and any children that contain the address. 125480093f4SDimitry Andric bool HasChildren = Data.getU8(&Offset) != 0; 126480093f4SDimitry Andric Inline.Name = Data.getU32(&Offset); 127480093f4SDimitry Andric Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); 128480093f4SDimitry Andric Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); 129480093f4SDimitry Andric if (HasChildren) { 130480093f4SDimitry Andric // Child address ranges are encoded relative to the first address in the 131480093f4SDimitry Andric // parent InlineInfo object. 13281ad6265SDimitry Andric const auto ChildBaseAddr = Inline.Ranges[0].start(); 133480093f4SDimitry Andric bool Done = false; 134480093f4SDimitry Andric while (!Done) 135480093f4SDimitry Andric Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err); 136480093f4SDimitry Andric } 137480093f4SDimitry Andric 138bdd1243dSDimitry Andric std::optional<FileEntry> CallFile = GR.getFile(Inline.CallFile); 139480093f4SDimitry Andric if (!CallFile) { 140480093f4SDimitry Andric Err = createStringError(std::errc::invalid_argument, 141480093f4SDimitry Andric "failed to extract file[%" PRIu32 "]", 142480093f4SDimitry Andric Inline.CallFile); 143480093f4SDimitry Andric return false; 144480093f4SDimitry Andric } 145480093f4SDimitry Andric 1465ffd83dbSDimitry Andric if (CallFile->Dir || CallFile->Base) { 147480093f4SDimitry Andric SourceLocation SrcLoc; 148480093f4SDimitry Andric SrcLoc.Name = SrcLocs.back().Name; 1495ffd83dbSDimitry Andric SrcLoc.Offset = SrcLocs.back().Offset; 150480093f4SDimitry Andric SrcLoc.Dir = GR.getString(CallFile->Dir); 151480093f4SDimitry Andric SrcLoc.Base = GR.getString(CallFile->Base); 152480093f4SDimitry Andric SrcLoc.Line = Inline.CallLine; 153480093f4SDimitry Andric SrcLocs.back().Name = GR.getString(Inline.Name); 15481ad6265SDimitry Andric SrcLocs.back().Offset = Addr - Inline.Ranges[0].start(); 155480093f4SDimitry Andric SrcLocs.push_back(SrcLoc); 1565ffd83dbSDimitry Andric } 157480093f4SDimitry Andric return true; 158480093f4SDimitry Andric } 159480093f4SDimitry Andric 160480093f4SDimitry Andric llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data, 161480093f4SDimitry Andric uint64_t BaseAddr, uint64_t Addr, 162480093f4SDimitry Andric SourceLocations &SrcLocs) { 163480093f4SDimitry Andric // Call our recursive helper function starting at offset zero. 164480093f4SDimitry Andric uint64_t Offset = 0; 165480093f4SDimitry Andric llvm::Error Err = Error::success(); 166480093f4SDimitry Andric ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err); 167480093f4SDimitry Andric return Err; 168480093f4SDimitry Andric } 169480093f4SDimitry Andric 1708bcb0991SDimitry Andric /// Decode an InlineInfo in Data at the specified offset. 1718bcb0991SDimitry Andric /// 1728bcb0991SDimitry Andric /// A local helper function to decode InlineInfo objects. This function is 1738bcb0991SDimitry Andric /// called recursively when parsing child InlineInfo objects. 1748bcb0991SDimitry Andric /// 1758bcb0991SDimitry Andric /// \param Data The data extractor to decode from. 1768bcb0991SDimitry Andric /// \param Offset The offset within \a Data to decode from. 1778bcb0991SDimitry Andric /// \param BaseAddr The base address to use when decoding address ranges. 1788bcb0991SDimitry Andric /// \returns An InlineInfo or an error describing the issue that was 1798bcb0991SDimitry Andric /// encountered during decoding. 1808bcb0991SDimitry Andric static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset, 1818bcb0991SDimitry Andric uint64_t BaseAddr) { 1828bcb0991SDimitry Andric InlineInfo Inline; 1838bcb0991SDimitry Andric if (!Data.isValidOffset(Offset)) 1848bcb0991SDimitry Andric return createStringError(std::errc::io_error, 1858bcb0991SDimitry Andric "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset); 18681ad6265SDimitry Andric decodeRanges(Inline.Ranges, Data, BaseAddr, Offset); 1878bcb0991SDimitry Andric if (Inline.Ranges.empty()) 1888bcb0991SDimitry Andric return Inline; 1898bcb0991SDimitry Andric if (!Data.isValidOffsetForDataOfSize(Offset, 1)) 1908bcb0991SDimitry Andric return createStringError(std::errc::io_error, 1918bcb0991SDimitry Andric "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children", 1928bcb0991SDimitry Andric Offset); 1938bcb0991SDimitry Andric bool HasChildren = Data.getU8(&Offset) != 0; 1948bcb0991SDimitry Andric if (!Data.isValidOffsetForDataOfSize(Offset, 4)) 1958bcb0991SDimitry Andric return createStringError(std::errc::io_error, 1968bcb0991SDimitry Andric "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset); 1978bcb0991SDimitry Andric Inline.Name = Data.getU32(&Offset); 1988bcb0991SDimitry Andric if (!Data.isValidOffset(Offset)) 1998bcb0991SDimitry Andric return createStringError(std::errc::io_error, 2008bcb0991SDimitry Andric "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset); 2018bcb0991SDimitry Andric Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); 2028bcb0991SDimitry Andric if (!Data.isValidOffset(Offset)) 2038bcb0991SDimitry Andric return createStringError(std::errc::io_error, 2048bcb0991SDimitry Andric "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset); 2058bcb0991SDimitry Andric Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); 2068bcb0991SDimitry Andric if (HasChildren) { 2078bcb0991SDimitry Andric // Child address ranges are encoded relative to the first address in the 2088bcb0991SDimitry Andric // parent InlineInfo object. 20981ad6265SDimitry Andric const auto ChildBaseAddr = Inline.Ranges[0].start(); 2108bcb0991SDimitry Andric while (true) { 2118bcb0991SDimitry Andric llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr); 2128bcb0991SDimitry Andric if (!Child) 2138bcb0991SDimitry Andric return Child.takeError(); 2148bcb0991SDimitry Andric // InlineInfo with empty Ranges termintes a child sibling chain. 2158bcb0991SDimitry Andric if (Child.get().Ranges.empty()) 2168bcb0991SDimitry Andric break; 2178bcb0991SDimitry Andric Inline.Children.emplace_back(std::move(*Child)); 2188bcb0991SDimitry Andric } 2198bcb0991SDimitry Andric } 2208bcb0991SDimitry Andric return Inline; 2218bcb0991SDimitry Andric } 2228bcb0991SDimitry Andric 2238bcb0991SDimitry Andric llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data, 2248bcb0991SDimitry Andric uint64_t BaseAddr) { 2258bcb0991SDimitry Andric uint64_t Offset = 0; 2268bcb0991SDimitry Andric return ::decode(Data, Offset, BaseAddr); 2278bcb0991SDimitry Andric } 2288bcb0991SDimitry Andric 2298bcb0991SDimitry Andric llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const { 2308bcb0991SDimitry Andric // Users must verify the InlineInfo is valid prior to calling this funtion. 2318bcb0991SDimitry Andric // We don't want to emit any InlineInfo objects if they are not valid since 2328bcb0991SDimitry Andric // it will waste space in the GSYM file. 2338bcb0991SDimitry Andric if (!isValid()) 2348bcb0991SDimitry Andric return createStringError(std::errc::invalid_argument, 2358bcb0991SDimitry Andric "attempted to encode invalid InlineInfo object"); 23681ad6265SDimitry Andric encodeRanges(Ranges, O, BaseAddr); 2378bcb0991SDimitry Andric bool HasChildren = !Children.empty(); 2388bcb0991SDimitry Andric O.writeU8(HasChildren); 2398bcb0991SDimitry Andric O.writeU32(Name); 2408bcb0991SDimitry Andric O.writeULEB(CallFile); 2418bcb0991SDimitry Andric O.writeULEB(CallLine); 2428bcb0991SDimitry Andric if (HasChildren) { 2438bcb0991SDimitry Andric // Child address ranges are encoded as relative to the first 2448bcb0991SDimitry Andric // address in the Ranges for this object. This keeps the offsets 2458bcb0991SDimitry Andric // small and allows for efficient encoding using ULEB offsets. 24681ad6265SDimitry Andric const uint64_t ChildBaseAddr = Ranges[0].start(); 2478bcb0991SDimitry Andric for (const auto &Child : Children) { 2488bcb0991SDimitry Andric // Make sure all child address ranges are contained in the parent address 2498bcb0991SDimitry Andric // ranges. 2508bcb0991SDimitry Andric for (const auto &ChildRange: Child.Ranges) { 2518bcb0991SDimitry Andric if (!Ranges.contains(ChildRange)) 2528bcb0991SDimitry Andric return createStringError(std::errc::invalid_argument, 2538bcb0991SDimitry Andric "child range not contained in parent"); 2548bcb0991SDimitry Andric } 2558bcb0991SDimitry Andric llvm::Error Err = Child.encode(O, ChildBaseAddr); 2568bcb0991SDimitry Andric if (Err) 2578bcb0991SDimitry Andric return Err; 2588bcb0991SDimitry Andric } 2598bcb0991SDimitry Andric 2608bcb0991SDimitry Andric // Terminate child sibling chain by emitting a zero. This zero will cause 2618bcb0991SDimitry Andric // the decodeAll() function above to return false and stop the decoding 2628bcb0991SDimitry Andric // of child InlineInfo objects that are siblings. 2638bcb0991SDimitry Andric O.writeULEB(0); 2648bcb0991SDimitry Andric } 2658bcb0991SDimitry Andric return Error::success(); 2668bcb0991SDimitry Andric } 267*5f757f3fSDimitry Andric 268*5f757f3fSDimitry Andric static uint64_t GetTotalNumChildren(const InlineInfo &II) { 269*5f757f3fSDimitry Andric uint64_t NumChildren = II.Children.size(); 270*5f757f3fSDimitry Andric for (const auto &Child : II.Children) 271*5f757f3fSDimitry Andric NumChildren += GetTotalNumChildren(Child); 272*5f757f3fSDimitry Andric return NumChildren; 273*5f757f3fSDimitry Andric } 274*5f757f3fSDimitry Andric 275*5f757f3fSDimitry Andric bool InlineInfo::operator<(const InlineInfo &RHS) const { 276*5f757f3fSDimitry Andric return GetTotalNumChildren(*this) < GetTotalNumChildren(RHS); 277*5f757f3fSDimitry Andric } 278