LLVM 24.0.0git
PerfSupportPlugin.cpp
Go to the documentation of this file.
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
14
19
20#define DEBUG_TYPE "orc"
21
22using namespace llvm;
23using namespace llvm::orc;
24using namespace llvm::jitlink;
25
26// Controller-interface descriptors for the executor's perf-support wrapper
27// calls.
30 static constexpr SymbolNameSpec Name =
31 SymbolNameSpec::c("llvm_orc_registerJITLoaderPerfStart");
32 using SPSSig = void();
33};
35 static constexpr SymbolNameSpec Name =
36 SymbolNameSpec::c("llvm_orc_registerJITLoaderPerfEnd");
37 using SPSSig = void();
38};
39} // namespace llvm::orc::perf_sps_ci
40
41namespace {
42
43// Creates an EH frame header prepared for a 32-bit relative relocation
44// to the start of the .eh_frame section. Absolute injects a 64-bit absolute
45// address space offset 4 bytes from the start instead of 4 bytes
46Expected<std::string> createX64EHFrameHeader(Section &EHFrame,
48 bool absolute) {
49 uint8_t Version = 1;
50 uint8_t EhFramePtrEnc = 0;
51 if (absolute) {
53 } else {
55 }
56 uint8_t FDECountEnc = dwarf::DW_EH_PE_omit;
58 // X86_64_64 relocation to the start of the .eh_frame section
59 uint32_t EHFrameRelocation = 0;
60 // uint32_t FDECount = 0;
61 // Skip the FDE binary search table
62 // We'd have to reprocess the CIEs to get this information,
63 // which seems like more trouble than it's worth
64 // TODO consider implementing this.
65 // binary search table goes here
66
67 size_t HeaderSize =
68 (sizeof(Version) + sizeof(EhFramePtrEnc) + sizeof(FDECountEnc) +
69 sizeof(TableEnc) +
70 (absolute ? sizeof(uint64_t) : sizeof(EHFrameRelocation)));
71 std::string HeaderContent(HeaderSize, '\0');
72 BinaryStreamWriter Writer(
74 reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),
76 if (auto Err = Writer.writeInteger(Version))
77 return std::move(Err);
78 if (auto Err = Writer.writeInteger(EhFramePtrEnc))
79 return std::move(Err);
80 if (auto Err = Writer.writeInteger(FDECountEnc))
81 return std::move(Err);
82 if (auto Err = Writer.writeInteger(TableEnc))
83 return std::move(Err);
84 if (absolute) {
85 uint64_t EHFrameAddr = SectionRange(EHFrame).getStart().getValue();
86 if (auto Err = Writer.writeInteger(EHFrameAddr))
87 return std::move(Err);
88 } else {
89 if (auto Err = Writer.writeInteger(EHFrameRelocation))
90 return std::move(Err);
91 }
92 return HeaderContent;
93}
94
95constexpr SymbolNameSpec RegisterPerfImplSymbolName =
96 SymbolNameSpec::c("llvm_orc_registerJITLoaderPerfImpl");
97
99getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {
101 auto Name = *Sym.getName();
102 auto Addr = Sym.getAddress();
103 auto Size = Sym.getSize();
105 // Runtime sets PID
106 Record.Pid = 0;
107 // Runtime sets TID
108 Record.Tid = 0;
109 Record.Vma = Addr.getValue();
110 Record.CodeAddr = Addr.getValue();
111 Record.CodeSize = Size;
112 Record.CodeIndex = CodeIndex++;
113 Record.Name = Name.str();
114 // Initialize last, once all the other fields are filled
115 Record.Prefix.TotalSize =
116 (2 * sizeof(uint32_t) // id, total_size
117 + sizeof(uint64_t) // timestamp
118 + 2 * sizeof(uint32_t) // pid, tid
119 + 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index
120 + Name.size() + 1 // symbol name
121 + Record.CodeSize // code
122 );
123 return Record;
124}
125
126static std::optional<PerfJITDebugInfoRecord>
127getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) {
128 auto &Section = Sym.getSection();
129 auto Addr = Sym.getAddress();
130 auto Size = Sym.getSize();
131 auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()};
132 LLVM_DEBUG(dbgs() << "Getting debug info for symbol " << Sym.getName()
133 << " at address " << Addr.getValue() << " with size "
134 << Size << "\n"
135 << "Section ordinal: " << Section.getOrdinal() << "\n");
136 auto LInfo = DC.getLineInfoForAddressRange(
138 if (LInfo.empty()) {
139 // No line info available
140 LLVM_DEBUG(dbgs() << "No line info available\n");
141 return std::nullopt;
142 }
145 Record.CodeAddr = Addr.getValue();
146 for (const auto &Entry : LInfo) {
147 auto Addr = Entry.first;
148 // The function re-created by perf is preceded by a elf
149 // header. Need to adjust for that, otherwise the results are
150 // wrong.
151 Addr += 0x40;
152 Record.Entries.push_back({Addr, Entry.second.Line,
153 Entry.second.Discriminator,
154 Entry.second.FileName});
155 }
156 size_t EntriesBytes = (2 // record header
157 + 2 // record fields
158 ) *
159 sizeof(uint64_t);
160 for (const auto &Entry : Record.Entries) {
161 EntriesBytes +=
162 sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim
163 EntriesBytes += Entry.Name.size() + 1; // Name
164 }
165 Record.Prefix.TotalSize = EntriesBytes;
166 LLVM_DEBUG(dbgs() << "Created debug info record\n"
167 << "Total size: " << Record.Prefix.TotalSize << "\n"
168 << "Nr entries: " << Record.Entries.size() << "\n");
169 return Record;
170}
171
173getUnwindingRecord(LinkGraph &G) {
176 Record.Prefix.TotalSize = 0;
177 auto Eh_frame = G.findSectionByName(".eh_frame");
178 if (!Eh_frame) {
179 LLVM_DEBUG(dbgs() << "No .eh_frame section found\n");
180 return Record;
181 }
182 if (!G.getTargetTriple().isOSBinFormatELF()) {
183 LLVM_DEBUG(dbgs() << "Not an ELF file, will not emit unwinding info\n");
184 return Record;
185 }
186 auto SR = SectionRange(*Eh_frame);
187 auto EHFrameSize = SR.getSize();
188 auto Eh_frame_hdr = G.findSectionByName(".eh_frame_hdr");
189 if (!Eh_frame_hdr) {
190 if (G.getTargetTriple().getArch() == Triple::x86_64) {
191 auto Hdr = createX64EHFrameHeader(*Eh_frame, G.getEndianness(), true);
192 if (!Hdr)
193 return Hdr.takeError();
194 Record.EHFrameHdr = std::move(*Hdr);
195 } else {
196 LLVM_DEBUG(dbgs() << "No .eh_frame_hdr section found\n");
197 return Record;
198 }
199 Record.EHFrameHdrAddr = 0;
200 Record.EHFrameHdrSize = Record.EHFrameHdr.size();
201 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
202 Record.MappedSize = 0; // Because the EHFrame header was not mapped
203 } else {
204 auto SR = SectionRange(*Eh_frame_hdr);
205 Record.EHFrameHdrAddr = SR.getStart().getValue();
206 Record.EHFrameHdrSize = SR.getSize();
207 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
208 Record.MappedSize = Record.UnwindDataSize;
209 }
210 Record.EHFrameAddr = SR.getStart().getValue();
211 Record.Prefix.TotalSize =
212 (2 * sizeof(uint32_t) // id, total_size
213 + sizeof(uint64_t) // timestamp
214 +
215 3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size
216 + Record.UnwindDataSize // eh_frame_hdr, eh_frame
217 );
218 LLVM_DEBUG(dbgs() << "Created unwind record\n"
219 << "Total size: " << Record.Prefix.TotalSize << "\n"
220 << "Unwind size: " << Record.UnwindDataSize << "\n"
221 << "EHFrame size: " << EHFrameSize << "\n"
222 << "EHFrameHdr size: " << Record.EHFrameHdrSize << "\n");
223 return Record;
224}
225
226static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,
227 std::atomic<uint64_t> &CodeIndex,
228 bool EmitDebugInfo, bool EmitUnwindInfo) {
229 std::unique_ptr<DWARFContext> DC;
231 if (EmitDebugInfo) {
232 auto EDC = createDWARFContext(G);
233 if (!EDC) {
234 ES.reportError(EDC.takeError());
235 EmitDebugInfo = false;
236 } else {
237 DC = std::move(EDC->first);
238 DCBacking = std::move(EDC->second);
239 }
240 }
241 PerfJITRecordBatch Batch;
242 for (auto Sym : G.defined_symbols()) {
243 if (!Sym->hasName() || !Sym->isCallable())
244 continue;
245 if (EmitDebugInfo) {
246 auto DebugInfo = getDebugInfoRecord(*Sym, *DC);
247 if (DebugInfo)
248 Batch.DebugInfoRecords.push_back(std::move(*DebugInfo));
249 }
250 Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex));
251 }
252 if (EmitUnwindInfo) {
253 auto UWR = getUnwindingRecord(G);
254 if (!UWR) {
255 ES.reportError(UWR.takeError());
256 } else {
257 Batch.UnwindingRecord = std::move(*UWR);
258 }
259 } else {
261 }
262 return Batch;
263}
264} // namespace
265
267 ExecutorAddr RegisterPerfStartAddr,
268 ExecutorAddr RegisterPerfEndAddr,
269 ExecutorAddr RegisterPerfImplAddr,
270 bool EmitDebugInfo, bool EmitUnwindInfo)
271 : EPC(EPC), RegisterPerfStart(
272 sps::ProxySpec<Proxy<void()>,
273 perf_sps_ci::RegisterPerfStart>::dispatch,
274 RegisterPerfStartAddr),
275 RegisterPerfEnd(
276 sps::ProxySpec<Proxy<void()>, perf_sps_ci::RegisterPerfEnd>::dispatch,
277 RegisterPerfEndAddr),
278 RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0),
279 EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) {
280 cantFail(RegisterPerfStart(EPC.getExecutionSession()));
281}
283 // FIXME: End message from destructor is unreliable. Executor-side perf
284 // support (currently JITLoaderPerf.cpp in OrcTargetProcess) should be
285 // reimplemented as a service in the new ORC runtime, where cleanup
286 // can be run as an on-shutdown event.
287 //
288 // For now, switch to async dispatch here so that a message failure doesn't
289 // load to a crash when we try to read from a std::future whose std::promise
290 // has been destoryed.
291 RegisterPerfEnd([](Error Err) { consumeError(std::move(Err)); },
292 EPC.getExecutionSession());
293}
294
296 LinkGraph &G,
297 PassConfiguration &Config) {
298 Config.PostFixupPasses.push_back([this](LinkGraph &G) {
299 auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex,
300 EmitDebugInfo, EmitUnwindInfo);
301 G.allocActions().push_back(
304 RegisterPerfImplAddr, Batch)),
305 {}});
306 return Error::success();
307 });
308}
309
312 bool EmitDebugInfo, bool EmitUnwindInfo) {
313 if (!EPC.getTargetTriple().isOSBinFormatELF()) {
315 "Perf support only available for ELF LinkGraphs!",
317 }
318 ExecutorAddr StartAddr, EndAddr, ImplAddr;
319 if (auto Err = lookupAndApply(
322 recordAddr(RegisterPerfImplSymbolName, &ImplAddr)}))
323 return std::move(Err);
324 return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr,
325 EmitDebugInfo, EmitUnwindInfo);
326}
unsigned uint64_t
static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info)
#define G(x, y, z)
Definition MD5.cpp:55
#define LLVM_DEBUG(...)
Definition Debug.h:119
Provides write only access to a subclass of WritableBinaryStream.
DWARFContext This data structure is the top level entity that deals with dwarf debug information pars...
DILineInfoTable getLineInfoForAddressRange(object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Specifier=DILineInfoSpecifier()) override
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Represent a mutable reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:294
const RecordVal * getValue(const Init *Name) const
Definition Record.h:1818
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:129
An ExecutionSession represents a running JIT program.
Definition Core.h:1112
void reportError(Error Err)
Report a error for this execution session.
Definition Core.h:1290
Represents an address in the executor process.
uint64_t getValue() const
ExecutorProcessControl supports interaction with a JIT target process.
Represents a JIT'd dynamic library.
Definition Core.h:676
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition Core.h:350
void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::PassConfiguration &Config) override
static Expected< std::unique_ptr< PerfSupportPlugin > > Create(ExecutorProcessControl &EPC, JITDylib &JD, bool EmitDebugInfo, bool EmitUnwindInfo)
PerfSupportPlugin(ExecutorProcessControl &EPC, ExecutorAddr RegisterPerfStartAddr, ExecutorAddr RegisterPerfEndAddr, ExecutorAddr RegisterPerfImplAddr, bool EmitDebugInfo, bool EmitUnwindInfo)
A symbol name together with the naming level (SymbolNameKind) it is expressed in, so that a Mangler /...
static constexpr SymbolNameSpec c(StringRef Name)
A utility class for serializing to a blob from a variadic list.
static Expected< WrapperFunctionCall > Create(ExecutorAddr FnAddr, const ArgTs &...Args)
Create a WrapperFunctionCall using the given SPS serializer to serialize the arguments.
@ DW_EH_PE_datarel
Definition Dwarf.h:979
@ DW_EH_PE_sdata4
Definition Dwarf.h:974
@ DW_EH_PE_sdata8
Definition Dwarf.h:975
@ DW_EH_PE_absptr
Definition Dwarf.h:966
@ DW_EH_PE_omit
Definition Dwarf.h:967
LLVM_ABI void lookupAndApply(unique_function< void(Error)> OnApplied, LookupKind K, const JITDylibSearchOrder &SearchOrder, ArrayRef< LookupPrepareFn > PrepareFns)
Resolve the symbols contributed by every prepare function with a single lookup, then let each of thei...
LookupPrepareFn recordAddr(SymbolNameSpec Name, ExecutorAddr *A, SymbolLookupFlags LF=SymbolLookupFlags::RequiredSymbol)
Records the address of the symbol with the given name.
LLVM_ABI Expected< std::pair< std::unique_ptr< DWARFContext >, StringMap< std::unique_ptr< MemoryBuffer > > > > createDWARFContext(jitlink::LinkGraph &G)
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
endianness
Definition bit.h:71
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
std::vector< PerfJITDebugInfoRecord > DebugInfoRecords
PerfJITCodeUnwindingInfoRecord UnwindingRecord
std::vector< PerfJITCodeLoadRecord > CodeLoadRecords
static constexpr SymbolNameSpec Name