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/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
22 using namespace llvm;
23 using namespace gsym;
24
encode(FileWriter & O) const25 Error 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
decode(DataExtractor & Data,uint64_t & Offset)34 Expected<CallSiteInfo> CallSiteInfo::decode(DataExtractor &Data,
35 uint64_t &Offset) {
36 CallSiteInfo CSI;
37
38 // Read ReturnOffset
39 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 Flags
45 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 entries
51 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
encode(FileWriter & O) const70 Error 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
79 Expected<CallSiteInfoCollection>
decode(DataExtractor & Data)80 CallSiteInfoCollection::decode(DataExtractor &Data) {
81 CallSiteInfoCollection CSC;
82 uint64_t Offset = 0;
83
84 // Read number of CallSiteInfo entries
85 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.
103 namespace llvm {
104 namespace yaml {
105
106 struct CallSiteYAML {
107 // The offset of the return address of the call site - relative to the start
108 // of the function.
109 Hex64 return_offset;
110 std::vector<std::string> match_regex;
111 std::vector<std::string> flags;
112 };
113
114 struct FunctionYAML {
115 std::string name;
116 std::vector<CallSiteYAML> callsites;
117 };
118
119 struct FunctionsYAML {
120 std::vector<FunctionYAML> functions;
121 };
122
123 template <> struct MappingTraits<CallSiteYAML> {
mappingllvm::yaml::MappingTraits124 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
131 template <> struct MappingTraits<FunctionYAML> {
mappingllvm::yaml::MappingTraits132 static void mapping(IO &io, FunctionYAML &func) {
133 io.mapRequired("name", func.name);
134 io.mapOptional("callsites", func.callsites);
135 }
136 };
137
138 template <> struct MappingTraits<FunctionsYAML> {
mappingllvm::yaml::MappingTraits139 static void mapping(IO &io, FunctionsYAML &FuncYAMLs) {
140 io.mapRequired("functions", FuncYAMLs.functions);
141 }
142 };
143
144 } // namespace yaml
145 } // namespace llvm
146
147 LLVM_YAML_IS_SEQUENCE_VECTOR(CallSiteYAML)
LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML)148 LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML)
149
150 Error CallSiteInfoLoader::loadYAML(StringRef YAMLFile) {
151 // Step 1: Read YAML file
152 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 content
159 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 Funcs
167 auto FuncMap = buildFunctionMap();
168
169 // Step 4: Process parsed YAML functions and update FuncMap
170 return processYAMLFunctions(FuncsYAML, FuncMap);
171 }
172
buildFunctionMap()173 StringMap<FunctionInfo *> CallSiteInfoLoader::buildFunctionMap() {
174 // If the function name is already in the map, don't update it. This way we
175 // preferentially use the first encountered function. Since symbols are
176 // loaded from dSYM first, we end up preferring keeping track of symbols
177 // from dSYM rather than from the symbol table - which is what we want to
178 // 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
processYAMLFunctions(const yaml::FunctionsYAML & FuncYAMLs,StringMap<FunctionInfo * > & FuncMap)189 Error CallSiteInfoLoader::processYAMLFunctions(
190 const yaml::FunctionsYAML &FuncYAMLs, StringMap<FunctionInfo *> &FuncMap) {
191 // For each function in the YAML file
192 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 present
202 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 function
207 // 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 them
215 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
operator <<(raw_ostream & OS,const CallSiteInfo & CSI)232 raw_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
operator <<(raw_ostream & OS,const CallSiteInfoCollection & CSIC)239 raw_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