LLVM 20.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
18
19#define DEBUG_TYPE "orc"
20
21using namespace llvm;
22using namespace llvm::orc;
23using namespace llvm::jitlink;
24
25namespace {
26
27// Creates an EH frame header prepared for a 32-bit relative relocation
28// to the start of the .eh_frame section. Absolute injects a 64-bit absolute
29// address space offset 4 bytes from the start instead of 4 bytes
30Expected<std::string> createX64EHFrameHeader(Section &EHFrame,
32 bool absolute) {
33 uint8_t Version = 1;
34 uint8_t EhFramePtrEnc = 0;
35 if (absolute) {
37 } else {
39 }
40 uint8_t FDECountEnc = dwarf::DW_EH_PE_omit;
42 // X86_64_64 relocation to the start of the .eh_frame section
43 uint32_t EHFrameRelocation = 0;
44 // uint32_t FDECount = 0;
45 // Skip the FDE binary search table
46 // We'd have to reprocess the CIEs to get this information,
47 // which seems like more trouble than it's worth
48 // TODO consider implementing this.
49 // binary search table goes here
50
51 size_t HeaderSize =
52 (sizeof(Version) + sizeof(EhFramePtrEnc) + sizeof(FDECountEnc) +
53 sizeof(TableEnc) +
54 (absolute ? sizeof(uint64_t) : sizeof(EHFrameRelocation)));
55 std::string HeaderContent(HeaderSize, '\0');
56 BinaryStreamWriter Writer(
58 reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),
60 if (auto Err = Writer.writeInteger(Version))
61 return std::move(Err);
62 if (auto Err = Writer.writeInteger(EhFramePtrEnc))
63 return std::move(Err);
64 if (auto Err = Writer.writeInteger(FDECountEnc))
65 return std::move(Err);
66 if (auto Err = Writer.writeInteger(TableEnc))
67 return std::move(Err);
68 if (absolute) {
69 uint64_t EHFrameAddr = SectionRange(EHFrame).getStart().getValue();
70 if (auto Err = Writer.writeInteger(EHFrameAddr))
71 return std::move(Err);
72 } else {
73 if (auto Err = Writer.writeInteger(EHFrameRelocation))
74 return std::move(Err);
75 }
76 return HeaderContent;
77}
78
79constexpr StringRef RegisterPerfStartSymbolName =
80 "llvm_orc_registerJITLoaderPerfStart";
81constexpr StringRef RegisterPerfEndSymbolName =
82 "llvm_orc_registerJITLoaderPerfEnd";
83constexpr StringRef RegisterPerfImplSymbolName =
84 "llvm_orc_registerJITLoaderPerfImpl";
85
87getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {
89 auto Name = *Sym.getName();
90 auto Addr = Sym.getAddress();
91 auto Size = Sym.getSize();
92 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_LOAD;
93 // Runtime sets PID
94 Record.Pid = 0;
95 // Runtime sets TID
96 Record.Tid = 0;
97 Record.Vma = Addr.getValue();
98 Record.CodeAddr = Addr.getValue();
99 Record.CodeSize = Size;
100 Record.CodeIndex = CodeIndex++;
101 Record.Name = Name.str();
102 // Initialize last, once all the other fields are filled
103 Record.Prefix.TotalSize =
104 (2 * sizeof(uint32_t) // id, total_size
105 + sizeof(uint64_t) // timestamp
106 + 2 * sizeof(uint32_t) // pid, tid
107 + 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index
108 + Name.size() + 1 // symbol name
109 + Record.CodeSize // code
110 );
111 return Record;
112}
113
114static std::optional<PerfJITDebugInfoRecord>
115getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) {
116 auto &Section = Sym.getBlock().getSection();
117 auto Addr = Sym.getAddress();
118 auto Size = Sym.getSize();
119 auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()};
120 LLVM_DEBUG(dbgs() << "Getting debug info for symbol " << Sym.getName()
121 << " at address " << Addr.getValue() << " with size "
122 << Size << "\n"
123 << "Section ordinal: " << Section.getOrdinal() << "\n");
124 auto LInfo = DC.getLineInfoForAddressRange(
125 SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);
126 if (LInfo.empty()) {
127 // No line info available
128 LLVM_DEBUG(dbgs() << "No line info available\n");
129 return std::nullopt;
130 }
132 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_DEBUG_INFO;
133 Record.CodeAddr = Addr.getValue();
134 for (const auto &Entry : LInfo) {
135 auto Addr = Entry.first;
136 // The function re-created by perf is preceded by a elf
137 // header. Need to adjust for that, otherwise the results are
138 // wrong.
139 Addr += 0x40;
140 Record.Entries.push_back({Addr, Entry.second.Line,
141 Entry.second.Discriminator,
142 Entry.second.FileName});
143 }
144 size_t EntriesBytes = (2 // record header
145 + 2 // record fields
146 ) *
147 sizeof(uint64_t);
148 for (const auto &Entry : Record.Entries) {
149 EntriesBytes +=
150 sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim
151 EntriesBytes += Entry.Name.size() + 1; // Name
152 }
153 Record.Prefix.TotalSize = EntriesBytes;
154 LLVM_DEBUG(dbgs() << "Created debug info record\n"
155 << "Total size: " << Record.Prefix.TotalSize << "\n"
156 << "Nr entries: " << Record.Entries.size() << "\n");
157 return Record;
158}
159
161getUnwindingRecord(LinkGraph &G) {
163 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_UNWINDING_INFO;
164 Record.Prefix.TotalSize = 0;
165 auto Eh_frame = G.findSectionByName(".eh_frame");
166 if (!Eh_frame) {
167 LLVM_DEBUG(dbgs() << "No .eh_frame section found\n");
168 return Record;
169 }
170 if (!G.getTargetTriple().isOSBinFormatELF()) {
171 LLVM_DEBUG(dbgs() << "Not an ELF file, will not emit unwinding info\n");
172 return Record;
173 }
174 auto SR = SectionRange(*Eh_frame);
175 auto EHFrameSize = SR.getSize();
176 auto Eh_frame_hdr = G.findSectionByName(".eh_frame_hdr");
177 if (!Eh_frame_hdr) {
178 if (G.getTargetTriple().getArch() == Triple::x86_64) {
179 auto Hdr = createX64EHFrameHeader(*Eh_frame, G.getEndianness(), true);
180 if (!Hdr)
181 return Hdr.takeError();
182 Record.EHFrameHdr = std::move(*Hdr);
183 } else {
184 LLVM_DEBUG(dbgs() << "No .eh_frame_hdr section found\n");
185 return Record;
186 }
187 Record.EHFrameHdrAddr = 0;
188 Record.EHFrameHdrSize = Record.EHFrameHdr.size();
189 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
190 Record.MappedSize = 0; // Because the EHFrame header was not mapped
191 } else {
192 auto SR = SectionRange(*Eh_frame_hdr);
193 Record.EHFrameHdrAddr = SR.getStart().getValue();
194 Record.EHFrameHdrSize = SR.getSize();
195 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;
196 Record.MappedSize = Record.UnwindDataSize;
197 }
198 Record.EHFrameAddr = SR.getStart().getValue();
199 Record.Prefix.TotalSize =
200 (2 * sizeof(uint32_t) // id, total_size
201 + sizeof(uint64_t) // timestamp
202 +
203 3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size
204 + Record.UnwindDataSize // eh_frame_hdr, eh_frame
205 );
206 LLVM_DEBUG(dbgs() << "Created unwind record\n"
207 << "Total size: " << Record.Prefix.TotalSize << "\n"
208 << "Unwind size: " << Record.UnwindDataSize << "\n"
209 << "EHFrame size: " << EHFrameSize << "\n"
210 << "EHFrameHdr size: " << Record.EHFrameHdrSize << "\n");
211 return Record;
212}
213
214static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,
215 std::atomic<uint64_t> &CodeIndex,
216 bool EmitDebugInfo, bool EmitUnwindInfo) {
217 std::unique_ptr<DWARFContext> DC;
219 if (EmitDebugInfo) {
220 auto EDC = createDWARFContext(G);
221 if (!EDC) {
222 ES.reportError(EDC.takeError());
223 EmitDebugInfo = false;
224 } else {
225 DC = std::move(EDC->first);
226 DCBacking = std::move(EDC->second);
227 }
228 }
229 PerfJITRecordBatch Batch;
230 for (auto Sym : G.defined_symbols()) {
231 if (!Sym->hasName() || !Sym->isCallable())
232 continue;
233 if (EmitDebugInfo) {
234 auto DebugInfo = getDebugInfoRecord(*Sym, *DC);
235 if (DebugInfo)
236 Batch.DebugInfoRecords.push_back(std::move(*DebugInfo));
237 }
238 Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex));
239 }
240 if (EmitUnwindInfo) {
241 auto UWR = getUnwindingRecord(G);
242 if (!UWR) {
243 ES.reportError(UWR.takeError());
244 } else {
245 Batch.UnwindingRecord = std::move(*UWR);
246 }
247 } else {
249 }
250 return Batch;
251}
252} // namespace
253
255 ExecutorAddr RegisterPerfStartAddr,
256 ExecutorAddr RegisterPerfEndAddr,
257 ExecutorAddr RegisterPerfImplAddr,
258 bool EmitDebugInfo, bool EmitUnwindInfo)
259 : EPC(EPC), RegisterPerfStartAddr(RegisterPerfStartAddr),
260 RegisterPerfEndAddr(RegisterPerfEndAddr),
261 RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0),
262 EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) {
263 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfStartAddr));
264}
266 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr));
267}
268
270 LinkGraph &G,
272 Config.PostFixupPasses.push_back([this](LinkGraph &G) {
273 auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex,
274 EmitDebugInfo, EmitUnwindInfo);
275 G.allocActions().push_back(
278 RegisterPerfImplAddr, Batch)),
279 {}});
280 return Error::success();
281 });
282}
283
286 bool EmitDebugInfo, bool EmitUnwindInfo) {
287 if (!EPC.getTargetTriple().isOSBinFormatELF()) {
288 return make_error<StringError>(
289 "Perf support only available for ELF LinkGraphs!",
291 }
292 auto &ES = EPC.getExecutionSession();
293 ExecutorAddr StartAddr, EndAddr, ImplAddr;
294 if (auto Err = lookupAndRecordAddrs(
296 {{ES.intern(RegisterPerfStartSymbolName), &StartAddr},
297 {ES.intern(RegisterPerfEndSymbolName), &EndAddr},
298 {ES.intern(RegisterPerfImplSymbolName), &ImplAddr}}))
299 return std::move(Err);
300 return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr,
301 EmitDebugInfo, EmitUnwindInfo);
302}
#define LLVM_DEBUG(...)
Definition: Debug.h:106
uint64_t Addr
std::string Name
uint64_t Size
RelaxConfig Config
Definition: ELF_riscv.cpp:506
Symbol * Sym
Definition: ELF_riscv.cpp:479
static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info)
Definition: MCWin64EH.cpp:166
#define G(x, y, z)
Definition: MD5.cpp:56
Provides write only access to a subclass of WritableBinaryStream.
Error writeInteger(T Value)
Write the integer Value to the underlying stream in the specified endianness.
DWARFContext This data structure is the top level entity that deals with dwarf debug information pars...
Definition: DWARFContext.h:48
DILineInfoTable getLineInfoForAddressRange(object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Specifier=DILineInfoSpecifier()) override
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition: ArrayRef.h:310
const RecordVal * getValue(const Init *Name) const
Definition: Record.h:1789
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
bool isOSBinFormatELF() const
Tests whether the OS uses the ELF binary format.
Definition: Triple.h:730
An ExecutionSession represents a running JIT program.
Definition: Core.h:1339
void reportError(Error Err)
Report a error for this execution session.
Definition: Core.h:1474
Represents an address in the executor process.
uint64_t getValue() const
ExecutorProcessControl supports interaction with a JIT target process.
const Triple & getTargetTriple() const
Return the Triple for the target process.
Error callSPSWrapper(ExecutorAddr WrapperFnAddr, WrapperCallArgTs &&...WrapperCallArgs)
Run a wrapper function using SPS to serialize the arguments and deserialize the results.
ExecutionSession & getExecutionSession()
Return the ExecutionSession associated with this instance.
Represents a JIT'd dynamic library.
Definition: Core.h:897
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:571
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 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:860
@ DW_EH_PE_sdata4
Definition: Dwarf.h:855
@ DW_EH_PE_sdata8
Definition: Dwarf.h:856
@ DW_EH_PE_absptr
Definition: Dwarf.h:847
@ DW_EH_PE_omit
Definition: Dwarf.h:848
JITDylibSearchOrder makeJITDylibSearchOrder(ArrayRef< JITDylib * > JDs, JITDylibLookupFlags Flags=JITDylibLookupFlags::MatchExportedSymbolsOnly)
Convenience function for creating a search order from an ArrayRef of JITDylib*, all with the same fla...
Definition: Core.h:177
void lookupAndRecordAddrs(unique_function< void(Error)> OnRecorded, ExecutionSession &ES, LookupKind K, const JITDylibSearchOrder &SearchOrder, std::vector< std::pair< SymbolStringPtr, ExecutorAddr * > > Pairs, SymbolLookupFlags LookupFlags=SymbolLookupFlags::RequiredSymbol)
Record addresses of the given symbols in the given ExecutorAddrs.
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.
Definition: AddressRanges.h:18
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:756
endianness
Definition: bit.h:70
std::vector< PerfJITDebugInfoRecord > DebugInfoRecords
PerfJITCodeUnwindingInfoRecord UnwindingRecord
std::vector< PerfJITCodeLoadRecord > CodeLoadRecords