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