brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.6 KiB · bc5b3bb Raw
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