diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h @@ -0,0 +1,62 @@ +//===--- DebugInfoSupport.h ---- Utils for debug info support ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Utilities to preserve and parse debug info from LinkGraphs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_DEBUGINFOSUPPORT_H +#define LLVM_EXECUTIONENGINE_ORC_DEBUGINFOSUPPORT_H + +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" + +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +namespace llvm { + +namespace orc { + +Error preserveDebugSections(jitlink::LinkGraph &G); +// The backing stringmap is also returned, for memory liftime management. +Expected, + StringMap>>> +createDWARFContext(jitlink::LinkGraph &G); + +// Thin wrapper around preserveDebugSections to be used as a standalone plugin. +class DebugInfoPreservationPlugin : public ObjectLinkingLayer::Plugin { +public: + void modifyPassConfig(MaterializationResponsibility &MR, + jitlink::LinkGraph &LG, + jitlink::PassConfiguration &PassConfig) override { + PassConfig.PrePrunePasses.push_back(preserveDebugSections); + } + + Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { + // Do nothing. + return Error::success(); + } + Error notifyFailed(MaterializationResponsibility &MR) override { + // Do nothing. + return Error::success(); + } + void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, + ResourceKey SrcKey) override { + // Do nothing. + } + + static Expected> Create() { + return std::make_unique(); + } +}; + +} // namespace orc + +} // namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/include/llvm/ExecutionEngine/Orc/PerfSupportPlugin.h b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h rename from llvm/include/llvm/ExecutionEngine/Orc/PerfSupportPlugin.h rename to llvm/include/llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h --- a/llvm/include/llvm/ExecutionEngine/Orc/PerfSupportPlugin.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h @@ -29,7 +29,8 @@ PerfSupportPlugin(ExecutorProcessControl &EPC, ExecutorAddr RegisterPerfStartAddr, ExecutorAddr RegisterPerfEndAddr, - ExecutorAddr RegisterPerfImplAddr, bool EmitUnwindInfo); + ExecutorAddr RegisterPerfImplAddr, bool EmitDebugInfo, + bool EmitUnwindInfo); ~PerfSupportPlugin(); void modifyPassConfig(MaterializationResponsibility &MR, @@ -48,7 +49,8 @@ ResourceKey SrcKey) override {} static Expected> - Create(ExecutorProcessControl &EPC, JITDylib &JD, bool EmitUnwindInfo); + Create(ExecutorProcessControl &EPC, JITDylib &JD, bool EmitDebugInfo, + bool EmitUnwindInfo); private: ExecutorProcessControl &EPC; @@ -56,6 +58,7 @@ ExecutorAddr RegisterPerfEndAddr; ExecutorAddr RegisterPerfImplAddr; std::atomic CodeIndex; + bool EmitDebugInfo; bool EmitUnwindInfo; }; diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -42,7 +42,6 @@ ObjectTransformLayer.cpp OrcABISupport.cpp OrcV2CBindings.cpp - PerfSupportPlugin.cpp RTDyldObjectLinkingLayer.cpp SimpleRemoteEPC.cpp Speculation.cpp @@ -78,6 +77,7 @@ TransformUtils ) +add_subdirectory(Debugging) add_subdirectory(Shared) add_subdirectory(TargetProcess) diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Debugging/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Debugging/CMakeLists.txt @@ -0,0 +1,22 @@ +if( CMAKE_HOST_UNIX AND HAVE_LIBRT ) + set(rt_lib rt) +endif() + +add_llvm_component_library(LLVMOrcDebugging + DebugInfoSupport.cpp + PerfSupportPlugin.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc/Debugging/ + + LINK_LIBS + ${LLVM_PTHREAD_LIB} + ${rt_lib} + + LINK_COMPONENTS + DebugInfoDWARF + OrcJIT + OrcShared + Support + TargetParser + ) diff --git a/llvm/lib/ExecutionEngine/Orc/Debugging/DebugInfoSupport.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/DebugInfoSupport.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/Debugging/DebugInfoSupport.cpp @@ -0,0 +1,120 @@ +//===--- DebugInfoSupport.cpp -- Utils for debug info support ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Utilities to preserve and parse debug info from LinkGraphs. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h" + +#include "llvm/Support/SmallVectorMemoryBuffer.h" + +#define DEBUG_TYPE "orc" + +using namespace llvm; +using namespace llvm::orc; +using namespace llvm::jitlink; + +namespace { +static DenseSet DWARFSectionNames = { +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ + StringRef(ELF_NAME), +#include "llvm/BinaryFormat/Dwarf.def" +#undef HANDLE_DWARF_SECTION +}; + +// We might be able to drop relocations to symbols that do end up +// being pruned by the linker, but for now we just preserve all +static void preserveDWARFSection(LinkGraph &G, Section &Sec) { + DenseMap Preserved; + for (auto Sym : Sec.symbols()) { + if (Sym->isLive()) + Preserved[&Sym->getBlock()] = Sym; + else if (!Preserved.count(&Sym->getBlock())) + Preserved[&Sym->getBlock()] = Sym; + } + for (auto Block : Sec.blocks()) { + auto &PSym = Preserved[Block]; + if (!PSym) + PSym = &G.addAnonymousSymbol(*Block, 0, 0, false, true); + else if (!PSym->isLive()) + PSym->setLive(true); + } +} + +static SmallVector getSectionData(Section &Sec) { + SmallVector SecData; + SmallVector SecBlocks(Sec.blocks().begin(), Sec.blocks().end()); + std::sort(SecBlocks.begin(), SecBlocks.end(), [](Block *LHS, Block *RHS) { + return LHS->getAddress() < RHS->getAddress(); + }); + // Convert back to what object file would have, one blob of section content + // Assumes all zerofill + // TODO handle alignment? + // TODO handle alignment offset? + for (auto *Block : SecBlocks) { + if (Block->isZeroFill()) + SecData.resize(SecData.size() + Block->getSize(), 0); + else + SecData.append(Block->getContent().begin(), Block->getContent().end()); + } + return SecData; +} + +static void dumpDWARFContext(DWARFContext &DC) { + auto options = llvm::DIDumpOptions(); + options.DumpType &= ~DIDT_UUID; + options.DumpType &= ~(1 << DIDT_ID_DebugFrame); + LLVM_DEBUG(DC.dump(dbgs(), options)); +} + +} // namespace + +Error llvm::orc::preserveDebugSections(LinkGraph &G) { + if (!G.getTargetTriple().isOSBinFormatELF()) { + return make_error( + "preserveDebugSections only supports ELF LinkGraphs!", + inconvertibleErrorCode()); + } + for (auto &Sec : G.sections()) { + if (DWARFSectionNames.count(Sec.getName())) { + LLVM_DEBUG(dbgs() << "Preserving DWARF section " << Sec.getName() + << "\n"); + preserveDWARFSection(G, Sec); + } + } + return Error::success(); +} + +Expected, + StringMap>>> +llvm::orc::createDWARFContext(LinkGraph &G) { + if (!G.getTargetTriple().isOSBinFormatELF()) { + return make_error( + "createDWARFContext only supports ELF LinkGraphs!", + inconvertibleErrorCode()); + } + StringMap> DWARFSectionData; + for (auto &Sec : G.sections()) { + if (DWARFSectionNames.count(Sec.getName())) { + auto SecData = getSectionData(Sec); + auto Name = Sec.getName(); + // DWARFContext expects the section name to not start with a dot + if (Name.startswith(".")) + Name = Name.drop_front(); + LLVM_DEBUG(dbgs() << "Creating DWARFContext section " << Name + << " with size " << SecData.size() << "\n"); + DWARFSectionData[Name] = + std::make_unique(std::move(SecData)); + } + } + auto Ctx = DWARFContext::create(DWARFSectionData, G.getPointerSize(), + G.getEndianness() == support::little); + dumpDWARFContext(*Ctx); + return std::make_pair(std::move(Ctx), std::move(DWARFSectionData)); +} diff --git a/llvm/lib/ExecutionEngine/Orc/PerfSupportPlugin.cpp b/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp rename from llvm/lib/ExecutionEngine/Orc/PerfSupportPlugin.cpp rename to llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp --- a/llvm/lib/ExecutionEngine/Orc/PerfSupportPlugin.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.cpp @@ -10,13 +10,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ExecutionEngine/Orc/PerfSupportPlugin.h" +#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h" -#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" - -#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/ExecutionEngine/JITLink/x86_64.h" +#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h" #include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h" +#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" #define DEBUG_TYPE "orc" @@ -114,11 +113,7 @@ } static std::optional -getDebugInfoRecord(const Symbol &Sym, DWARFContext *DC) { - if (!DC) { - LLVM_DEBUG(dbgs() << "No debug info available\n"); - return std::nullopt; - } +getDebugInfoRecord(const Symbol &Sym, DWARFContext &DC) { auto &Section = Sym.getBlock().getSection(); auto Addr = Sym.getAddress(); auto Size = Sym.getSize(); @@ -127,7 +122,7 @@ << " at address " << Addr.getValue() << " with size " << Size << "\n" << "Section ordinal: " << Section.getOrdinal() << "\n"); - auto LInfo = DC->getLineInfoForAddressRange( + auto LInfo = DC.getLineInfoForAddressRange( SAddr, Size, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); if (LInfo.empty()) { // No line info available @@ -218,16 +213,29 @@ } static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G, - DWARFContext *DC, std::atomic &CodeIndex, - bool EmitUnwindInfo) { + bool EmitDebugInfo, bool EmitUnwindInfo) { + std::unique_ptr DC; + StringMap> DCBacking; + if (EmitDebugInfo) { + auto EDC = createDWARFContext(G); + if (!EDC) { + ES.reportError(EDC.takeError()); + EmitDebugInfo = false; + } else { + DC = std::move(EDC->first); + DCBacking = std::move(EDC->second); + } + } PerfJITRecordBatch Batch; for (auto Sym : G.defined_symbols()) { if (!Sym->hasName() || !Sym->isCallable()) continue; - auto DebugInfo = getDebugInfoRecord(*Sym, DC); - if (DebugInfo) - Batch.DebugInfoRecords.push_back(std::move(*DebugInfo)); + if (EmitDebugInfo) { + auto DebugInfo = getDebugInfoRecord(*Sym, *DC); + if (DebugInfo) + Batch.DebugInfoRecords.push_back(std::move(*DebugInfo)); + } Batch.CodeLoadRecords.push_back(getCodeLoadRecord(*Sym, CodeIndex)); } if (EmitUnwindInfo) { @@ -248,11 +256,11 @@ ExecutorAddr RegisterPerfStartAddr, ExecutorAddr RegisterPerfEndAddr, ExecutorAddr RegisterPerfImplAddr, - bool EmitUnwindInfo) + bool EmitDebugInfo, bool EmitUnwindInfo) : EPC(EPC), RegisterPerfStartAddr(RegisterPerfStartAddr), RegisterPerfEndAddr(RegisterPerfEndAddr), RegisterPerfImplAddr(RegisterPerfImplAddr), CodeIndex(0), - EmitUnwindInfo(EmitUnwindInfo) { + EmitDebugInfo(EmitDebugInfo), EmitUnwindInfo(EmitUnwindInfo) { cantFail(EPC.callSPSWrapper(RegisterPerfStartAddr)); } PerfSupportPlugin::~PerfSupportPlugin() { @@ -263,10 +271,8 @@ LinkGraph &G, PassConfiguration &Config) { Config.PostFixupPasses.push_back([this](LinkGraph &G) { - // TODO get an actual DWARFContext for line info - DWARFContext *DWC = nullptr; - auto Batch = getRecords(EPC.getExecutionSession(), G, DWC, CodeIndex, - EmitUnwindInfo); + auto Batch = getRecords(EPC.getExecutionSession(), G, CodeIndex, + EmitDebugInfo, EmitUnwindInfo); G.allocActions().push_back( {cantFail(shared::WrapperFunctionCall::Create< shared::SPSArgList>( @@ -278,7 +284,7 @@ Expected> PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD, - bool EmitUnwindInfo) { + bool EmitDebugInfo, bool EmitUnwindInfo) { if (!EPC.getTargetTriple().isOSBinFormatELF()) { return make_error( "Perf support only available for ELF LinkGraphs!", @@ -293,5 +299,5 @@ {ES.intern(RegisterPerfImplSymbolName), &ImplAddr}})) return std::move(Err); return std::make_unique(EPC, StartAddr, EndAddr, ImplAddr, - EmitUnwindInfo); + EmitDebugInfo, EmitUnwindInfo); } diff --git a/llvm/tools/llvm-jitlink/CMakeLists.txt b/llvm/tools/llvm-jitlink/CMakeLists.txt --- a/llvm/tools/llvm-jitlink/CMakeLists.txt +++ b/llvm/tools/llvm-jitlink/CMakeLists.txt @@ -11,6 +11,7 @@ JITLink MC Object + OrcDebugging OrcJIT OrcShared OrcTargetProcess diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -19,6 +19,8 @@ #include "llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h" #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" #include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h" +#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h" +#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h" #include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h" #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" @@ -28,7 +30,6 @@ #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h" #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" -#include "llvm/ExecutionEngine/Orc/PerfSupportPlugin.h" #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.h" @@ -990,9 +991,11 @@ ObjLayer.addPlugin(ExitOnErr( GDBJITDebugInfoRegistrationPlugin::Create(this->ES, *MainJD, TT))); - if (PerfSupport && TT.isOSBinFormatELF()) + if (PerfSupport && TT.isOSBinFormatELF()) { + ObjLayer.addPlugin(ExitOnErr(DebugInfoPreservationPlugin::Create())); ObjLayer.addPlugin(ExitOnErr(PerfSupportPlugin::Create( - this->ES.getExecutorProcessControl(), *MainJD, true))); + this->ES.getExecutorProcessControl(), *MainJD, true, true))); + } // Set up the platform. if (TT.isOSBinFormatMachO() && !OrcRuntime.empty()) {