xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/CallSiteInfo.cpp (revision 1342eb5a832fa10e689a29faab3acb6054e4778c)
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 
25 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 
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 
70 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>
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> {
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 
131 template <> 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 
138 template <> struct MappingTraits<FunctionsYAML> {
139   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)
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 
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 
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 
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 
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