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
19
20#define DEBUG_TYPE "orc"
21
22using namespace llvm;
23using namespace llvm::orc;
24using namespace llvm::jitlink;
25
26namespace {
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
31Expected<std::string> createX64EHFrameHeader(Section &EHFrame,
33 bool absolute) {
34 uint8_t Version = 1;
35 uint8_t EhFramePtrEnc = 0;
36 if (absolute) {
38 } else {
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(
59 reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),
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
80constexpr StringRef RegisterPerfStartSymbolName =
81 "llvm_orc_registerJITLoaderPerfStart";
82constexpr StringRef RegisterPerfEndSymbolName =
83 "llvm_orc_registerJITLoaderPerfEnd";
84constexpr StringRef RegisterPerfImplSymbolName =
85 "llvm_orc_registerJITLoaderPerfImpl";
86
88getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {
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
115static std::optional<PerfJITDebugInfoRecord>
116getDebugInfoRecord(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 }
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
162getUnwindingRecord(LinkGraph &G) {
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
215static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,
216 std::atomic<uint64_t> &CodeIndex,
217 bool EmitDebugInfo, bool EmitUnwindInfo) {
218 std::unique_ptr<DWARFContext> DC;
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 {
250 }
251 return Batch;
252}
253} // namespace
254
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}
267 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr));
268}
269
271 LinkGraph &G,
273 Config.PostFixupPasses.push_back([this](LinkGraph &G) {
274 auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex,
275 EmitDebugInfo, EmitUnwindInfo);
276 G.allocActions().push_back(
279 RegisterPerfImplAddr, Batch)),
280 {}});
281 return Error::success();
282 });
283}
284
287 bool EmitDebugInfo, bool EmitUnwindInfo) {
288 if (!EPC.getTargetTriple().isOSBinFormatELF()) {
289 return make_error<StringError>(
290 "Perf support only available for ELF LinkGraphs!",
292 }
293 auto &ES = EPC.getExecutionSession();
294 ExecutorAddr StartAddr, EndAddr, ImplAddr;
295 if (auto Err = lookupAndRecordAddrs(
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}
#define LLVM_DEBUG(X)
Definition: Debug.h:101
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:307
const RecordVal * getValue(const Init *Name) const
Definition: Record.h:1774
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:50
bool isOSBinFormatELF() const
Tests whether the OS uses the ELF binary format.
Definition: Triple.h:719
An ExecutionSession represents a running JIT program.
Definition: Core.h:1431
void reportError(Error Err)
Report a error for this execution session.
Definition: Core.h:1566
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:989
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition: Core.h:555
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:853
@ DW_EH_PE_sdata4
Definition: Dwarf.h:848
@ DW_EH_PE_sdata8
Definition: Dwarf.h:849
@ DW_EH_PE_absptr
Definition: Dwarf.h:840
@ DW_EH_PE_omit
Definition: Dwarf.h:841
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:166
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