1 //===- CallSiteInfo.cpp -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
21 #include <unordered_map>
27 Error
CallSiteInfo::encode(FileWriter
&O
) const {
28 O
.writeU64(ReturnOffset
);
30 O
.writeU32(MatchRegex
.size());
31 for (uint32_t Entry
: MatchRegex
)
33 return Error::success();
36 Expected
<CallSiteInfo
> CallSiteInfo::decode(DataExtractor
&Data
,
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
);
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
);
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",
57 uint32_t NumEntries
= Data
.getU32(&Offset
);
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",
65 uint32_t Entry
= Data
.getU32(&Offset
);
66 CSI
.MatchRegex
.push_back(Entry
);
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
))
78 return Error::success();
81 Expected
<CallSiteInfoCollection
>
82 CallSiteInfoCollection::decode(DataExtractor
&Data
) {
83 CallSiteInfoCollection CSC
;
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",
91 uint32_t NumCallSites
= Data
.getU32(&Offset
);
93 CSC
.CallSites
.reserve(NumCallSites
);
94 for (uint32_t i
= 0; i
< NumCallSites
; ++i
) {
95 Expected
<CallSiteInfo
> ECSI
= CallSiteInfo::decode(Data
, Offset
);
97 return ECSI
.takeError();
98 CSC
.CallSites
.emplace_back(*ECSI
);
104 /// Structures necessary for reading CallSiteInfo from YAML.
108 struct CallSiteYAML
{
109 // The offset of the return address of the call site - relative to the start
112 std::vector
<std::string
> match_regex
;
113 std::vector
<std::string
> flags
;
116 struct FunctionYAML
{
118 std::vector
<CallSiteYAML
> callsites
;
121 struct FunctionsYAML
{
122 std::vector
<FunctionYAML
> functions
;
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
);
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
);
140 template <> struct MappingTraits
<FunctionsYAML
> {
141 static void mapping(IO
&io
, FunctionsYAML
&FuncYAMLs
) {
142 io
.mapRequired("functions", FuncYAMLs
.functions
);
149 LLVM_YAML_IS_SEQUENCE_VECTOR(CallSiteYAML
)
150 LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionYAML
)
152 Error
CallSiteInfoLoader::loadYAML(StringRef YAMLFile
) {
153 // Step 1: Read YAML file
154 auto BufferOrError
= MemoryBuffer::getFile(YAMLFile
, /*IsText=*/true);
156 return errorCodeToError(BufferOrError
.getError());
158 std::unique_ptr
<MemoryBuffer
> Buffer
= std::move(*BufferOrError
);
160 // Step 2: Parse YAML content
161 yaml::FunctionsYAML FuncsYAML
;
162 yaml::Input
Yin(Buffer
->getMemBufferRef());
165 return createStringError(Yin
.error(), "Error parsing YAML file: %s\n",
166 Buffer
->getBufferIdentifier().str().c_str());
168 // Step 3: Build function map from Funcs
169 auto FuncMap
= buildFunctionMap();
171 // Step 4: Process parsed YAML functions and update FuncMap
172 return processYAMLFunctions(FuncsYAML
, FuncMap
);
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
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
);
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());
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
) {
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
);
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
);
223 return createStringError(std::errc::invalid_argument
,
224 "Unknown flag in callsite YAML: %s\n",
228 FuncInfo
->CallSites
->CallSites
.push_back(CSI
);
231 return Error::success();
234 raw_ostream
&gsym::operator<<(raw_ostream
&OS
, const CallSiteInfo
&CSI
) {
235 OS
<< " Return=" << HEX64(CSI
.ReturnOffset
);
236 OS
<< " Flags=" << HEX8(CSI
.Flags
);
239 for (uint32_t i
= 0; i
< CSI
.MatchRegex
.size(); ++i
) {
242 OS
<< CSI
.MatchRegex
[i
];
247 raw_ostream
&gsym::operator<<(raw_ostream
&OS
,
248 const CallSiteInfoCollection
&CSIC
) {
249 for (const auto &CS
: CSIC
.CallSites
) {