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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Handles support for registering code with perf 10 // 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 21 using namespace llvm; 22 using namespace llvm::orc; 23 using namespace llvm::jitlink; 24 25 namespace { 26 27 // Creates an EH frame header prepared for a 32-bit relative relocation 28 // to the start of the .eh_frame section. Absolute injects a 64-bit absolute 29 // address space offset 4 bytes from the start instead of 4 bytes 30 Expected<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 section 43 uint32_t EHFrameRelocation = 0; 44 // uint32_t FDECount = 0; 45 // Skip the FDE binary search table 46 // We'd have to reprocess the CIEs to get this information, 47 // which seems like more trouble than it's worth 48 // TODO consider implementing this. 49 // binary search table goes here 50 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 79 constexpr StringRef RegisterPerfStartSymbolName = 80 "llvm_orc_registerJITLoaderPerfStart"; 81 constexpr StringRef RegisterPerfEndSymbolName = 82 "llvm_orc_registerJITLoaderPerfEnd"; 83 constexpr StringRef RegisterPerfImplSymbolName = 84 "llvm_orc_registerJITLoaderPerfImpl"; 85 86 static PerfJITCodeLoadRecord 87 getCodeLoadRecord(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 PID 94 Record.Pid = 0; 95 // Runtime sets TID 96 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 filled 103 Record.Prefix.TotalSize = 104 (2 * sizeof(uint32_t) // id, total_size 105 + sizeof(uint64_t) // timestamp 106 + 2 * sizeof(uint32_t) // pid, tid 107 + 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index 108 + Name.size() + 1 // symbol name 109 + Record.CodeSize // code 110 ); 111 return Record; 112 } 113 114 static std::optional<PerfJITDebugInfoRecord> 115 getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) { 116 auto &Section = Sym.getBlock().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 available 128 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 elf 137 // header. Need to adjust for that, otherwise the results are 138 // 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 header 145 + 2 // record fields 146 ) * 147 sizeof(uint64_t); 148 for (const auto &Entry : Record.Entries) { 149 EntriesBytes += 150 sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim 151 EntriesBytes += Entry.Name.size() + 1; // Name 152 } 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 160 static Expected<PerfJITCodeUnwindingInfoRecord> 161 getUnwindingRecord(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 mapped 191 } 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_size 201 + sizeof(uint64_t) // timestamp 202 + 203 3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size 204 + Record.UnwindDataSize // eh_frame_hdr, eh_frame 205 ); 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 214 static 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 } // namespace 253 254 PerfSupportPlugin::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 } 265 PerfSupportPlugin::~PerfSupportPlugin() { 266 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr)); 267 } 268 269 void 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 284 Expected<std::unique_ptr<PerfSupportPlugin>> 285 PerfSupportPlugin::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