xref: /llvm-project/llvm/lib/DebugInfo/GSYM/CallSiteInfo.cpp (revision 1d5154663509b6200038a2f0b0ac958ea556fa9e)
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