LLVM 22.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();
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.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(
126 if (LInfo.empty()) {
127 // No line info available
128 LLVM_DEBUG(dbgs() << "No line info available\n");
129 return std::nullopt;
130 }
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) {
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,
271 PassConfiguration &Config) {
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()) {
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}
static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info)
#define G(x, y, z)
Definition MD5.cpp:56
#define LLVM_DEBUG(...)
Definition Debug.h:114
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...
DILineInfoTable getLineInfoForAddressRange(object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Specifier=DILineInfoSpecifier()) override
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition ArrayRef.h:303
const RecordVal * getValue(const Init *Name) const
Definition Record.h:1784
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:133
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
An ExecutionSession represents a running JIT program.
Definition Core.h:1355
void reportError(Error Err)
Report a error for this execution session.
Definition Core.h:1490
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:902
Tracks responsibility for materialization, and mediates interactions between MaterializationUnits and...
Definition Core.h:576
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:874
@ DW_EH_PE_sdata4
Definition Dwarf.h:869
@ DW_EH_PE_sdata8
Definition Dwarf.h:870
@ DW_EH_PE_absptr
Definition Dwarf.h:861
@ DW_EH_PE_omit
Definition Dwarf.h:862
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:178
LLVM_ABI 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.
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:98
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
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
std::vector< PerfJITDebugInfoRecord > DebugInfoRecords
PerfJITCodeUnwindingInfoRecord UnwindingRecord
std::vector< PerfJITCodeLoadRecord > CodeLoadRecords