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