303 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Handles support for registering code with perf10//11//===----------------------------------------------------------------------===//12 13#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h"14 15#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h"16#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"17#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"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 relocation28// to the start of the .eh_frame section. Absolute injects a 64-bit absolute29// address space offset 4 bytes from the start instead of 4 bytes30Expected<std::string> createX64EHFrameHeader(Section &EHFrame,31 llvm::endianness endianness,32 bool absolute) {33 uint8_t Version = 1;34 uint8_t EhFramePtrEnc = 0;35 if (absolute) {36 EhFramePtrEnc |= dwarf::DW_EH_PE_sdata8 | dwarf::DW_EH_PE_absptr;37 } else {38 EhFramePtrEnc |= dwarf::DW_EH_PE_sdata4 | dwarf::DW_EH_PE_datarel;39 }40 uint8_t FDECountEnc = dwarf::DW_EH_PE_omit;41 uint8_t TableEnc = dwarf::DW_EH_PE_omit;42 // X86_64_64 relocation to the start of the .eh_frame section43 uint32_t EHFrameRelocation = 0;44 // uint32_t FDECount = 0;45 // Skip the FDE binary search table46 // We'd have to reprocess the CIEs to get this information,47 // which seems like more trouble than it's worth48 // TODO consider implementing this.49 // binary search table goes here50 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(57 MutableArrayRef<uint8_t>(58 reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),59 endianness);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 86static PerfJITCodeLoadRecord87getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {88 PerfJITCodeLoadRecord Record;89 auto Name = *Sym.getName();90 auto Addr = Sym.getAddress();91 auto Size = Sym.getSize();92 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_LOAD;93 // Runtime sets PID94 Record.Pid = 0;95 // Runtime sets TID96 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 filled103 Record.Prefix.TotalSize =104 (2 * sizeof(uint32_t) // id, total_size105 + sizeof(uint64_t) // timestamp106 + 2 * sizeof(uint32_t) // pid, tid107 + 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index108 + Name.size() + 1 // symbol name109 + Record.CodeSize // code110 );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(125 SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);126 if (LInfo.empty()) {127 // No line info available128 LLVM_DEBUG(dbgs() << "No line info available\n");129 return std::nullopt;130 }131 PerfJITDebugInfoRecord Record;132 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_DEBUG_INFO;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 elf137 // header. Need to adjust for that, otherwise the results are138 // 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 header145 + 2 // record fields146 ) *147 sizeof(uint64_t);148 for (const auto &Entry : Record.Entries) {149 EntriesBytes +=150 sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim151 EntriesBytes += Entry.Name.size() + 1; // Name152 }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 160static Expected<PerfJITCodeUnwindingInfoRecord>161getUnwindingRecord(LinkGraph &G) {162 PerfJITCodeUnwindingInfoRecord Record;163 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_UNWINDING_INFO;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 mapped191 } 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_size201 + sizeof(uint64_t) // timestamp202 +203 3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size204 + Record.UnwindDataSize // eh_frame_hdr, eh_frame205 );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;218 StringMap<std::unique_ptr<MemoryBuffer>> DCBacking;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 {248 Batch.UnwindingRecord.Prefix.TotalSize = 0;249 }250 return Batch;251}252} // namespace253 254PerfSupportPlugin::PerfSupportPlugin(ExecutorProcessControl &EPC,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}265PerfSupportPlugin::~PerfSupportPlugin() {266 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr));267}268 269void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR,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(276 {cantFail(shared::WrapperFunctionCall::Create<277 shared::SPSArgList<shared::SPSPerfJITRecordBatch>>(278 RegisterPerfImplAddr, Batch)),279 {}});280 return Error::success();281 });282}283 284Expected<std::unique_ptr<PerfSupportPlugin>>285PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD,286 bool EmitDebugInfo, bool EmitUnwindInfo) {287 if (!EPC.getTargetTriple().isOSBinFormatELF()) {288 return make_error<StringError>(289 "Perf support only available for ELF LinkGraphs!",290 inconvertibleErrorCode());291 }292 auto &ES = EPC.getExecutionSession();293 ExecutorAddr StartAddr, EndAddr, ImplAddr;294 if (auto Err = lookupAndRecordAddrs(295 ES, LookupKind::Static, makeJITDylibSearchOrder({&JD}),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}303