blob: c112c0bc3ddc9df6ed82c3cec13d58f730a0f1e2 [file] [log] [blame]
//===- CallSiteInfo.cpp -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/GSYM/CallSiteInfo.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/DebugInfo/GSYM/FileWriter.h"
#include "llvm/DebugInfo/GSYM/FunctionInfo.h"
#include "llvm/DebugInfo/GSYM/GsymCreator.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <string>
#include <unordered_map>
#include <vector>
using namespace llvm;
using namespace gsym;
Error CallSiteInfo::encode(FileWriter &O) const {
O.writeU64(ReturnOffset);
O.writeU8(Flags);
O.writeU32(MatchRegex.size());
for (uint32_t Entry : MatchRegex)
O.writeU32(Entry);
return Error::success();
}
Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,
uint64_t &Offset) {
CallSiteInfo CSI;
// Read ReturnOffset
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint64_t)))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing ReturnOffset", Offset);
CSI.ReturnOffset = Data.getU64(&Offset);
// Read Flags
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint8_t)))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing Flags", Offset);
CSI.Flags = Data.getU8(&Offset);
// Read number of MatchRegex entries
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing MatchRegex count",
Offset);
uint32_t NumEntries = Data.getU32(&Offset);
CSI.MatchRegex.reserve(NumEntries);
for (uint32_t i = 0; i < NumEntries; ++i) {
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing MatchRegex entry",
Offset);
uint32_t Entry = Data.getU32(&Offset);
CSI.MatchRegex.push_back(Entry);
}
return CSI;
}
Error CallSiteInfoCollection::encode(FileWriter &O) const {
O.writeU32(CallSites.size());
for (const CallSiteInfo &CSI : CallSites)
if (Error Err = CSI.encode(O))
return Err;
return Error::success();
}
Expected<CallSiteInfoCollection>
CallSiteInfoCollection::decode(DataExtractor &Data) {
CallSiteInfoCollection CSC;
uint64_t Offset = 0;
// Read number of CallSiteInfo entries
if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(uint32_t)))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing CallSiteInfo count",
Offset);
uint32_t NumCallSites = Data.getU32(&Offset);
CSC.CallSites.reserve(NumCallSites);
for (uint32_t i = 0; i < NumCallSites; ++i) {
Expected<CallSiteInfo> ECSI = CallSiteInfo::decode(Data, Offset);
if (!ECSI)
return ECSI.takeError();
CSC.CallSites.emplace_back(*ECSI);
}
return CSC;
}
/// Structures necessary for reading CallSiteInfo from YAML.
namespace llvm {
namespace yaml {
struct CallSiteYAML {
// The offset of the return address of the call site - relative to the start
// of the function.
Hex64 return_offset;
std::vector<std::string> match_regex;
std::vector<std::string> flags;
};
struct FunctionYAML {
std::string name;
std::vector<CallSiteYAML> callsites;
};
struct FunctionsYAML {
std::vector<FunctionYAML> functions;
};
template <> struct MappingTraits<CallSiteYAML> {
static void mapping(IO &io, CallSiteYAML &callsite) {
io.mapRequired("return_offset", callsite.return_offset);
io.mapRequired("match_regex", callsite.match_regex);
io.mapOptional("flags", callsite.flags);
}
};
template <> struct MappingTraits<FunctionYAML> {
static void mapping(IO &io, FunctionYAML &func) {
io.mapRequired("name", func.name);
io.mapOptional("callsites", func.callsites);
}
};
template <> struct MappingTraits<FunctionsYAML> {
static void mapping(IO &io, FunctionsYAML &FuncYAMLs) {
io.mapRequired("functions", FuncYAMLs.functions);
}
};
} // namespace yaml
} // namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(CallSiteYAML)
LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML)
Error CallSiteInfoLoader::loadYAML(StringRef YAMLFile) {
// Step 1: Read YAML file
auto BufferOrError = MemoryBuffer::getFile(YAMLFile, /*IsText=*/true);
if (!BufferOrError)
return errorCodeToError(BufferOrError.getError());
std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrError);
// Step 2: Parse YAML content
yaml::FunctionsYAML FuncsYAML;
yaml::Input Yin(Buffer->getMemBufferRef());
Yin >> FuncsYAML;
if (Yin.error())
return createStringError(Yin.error(), "Error parsing YAML file: %s\n",
Buffer->getBufferIdentifier().str().c_str());
// Step 3: Build function map from Funcs
auto FuncMap = buildFunctionMap();
// Step 4: Process parsed YAML functions and update FuncMap
return processYAMLFunctions(FuncsYAML, FuncMap);
}
StringMap<FunctionInfo *> CallSiteInfoLoader::buildFunctionMap() {
// If the function name is already in the map, don't update it. This way we
// preferentially use the first encountered function. Since symbols are
// loaded from dSYM first, we end up preferring keeping track of symbols
// from dSYM rather than from the symbol table - which is what we want to
// do.
StringMap<FunctionInfo *> FuncMap;
for (auto &Func : Funcs) {
FuncMap.try_emplace(GCreator.getString(Func.Name), &Func);
if (auto &MFuncs = Func.MergedFunctions)
for (auto &MFunc : MFuncs->MergedFunctions)
FuncMap.try_emplace(GCreator.getString(MFunc.Name), &MFunc);
}
return FuncMap;
}
Error CallSiteInfoLoader::processYAMLFunctions(
const yaml::FunctionsYAML &FuncYAMLs, StringMap<FunctionInfo *> &FuncMap) {
// For each function in the YAML file
for (const auto &FuncYAML : FuncYAMLs.functions) {
auto It = FuncMap.find(FuncYAML.name);
if (It == FuncMap.end())
return createStringError(
std::errc::invalid_argument,
"Can't find function '%s' specified in callsite YAML\n",
FuncYAML.name.c_str());
FunctionInfo *FuncInfo = It->second;
// Create a CallSiteInfoCollection if not already present
if (!FuncInfo->CallSites)
FuncInfo->CallSites = CallSiteInfoCollection();
for (const auto &CallSiteYAML : FuncYAML.callsites) {
CallSiteInfo CSI;
// Since YAML has specifies relative return offsets, add the function
// start address to make the offset absolute.
CSI.ReturnOffset = CallSiteYAML.return_offset;
for (const auto &Regex : CallSiteYAML.match_regex) {
uint32_t StrOffset = GCreator.insertString(Regex);
CSI.MatchRegex.push_back(StrOffset);
}
// Parse flags and combine them
for (const auto &FlagStr : CallSiteYAML.flags) {
if (FlagStr == "InternalCall") {
CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::InternalCall);
} else if (FlagStr == "ExternalCall") {
CSI.Flags |= static_cast<uint8_t>(CallSiteInfo::ExternalCall);
} else {
return createStringError(std::errc::invalid_argument,
"Unknown flag in callsite YAML: %s\n",
FlagStr.c_str());
}
}
FuncInfo->CallSites->CallSites.push_back(CSI);
}
}
return Error::success();
}
raw_ostream &gsym::operator<<(raw_ostream &OS, const CallSiteInfo &CSI) {
OS << " Return=" << HEX64(CSI.ReturnOffset);
OS << " Flags=" << HEX8(CSI.Flags);
OS << " RegEx=";
for (uint32_t i = 0; i < CSI.MatchRegex.size(); ++i) {
if (i > 0)
OS << ",";
OS << CSI.MatchRegex[i];
}
return OS;
}
raw_ostream &gsym::operator<<(raw_ostream &OS,
const CallSiteInfoCollection &CSIC) {
for (const auto &CS : CSIC.CallSites) {
OS << CS;
OS << "\n";
}
return OS;
}