1 //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/DebugInfo/GSYM/FileEntry.h"
10 #include "llvm/DebugInfo/GSYM/FileWriter.h"
11 #include "llvm/DebugInfo/GSYM/GsymReader.h"
12 #include "llvm/DebugInfo/GSYM/InlineInfo.h"
13 #include "llvm/Support/DataExtractor.h"
14 #include <algorithm>
15 #include <inttypes.h>
16
17 using namespace llvm;
18 using namespace gsym;
19
20
operator <<(raw_ostream & OS,const InlineInfo & II)21 raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const InlineInfo &II) {
22 if (!II.isValid())
23 return OS;
24 bool First = true;
25 for (auto Range : II.Ranges) {
26 if (First)
27 First = false;
28 else
29 OS << ' ';
30 OS << Range;
31 }
32 OS << " Name = " << HEX32(II.Name) << ", CallFile = " << II.CallFile
33 << ", CallLine = " << II.CallFile << '\n';
34 for (const auto &Child : II.Children)
35 OS << Child;
36 return OS;
37 }
38
getInlineStackHelper(const InlineInfo & II,uint64_t Addr,std::vector<const InlineInfo * > & InlineStack)39 static bool getInlineStackHelper(const InlineInfo &II, uint64_t Addr,
40 std::vector<const InlineInfo *> &InlineStack) {
41 if (II.Ranges.contains(Addr)) {
42 // If this is the top level that represents the concrete function,
43 // there will be no name and we shoud clear the inline stack. Otherwise
44 // we have found an inline call stack that we need to insert.
45 if (II.Name != 0)
46 InlineStack.insert(InlineStack.begin(), &II);
47 for (const auto &Child : II.Children) {
48 if (::getInlineStackHelper(Child, Addr, InlineStack))
49 break;
50 }
51 return !InlineStack.empty();
52 }
53 return false;
54 }
55
getInlineStack(uint64_t Addr) const56 llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr) const {
57 InlineArray Result;
58 if (getInlineStackHelper(*this, Addr, Result))
59 return Result;
60 return llvm::None;
61 }
62
63 /// Skip an InlineInfo object in the specified data at the specified offset.
64 ///
65 /// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo
66 /// objects where the addres ranges isn't contained in the InlineInfo object
67 /// or its children. This avoids allocations by not appending child InlineInfo
68 /// objects to the InlineInfo::Children array.
69 ///
70 /// \param Data The binary stream to read the data from.
71 ///
72 /// \param Offset The byte offset within \a Data.
73 ///
74 /// \param SkippedRanges If true, address ranges have already been skipped.
75
skip(DataExtractor & Data,uint64_t & Offset,bool SkippedRanges)76 static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) {
77 if (!SkippedRanges) {
78 if (AddressRanges::skip(Data, Offset) == 0)
79 return false;
80 }
81 bool HasChildren = Data.getU8(&Offset) != 0;
82 Data.getU32(&Offset); // Skip Inline.Name.
83 Data.getULEB128(&Offset); // Skip Inline.CallFile.
84 Data.getULEB128(&Offset); // Skip Inline.CallLine.
85 if (HasChildren) {
86 while (skip(Data, Offset, false /* SkippedRanges */))
87 /* Do nothing */;
88 }
89 // We skipped a valid InlineInfo.
90 return true;
91 }
92
93 /// A Lookup helper functions.
94 ///
95 /// Used during the InlineInfo::lookup() call to quickly only parse an
96 /// InlineInfo object if the address falls within this object. This avoids
97 /// allocations by not appending child InlineInfo objects to the
98 /// InlineInfo::Children array and also skips any InlineInfo objects that do
99 /// not contain the address we are looking up.
100 ///
101 /// \param Data The binary stream to read the data from.
102 ///
103 /// \param Offset The byte offset within \a Data.
104 ///
105 /// \param BaseAddr The address that the relative address range offsets are
106 /// relative to.
107
lookup(const GsymReader & GR,DataExtractor & Data,uint64_t & Offset,uint64_t BaseAddr,uint64_t Addr,SourceLocations & SrcLocs,llvm::Error & Err)108 static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset,
109 uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs,
110 llvm::Error &Err) {
111 InlineInfo Inline;
112 Inline.Ranges.decode(Data, BaseAddr, Offset);
113 if (Inline.Ranges.empty())
114 return true;
115 // Check if the address is contained within the inline information, and if
116 // not, quickly skip this InlineInfo object and all its children.
117 if (!Inline.Ranges.contains(Addr)) {
118 skip(Data, Offset, true /* SkippedRanges */);
119 return false;
120 }
121
122 // The address range is contained within this InlineInfo, add the source
123 // location for this InlineInfo and any children that contain the address.
124 bool HasChildren = Data.getU8(&Offset) != 0;
125 Inline.Name = Data.getU32(&Offset);
126 Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
127 Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
128 if (HasChildren) {
129 // Child address ranges are encoded relative to the first address in the
130 // parent InlineInfo object.
131 const auto ChildBaseAddr = Inline.Ranges[0].Start;
132 bool Done = false;
133 while (!Done)
134 Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err);
135 }
136
137 Optional<FileEntry> CallFile = GR.getFile(Inline.CallFile);
138 if (!CallFile) {
139 Err = createStringError(std::errc::invalid_argument,
140 "failed to extract file[%" PRIu32 "]",
141 Inline.CallFile);
142 return false;
143 }
144
145 SourceLocation SrcLoc;
146 SrcLoc.Name = SrcLocs.back().Name;
147 SrcLoc.Dir = GR.getString(CallFile->Dir);
148 SrcLoc.Base = GR.getString(CallFile->Base);
149 SrcLoc.Line = Inline.CallLine;
150 SrcLocs.back().Name = GR.getString(Inline.Name);
151 SrcLocs.push_back(SrcLoc);
152 return true;
153 }
154
lookup(const GsymReader & GR,DataExtractor & Data,uint64_t BaseAddr,uint64_t Addr,SourceLocations & SrcLocs)155 llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data,
156 uint64_t BaseAddr, uint64_t Addr,
157 SourceLocations &SrcLocs) {
158 // Call our recursive helper function starting at offset zero.
159 uint64_t Offset = 0;
160 llvm::Error Err = Error::success();
161 ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err);
162 return Err;
163 }
164
165 /// Decode an InlineInfo in Data at the specified offset.
166 ///
167 /// A local helper function to decode InlineInfo objects. This function is
168 /// called recursively when parsing child InlineInfo objects.
169 ///
170 /// \param Data The data extractor to decode from.
171 /// \param Offset The offset within \a Data to decode from.
172 /// \param BaseAddr The base address to use when decoding address ranges.
173 /// \returns An InlineInfo or an error describing the issue that was
174 /// encountered during decoding.
decode(DataExtractor & Data,uint64_t & Offset,uint64_t BaseAddr)175 static llvm::Expected<InlineInfo> decode(DataExtractor &Data, uint64_t &Offset,
176 uint64_t BaseAddr) {
177 InlineInfo Inline;
178 if (!Data.isValidOffset(Offset))
179 return createStringError(std::errc::io_error,
180 "0x%8.8" PRIx64 ": missing InlineInfo address ranges data", Offset);
181 Inline.Ranges.decode(Data, BaseAddr, Offset);
182 if (Inline.Ranges.empty())
183 return Inline;
184 if (!Data.isValidOffsetForDataOfSize(Offset, 1))
185 return createStringError(std::errc::io_error,
186 "0x%8.8" PRIx64 ": missing InlineInfo uint8_t indicating children",
187 Offset);
188 bool HasChildren = Data.getU8(&Offset) != 0;
189 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
190 return createStringError(std::errc::io_error,
191 "0x%8.8" PRIx64 ": missing InlineInfo uint32_t for name", Offset);
192 Inline.Name = Data.getU32(&Offset);
193 if (!Data.isValidOffset(Offset))
194 return createStringError(std::errc::io_error,
195 "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call file", Offset);
196 Inline.CallFile = (uint32_t)Data.getULEB128(&Offset);
197 if (!Data.isValidOffset(Offset))
198 return createStringError(std::errc::io_error,
199 "0x%8.8" PRIx64 ": missing ULEB128 for InlineInfo call line", Offset);
200 Inline.CallLine = (uint32_t)Data.getULEB128(&Offset);
201 if (HasChildren) {
202 // Child address ranges are encoded relative to the first address in the
203 // parent InlineInfo object.
204 const auto ChildBaseAddr = Inline.Ranges[0].Start;
205 while (true) {
206 llvm::Expected<InlineInfo> Child = decode(Data, Offset, ChildBaseAddr);
207 if (!Child)
208 return Child.takeError();
209 // InlineInfo with empty Ranges termintes a child sibling chain.
210 if (Child.get().Ranges.empty())
211 break;
212 Inline.Children.emplace_back(std::move(*Child));
213 }
214 }
215 return Inline;
216 }
217
decode(DataExtractor & Data,uint64_t BaseAddr)218 llvm::Expected<InlineInfo> InlineInfo::decode(DataExtractor &Data,
219 uint64_t BaseAddr) {
220 uint64_t Offset = 0;
221 return ::decode(Data, Offset, BaseAddr);
222 }
223
encode(FileWriter & O,uint64_t BaseAddr) const224 llvm::Error InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const {
225 // Users must verify the InlineInfo is valid prior to calling this funtion.
226 // We don't want to emit any InlineInfo objects if they are not valid since
227 // it will waste space in the GSYM file.
228 if (!isValid())
229 return createStringError(std::errc::invalid_argument,
230 "attempted to encode invalid InlineInfo object");
231 Ranges.encode(O, BaseAddr);
232 bool HasChildren = !Children.empty();
233 O.writeU8(HasChildren);
234 O.writeU32(Name);
235 O.writeULEB(CallFile);
236 O.writeULEB(CallLine);
237 if (HasChildren) {
238 // Child address ranges are encoded as relative to the first
239 // address in the Ranges for this object. This keeps the offsets
240 // small and allows for efficient encoding using ULEB offsets.
241 const uint64_t ChildBaseAddr = Ranges[0].Start;
242 for (const auto &Child : Children) {
243 // Make sure all child address ranges are contained in the parent address
244 // ranges.
245 for (const auto &ChildRange: Child.Ranges) {
246 if (!Ranges.contains(ChildRange))
247 return createStringError(std::errc::invalid_argument,
248 "child range not contained in parent");
249 }
250 llvm::Error Err = Child.encode(O, ChildBaseAddr);
251 if (Err)
252 return Err;
253 }
254
255 // Terminate child sibling chain by emitting a zero. This zero will cause
256 // the decodeAll() function above to return false and stop the decoding
257 // of child InlineInfo objects that are siblings.
258 O.writeULEB(0);
259 }
260 return Error::success();
261 }
262