Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/CallSiteInfo.cpp
213799 views
//===- CallSiteInfo.cpp -----------------------------------------*- C++ -*-===//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/CallSiteInfo.h"9#include "llvm/DebugInfo/GSYM/FileWriter.h"10#include "llvm/DebugInfo/GSYM/FunctionInfo.h"11#include "llvm/DebugInfo/GSYM/GsymCreator.h"12#include "llvm/MC/StringTableBuilder.h"13#include "llvm/Support/DataExtractor.h"14#include "llvm/Support/InterleavedRange.h"15#include "llvm/Support/YAMLParser.h"16#include "llvm/Support/YAMLTraits.h"17#include "llvm/Support/raw_ostream.h"18#include <string>19#include <vector>2021using namespace llvm;22using namespace gsym;2324Error CallSiteInfo::encode(FileWriter &O) const {25O.writeU64(ReturnOffset);26O.writeU8(Flags);27O.writeU32(MatchRegex.size());28for (uint32_t Entry : MatchRegex)29O.writeU32(Entry);30return Error::success();31}3233Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,34uint64_t &Offset) {35CallSiteInfo CSI;3637// Read ReturnOffset38if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint64_t)))39return createStringError(std::errc::io_error,40"0x%8.8" PRIx64 ": missing ReturnOffset", Offset);41CSI.ReturnOffset = Data.getU64(&Offset);4243// Read Flags44if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint8_t)))45return createStringError(std::errc::io_error,46"0x%8.8" PRIx64 ": missing Flags", Offset);47CSI.Flags = Data.getU8(&Offset);4849// Read number of MatchRegex entries50if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))51return createStringError(std::errc::io_error,52"0x%8.8" PRIx64 ": missing MatchRegex count",53Offset);54uint32_t NumEntries = Data.getU32(&Offset);5556CSI.MatchRegex.reserve(NumEntries);57for (uint32_t i = 0; i < NumEntries; ++i) {58if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))59return createStringError(std::errc::io_error,60"0x%8.8" PRIx64 ": missing MatchRegex entry",61Offset);62uint32_t Entry = Data.getU32(&Offset);63CSI.MatchRegex.push_back(Entry);64}6566return CSI;67}6869Error CallSiteInfoCollection::encode(FileWriter &O) const {70O.writeU32(CallSites.size());71for (const CallSiteInfo &CSI : CallSites)72if (Error Err = CSI.encode(O))73return Err;7475return Error::success();76}7778Expected<CallSiteInfoCollection>79CallSiteInfoCollection::decode(DataExtractor &Data) {80CallSiteInfoCollection CSC;81uint64_t Offset = 0;8283// Read number of CallSiteInfo entries84if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))85return createStringError(std::errc::io_error,86"0x%8.8" PRIx64 ": missing CallSiteInfo count",87Offset);88uint32_t NumCallSites = Data.getU32(&Offset);8990CSC.CallSites.reserve(NumCallSites);91for (uint32_t i = 0; i < NumCallSites; ++i) {92Expected<CallSiteInfo> ECSI = CallSiteInfo::decode(Data, Offset);93if (!ECSI)94return ECSI.takeError();95CSC.CallSites.emplace_back(*ECSI);96}9798return CSC;99}100101/// Structures necessary for reading CallSiteInfo from YAML.102namespace llvm {103namespace yaml {104105struct CallSiteYAML {106// The offset of the return address of the call site - relative to the start107// of the function.108Hex64 return_offset;109std::vector<std::string> match_regex;110std::vector<std::string> flags;111};112113struct FunctionYAML {114std::string name;115std::vector<CallSiteYAML> callsites;116};117118struct FunctionsYAML {119std::vector<FunctionYAML> functions;120};121122template <> struct MappingTraits<CallSiteYAML> {123static void mapping(IO &io, CallSiteYAML &callsite) {124io.mapRequired("return_offset", callsite.return_offset);125io.mapRequired("match_regex", callsite.match_regex);126io.mapOptional("flags", callsite.flags);127}128};129130template <> struct MappingTraits<FunctionYAML> {131static void mapping(IO &io, FunctionYAML &func) {132io.mapRequired("name", func.name);133io.mapOptional("callsites", func.callsites);134}135};136137template <> struct MappingTraits<FunctionsYAML> {138static void mapping(IO &io, FunctionsYAML &FuncYAMLs) {139io.mapRequired("functions", FuncYAMLs.functions);140}141};142143} // namespace yaml144} // namespace llvm145146LLVM_YAML_IS_SEQUENCE_VECTOR(CallSiteYAML)147LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML)148149Error CallSiteInfoLoader::loadYAML(StringRef YAMLFile) {150// Step 1: Read YAML file151auto BufferOrError = MemoryBuffer::getFile(YAMLFile, /*IsText=*/true);152if (!BufferOrError)153return errorCodeToError(BufferOrError.getError());154155std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrError);156157// Step 2: Parse YAML content158yaml::FunctionsYAML FuncsYAML;159yaml::Input Yin(Buffer->getMemBufferRef());160Yin >> FuncsYAML;161if (Yin.error())162return createStringError(Yin.error(), "Error parsing YAML file: %s\n",163Buffer->getBufferIdentifier().str().c_str());164165// Step 3: Build function map from Funcs166auto FuncMap = buildFunctionMap();167168// Step 4: Process parsed YAML functions and update FuncMap169return processYAMLFunctions(FuncsYAML, FuncMap);170}171172StringMap<FunctionInfo *> CallSiteInfoLoader::buildFunctionMap() {173// If the function name is already in the map, don't update it. This way we174// preferentially use the first encountered function. Since symbols are175// loaded from dSYM first, we end up preferring keeping track of symbols176// from dSYM rather than from the symbol table - which is what we want to177// do.178StringMap<FunctionInfo *> FuncMap;179for (auto &Func : Funcs) {180FuncMap.try_emplace(GCreator.getString(Func.Name), &Func);181if (auto &MFuncs = Func.MergedFunctions)182for (auto &MFunc : MFuncs->MergedFunctions)183FuncMap.try_emplace(GCreator.getString(MFunc.Name), &MFunc);184}185return FuncMap;186}187188Error CallSiteInfoLoader::processYAMLFunctions(189const yaml::FunctionsYAML &FuncYAMLs, StringMap<FunctionInfo *> &FuncMap) {190// For each function in the YAML file191for (const auto &FuncYAML : FuncYAMLs.functions) {192auto It = FuncMap.find(FuncYAML.name);193if (It == FuncMap.end())194return createStringError(195std::errc::invalid_argument,196"Can't find function '%s' specified in callsite YAML\n",197FuncYAML.name.c_str());198199FunctionInfo *FuncInfo = It->second;200// Create a CallSiteInfoCollection if not already present201if (!FuncInfo->CallSites)202FuncInfo->CallSites = CallSiteInfoCollection();203for (const auto &CallSiteYAML : FuncYAML.callsites) {204CallSiteInfo CSI;205// Since YAML has specifies relative return offsets, add the function206// start address to make the offset absolute.207CSI.ReturnOffset = CallSiteYAML.return_offset;208for (const auto &Regex : CallSiteYAML.match_regex) {209uint32_t StrOffset = GCreator.insertString(Regex);210CSI.MatchRegex.push_back(StrOffset);211}212213// Parse flags and combine them214for (const auto &FlagStr : CallSiteYAML.flags) {215if (FlagStr == "InternalCall") {216CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::InternalCall);217} else if (FlagStr == "ExternalCall") {218CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::ExternalCall);219} else {220return createStringError(std::errc::invalid_argument,221"Unknown flag in callsite YAML: %s\n",222FlagStr.c_str());223}224}225FuncInfo->CallSites->CallSites.push_back(CSI);226}227}228return Error::success();229}230231raw_ostream &gsym::operator<<(raw_ostream &OS, const CallSiteInfo &CSI) {232OS << " Return=" << HEX64(CSI.ReturnOffset);233OS << " Flags=" << HEX8(CSI.Flags);234OS << " RegEx=" << llvm::interleaved(CSI.MatchRegex, ",");235return OS;236}237238raw_ostream &gsym::operator<<(raw_ostream &OS,239const CallSiteInfoCollection &CSIC) {240for (const auto &CS : CSIC.CallSites) {241OS << CS;242OS << "\n";243}244return OS;245}246247248