LLVM 24.0.0git
FunctionInfo.cpp
Go to the documentation of this file.
1//===- FunctionInfo.cpp ---------------------------------------------------===//
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
16#include <optional>
17
18using namespace llvm;
19using namespace gsym;
20
21/// FunctionInfo information type that is used to encode the optional data
22/// that is associated with a FunctionInfo object.
30
32 OS << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';
33 if (FI.OptLineTable)
34 OS << FI.OptLineTable << '\n';
35 if (FI.Inline)
36 OS << FI.Inline << '\n';
37 if (FI.CallSites)
38 OS << *FI.CallSites << '\n';
39 return OS;
40}
41
43 uint64_t BaseAddr) {
44 FunctionInfo FI;
45 uint64_t Offset = 0;
46 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
47 return createStringError(std::errc::io_error,
48 "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);
49 FI.Range = {BaseAddr, BaseAddr + Data.getU32(&Offset)};
50 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
51 return createStringError(std::errc::io_error,
52 "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
53 FI.Name = Data.getStringOffset(&Offset);
54 if (FI.Name == 0)
55 return createStringError(std::errc::io_error,
56 "0x%8.8" PRIx64
57 ": invalid FunctionInfo Name value 0x%" PRIx64,
58 Offset - Data.getStringOffsetSize(), FI.Name);
59 bool Done = false;
60 while (!Done) {
61 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
62 return createStringError(std::errc::io_error,
63 "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);
64 const uint32_t IT = Data.getU32(&Offset);
65 if (!Data.isValidOffsetForDataOfSize(Offset, 4))
66 return createStringError(std::errc::io_error,
67 "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);
68 const uint32_t InfoLength = Data.getU32(&Offset);
69 if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
70 return createStringError(std::errc::io_error,
71 "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
72 Offset, IT);
73 GsymDataExtractor InfoData(Data, Offset, InfoLength);
74 switch (IT) {
76 Done = true;
77 break;
78
80 if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))
81 FI.OptLineTable = std::move(LT.get());
82 else
83 return LT.takeError();
84 break;
85
87 if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))
88 FI.Inline = std::move(II.get());
89 else
90 return II.takeError();
91 break;
92
95 MergedFunctionsInfo::decode(InfoData, BaseAddr))
96 FI.MergedFunctions = std::move(MI.get());
97 else
98 return MI.takeError();
99 break;
100
104 FI.CallSites = std::move(CI.get());
105 else
106 return CI.takeError();
107 break;
108
109 default:
110 return createStringError(std::errc::io_error,
111 "0x%8.8" PRIx64 ": unsupported InfoType %u",
112 Offset-8, IT);
113 }
114 Offset += InfoLength;
115 }
116 return std::move(FI);
117}
118
120 EncodingCache.clear();
121 if (!isValid())
122 return 0;
125 FW.setStringOffsetSize(GC.getStringOffsetSize());
126 llvm::Expected<uint64_t> Result = encode(FW);
127 if (!Result) {
128 EncodingCache.clear();
129 consumeError(Result.takeError());
130 return 0;
131 }
132 return EncodingCache.size();
133}
134
136 bool NoPadding) const {
137 if (!isValid())
138 return createStringError(std::errc::invalid_argument,
139 "attempted to encode invalid FunctionInfo object");
140 // Align FunctionInfo data to a 4 byte alignment, if padding is allowed
141 if (NoPadding == false)
142 Out.alignTo(4);
143 const uint64_t FuncInfoOffset = Out.tell();
144 // Check if we have already encoded this function info into EncodingCache.
145 // This will be non empty when creating segmented GSYM files as we need to
146 // precompute exactly how big FunctionInfo objects encode into so we can
147 // accurately make segments of a specific size.
148 if (!EncodingCache.empty() &&
150 // We already encoded this object, just write out the bytes.
152 EncodingCache.size()));
153 return FuncInfoOffset;
154 }
155 // Write the size in bytes of this function as a uint32_t. This can be zero
156 // if we just have a symbol from a symbol table and that symbol has no size.
157 Out.writeU32(size());
158 // Write the name of this function as a string table offset.
160
161 if (OptLineTable) {
163 // Write a uint32_t length as zero for now, we will fix this up after
164 // writing the LineTable out with the number of bytes that were written.
165 Out.writeU32(0);
166 const auto StartOffset = Out.tell();
167 llvm::Error err = OptLineTable->encode(Out, Range.start());
168 if (err)
169 return std::move(err);
170 const auto Length = Out.tell() - StartOffset;
171 if (Length > UINT32_MAX)
172 return createStringError(std::errc::invalid_argument,
173 "LineTable length is greater than UINT32_MAX");
174 // Fixup the size of the LineTable data with the correct size.
175 Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
176 }
177
178 // Write out the inline function info if we have any and if it is valid.
179 if (Inline) {
181 // Write a uint32_t length as zero for now, we will fix this up after
182 // writing the LineTable out with the number of bytes that were written.
183 Out.writeU32(0);
184 const auto StartOffset = Out.tell();
185 llvm::Error err = Inline->encode(Out, Range.start());
186 if (err)
187 return std::move(err);
188 const auto Length = Out.tell() - StartOffset;
189 if (Length > UINT32_MAX)
190 return createStringError(std::errc::invalid_argument,
191 "InlineInfo length is greater than UINT32_MAX");
192 // Fixup the size of the InlineInfo data with the correct size.
193 Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
194 }
195
196 // Write out the merged functions info if we have any and if it is valid.
197 if (MergedFunctions) {
199 // Write a uint32_t length as zero for now, we will fix this up after
200 // writing the LineTable out with the number of bytes that were written.
201 Out.writeU32(0);
202 const auto StartOffset = Out.tell();
203 llvm::Error err = MergedFunctions->encode(Out);
204 if (err)
205 return std::move(err);
206 const auto Length = Out.tell() - StartOffset;
207 if (Length > UINT32_MAX)
208 return createStringError(
209 std::errc::invalid_argument,
210 "MergedFunctionsInfo length is greater than UINT32_MAX");
211 // Fixup the size of the MergedFunctionsInfo data with the correct size.
212 Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
213 }
214
215 // Write out the call sites if we have any and if they are valid.
216 if (CallSites) {
218 // Write a uint32_t length as zero for now, we will fix this up after
219 // writing the CallSites out with the number of bytes that were written.
220 Out.writeU32(0);
221 const auto StartOffset = Out.tell();
222 Error Err = CallSites->encode(Out);
223 if (Err)
224 return std::move(Err);
225 const auto Length = Out.tell() - StartOffset;
226 if (Length > UINT32_MAX)
227 return createStringError(std::errc::invalid_argument,
228 "CallSites length is greater than UINT32_MAX");
229 // Fixup the size of the CallSites data with the correct size.
230 Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
231 }
232
233 // Terminate the data chunks with an end of list with zero size.
235 Out.writeU32(0);
236 return FuncInfoOffset;
237}
238
241 FunctionInfoStats *MergedFuncInfoStats) {
242 uint64_t Offset = 0;
243 // FunctionInfo header: Size (a uint32_t) followed by Name (a string offset).
244 // The string offset width is 4 bytes in GSYM v1 but variable (1-8 bytes) in
245 // v2, so query it from the extractor instead of assuming 4.
246 const uint64_t SizeAndNameSize = 4 + Data.getStringOffsetSize();
247 if (!Data.isValidOffsetForDataOfSize(Offset, SizeAndNameSize))
248 return;
249 Stats.SizeAndName += SizeAndNameSize;
250 Offset += SizeAndNameSize;
251 while (true) {
252 if (!Data.isValidOffsetForDataOfSize(Offset, 8))
253 return;
254 const uint32_t InfoType = Data.getU32(&Offset);
255 const uint32_t InfoLength = Data.getU32(&Offset);
257 Stats.EndOfList += 8; // InfoType (0) + InfoLength (0)
258 return;
259 }
260 if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
261 return;
262 // Each InfoType's size includes its 8-byte InfoType+InfoLength header plus
263 // the payload.
264 const uint64_t TLVSize = InfoLength + 8;
265 switch (InfoType) {
267 Stats.LineTableInfo += TLVSize;
268 break;
270 Stats.InlineInfo += TLVSize;
271 break;
273 Stats.CallSiteInfo += TLVSize;
274 break;
276 Stats.MergedFuncInfo += TLVSize;
277 // A MergedFunctionsInfo should never be nested inside another one.
278 if (!MergedFuncInfoStats) {
279 errs()
280 << "error: MergedFunctionsInfo found inside a MergedFunctionsInfo, "
281 "which is not supported\n";
282 break;
283 }
284 // Account the MergedFunctionsInfo structural bytes: its own
285 // InfoType+InfoLength (8), then Count (uint32_t) and each FnSize
286 // (uint32_t). Then recurse into each inner FunctionInfo (encoded with no
287 // padding, so no gaps between them).
288 MergedFuncInfoStats->InfoTypeInfoLengthCountAndFnSize += 8;
289 // Sub-range extractor inherits the parent's string offset size.
290 GsymDataExtractor MergedData(Data, Offset, InfoLength);
291 uint64_t MOffset = 0;
292 if (MergedData.isValidOffsetForDataOfSize(MOffset, 4)) {
293 const uint32_t Count = MergedData.getU32(&MOffset);
294 MergedFuncInfoStats->InfoTypeInfoLengthCountAndFnSize += 4; // Count
295 for (uint32_t I = 0; I < Count; ++I) {
296 if (!MergedData.isValidOffsetForDataOfSize(MOffset, 4))
297 break;
298 const uint32_t FnSize = MergedData.getU32(&MOffset);
299 MergedFuncInfoStats->InfoTypeInfoLengthCountAndFnSize += 4; // FnSize
300 if (!MergedData.isValidOffsetForDataOfSize(MOffset, FnSize))
301 break;
302 GsymDataExtractor FuncData(MergedData, MOffset, FnSize);
303 parseStatistics(FuncData, *MergedFuncInfoStats, nullptr);
304 MOffset += FnSize;
305 }
306 }
307 break;
308 }
309 default:
310 // Unknown InfoType: its bytes are left unattributed and surface as part
311 // of the file-level FunctionInfo "padding" remainder.
312 break;
313 }
314 Offset += InfoLength;
315 }
316}
317
320 uint64_t FuncAddr, uint64_t Addr,
321 std::optional<GsymDataExtractor> *MergedFuncsData) {
322 LookupResult LR;
323 LR.LookupAddr = Addr;
324 uint64_t Offset = 0;
325 LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
326 gsym_strp_t NameOffset = Data.getStringOffset(&Offset);
327 // The "lookup" functions doesn't report errors as accurately as the "decode"
328 // function as it is meant to be fast. For more accurage errors we could call
329 // "decode".
330 if (!Data.isValidOffset(Offset))
331 return createStringError(std::errc::io_error,
332 "FunctionInfo data is truncated");
333 // This function will be called with the result of a binary search of the
334 // address table, we must still make sure the address does not fall into a
335 // gap between functions are after the last function.
336 if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
337 return createStringError(std::errc::io_error,
338 "address 0x%" PRIx64 " is not in GSYM", Addr);
339
340 if (NameOffset == 0)
341 return createStringError(std::errc::io_error,
342 "0x%8.8" PRIx64
343 ": invalid FunctionInfo Name value 0x0",
344 Offset - Data.getStringOffsetSize());
345 LR.FuncName = GR.getString(NameOffset);
346 bool Done = false;
347 std::optional<LineEntry> LineEntry;
348 std::optional<GsymDataExtractor> InlineInfoData;
349 while (!Done) {
350 if (!Data.isValidOffsetForDataOfSize(Offset, 8))
351 return createStringError(std::errc::io_error,
352 "FunctionInfo data is truncated");
353 const uint32_t IT = Data.getU32(&Offset);
354 const uint32_t InfoLength = Data.getU32(&Offset);
355 const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
356 if (InfoLength != InfoBytes.size())
357 return createStringError(std::errc::io_error,
358 "FunctionInfo data is truncated");
359 GsymDataExtractor InfoData(Data, Offset, InfoLength);
360 switch (IT) {
362 Done = true;
363 break;
364
366 if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
367 LineEntry = ExpectedLE.get();
368 else
369 return ExpectedLE.takeError();
370 break;
371
373 // Store the merged functions data for later parsing, if needed.
374 if (MergedFuncsData)
375 *MergedFuncsData = InfoData;
376 break;
377
379 // We will parse the inline info after our line table, but only if
380 // we have a line entry.
381 InlineInfoData = InfoData;
382 break;
383
385 if (auto CSIC = CallSiteInfoCollection::decode(InfoData)) {
386 // Find matching call site based on relative offset
387 for (const auto &CS : CSIC->CallSites) {
388 // Check if the call site matches the lookup address
389 if (CS.ReturnOffset == Addr - FuncAddr) {
390 // Get regex patterns
391 for (gsym_strp_t RegexOffset : CS.MatchRegex) {
392 LR.CallSiteFuncRegex.push_back(GR.getString(RegexOffset));
393 }
394 break;
395 }
396 }
397 } else {
398 return CSIC.takeError();
399 }
400 break;
401
402 default:
403 break;
404 }
405 Offset += InfoLength;
406 }
407
408 if (!LineEntry) {
409 // We don't have a valid line entry for our address, fill in our source
410 // location as best we can and return.
411 SourceLocation SrcLoc;
412 SrcLoc.Name = LR.FuncName;
413 SrcLoc.Offset = Addr - FuncAddr;
414 LR.Locations.push_back(SrcLoc);
415 return LR;
416 }
417
418 std::optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
419 if (!LineEntryFile)
420 return createStringError(std::errc::invalid_argument,
421 "failed to extract file[%" PRIu32 "]",
422 LineEntry->File);
423
424 SourceLocation SrcLoc;
425 SrcLoc.Name = LR.FuncName;
426 SrcLoc.Offset = Addr - FuncAddr;
427 SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
428 SrcLoc.Base = GR.getString(LineEntryFile->Base);
429 SrcLoc.Line = LineEntry->Line;
430 LR.Locations.push_back(SrcLoc);
431 // If we don't have inline information, we are done.
432 if (!InlineInfoData)
433 return LR;
434 // We have inline information. Try to augment the lookup result with this
435 // data.
436 llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
437 LR.Locations);
438 if (Err)
439 return std::move(Err);
440 return LR;
441}
static cl::opt< ITMode > IT(cl::desc("IT block support"), cl::Hidden, cl::init(DefaultIT), cl::values(clEnumValN(DefaultIT, "arm-default-it", "Generate any type of IT block"), clEnumValN(RestrictedIT, "arm-restrict-it", "Disallow complex IT blocks")))
InfoType
FunctionInfo information type that is used to encode the optional data that is associated with a Func...
@ MergedFunctionsInfo
@ EndOfList
@ CallSiteInfo
@ InlineInfo
@ LineTableInfo
#define HEX32(v)
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
block placement Basic Block Placement Stats
uint64_t IntrinsicInst * II
bool contains(uint64_t Addr) const
uint64_t size() const
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
LLVM_ABI uint32_t getU32(uint64_t *offset_ptr, Error *Err=nullptr) const
Extract a uint32_t value from *offset_ptr.
bool isValidOffsetForDataOfSize(uint64_t offset, uint64_t length) const
Test the availability of length bytes of data from offset.
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
Tagged union holding either a T or a Error.
Definition Error.h:485
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
A simplified binary data writer class that doesn't require targets, target definitions,...
Definition FileWriter.h:30
LLVM_ABI uint64_t tell()
Return the current offset within the file.
LLVM_ABI void fixup32(uint32_t Value, uint64_t Offset)
Fixup a uint32_t value at the specified offset in the stream.
LLVM_ABI void alignTo(size_t Align)
Pad with zeroes at the current file position until the current file position matches the specified al...
LLVM_ABI void writeStringOffset(uint64_t Value)
Write a string table offset of StringOffsetSize bytes into the stream.
LLVM_ABI void writeU32(uint32_t Value)
Write a single uint32_t value into the stream at the current file position.
void setStringOffsetSize(uint8_t Size)
Set the string offset size for this writer.
Definition FileWriter.h:139
LLVM_ABI void writeData(llvm::ArrayRef< uint8_t > Data)
Write an array of uint8_t values into the stream at the current file position.
llvm::endianness getByteOrder() const
Definition FileWriter.h:134
GsymCreator is used to emit GSYM data to a stand alone file or section within a file.
A DataExtractor subclass that adds GSYM-specific string offset support.
GsymReader is used to read GSYM data from a file or buffer.
Definition GsymReader.h:48
StringRef getString(gsym_strp_t Offset) const
Get a string from the string table.
Definition GsymReader.h:178
std::optional< FileEntry > getFile(uint32_t Index) const
Get the a file entry for the suppplied file index.
Definition GsymReader.h:189
static LLVM_ABI Expected< LineEntry > lookup(GsymDataExtractor &Data, uint64_t BaseAddr, uint64_t Addr)
Lookup a single address within a line table's data.
static LLVM_ABI llvm::Expected< LineTable > decode(GsymDataExtractor &Data, uint64_t BaseAddr)
Decode an LineTable object from a binary data stream.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
A raw_ostream that writes to an SmallVector or SmallString.
uint64_t gsym_strp_t
The type of string offset used in the code.
Definition GsymTypes.h:21
LLVM_ABI raw_ostream & operator<<(raw_ostream &OS, const CallSiteInfo &CSI)
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
@ Length
Definition DWP.cpp:578
@ Done
Definition Threading.h:60
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
static LLVM_ABI llvm::Expected< CallSiteInfoCollection > decode(GsymDataExtractor &Data)
Decode a CallSiteInfoCollection object from a binary data stream.
Byte-size accounting for a FunctionInfo, broken down by field / InfoType.
Function information in GSYM files encodes information for one contiguous address range.
std::optional< InlineInfo > Inline
std::optional< MergedFunctionsInfo > MergedFunctions
static LLVM_ABI llvm::Expected< FunctionInfo > decode(GsymDataExtractor &Data, uint64_t BaseAddr)
Decode an object from a binary data stream.
bool isValid() const
Query if a FunctionInfo object is valid.
std::optional< CallSiteInfoCollection > CallSites
uint64_t size() const
FunctionInfo(uint64_t Addr=0, uint64_t Size=0, gsym_strp_t Name=0)
static LLVM_ABI void parseStatistics(GsymDataExtractor &Data, FunctionInfoStats &Stats, FunctionInfoStats *MergedFuncInfoStats=nullptr)
Parse the function info data and accumulate the byte size of each field / InfoType into Stats.
gsym_strp_t Name
String table offset in the string table.
LLVM_ABI llvm::Expected< uint64_t > encode(FileWriter &O, bool NoPadding=false) const
Encode this object into FileWriter stream.
SmallString< 32 > EncodingCache
If we encode a FunctionInfo during segmenting so we know its size, we can cache that encoding here so...
std::optional< LineTable > OptLineTable
LLVM_ABI uint64_t cacheEncoding(GsymCreator &GC)
Encode this function info into the internal byte cache and return the size in bytes.
static LLVM_ABI llvm::Expected< LookupResult > lookup(GsymDataExtractor &Data, const GsymReader &GR, uint64_t FuncAddr, uint64_t Addr, std::optional< GsymDataExtractor > *MergedFuncsData=nullptr)
Lookup an address within a FunctionInfo object's data stream.
Inline information stores the name of the inline function along with an array of address ranges.
Definition InlineInfo.h:61
static LLVM_ABI llvm::Expected< InlineInfo > decode(GsymDataExtractor &Data, uint64_t BaseAddr)
Decode an InlineInfo object from a binary data stream.
static LLVM_ABI llvm::Error lookup(const GsymReader &GR, GsymDataExtractor &Data, uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs)
Lookup a single address within the inline info data.
Line entries are used to encode the line tables in FunctionInfo objects.
Definition LineEntry.h:22
uint32_t File
1 based index of file in FileTable
Definition LineEntry.h:24
uint32_t Line
Source line number.
Definition LineEntry.h:25
uint64_t LookupAddr
The address that this lookup pertains to.
AddressRange FuncRange
The concrete function address range.
std::vector< StringRef > CallSiteFuncRegex
Function name regex patterns associated with a call site at the lookup address.
StringRef FuncName
The concrete function name that contains LookupAddr.
SourceLocations Locations
The source locations that match this address.
static LLVM_ABI llvm::Expected< MergedFunctionsInfo > decode(GsymDataExtractor &Data, uint64_t BaseAddr)
Decode an MergedFunctionsInfo object from a binary data stream.
StringRef Base
Line entry source file basename.
uint32_t Line
Source file line number.
uint32_t Offset
Byte size offset within the named function.
StringRef Dir
Line entry source file directory path.
StringRef Name
Function or symbol name.