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
operator <<(raw_ostream & OS,const InlineInfo & II)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
getInlineStackHelper(const InlineInfo & II,uint64_t Addr,std::vector<const InlineInfo * > & InlineStack)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>
getInlineStack(uint64_t Addr) const57bdd1243dSDimitry 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
skip(DataExtractor & Data,uint64_t & Offset,bool SkippedRanges)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
lookup(const GsymReader & GR,DataExtractor & Data,uint64_t & Offset,uint64_t BaseAddr,uint64_t Addr,SourceLocations & SrcLocs,llvm::Error & Err)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
lookup(const GsymReader & GR,DataExtractor & Data,uint64_t BaseAddr,uint64_t Addr,SourceLocations & SrcLocs)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.
decode(DataExtractor & Data,uint64_t & Offset,uint64_t BaseAddr)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
decode(DataExtractor & Data,uint64_t BaseAddr)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
encode(FileWriter & O,uint64_t BaseAddr) const2298bcb0991SDimitry 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
GetTotalNumChildren(const InlineInfo & II)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
operator <(const InlineInfo & RHS) const275*5f757f3fSDimitry Andric bool InlineInfo::operator<(const InlineInfo &RHS) const {
276*5f757f3fSDimitry Andric return GetTotalNumChildren(*this) < GetTotalNumChildren(RHS);
277*5f757f3fSDimitry Andric }
278