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 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 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> 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> 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 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 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 } 266*5f757f3fSDimitry Andric PerfSupportPlugin::~PerfSupportPlugin() { 267*5f757f3fSDimitry Andric cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr)); 268*5f757f3fSDimitry Andric } 269*5f757f3fSDimitry Andric 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>> 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