247 lines · cpp
1//===- CallSiteInfo.cpp -----------------------------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"10#include "llvm/DebugInfo/GSYM/FileWriter.h"11#include "llvm/DebugInfo/GSYM/FunctionInfo.h"12#include "llvm/DebugInfo/GSYM/GsymCreator.h"13#include "llvm/MC/StringTableBuilder.h"14#include "llvm/Support/DataExtractor.h"15#include "llvm/Support/InterleavedRange.h"16#include "llvm/Support/YAMLParser.h"17#include "llvm/Support/YAMLTraits.h"18#include "llvm/Support/raw_ostream.h"19#include <string>20#include <vector>21 22using namespace llvm;23using namespace gsym;24 25Error CallSiteInfo::encode(FileWriter &O) const {26 O.writeU64(ReturnOffset);27 O.writeU8(Flags);28 O.writeU32(MatchRegex.size());29 for (uint32_t Entry : MatchRegex)30 O.writeU32(Entry);31 return Error::success();32}33 34Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,35 uint64_t &Offset) {36 CallSiteInfo CSI;37 38 // Read ReturnOffset39 if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint64_t)))40 return createStringError(std::errc::io_error,41 "0x%8.8" PRIx64 ": missing ReturnOffset", Offset);42 CSI.ReturnOffset = Data.getU64(&Offset);43 44 // Read Flags45 if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint8_t)))46 return createStringError(std::errc::io_error,47 "0x%8.8" PRIx64 ": missing Flags", Offset);48 CSI.Flags = Data.getU8(&Offset);49 50 // Read number of MatchRegex entries51 if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))52 return createStringError(std::errc::io_error,53 "0x%8.8" PRIx64 ": missing MatchRegex count",54 Offset);55 uint32_t NumEntries = Data.getU32(&Offset);56 57 CSI.MatchRegex.reserve(NumEntries);58 for (uint32_t i = 0; i < NumEntries; ++i) {59 if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))60 return createStringError(std::errc::io_error,61 "0x%8.8" PRIx64 ": missing MatchRegex entry",62 Offset);63 uint32_t Entry = Data.getU32(&Offset);64 CSI.MatchRegex.push_back(Entry);65 }66 67 return CSI;68}69 70Error CallSiteInfoCollection::encode(FileWriter &O) const {71 O.writeU32(CallSites.size());72 for (const CallSiteInfo &CSI : CallSites)73 if (Error Err = CSI.encode(O))74 return Err;75 76 return Error::success();77}78 79Expected<CallSiteInfoCollection>80CallSiteInfoCollection::decode(DataExtractor &Data) {81 CallSiteInfoCollection CSC;82 uint64_t Offset = 0;83 84 // Read number of CallSiteInfo entries85 if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))86 return createStringError(std::errc::io_error,87 "0x%8.8" PRIx64 ": missing CallSiteInfo count",88 Offset);89 uint32_t NumCallSites = Data.getU32(&Offset);90 91 CSC.CallSites.reserve(NumCallSites);92 for (uint32_t i = 0; i < NumCallSites; ++i) {93 Expected<CallSiteInfo> ECSI = CallSiteInfo::decode(Data, Offset);94 if (!ECSI)95 return ECSI.takeError();96 CSC.CallSites.emplace_back(*ECSI);97 }98 99 return CSC;100}101 102/// Structures necessary for reading CallSiteInfo from YAML.103namespace llvm {104namespace yaml {105 106struct CallSiteYAML {107 // The offset of the return address of the call site - relative to the start108 // of the function.109 Hex64 return_offset;110 std::vector<std::string> match_regex;111 std::vector<std::string> flags;112};113 114struct FunctionYAML {115 std::string name;116 std::vector<CallSiteYAML> callsites;117};118 119struct FunctionsYAML {120 std::vector<FunctionYAML> functions;121};122 123template <> struct MappingTraits<CallSiteYAML> {124 static void mapping(IO &io, CallSiteYAML &callsite) {125 io.mapRequired("return_offset", callsite.return_offset);126 io.mapRequired("match_regex", callsite.match_regex);127 io.mapOptional("flags", callsite.flags);128 }129};130 131template <> struct MappingTraits<FunctionYAML> {132 static void mapping(IO &io, FunctionYAML &func) {133 io.mapRequired("name", func.name);134 io.mapOptional("callsites", func.callsites);135 }136};137 138template <> struct MappingTraits<FunctionsYAML> {139 static void mapping(IO &io, FunctionsYAML &FuncYAMLs) {140 io.mapRequired("functions", FuncYAMLs.functions);141 }142};143 144} // namespace yaml145} // namespace llvm146 147LLVM_YAML_IS_SEQUENCE_VECTOR(CallSiteYAML)148LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML)149 150Error CallSiteInfoLoader::loadYAML(StringRef YAMLFile) {151 // Step 1: Read YAML file152 auto BufferOrError = MemoryBuffer::getFile(YAMLFile, /*IsText=*/true);153 if (!BufferOrError)154 return errorCodeToError(BufferOrError.getError());155 156 std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrError);157 158 // Step 2: Parse YAML content159 yaml::FunctionsYAML FuncsYAML;160 yaml::Input Yin(Buffer->getMemBufferRef());161 Yin >> FuncsYAML;162 if (Yin.error())163 return createStringError(Yin.error(), "Error parsing YAML file: %s\n",164 Buffer->getBufferIdentifier().str().c_str());165 166 // Step 3: Build function map from Funcs167 auto FuncMap = buildFunctionMap();168 169 // Step 4: Process parsed YAML functions and update FuncMap170 return processYAMLFunctions(FuncsYAML, FuncMap);171}172 173StringMap<FunctionInfo *> CallSiteInfoLoader::buildFunctionMap() {174 // If the function name is already in the map, don't update it. This way we175 // preferentially use the first encountered function. Since symbols are176 // loaded from dSYM first, we end up preferring keeping track of symbols177 // from dSYM rather than from the symbol table - which is what we want to178 // do.179 StringMap<FunctionInfo *> FuncMap;180 for (auto &Func : Funcs) {181 FuncMap.try_emplace(GCreator.getString(Func.Name), &Func);182 if (auto &MFuncs = Func.MergedFunctions)183 for (auto &MFunc : MFuncs->MergedFunctions)184 FuncMap.try_emplace(GCreator.getString(MFunc.Name), &MFunc);185 }186 return FuncMap;187}188 189Error CallSiteInfoLoader::processYAMLFunctions(190 const yaml::FunctionsYAML &FuncYAMLs, StringMap<FunctionInfo *> &FuncMap) {191 // For each function in the YAML file192 for (const auto &FuncYAML : FuncYAMLs.functions) {193 auto It = FuncMap.find(FuncYAML.name);194 if (It == FuncMap.end())195 return createStringError(196 std::errc::invalid_argument,197 "Can't find function '%s' specified in callsite YAML\n",198 FuncYAML.name.c_str());199 200 FunctionInfo *FuncInfo = It->second;201 // Create a CallSiteInfoCollection if not already present202 if (!FuncInfo->CallSites)203 FuncInfo->CallSites = CallSiteInfoCollection();204 for (const auto &CallSiteYAML : FuncYAML.callsites) {205 CallSiteInfo CSI;206 // Since YAML has specifies relative return offsets, add the function207 // start address to make the offset absolute.208 CSI.ReturnOffset = CallSiteYAML.return_offset;209 for (const auto &Regex : CallSiteYAML.match_regex) {210 uint32_t StrOffset = GCreator.insertString(Regex);211 CSI.MatchRegex.push_back(StrOffset);212 }213 214 // Parse flags and combine them215 for (const auto &FlagStr : CallSiteYAML.flags) {216 if (FlagStr == "InternalCall") {217 CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::InternalCall);218 } else if (FlagStr == "ExternalCall") {219 CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::ExternalCall);220 } else {221 return createStringError(std::errc::invalid_argument,222 "Unknown flag in callsite YAML: %s\n",223 FlagStr.c_str());224 }225 }226 FuncInfo->CallSites->CallSites.push_back(CSI);227 }228 }229 return Error::success();230}231 232raw_ostream &gsym::operator<<(raw_ostream &OS, const CallSiteInfo &CSI) {233 OS << " Return=" << HEX64(CSI.ReturnOffset);234 OS << " Flags=" << HEX8(CSI.Flags);235 OS << " RegEx=" << llvm::interleaved(CSI.MatchRegex, ",");236 return OS;237}238 239raw_ostream &gsym::operator<<(raw_ostream &OS,240 const CallSiteInfoCollection &CSIC) {241 for (const auto &CS : CSIC.CallSites) {242 OS << CS;243 OS << "\n";244 }245 return OS;246}247