Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp
35294 views
//===----- PerfSupportPlugin.cpp --- Utils for perf support -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Handles support for registering code with perf9//10//===----------------------------------------------------------------------===//1112#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h"1314#include "llvm/ExecutionEngine/JITLink/x86_64.h"15#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h"16#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"17#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"1819#define DEBUG_TYPE "orc"2021using namespace llvm;22using namespace llvm::orc;23using namespace llvm::jitlink;2425namespace {2627// 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,31llvm::endianness endianness,32bool absolute) {33uint8_t Version = 1;34uint8_t EhFramePtrEnc = 0;35if (absolute) {36EhFramePtrEnc |= dwarf::DW_EH_PE_sdata8 | dwarf::DW_EH_PE_absptr;37} else {38EhFramePtrEnc |= dwarf::DW_EH_PE_sdata4 | dwarf::DW_EH_PE_datarel;39}40uint8_t FDECountEnc = dwarf::DW_EH_PE_omit;41uint8_t TableEnc = dwarf::DW_EH_PE_omit;42// X86_64_64 relocation to the start of the .eh_frame section43uint32_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 here5051size_t HeaderSize =52(sizeof(Version) + sizeof(EhFramePtrEnc) + sizeof(FDECountEnc) +53sizeof(TableEnc) +54(absolute ? sizeof(uint64_t) : sizeof(EHFrameRelocation)));55std::string HeaderContent(HeaderSize, '\0');56BinaryStreamWriter Writer(57MutableArrayRef<uint8_t>(58reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize),59endianness);60if (auto Err = Writer.writeInteger(Version))61return std::move(Err);62if (auto Err = Writer.writeInteger(EhFramePtrEnc))63return std::move(Err);64if (auto Err = Writer.writeInteger(FDECountEnc))65return std::move(Err);66if (auto Err = Writer.writeInteger(TableEnc))67return std::move(Err);68if (absolute) {69uint64_t EHFrameAddr = SectionRange(EHFrame).getStart().getValue();70if (auto Err = Writer.writeInteger(EHFrameAddr))71return std::move(Err);72} else {73if (auto Err = Writer.writeInteger(EHFrameRelocation))74return std::move(Err);75}76return HeaderContent;77}7879constexpr StringRef RegisterPerfStartSymbolName =80"llvm_orc_registerJITLoaderPerfStart";81constexpr StringRef RegisterPerfEndSymbolName =82"llvm_orc_registerJITLoaderPerfEnd";83constexpr StringRef RegisterPerfImplSymbolName =84"llvm_orc_registerJITLoaderPerfImpl";8586static PerfJITCodeLoadRecord87getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {88PerfJITCodeLoadRecord Record;89auto Name = Sym.getName();90auto Addr = Sym.getAddress();91auto Size = Sym.getSize();92Record.Prefix.Id = PerfJITRecordType::JIT_CODE_LOAD;93// Runtime sets PID94Record.Pid = 0;95// Runtime sets TID96Record.Tid = 0;97Record.Vma = Addr.getValue();98Record.CodeAddr = Addr.getValue();99Record.CodeSize = Size;100Record.CodeIndex = CodeIndex++;101Record.Name = Name.str();102// Initialize last, once all the other fields are filled103Record.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);111return Record;112}113114static std::optional<PerfJITDebugInfoRecord>115getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) {116auto &Section = Sym.getBlock().getSection();117auto Addr = Sym.getAddress();118auto Size = Sym.getSize();119auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()};120LLVM_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");124auto LInfo = DC.getLineInfoForAddressRange(125SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);126if (LInfo.empty()) {127// No line info available128LLVM_DEBUG(dbgs() << "No line info available\n");129return std::nullopt;130}131PerfJITDebugInfoRecord Record;132Record.Prefix.Id = PerfJITRecordType::JIT_CODE_DEBUG_INFO;133Record.CodeAddr = Addr.getValue();134for (const auto &Entry : LInfo) {135auto 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.139Addr += 0x40;140Record.Entries.push_back({Addr, Entry.second.Line,141Entry.second.Discriminator,142Entry.second.FileName});143}144size_t EntriesBytes = (2 // record header145+ 2 // record fields146) *147sizeof(uint64_t);148for (const auto &Entry : Record.Entries) {149EntriesBytes +=150sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim151EntriesBytes += Entry.Name.size() + 1; // Name152}153Record.Prefix.TotalSize = EntriesBytes;154LLVM_DEBUG(dbgs() << "Created debug info record\n"155<< "Total size: " << Record.Prefix.TotalSize << "\n"156<< "Nr entries: " << Record.Entries.size() << "\n");157return Record;158}159160static Expected<PerfJITCodeUnwindingInfoRecord>161getUnwindingRecord(LinkGraph &G) {162PerfJITCodeUnwindingInfoRecord Record;163Record.Prefix.Id = PerfJITRecordType::JIT_CODE_UNWINDING_INFO;164Record.Prefix.TotalSize = 0;165auto Eh_frame = G.findSectionByName(".eh_frame");166if (!Eh_frame) {167LLVM_DEBUG(dbgs() << "No .eh_frame section found\n");168return Record;169}170if (!G.getTargetTriple().isOSBinFormatELF()) {171LLVM_DEBUG(dbgs() << "Not an ELF file, will not emit unwinding info\n");172return Record;173}174auto SR = SectionRange(*Eh_frame);175auto EHFrameSize = SR.getSize();176auto Eh_frame_hdr = G.findSectionByName(".eh_frame_hdr");177if (!Eh_frame_hdr) {178if (G.getTargetTriple().getArch() == Triple::x86_64) {179auto Hdr = createX64EHFrameHeader(*Eh_frame, G.getEndianness(), true);180if (!Hdr)181return Hdr.takeError();182Record.EHFrameHdr = std::move(*Hdr);183} else {184LLVM_DEBUG(dbgs() << "No .eh_frame_hdr section found\n");185return Record;186}187Record.EHFrameHdrAddr = 0;188Record.EHFrameHdrSize = Record.EHFrameHdr.size();189Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;190Record.MappedSize = 0; // Because the EHFrame header was not mapped191} else {192auto SR = SectionRange(*Eh_frame_hdr);193Record.EHFrameHdrAddr = SR.getStart().getValue();194Record.EHFrameHdrSize = SR.getSize();195Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize;196Record.MappedSize = Record.UnwindDataSize;197}198Record.EHFrameAddr = SR.getStart().getValue();199Record.Prefix.TotalSize =200(2 * sizeof(uint32_t) // id, total_size201+ sizeof(uint64_t) // timestamp202+2033 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size204+ Record.UnwindDataSize // eh_frame_hdr, eh_frame205);206LLVM_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");211return Record;212}213214static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,215std::atomic<uint64_t> &CodeIndex,216bool EmitDebugInfo, bool EmitUnwindInfo) {217std::unique_ptr<DWARFContext> DC;218StringMap<std::unique_ptr<MemoryBuffer>> DCBacking;219if (EmitDebugInfo) {220auto EDC = createDWARFContext(G);221if (!EDC) {222ES.reportError(EDC.takeError());223EmitDebugInfo = false;224} else {225DC = std::move(EDC->first);226DCBacking = std::move(EDC->second);227}228}229PerfJITRecordBatch Batch;230for (auto Sym : G.defined_symbols()) {231if (!Sym->hasName() || !Sym->isCallable())232continue;233if (EmitDebugInfo) {234auto DebugInfo = getDebugInfoRecord(*Sym, *DC);235if (DebugInfo)236Batch.DebugInfoRecords.push_back(std::move(*DebugInfo));237}238Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex));239}240if (EmitUnwindInfo) {241auto UWR = getUnwindingRecord(G);242if (!UWR) {243ES.reportError(UWR.takeError());244} else {245Batch.UnwindingRecord = std::move(*UWR);246}247} else {248Batch.UnwindingRecord.Prefix.TotalSize = 0;249}250return Batch;251}252} // namespace253254PerfSupportPlugin::PerfSupportPlugin(ExecutorProcessControl &EPC,255ExecutorAddr RegisterPerfStartAddr,256ExecutorAddr RegisterPerfEndAddr,257ExecutorAddr RegisterPerfImplAddr,258bool EmitDebugInfo, bool EmitUnwindInfo)259: EPC(EPC), RegisterPerfStartAddr(RegisterPerfStartAddr),260RegisterPerfEndAddr(RegisterPerfEndAddr),261RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0),262EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) {263cantFail(EPC.callSPSWrapper<void()>(RegisterPerfStartAddr));264}265PerfSupportPlugin::~PerfSupportPlugin() {266cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr));267}268269void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR,270LinkGraph &G,271PassConfiguration &Config) {272Config.PostFixupPasses.push_back([this](LinkGraph &G) {273auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex,274EmitDebugInfo, EmitUnwindInfo);275G.allocActions().push_back(276{cantFail(shared::WrapperFunctionCall::Create<277shared::SPSArgList<shared::SPSPerfJITRecordBatch>>(278RegisterPerfImplAddr, Batch)),279{}});280return Error::success();281});282}283284Expected<std::unique_ptr<PerfSupportPlugin>>285PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD,286bool EmitDebugInfo, bool EmitUnwindInfo) {287if (!EPC.getTargetTriple().isOSBinFormatELF()) {288return make_error<StringError>(289"Perf support only available for ELF LinkGraphs!",290inconvertibleErrorCode());291}292auto &ES = EPC.getExecutionSession();293ExecutorAddr StartAddr, EndAddr, ImplAddr;294if (auto Err = lookupAndRecordAddrs(295ES, LookupKind::Static, makeJITDylibSearchOrder({&JD}),296{{ES.intern(RegisterPerfStartSymbolName), &StartAddr},297{ES.intern(RegisterPerfEndSymbolName), &EndAddr},298{ES.intern(RegisterPerfImplSymbolName), &ImplAddr}}))299return std::move(Err);300return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr,301EmitDebugInfo, EmitUnwindInfo);302}303304305