[ORC] Add DWARFContext generation from LinkGraphs, use in perf support.

This patch adds line numbers to perf jitdump records emitted by the
PerfSupportPlugin, by parsing and using a DWARFContext from preserved debug
sections.

To avoid making the OrcJIT library depend on DebugInfoDWARF this patch
introduces a new OrcDebugging library.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D146391
This commit is contained in:
Prem Chintalapudi
2023-09-28 14:40:43 -07:00
committed by Lang Hames
parent 2f3b7d33f4
commit 7ddf7d8783
8 changed files with 246 additions and 29 deletions

View File

@@ -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<std::pair<std::unique_ptr<DWARFContext>,
StringMap<std::unique_ptr<MemoryBuffer>>>>
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<std::unique_ptr<DebugInfoPreservationPlugin>> Create() {
return std::make_unique<DebugInfoPreservationPlugin>();
}
};
} // namespace orc
} // namespace llvm
#endif

View File

@@ -29,7 +29,8 @@ public:
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 @@ public:
ResourceKey SrcKey) override {}
static Expected<std::unique_ptr<PerfSupportPlugin>>
Create(ExecutorProcessControl &EPC, JITDylib &JD, bool EmitUnwindInfo);
Create(ExecutorProcessControl &EPC, JITDylib &JD, bool EmitDebugInfo,
bool EmitUnwindInfo);
private:
ExecutorProcessControl &EPC;
@@ -56,6 +58,7 @@ private:
ExecutorAddr RegisterPerfEndAddr;
ExecutorAddr RegisterPerfImplAddr;
std::atomic<uint64_t> CodeIndex;
bool EmitDebugInfo;
bool EmitUnwindInfo;
};

View File

@@ -42,7 +42,6 @@ add_llvm_component_library(LLVMOrcJIT
ObjectTransformLayer.cpp
OrcABISupport.cpp
OrcV2CBindings.cpp
PerfSupportPlugin.cpp
RTDyldObjectLinkingLayer.cpp
SimpleRemoteEPC.cpp
Speculation.cpp
@@ -78,6 +77,7 @@ add_llvm_component_library(LLVMOrcJIT
TransformUtils
)
add_subdirectory(Debugging)
add_subdirectory(Shared)
add_subdirectory(TargetProcess)

View File

@@ -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
)

View File

@@ -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<StringRef> 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<Block *, Symbol *> 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<char, 0> getSectionData(Section &Sec) {
SmallVector<char, 0> SecData;
SmallVector<Block *, 8> 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<StringError>(
"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<std::pair<std::unique_ptr<DWARFContext>,
StringMap<std::unique_ptr<MemoryBuffer>>>>
llvm::orc::createDWARFContext(LinkGraph &G) {
if (!G.getTargetTriple().isOSBinFormatELF()) {
return make_error<StringError>(
"createDWARFContext only supports ELF LinkGraphs!",
inconvertibleErrorCode());
}
StringMap<std::unique_ptr<MemoryBuffer>> 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<SmallVectorMemoryBuffer>(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));
}

View File

@@ -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 @@ getCodeLoadRecord(const Symbol &Sym, std::atomic<uint64_t> &CodeIndex) {
}
static std::optional<PerfJITDebugInfoRecord>
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 @@ getDebugInfoRecord(const Symbol &Sym, DWARFContext *DC) {
<< " 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 @@ getUnwindingRecord(LinkGraph &G) {
}
static PerfJITRecordBatch getRecords(ExecutionSession &ES, LinkGraph &G,
DWARFContext *DC,
std::atomic<uint64_t> &CodeIndex,
bool EmitUnwindInfo) {
bool EmitDebugInfo, bool EmitUnwindInfo) {
std::unique_ptr<DWARFContext> DC;
StringMap<std::unique_ptr<MemoryBuffer>> 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 @@ PerfSupportPlugin::PerfSupportPlugin(ExecutorProcessControl &EPC,
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<void()>(RegisterPerfStartAddr));
}
PerfSupportPlugin::~PerfSupportPlugin() {
@@ -263,10 +271,8 @@ void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR,
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<shared::SPSPerfJITRecordBatch>>(
@@ -278,7 +284,7 @@ void PerfSupportPlugin::modifyPassConfig(MaterializationResponsibility &MR,
Expected<std::unique_ptr<PerfSupportPlugin>>
PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD,
bool EmitUnwindInfo) {
bool EmitDebugInfo, bool EmitUnwindInfo) {
if (!EPC.getTargetTriple().isOSBinFormatELF()) {
return make_error<StringError>(
"Perf support only available for ELF LinkGraphs!",
@@ -293,5 +299,5 @@ PerfSupportPlugin::Create(ExecutorProcessControl &EPC, JITDylib &JD,
{ES.intern(RegisterPerfImplSymbolName), &ImplAddr}}))
return std::move(Err);
return std::make_unique<PerfSupportPlugin>(EPC, StartAddr, EndAddr, ImplAddr,
EmitUnwindInfo);
EmitDebugInfo, EmitUnwindInfo);
}

View File

@@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS
JITLink
MC
Object
OrcDebugging
OrcJIT
OrcShared
OrcTargetProcess

View File

@@ -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 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
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()) {