xref: /freebsd-src/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
18bcb0991SDimitry Andric //===- FunctionInfo.cpp ---------------------------------------------------===//
20b57cec5SDimitry Andric //
38bcb0991SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48bcb0991SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
58bcb0991SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "llvm/DebugInfo/GSYM/FunctionInfo.h"
108bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/FileWriter.h"
11480093f4SDimitry Andric #include "llvm/DebugInfo/GSYM/GsymReader.h"
128bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/LineTable.h"
138bcb0991SDimitry Andric #include "llvm/DebugInfo/GSYM/InlineInfo.h"
148bcb0991SDimitry Andric #include "llvm/Support/DataExtractor.h"
15bdd1243dSDimitry Andric #include <optional>
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric using namespace llvm;
180b57cec5SDimitry Andric using namespace gsym;
190b57cec5SDimitry Andric 
208bcb0991SDimitry Andric /// FunctionInfo information type that is used to encode the optional data
218bcb0991SDimitry Andric /// that is associated with a FunctionInfo object.
228bcb0991SDimitry Andric enum InfoType : uint32_t {
238bcb0991SDimitry Andric   EndOfList = 0u,
248bcb0991SDimitry Andric   LineTableInfo = 1u,
258bcb0991SDimitry Andric   InlineInfo = 2u
268bcb0991SDimitry Andric };
278bcb0991SDimitry Andric 
operator <<(raw_ostream & OS,const FunctionInfo & FI)280b57cec5SDimitry Andric raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {
295ffd83dbSDimitry Andric   OS << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';
305ffd83dbSDimitry Andric   if (FI.OptLineTable)
315ffd83dbSDimitry Andric     OS << FI.OptLineTable << '\n';
325ffd83dbSDimitry Andric   if (FI.Inline)
335ffd83dbSDimitry Andric     OS << FI.Inline << '\n';
340b57cec5SDimitry Andric   return OS;
350b57cec5SDimitry Andric }
368bcb0991SDimitry Andric 
decode(DataExtractor & Data,uint64_t BaseAddr)378bcb0991SDimitry Andric llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,
388bcb0991SDimitry Andric                                                   uint64_t BaseAddr) {
398bcb0991SDimitry Andric   FunctionInfo FI;
408bcb0991SDimitry Andric   uint64_t Offset = 0;
418bcb0991SDimitry Andric   if (!Data.isValidOffsetForDataOfSize(Offset, 4))
428bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
438bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);
4481ad6265SDimitry Andric   FI.Range = {BaseAddr, BaseAddr + Data.getU32(&Offset)};
458bcb0991SDimitry Andric   if (!Data.isValidOffsetForDataOfSize(Offset, 4))
468bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
478bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);
488bcb0991SDimitry Andric   FI.Name = Data.getU32(&Offset);
498bcb0991SDimitry Andric   if (FI.Name == 0)
508bcb0991SDimitry Andric     return createStringError(std::errc::io_error,
518bcb0991SDimitry Andric         "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",
528bcb0991SDimitry Andric         Offset - 4, FI.Name);
538bcb0991SDimitry Andric   bool Done = false;
548bcb0991SDimitry Andric   while (!Done) {
558bcb0991SDimitry Andric     if (!Data.isValidOffsetForDataOfSize(Offset, 4))
568bcb0991SDimitry Andric       return createStringError(std::errc::io_error,
578bcb0991SDimitry Andric           "0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);
588bcb0991SDimitry Andric     const uint32_t IT = Data.getU32(&Offset);
598bcb0991SDimitry Andric     if (!Data.isValidOffsetForDataOfSize(Offset, 4))
608bcb0991SDimitry Andric       return createStringError(std::errc::io_error,
618bcb0991SDimitry Andric           "0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);
628bcb0991SDimitry Andric     const uint32_t InfoLength = Data.getU32(&Offset);
638bcb0991SDimitry Andric     if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))
648bcb0991SDimitry Andric       return createStringError(std::errc::io_error,
658bcb0991SDimitry Andric           "0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",
668bcb0991SDimitry Andric           Offset, IT);
678bcb0991SDimitry Andric     DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),
688bcb0991SDimitry Andric                            Data.isLittleEndian(),
698bcb0991SDimitry Andric                            Data.getAddressSize());
708bcb0991SDimitry Andric     switch (IT) {
718bcb0991SDimitry Andric       case InfoType::EndOfList:
728bcb0991SDimitry Andric         Done = true;
738bcb0991SDimitry Andric         break;
748bcb0991SDimitry Andric 
758bcb0991SDimitry Andric       case InfoType::LineTableInfo:
768bcb0991SDimitry Andric         if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))
778bcb0991SDimitry Andric           FI.OptLineTable = std::move(LT.get());
788bcb0991SDimitry Andric         else
798bcb0991SDimitry Andric           return LT.takeError();
808bcb0991SDimitry Andric         break;
818bcb0991SDimitry Andric 
828bcb0991SDimitry Andric       case InfoType::InlineInfo:
838bcb0991SDimitry Andric         if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))
848bcb0991SDimitry Andric           FI.Inline = std::move(II.get());
858bcb0991SDimitry Andric         else
868bcb0991SDimitry Andric           return II.takeError();
878bcb0991SDimitry Andric         break;
888bcb0991SDimitry Andric 
898bcb0991SDimitry Andric       default:
908bcb0991SDimitry Andric         return createStringError(std::errc::io_error,
918bcb0991SDimitry Andric                                  "0x%8.8" PRIx64 ": unsupported InfoType %u",
928bcb0991SDimitry Andric                                  Offset-8, IT);
938bcb0991SDimitry Andric     }
948bcb0991SDimitry Andric     Offset += InfoLength;
958bcb0991SDimitry Andric   }
968bcb0991SDimitry Andric   return std::move(FI);
978bcb0991SDimitry Andric }
988bcb0991SDimitry Andric 
cacheEncoding()9906c3fb27SDimitry Andric uint64_t FunctionInfo::cacheEncoding() {
10006c3fb27SDimitry Andric   EncodingCache.clear();
10106c3fb27SDimitry Andric   if (!isValid())
10206c3fb27SDimitry Andric     return 0;
10306c3fb27SDimitry Andric   raw_svector_ostream OutStrm(EncodingCache);
104*5f757f3fSDimitry Andric   FileWriter FW(OutStrm, llvm::endianness::native);
10506c3fb27SDimitry Andric   llvm::Expected<uint64_t> Result = encode(FW);
10606c3fb27SDimitry Andric   if (!Result) {
10706c3fb27SDimitry Andric     EncodingCache.clear();
10806c3fb27SDimitry Andric     consumeError(Result.takeError());
10906c3fb27SDimitry Andric     return 0;
11006c3fb27SDimitry Andric   }
11106c3fb27SDimitry Andric   return EncodingCache.size();
11206c3fb27SDimitry Andric }
11306c3fb27SDimitry Andric 
encode(FileWriter & Out) const11406c3fb27SDimitry Andric llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &Out) const {
1158bcb0991SDimitry Andric   if (!isValid())
1168bcb0991SDimitry Andric     return createStringError(std::errc::invalid_argument,
1178bcb0991SDimitry Andric         "attempted to encode invalid FunctionInfo object");
1188bcb0991SDimitry Andric   // Align FunctionInfo data to a 4 byte alignment.
11906c3fb27SDimitry Andric   Out.alignTo(4);
12006c3fb27SDimitry Andric   const uint64_t FuncInfoOffset = Out.tell();
12106c3fb27SDimitry Andric   // Check if we have already encoded this function info into EncodingCache.
12206c3fb27SDimitry Andric   // This will be non empty when creating segmented GSYM files as we need to
12306c3fb27SDimitry Andric   // precompute exactly how big FunctionInfo objects encode into so we can
12406c3fb27SDimitry Andric   // accurately make segments of a specific size.
12506c3fb27SDimitry Andric   if (!EncodingCache.empty() &&
126*5f757f3fSDimitry Andric       llvm::endianness::native == Out.getByteOrder()) {
12706c3fb27SDimitry Andric     // We already encoded this object, just write out the bytes.
12806c3fb27SDimitry Andric     Out.writeData(llvm::ArrayRef<uint8_t>((const uint8_t *)EncodingCache.data(),
12906c3fb27SDimitry Andric                                           EncodingCache.size()));
13006c3fb27SDimitry Andric     return FuncInfoOffset;
13106c3fb27SDimitry Andric   }
1328bcb0991SDimitry Andric   // Write the size in bytes of this function as a uint32_t. This can be zero
1338bcb0991SDimitry Andric   // if we just have a symbol from a symbol table and that symbol has no size.
13406c3fb27SDimitry Andric   Out.writeU32(size());
1358bcb0991SDimitry Andric   // Write the name of this function as a uint32_t string table offset.
13606c3fb27SDimitry Andric   Out.writeU32(Name);
1378bcb0991SDimitry Andric 
13881ad6265SDimitry Andric   if (OptLineTable) {
13906c3fb27SDimitry Andric     Out.writeU32(InfoType::LineTableInfo);
1408bcb0991SDimitry Andric     // Write a uint32_t length as zero for now, we will fix this up after
1418bcb0991SDimitry Andric     // writing the LineTable out with the number of bytes that were written.
14206c3fb27SDimitry Andric     Out.writeU32(0);
14306c3fb27SDimitry Andric     const auto StartOffset = Out.tell();
14406c3fb27SDimitry Andric     llvm::Error err = OptLineTable->encode(Out, Range.start());
1458bcb0991SDimitry Andric     if (err)
1468bcb0991SDimitry Andric       return std::move(err);
14706c3fb27SDimitry Andric     const auto Length = Out.tell() - StartOffset;
1488bcb0991SDimitry Andric     if (Length > UINT32_MAX)
1498bcb0991SDimitry Andric         return createStringError(std::errc::invalid_argument,
1508bcb0991SDimitry Andric             "LineTable length is greater than UINT32_MAX");
1518bcb0991SDimitry Andric     // Fixup the size of the LineTable data with the correct size.
15206c3fb27SDimitry Andric     Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
1538bcb0991SDimitry Andric   }
1548bcb0991SDimitry Andric 
1558bcb0991SDimitry Andric   // Write out the inline function info if we have any and if it is valid.
15681ad6265SDimitry Andric   if (Inline) {
15706c3fb27SDimitry Andric     Out.writeU32(InfoType::InlineInfo);
1588bcb0991SDimitry Andric     // Write a uint32_t length as zero for now, we will fix this up after
1598bcb0991SDimitry Andric     // writing the LineTable out with the number of bytes that were written.
16006c3fb27SDimitry Andric     Out.writeU32(0);
16106c3fb27SDimitry Andric     const auto StartOffset = Out.tell();
16206c3fb27SDimitry Andric     llvm::Error err = Inline->encode(Out, Range.start());
1638bcb0991SDimitry Andric     if (err)
1648bcb0991SDimitry Andric       return std::move(err);
16506c3fb27SDimitry Andric     const auto Length = Out.tell() - StartOffset;
1668bcb0991SDimitry Andric     if (Length > UINT32_MAX)
1678bcb0991SDimitry Andric         return createStringError(std::errc::invalid_argument,
1688bcb0991SDimitry Andric             "InlineInfo length is greater than UINT32_MAX");
1698bcb0991SDimitry Andric     // Fixup the size of the InlineInfo data with the correct size.
17006c3fb27SDimitry Andric     Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);
1718bcb0991SDimitry Andric   }
1728bcb0991SDimitry Andric 
1738bcb0991SDimitry Andric   // Terminate the data chunks with and end of list with zero size
17406c3fb27SDimitry Andric   Out.writeU32(InfoType::EndOfList);
17506c3fb27SDimitry Andric   Out.writeU32(0);
1768bcb0991SDimitry Andric   return FuncInfoOffset;
1778bcb0991SDimitry Andric }
178480093f4SDimitry Andric 
179480093f4SDimitry Andric 
lookup(DataExtractor & Data,const GsymReader & GR,uint64_t FuncAddr,uint64_t Addr)180480093f4SDimitry Andric llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data,
181480093f4SDimitry Andric                                                   const GsymReader &GR,
182480093f4SDimitry Andric                                                   uint64_t FuncAddr,
183480093f4SDimitry Andric                                                   uint64_t Addr) {
184480093f4SDimitry Andric   LookupResult LR;
185480093f4SDimitry Andric   LR.LookupAddr = Addr;
186480093f4SDimitry Andric   uint64_t Offset = 0;
18781ad6265SDimitry Andric   LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};
188480093f4SDimitry Andric   uint32_t NameOffset = Data.getU32(&Offset);
189480093f4SDimitry Andric   // The "lookup" functions doesn't report errors as accurately as the "decode"
190480093f4SDimitry Andric   // function as it is meant to be fast. For more accurage errors we could call
191480093f4SDimitry Andric   // "decode".
192480093f4SDimitry Andric   if (!Data.isValidOffset(Offset))
193480093f4SDimitry Andric     return createStringError(std::errc::io_error,
194480093f4SDimitry Andric                               "FunctionInfo data is truncated");
195480093f4SDimitry Andric   // This function will be called with the result of a binary search of the
196480093f4SDimitry Andric   // address table, we must still make sure the address does not fall into a
197480093f4SDimitry Andric   // gap between functions are after the last function.
1985ffd83dbSDimitry Andric   if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))
199480093f4SDimitry Andric     return createStringError(std::errc::io_error,
200480093f4SDimitry Andric         "address 0x%" PRIx64 " is not in GSYM", Addr);
201480093f4SDimitry Andric 
202480093f4SDimitry Andric   if (NameOffset == 0)
203480093f4SDimitry Andric     return createStringError(std::errc::io_error,
204480093f4SDimitry Andric         "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",
205480093f4SDimitry Andric         Offset - 4);
206480093f4SDimitry Andric   LR.FuncName = GR.getString(NameOffset);
207480093f4SDimitry Andric   bool Done = false;
208bdd1243dSDimitry Andric   std::optional<LineEntry> LineEntry;
209bdd1243dSDimitry Andric   std::optional<DataExtractor> InlineInfoData;
210480093f4SDimitry Andric   while (!Done) {
211480093f4SDimitry Andric     if (!Data.isValidOffsetForDataOfSize(Offset, 8))
212480093f4SDimitry Andric       return createStringError(std::errc::io_error,
213480093f4SDimitry Andric                                "FunctionInfo data is truncated");
214480093f4SDimitry Andric     const uint32_t IT = Data.getU32(&Offset);
215480093f4SDimitry Andric     const uint32_t InfoLength = Data.getU32(&Offset);
216480093f4SDimitry Andric     const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);
217480093f4SDimitry Andric     if (InfoLength != InfoBytes.size())
218480093f4SDimitry Andric       return createStringError(std::errc::io_error,
219480093f4SDimitry Andric                                "FunctionInfo data is truncated");
220480093f4SDimitry Andric     DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),
221480093f4SDimitry Andric                            Data.getAddressSize());
222480093f4SDimitry Andric     switch (IT) {
223480093f4SDimitry Andric       case InfoType::EndOfList:
224480093f4SDimitry Andric         Done = true;
225480093f4SDimitry Andric         break;
226480093f4SDimitry Andric 
227480093f4SDimitry Andric       case InfoType::LineTableInfo:
228480093f4SDimitry Andric         if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))
229480093f4SDimitry Andric           LineEntry = ExpectedLE.get();
230480093f4SDimitry Andric         else
231480093f4SDimitry Andric           return ExpectedLE.takeError();
232480093f4SDimitry Andric         break;
233480093f4SDimitry Andric 
234480093f4SDimitry Andric       case InfoType::InlineInfo:
235480093f4SDimitry Andric         // We will parse the inline info after our line table, but only if
236480093f4SDimitry Andric         // we have a line entry.
237480093f4SDimitry Andric         InlineInfoData = InfoData;
238480093f4SDimitry Andric         break;
239480093f4SDimitry Andric 
240480093f4SDimitry Andric       default:
241480093f4SDimitry Andric         break;
242480093f4SDimitry Andric     }
243480093f4SDimitry Andric     Offset += InfoLength;
244480093f4SDimitry Andric   }
245480093f4SDimitry Andric 
246480093f4SDimitry Andric   if (!LineEntry) {
247480093f4SDimitry Andric     // We don't have a valid line entry for our address, fill in our source
248480093f4SDimitry Andric     // location as best we can and return.
249480093f4SDimitry Andric     SourceLocation SrcLoc;
250480093f4SDimitry Andric     SrcLoc.Name = LR.FuncName;
2515ffd83dbSDimitry Andric     SrcLoc.Offset = Addr - FuncAddr;
252480093f4SDimitry Andric     LR.Locations.push_back(SrcLoc);
253480093f4SDimitry Andric     return LR;
254480093f4SDimitry Andric   }
255480093f4SDimitry Andric 
256bdd1243dSDimitry Andric   std::optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);
257480093f4SDimitry Andric   if (!LineEntryFile)
258480093f4SDimitry Andric     return createStringError(std::errc::invalid_argument,
259480093f4SDimitry Andric                               "failed to extract file[%" PRIu32 "]",
260480093f4SDimitry Andric                               LineEntry->File);
261480093f4SDimitry Andric 
262480093f4SDimitry Andric   SourceLocation SrcLoc;
263480093f4SDimitry Andric   SrcLoc.Name = LR.FuncName;
2645ffd83dbSDimitry Andric   SrcLoc.Offset = Addr - FuncAddr;
265480093f4SDimitry Andric   SrcLoc.Dir = GR.getString(LineEntryFile->Dir);
266480093f4SDimitry Andric   SrcLoc.Base = GR.getString(LineEntryFile->Base);
267480093f4SDimitry Andric   SrcLoc.Line = LineEntry->Line;
268480093f4SDimitry Andric   LR.Locations.push_back(SrcLoc);
269480093f4SDimitry Andric   // If we don't have inline information, we are done.
270480093f4SDimitry Andric   if (!InlineInfoData)
271480093f4SDimitry Andric     return LR;
272480093f4SDimitry Andric   // We have inline information. Try to augment the lookup result with this
273480093f4SDimitry Andric   // data.
274480093f4SDimitry Andric   llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,
275480093f4SDimitry Andric                                        LR.Locations);
276480093f4SDimitry Andric   if (Err)
277480093f4SDimitry Andric     return std::move(Err);
278480093f4SDimitry Andric   return LR;
279480093f4SDimitry Andric }
280