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