Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp
35294 views
1
//===----- PerfSupportPlugin.cpp --- Utils for perf support -----*- 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
// Handles support for registering code with perf
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h"
14
15
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
16
#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h"
17
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
18
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
19
20
#define DEBUG_TYPE "orc"
21
22
using namespace llvm;
23
using namespace llvm::orc;
24
using namespace llvm::jitlink;
25
26
namespace {
27
28
// Creates an EH frame header prepared for a 32-bit relative relocation
29
// to the start of the .eh_frame section. Absolute injects a 64-bit absolute
30
// address space offset 4 bytes from the start instead of 4 bytes
31
Expected<std::string> createX64EHFrameHeader(Section &EHFrame,
32
llvm::endianness endianness,
33
bool absolute) {
34
uint8_t Version = 1;
35
uint8_t EhFramePtrEnc = 0;
36
if (absolute) {
37
EhFramePtrEnc |= dwarf::DW_EH_PE_sdata8 | dwarf::DW_EH_PE_absptr;
38
} else {
39
EhFramePtrEnc |= dwarf::DW_EH_PE_sdata4 | dwarf::DW_EH_PE_datarel;
40
}
41
uint8_t FDECountEnc = dwarf::DW_EH_PE_omit;
42
uint8_t TableEnc = dwarf::DW_EH_PE_omit;
43
// X86_64_64 relocation to the start of the .eh_frame section
44
uint32_t EHFrameRelocation = 0;
45
// uint32_t FDECount = 0;
46
// Skip the FDE binary search table
47
// We'd have to reprocess the CIEs to get this information,
48
// which seems like more trouble than it's worth
49
// TODO consider implementing this.
50
// binary search table goes here
51
52
size_t HeaderSize =
53
(sizeof(Version) + sizeof(EhFramePtrEnc) + sizeof(FDECountEnc) +
54
sizeof(TableEnc) +
55
(absolute ? sizeof(uint64_t) : sizeof(EHFrameRelocation)));
56
std::string HeaderContent(HeaderSize, '\0');
57
BinaryStreamWriter Writer(
58
MutableArrayRef<uint8_t>(
59
reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),
60
endianness);
61
if (auto Err = Writer.writeInteger(Version))
62
return std::move(Err);
63
if (auto Err = Writer.writeInteger(EhFramePtrEnc))
64
return std::move(Err);
65
if (auto Err = Writer.writeInteger(FDECountEnc))
66
return std::move(Err);
67
if (auto Err = Writer.writeInteger(TableEnc))
68
return std::move(Err);
69
if (absolute) {
70
uint64_t EHFrameAddr = SectionRange(EHFrame).getStart().getValue();
71
if (auto Err = Writer.writeInteger(EHFrameAddr))
72
return std::move(Err);
73
} else {
74
if (auto Err = Writer.writeInteger(EHFrameRelocation))
75
return std::move(Err);
76
}
77
return HeaderContent;
78
}
79
80
constexpr StringRef RegisterPerfStartSymbolName =
81
"llvm_orc_registerJITLoaderPerfStart";
82
constexpr StringRef RegisterPerfEndSymbolName =
83
"llvm_orc_registerJITLoaderPerfEnd";
84
constexpr StringRef RegisterPerfImplSymbolName =
85
"llvm_orc_registerJITLoaderPerfImpl";
86
87
static PerfJITCodeLoadRecord
88
getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {
89
PerfJITCodeLoadRecord Record;
90
auto Name = Sym.getName();
91
auto Addr = Sym.getAddress();
92
auto Size = Sym.getSize();
93
Record.Prefix.Id = PerfJITRecordType::JIT_CODE_LOAD;
94
// Runtime sets PID
95
Record.Pid = 0;
96
// Runtime sets TID
97
Record.Tid = 0;
98
Record.Vma = Addr.getValue();
99
Record.CodeAddr = Addr.getValue();
100
Record.CodeSize = Size;
101
Record.CodeIndex = CodeIndex++;
102
Record.Name = Name.str();
103
// Initialize last, once all the other fields are filled
104
Record.Prefix.TotalSize =
105
(2 * sizeof(uint32_t) // id, total_size
106
+ sizeof(uint64_t) // timestamp
107
+ 2 * sizeof(uint32_t) // pid, tid
108
+ 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index
109
+ Name.size() + 1 // symbol name
110
+ Record.CodeSize // code
111
);
112
return Record;
113
}
114
115
static std::optional<PerfJITDebugInfoRecord>
116
getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) {
117
auto &Section = Sym.getBlock().getSection();
118
auto Addr = Sym.getAddress();
119
auto Size = Sym.getSize();
120
auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()};
121
LLVM_DEBUG(dbgs() << "Getting debug info for symbol " << Sym.getName()
122
<< " at address " << Addr.getValue() << " with size "
123
<< Size << "\n"
124
<< "Section ordinal: " << Section.getOrdinal() << "\n");
125
auto LInfo = DC.getLineInfoForAddressRange(
126
SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
127
if (LInfo.empty()) {
128
// No line info available
129
LLVM_DEBUG(dbgs() << "No line info available\n");
130
return std::nullopt;
131
}
132
PerfJITDebugInfoRecord Record;
133
Record.Prefix.Id = PerfJITRecordType::JIT_CODE_DEBUG_INFO;
134
Record.CodeAddr = Addr.getValue();
135
for (const auto &Entry : LInfo) {
136
auto Addr = Entry.first;
137
// The function re-created by perf is preceded by a elf
138
// header. Need to adjust for that, otherwise the results are
139
// wrong.
140
Addr += 0x40;
141
Record.Entries.push_back({Addr, Entry.second.Line,
142
Entry.second.Discriminator,
143
Entry.second.FileName});
144
}
145
size_t EntriesBytes = (2 // record header
146
+ 2 // record fields
147
) *
148
sizeof(uint64_t);
149
for (const auto &Entry : Record.Entries) {
150
EntriesBytes +=
151
sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim
152
EntriesBytes += Entry.Name.size() + 1; // Name
153
}
154
Record.Prefix.TotalSize = EntriesBytes;
155
LLVM_DEBUG(dbgs() << "Created debug info record\n"
156
<< "Total size: " << Record.Prefix.TotalSize << "\n"
157
<< "Nr entries: " << Record.Entries.size() << "\n");
158
return Record;
159
}
160
161
static Expected<PerfJITCodeUnwindingInfoRecord>
162
getUnwindingRecord(LinkGraph &G) {
163
PerfJITCodeUnwindingInfoRecord Record;
164
Record.Prefix.Id = PerfJITRecordType::JIT_CODE_UNWINDING_INFO;
165
Record.Prefix.TotalSize = 0;
166
auto Eh_frame = G.findSectionByName(".eh_frame");
167
if (!Eh_frame) {
168
LLVM_DEBUG(dbgs() << "No .eh_frame section found\n");
169
return Record;
170
}
171
if (!G.getTargetTriple().isOSBinFormatELF()) {
172
LLVM_DEBUG(dbgs() << "Not an ELF file, will not emit unwinding info\n");
173
return Record;
174
}
175
auto SR = SectionRange(*Eh_frame);
176
auto EHFrameSize = SR.getSize();
177
auto Eh_frame_hdr = G.findSectionByName(".eh_frame_hdr");
178
if (!Eh_frame_hdr) {
179
if (G.getTargetTriple().getArch() == Triple::x86_64) {
180
auto Hdr = createX64EHFrameHeader(*Eh_frame, G.getEndianness(), true);
181
if (!Hdr)
182
return Hdr.takeError();
183
Record.EHFrameHdr = std::move(*Hdr);
184
} else {
185
LLVM_DEBUG(dbgs() << "No .eh_frame_hdr section found\n");
186
return Record;
187
}
188
Record.EHFrameHdrAddr = 0;
189
Record.EHFrameHdrSize = Record.EHFrameHdr.size();
190
Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
191
Record.MappedSize = 0; // Because the EHFrame header was not mapped
192
} else {
193
auto SR = SectionRange(*Eh_frame_hdr);
194
Record.EHFrameHdrAddr = SR.getStart().getValue();
195
Record.EHFrameHdrSize = SR.getSize();
196
Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
197
Record.MappedSize = Record.UnwindDataSize;
198
}
199
Record.EHFrameAddr = SR.getStart().getValue();
200
Record.Prefix.TotalSize =
201
(2 * sizeof(uint32_t) // id, total_size
202
+ sizeof(uint64_t) // timestamp
203
+
204
3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size
205
+ Record.UnwindDataSize // eh_frame_hdr, eh_frame
206
);
207
LLVM_DEBUG(dbgs() << "Created unwind record\n"
208
<< "Total size: " << Record.Prefix.TotalSize << "\n"
209
<< "Unwind size: " << Record.UnwindDataSize << "\n"
210
<< "EHFrame size: " << EHFrameSize << "\n"
211
<< "EHFrameHdr size: " << Record.EHFrameHdrSize << "\n");
212
return Record;
213
}
214
215
static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,
216
std::atomic<uint64_t> &CodeIndex,
217
bool EmitDebugInfo, bool EmitUnwindInfo) {
218
std::unique_ptr<DWARFContext> DC;
219
StringMap<std::unique_ptr<MemoryBuffer>> DCBacking;
220
if (EmitDebugInfo) {
221
auto EDC = createDWARFContext(G);
222
if (!EDC) {
223
ES.reportError(EDC.takeError());
224
EmitDebugInfo = false;
225
} else {
226
DC = std::move(EDC->first);
227
DCBacking = std::move(EDC->second);
228
}
229
}
230
PerfJITRecordBatch Batch;
231
for (auto Sym : G.defined_symbols()) {
232
if (!Sym->hasName() || !Sym->isCallable())
233
continue;
234
if (EmitDebugInfo) {
235
auto DebugInfo = getDebugInfoRecord(*Sym, *DC);
236
if (DebugInfo)
237
Batch.DebugInfoRecords.push_back(std::move(*DebugInfo));
238
}
239
Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex));
240
}
241
if (EmitUnwindInfo) {
242
auto UWR = getUnwindingRecord(G);
243
if (!UWR) {
244
ES.reportError(UWR.takeError());
245
} else {
246
Batch.UnwindingRecord = std::move(*UWR);
247
}
248
} else {
249
Batch.UnwindingRecord.Prefix.TotalSize = 0;
250
}
251
return Batch;
252
}
253
} // namespace
254
255
PerfSupportPlugin::PerfSupportPlugin(ExecutorProcessControl &EPC,
256
ExecutorAddr RegisterPerfStartAddr,
257
ExecutorAddr RegisterPerfEndAddr,
258
ExecutorAddr RegisterPerfImplAddr,
259
bool EmitDebugInfo, bool EmitUnwindInfo)
260
: EPC(EPC), RegisterPerfStartAddr(RegisterPerfStartAddr),
261
RegisterPerfEndAddr(RegisterPerfEndAddr),
262
RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0),
263
EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) {
264
cantFail(EPC.callSPSWrapper<void()>(RegisterPerfStartAddr));
265
}
266
PerfSupportPlugin::~PerfSupportPlugin() {
267
cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr));
268
}
269
270
void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR,
271
LinkGraph &G,
272
PassConfiguration &Config) {
273
Config.PostFixupPasses.push_back([this](LinkGraph &G) {
274
auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex,
275
EmitDebugInfo, EmitUnwindInfo);
276
G.allocActions().push_back(
277
{cantFail(shared::WrapperFunctionCall::Create<
278
shared::SPSArgList<shared::SPSPerfJITRecordBatch>>(
279
RegisterPerfImplAddr, Batch)),
280
{}});
281
return Error::success();
282
});
283
}
284
285
Expected<std::unique_ptr<PerfSupportPlugin>>
286
PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD,
287
bool EmitDebugInfo, bool EmitUnwindInfo) {
288
if (!EPC.getTargetTriple().isOSBinFormatELF()) {
289
return make_error<StringError>(
290
"Perf support only available for ELF LinkGraphs!",
291
inconvertibleErrorCode());
292
}
293
auto &ES = EPC.getExecutionSession();
294
ExecutorAddr StartAddr, EndAddr, ImplAddr;
295
if (auto Err = lookupAndRecordAddrs(
296
ES, LookupKind::Static, makeJITDylibSearchOrder({&JD}),
297
{{ES.intern(RegisterPerfStartSymbolName), &StartAddr},
298
{ES.intern(RegisterPerfEndSymbolName), &EndAddr},
299
{ES.intern(RegisterPerfImplSymbolName), &ImplAddr}}))
300
return std::move(Err);
301
return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr,
302
EmitDebugInfo, EmitUnwindInfo);
303
}
304
305