Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp
35266 views
//===- FunctionInfo.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DebugInfo/GSYM/FunctionInfo.h"9#include "llvm/DebugInfo/GSYM/FileWriter.h"10#include "llvm/DebugInfo/GSYM/GsymReader.h"11#include "llvm/DebugInfo/GSYM/LineTable.h"12#include "llvm/DebugInfo/GSYM/InlineInfo.h"13#include "llvm/Support/DataExtractor.h"14#include <optional>1516using namespace llvm;17using namespace gsym;1819/// FunctionInfo information type that is used to encode the optional data20/// that is associated with a FunctionInfo object.21enum InfoType : uint32_t {22EndOfList = 0u,23LineTableInfo = 1u,24InlineInfo = 2u25};2627raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const FunctionInfo &FI) {28OS << FI.Range << ": " << "Name=" << HEX32(FI.Name) << '\n';29if (FI.OptLineTable)30OS << FI.OptLineTable << '\n';31if (FI.Inline)32OS << FI.Inline << '\n';33return OS;34}3536llvm::Expected<FunctionInfo> FunctionInfo::decode(DataExtractor &Data,37uint64_t BaseAddr) {38FunctionInfo FI;39uint64_t Offset = 0;40if (!Data.isValidOffsetForDataOfSize(Offset, 4))41return createStringError(std::errc::io_error,42"0x%8.8" PRIx64 ": missing FunctionInfo Size", Offset);43FI.Range = {BaseAddr, BaseAddr + Data.getU32(&Offset)};44if (!Data.isValidOffsetForDataOfSize(Offset, 4))45return createStringError(std::errc::io_error,46"0x%8.8" PRIx64 ": missing FunctionInfo Name", Offset);47FI.Name = Data.getU32(&Offset);48if (FI.Name == 0)49return createStringError(std::errc::io_error,50"0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x%8.8x",51Offset - 4, FI.Name);52bool Done = false;53while (!Done) {54if (!Data.isValidOffsetForDataOfSize(Offset, 4))55return createStringError(std::errc::io_error,56"0x%8.8" PRIx64 ": missing FunctionInfo InfoType value", Offset);57const uint32_t IT = Data.getU32(&Offset);58if (!Data.isValidOffsetForDataOfSize(Offset, 4))59return createStringError(std::errc::io_error,60"0x%8.8" PRIx64 ": missing FunctionInfo InfoType length", Offset);61const uint32_t InfoLength = Data.getU32(&Offset);62if (!Data.isValidOffsetForDataOfSize(Offset, InfoLength))63return createStringError(std::errc::io_error,64"0x%8.8" PRIx64 ": missing FunctionInfo data for InfoType %u",65Offset, IT);66DataExtractor InfoData(Data.getData().substr(Offset, InfoLength),67Data.isLittleEndian(),68Data.getAddressSize());69switch (IT) {70case InfoType::EndOfList:71Done = true;72break;7374case InfoType::LineTableInfo:75if (Expected<LineTable> LT = LineTable::decode(InfoData, BaseAddr))76FI.OptLineTable = std::move(LT.get());77else78return LT.takeError();79break;8081case InfoType::InlineInfo:82if (Expected<InlineInfo> II = InlineInfo::decode(InfoData, BaseAddr))83FI.Inline = std::move(II.get());84else85return II.takeError();86break;8788default:89return createStringError(std::errc::io_error,90"0x%8.8" PRIx64 ": unsupported InfoType %u",91Offset-8, IT);92}93Offset += InfoLength;94}95return std::move(FI);96}9798uint64_t FunctionInfo::cacheEncoding() {99EncodingCache.clear();100if (!isValid())101return 0;102raw_svector_ostream OutStrm(EncodingCache);103FileWriter FW(OutStrm, llvm::endianness::native);104llvm::Expected<uint64_t> Result = encode(FW);105if (!Result) {106EncodingCache.clear();107consumeError(Result.takeError());108return 0;109}110return EncodingCache.size();111}112113llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &Out) const {114if (!isValid())115return createStringError(std::errc::invalid_argument,116"attempted to encode invalid FunctionInfo object");117// Align FunctionInfo data to a 4 byte alignment.118Out.alignTo(4);119const uint64_t FuncInfoOffset = Out.tell();120// Check if we have already encoded this function info into EncodingCache.121// This will be non empty when creating segmented GSYM files as we need to122// precompute exactly how big FunctionInfo objects encode into so we can123// accurately make segments of a specific size.124if (!EncodingCache.empty() &&125llvm::endianness::native == Out.getByteOrder()) {126// We already encoded this object, just write out the bytes.127Out.writeData(llvm::ArrayRef<uint8_t>((const uint8_t *)EncodingCache.data(),128EncodingCache.size()));129return FuncInfoOffset;130}131// Write the size in bytes of this function as a uint32_t. This can be zero132// if we just have a symbol from a symbol table and that symbol has no size.133Out.writeU32(size());134// Write the name of this function as a uint32_t string table offset.135Out.writeU32(Name);136137if (OptLineTable) {138Out.writeU32(InfoType::LineTableInfo);139// Write a uint32_t length as zero for now, we will fix this up after140// writing the LineTable out with the number of bytes that were written.141Out.writeU32(0);142const auto StartOffset = Out.tell();143llvm::Error err = OptLineTable->encode(Out, Range.start());144if (err)145return std::move(err);146const auto Length = Out.tell() - StartOffset;147if (Length > UINT32_MAX)148return createStringError(std::errc::invalid_argument,149"LineTable length is greater than UINT32_MAX");150// Fixup the size of the LineTable data with the correct size.151Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);152}153154// Write out the inline function info if we have any and if it is valid.155if (Inline) {156Out.writeU32(InfoType::InlineInfo);157// Write a uint32_t length as zero for now, we will fix this up after158// writing the LineTable out with the number of bytes that were written.159Out.writeU32(0);160const auto StartOffset = Out.tell();161llvm::Error err = Inline->encode(Out, Range.start());162if (err)163return std::move(err);164const auto Length = Out.tell() - StartOffset;165if (Length > UINT32_MAX)166return createStringError(std::errc::invalid_argument,167"InlineInfo length is greater than UINT32_MAX");168// Fixup the size of the InlineInfo data with the correct size.169Out.fixup32(static_cast<uint32_t>(Length), StartOffset - 4);170}171172// Terminate the data chunks with and end of list with zero size173Out.writeU32(InfoType::EndOfList);174Out.writeU32(0);175return FuncInfoOffset;176}177178179llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data,180const GsymReader &GR,181uint64_t FuncAddr,182uint64_t Addr) {183LookupResult LR;184LR.LookupAddr = Addr;185uint64_t Offset = 0;186LR.FuncRange = {FuncAddr, FuncAddr + Data.getU32(&Offset)};187uint32_t NameOffset = Data.getU32(&Offset);188// The "lookup" functions doesn't report errors as accurately as the "decode"189// function as it is meant to be fast. For more accurage errors we could call190// "decode".191if (!Data.isValidOffset(Offset))192return createStringError(std::errc::io_error,193"FunctionInfo data is truncated");194// This function will be called with the result of a binary search of the195// address table, we must still make sure the address does not fall into a196// gap between functions are after the last function.197if (LR.FuncRange.size() > 0 && !LR.FuncRange.contains(Addr))198return createStringError(std::errc::io_error,199"address 0x%" PRIx64 " is not in GSYM", Addr);200201if (NameOffset == 0)202return createStringError(std::errc::io_error,203"0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000",204Offset - 4);205LR.FuncName = GR.getString(NameOffset);206bool Done = false;207std::optional<LineEntry> LineEntry;208std::optional<DataExtractor> InlineInfoData;209while (!Done) {210if (!Data.isValidOffsetForDataOfSize(Offset, 8))211return createStringError(std::errc::io_error,212"FunctionInfo data is truncated");213const uint32_t IT = Data.getU32(&Offset);214const uint32_t InfoLength = Data.getU32(&Offset);215const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength);216if (InfoLength != InfoBytes.size())217return createStringError(std::errc::io_error,218"FunctionInfo data is truncated");219DataExtractor InfoData(InfoBytes, Data.isLittleEndian(),220Data.getAddressSize());221switch (IT) {222case InfoType::EndOfList:223Done = true;224break;225226case InfoType::LineTableInfo:227if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr))228LineEntry = ExpectedLE.get();229else230return ExpectedLE.takeError();231break;232233case InfoType::InlineInfo:234// We will parse the inline info after our line table, but only if235// we have a line entry.236InlineInfoData = InfoData;237break;238239default:240break;241}242Offset += InfoLength;243}244245if (!LineEntry) {246// We don't have a valid line entry for our address, fill in our source247// location as best we can and return.248SourceLocation SrcLoc;249SrcLoc.Name = LR.FuncName;250SrcLoc.Offset = Addr - FuncAddr;251LR.Locations.push_back(SrcLoc);252return LR;253}254255std::optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File);256if (!LineEntryFile)257return createStringError(std::errc::invalid_argument,258"failed to extract file[%" PRIu32 "]",259LineEntry->File);260261SourceLocation SrcLoc;262SrcLoc.Name = LR.FuncName;263SrcLoc.Offset = Addr - FuncAddr;264SrcLoc.Dir = GR.getString(LineEntryFile->Dir);265SrcLoc.Base = GR.getString(LineEntryFile->Base);266SrcLoc.Line = LineEntry->Line;267LR.Locations.push_back(SrcLoc);268// If we don't have inline information, we are done.269if (!InlineInfoData)270return LR;271// We have inline information. Try to augment the lookup result with this272// data.273llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr,274LR.Locations);275if (Err)276return std::move(Err);277return LR;278}279280281