//===- 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/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/InterleavedRange.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include #include 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::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::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 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 match_regex; std::vector flags; }; struct FunctionYAML { std::string name; std::vector callsites; }; struct FunctionsYAML { std::vector functions; }; template <> struct MappingTraits { 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 { static void mapping(IO &io, FunctionYAML &func) { io.mapRequired("name", func.name); io.mapOptional("callsites", func.callsites); } }; template <> struct MappingTraits { 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 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 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 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 &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(CallSiteInfo::InternalCall); } else if (FlagStr == "ExternalCall") { CSI.Flags |= static_cast(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=" << llvm::interleaved(CSI.MatchRegex, ","); return OS; } raw_ostream &gsym::operator<<(raw_ostream &OS, const CallSiteInfoCollection &CSIC) { for (const auto &CS : CSIC.CallSites) { OS << CS; OS << "\n"; } return OS; }