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/JITLink/x86_64.h" 16 #include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h" 17 #include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h" 18 #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" 19 20 #define DEBUG_TYPE "orc" 21 22 using namespace llvm; 23 using namespace llvm::orc; 24 using namespace llvm::jitlink; 25 26 namespace { 27 28 // Creates an EH frame header prepared for a 32-bit relative relocation 29 // to the start of the .eh_frame section. Absolute injects a 64-bit absolute 30 // address space offset 4 bytes from the start instead of 4 bytes 31 Expected<std::string> createX64EHFrameHeader(Section &EHFrame, 32 llvm::endianness endianness, 33 bool absolute) { 34 uint8_t Version = 1; 35 uint8_t EhFramePtrEnc = 0; 36 if (absolute) { 37 EhFramePtrEnc |= dwarf::DW_EH_PE_sdata8 | dwarf::DW_EH_PE_absptr; 38 } else { 39 EhFramePtrEnc |= dwarf::DW_EH_PE_sdata4 | dwarf::DW_EH_PE_datarel; 40 } 41 uint8_t FDECountEnc = dwarf::DW_EH_PE_omit; 42 uint8_t TableEnc = dwarf::DW_EH_PE_omit; 43 // X86_64_64 relocation to the start of the .eh_frame section 44 uint32_t EHFrameRelocation = 0; 45 // uint32_t FDECount = 0; 46 // Skip the FDE binary search table 47 // We'd have to reprocess the CIEs to get this information, 48 // which seems like more trouble than it's worth 49 // TODO consider implementing this. 50 // binary search table goes here 51 52 size_t HeaderSize = 53 (sizeof(Version) + sizeof(EhFramePtrEnc) + sizeof(FDECountEnc) + 54 sizeof(TableEnc) + 55 (absolute ? sizeof(uint64_t) : sizeof(EHFrameRelocation))); 56 std::string HeaderContent(HeaderSize, '\0'); 57 BinaryStreamWriter Writer( 58 MutableArrayRef<uint8_t>( 59 reinterpret_cast<uint8_t *>(HeaderContent.data()), HeaderSize), 60 endianness); 61 if (auto Err = Writer.writeInteger(Version)) 62 return std::move(Err); 63 if (auto Err = Writer.writeInteger(EhFramePtrEnc)) 64 return std::move(Err); 65 if (auto Err = Writer.writeInteger(FDECountEnc)) 66 return std::move(Err); 67 if (auto Err = Writer.writeInteger(TableEnc)) 68 return std::move(Err); 69 if (absolute) { 70 uint64_t EHFrameAddr = SectionRange(EHFrame).getStart().getValue(); 71 if (auto Err = Writer.writeInteger(EHFrameAddr)) 72 return std::move(Err); 73 } else { 74 if (auto Err = Writer.writeInteger(EHFrameRelocation)) 75 return std::move(Err); 76 } 77 return HeaderContent; 78 } 79 80 constexpr StringRef RegisterPerfStartSymbolName = 81 "llvm_orc_registerJITLoaderPerfStart"; 82 constexpr StringRef RegisterPerfEndSymbolName = 83 "llvm_orc_registerJITLoaderPerfEnd"; 84 constexpr StringRef RegisterPerfImplSymbolName = 85 "llvm_orc_registerJITLoaderPerfImpl"; 86 87 static PerfJITCodeLoadRecord 88 getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) { 89 PerfJITCodeLoadRecord Record; 90 auto Name = Sym.getName(); 91 auto Addr = Sym.getAddress(); 92 auto Size = Sym.getSize(); 93 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_LOAD; 94 // Runtime sets PID 95 Record.Pid = 0; 96 // Runtime sets TID 97 Record.Tid = 0; 98 Record.Vma = Addr.getValue(); 99 Record.CodeAddr = Addr.getValue(); 100 Record.CodeSize = Size; 101 Record.CodeIndex = CodeIndex++; 102 Record.Name = Name.str(); 103 // Initialize last, once all the other fields are filled 104 Record.Prefix.TotalSize = 105 (2 * sizeof(uint32_t) // id, total_size 106 + sizeof(uint64_t) // timestamp 107 + 2 * sizeof(uint32_t) // pid, tid 108 + 4 * sizeof(uint64_t) // vma, code_addr, code_size, code_index 109 + Name.size() + 1 // symbol name 110 + Record.CodeSize // code 111 ); 112 return Record; 113 } 114 115 static std::optional<PerfJITDebugInfoRecord> 116 getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) { 117 auto &Section = Sym.getBlock().getSection(); 118 auto Addr = Sym.getAddress(); 119 auto Size = Sym.getSize(); 120 auto SAddr = object::SectionedAddress{Addr.getValue(), Section.getOrdinal()}; 121 LLVM_DEBUG(dbgs() << "Getting debug info for symbol " << Sym.getName() 122 << " at address " << Addr.getValue() << " with size " 123 << Size << "\n" 124 << "Section ordinal: " << Section.getOrdinal() << "\n"); 125 auto LInfo = DC.getLineInfoForAddressRange( 126 SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); 127 if (LInfo.empty()) { 128 // No line info available 129 LLVM_DEBUG(dbgs() << "No line info available\n"); 130 return std::nullopt; 131 } 132 PerfJITDebugInfoRecord Record; 133 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_DEBUG_INFO; 134 Record.CodeAddr = Addr.getValue(); 135 for (const auto &Entry : LInfo) { 136 auto Addr = Entry.first; 137 // The function re-created by perf is preceded by a elf 138 // header. Need to adjust for that, otherwise the results are 139 // wrong. 140 Addr += 0x40; 141 Record.Entries.push_back({Addr, Entry.second.Line, 142 Entry.second.Discriminator, 143 Entry.second.FileName}); 144 } 145 size_t EntriesBytes = (2 // record header 146 + 2 // record fields 147 ) * 148 sizeof(uint64_t); 149 for (const auto &Entry : Record.Entries) { 150 EntriesBytes += 151 sizeof(uint64_t) + 2 * sizeof(uint32_t); // Addr, Line/Discrim 152 EntriesBytes += Entry.Name.size() + 1; // Name 153 } 154 Record.Prefix.TotalSize = EntriesBytes; 155 LLVM_DEBUG(dbgs() << "Created debug info record\n" 156 << "Total size: " << Record.Prefix.TotalSize << "\n" 157 << "Nr entries: " << Record.Entries.size() << "\n"); 158 return Record; 159 } 160 161 static Expected<PerfJITCodeUnwindingInfoRecord> 162 getUnwindingRecord(LinkGraph &G) { 163 PerfJITCodeUnwindingInfoRecord Record; 164 Record.Prefix.Id = PerfJITRecordType::JIT_CODE_UNWINDING_INFO; 165 Record.Prefix.TotalSize = 0; 166 auto Eh_frame = G.findSectionByName(".eh_frame"); 167 if (!Eh_frame) { 168 LLVM_DEBUG(dbgs() << "No .eh_frame section found\n"); 169 return Record; 170 } 171 if (!G.getTargetTriple().isOSBinFormatELF()) { 172 LLVM_DEBUG(dbgs() << "Not an ELF file, will not emit unwinding info\n"); 173 return Record; 174 } 175 auto SR = SectionRange(*Eh_frame); 176 auto EHFrameSize = SR.getSize(); 177 auto Eh_frame_hdr = G.findSectionByName(".eh_frame_hdr"); 178 if (!Eh_frame_hdr) { 179 if (G.getTargetTriple().getArch() == Triple::x86_64) { 180 auto Hdr = createX64EHFrameHeader(*Eh_frame, G.getEndianness(), true); 181 if (!Hdr) 182 return Hdr.takeError(); 183 Record.EHFrameHdr = std::move(*Hdr); 184 } else { 185 LLVM_DEBUG(dbgs() << "No .eh_frame_hdr section found\n"); 186 return Record; 187 } 188 Record.EHFrameHdrAddr = 0; 189 Record.EHFrameHdrSize = Record.EHFrameHdr.size(); 190 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize; 191 Record.MappedSize = 0; // Because the EHFrame header was not mapped 192 } else { 193 auto SR = SectionRange(*Eh_frame_hdr); 194 Record.EHFrameHdrAddr = SR.getStart().getValue(); 195 Record.EHFrameHdrSize = SR.getSize(); 196 Record.UnwindDataSize = EHFrameSize + Record.EHFrameHdrSize; 197 Record.MappedSize = Record.UnwindDataSize; 198 } 199 Record.EHFrameAddr = SR.getStart().getValue(); 200 Record.Prefix.TotalSize = 201 (2 * sizeof(uint32_t) // id, total_size 202 + sizeof(uint64_t) // timestamp 203 + 204 3 * sizeof(uint64_t) // unwind_data_size, eh_frame_hdr_size, mapped_size 205 + Record.UnwindDataSize // eh_frame_hdr, eh_frame 206 ); 207 LLVM_DEBUG(dbgs() << "Created unwind record\n" 208 << "Total size: " << Record.Prefix.TotalSize << "\n" 209 << "Unwind size: " << Record.UnwindDataSize << "\n" 210 << "EHFrame size: " << EHFrameSize << "\n" 211 << "EHFrameHdr size: " << Record.EHFrameHdrSize << "\n"); 212 return Record; 213 } 214 215 static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G, 216 std::atomic<uint64_t> &CodeIndex, 217 bool EmitDebugInfo, bool EmitUnwindInfo) { 218 std::unique_ptr<DWARFContext> DC; 219 StringMap<std::unique_ptr<MemoryBuffer>> DCBacking; 220 if (EmitDebugInfo) { 221 auto EDC = createDWARFContext(G); 222 if (!EDC) { 223 ES.reportError(EDC.takeError()); 224 EmitDebugInfo = false; 225 } else { 226 DC = std::move(EDC->first); 227 DCBacking = std::move(EDC->second); 228 } 229 } 230 PerfJITRecordBatch Batch; 231 for (auto Sym : G.defined_symbols()) { 232 if (!Sym->hasName() || !Sym->isCallable()) 233 continue; 234 if (EmitDebugInfo) { 235 auto DebugInfo = getDebugInfoRecord(*Sym, *DC); 236 if (DebugInfo) 237 Batch.DebugInfoRecords.push_back(std::move(*DebugInfo)); 238 } 239 Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex)); 240 } 241 if (EmitUnwindInfo) { 242 auto UWR = getUnwindingRecord(G); 243 if (!UWR) { 244 ES.reportError(UWR.takeError()); 245 } else { 246 Batch.UnwindingRecord = std::move(*UWR); 247 } 248 } else { 249 Batch.UnwindingRecord.Prefix.TotalSize = 0; 250 } 251 return Batch; 252 } 253 } // namespace 254 255 PerfSupportPlugin::PerfSupportPlugin(ExecutorProcessControl &EPC, 256 ExecutorAddr RegisterPerfStartAddr, 257 ExecutorAddr RegisterPerfEndAddr, 258 ExecutorAddr RegisterPerfImplAddr, 259 bool EmitDebugInfo, bool EmitUnwindInfo) 260 : EPC(EPC), RegisterPerfStartAddr(RegisterPerfStartAddr), 261 RegisterPerfEndAddr(RegisterPerfEndAddr), 262 RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0), 263 EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) { 264 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfStartAddr)); 265 } 266 PerfSupportPlugin::~PerfSupportPlugin() { 267 cantFail(EPC.callSPSWrapper<void()>(RegisterPerfEndAddr)); 268 } 269 270 void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR, 271 LinkGraph &G, 272 PassConfiguration &Config) { 273 Config.PostFixupPasses.push_back([this](LinkGraph &G) { 274 auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex, 275 EmitDebugInfo, EmitUnwindInfo); 276 G.allocActions().push_back( 277 {cantFail(shared::WrapperFunctionCall::Create< 278 shared::SPSArgList<shared::SPSPerfJITRecordBatch>>( 279 RegisterPerfImplAddr, Batch)), 280 {}}); 281 return Error::success(); 282 }); 283 } 284 285 Expected<std::unique_ptr<PerfSupportPlugin>> 286 PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD, 287 bool EmitDebugInfo, bool EmitUnwindInfo) { 288 if (!EPC.getTargetTriple().isOSBinFormatELF()) { 289 return make_error<StringError>( 290 "Perf support only available for ELF LinkGraphs!", 291 inconvertibleErrorCode()); 292 } 293 auto &ES = EPC.getExecutionSession(); 294 ExecutorAddr StartAddr, EndAddr, ImplAddr; 295 if (auto Err = lookupAndRecordAddrs( 296 ES, LookupKind::Static, makeJITDylibSearchOrder({&JD}), 297 {{ES.intern(RegisterPerfStartSymbolName), &StartAddr}, 298 {ES.intern(RegisterPerfEndSymbolName), &EndAddr}, 299 {ES.intern(RegisterPerfImplSymbolName), &ImplAddr}})) 300 return std::move(Err); 301 return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr, 302 EmitDebugInfo, EmitUnwindInfo); 303 } 304