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