xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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