[Orc] Add AutoRegisterCode option for DebugObjectManagerPlugin

Configure the plugin to automatically call the debugger rendezvous breakpoint `__jit_debug_register_code()` for every translation unit (enabled) or never at all (disabled). Default API and behavior remain unchanged.

If AutoRegisterCode is turned off, it's the client's own responsibility to call the rendezvous breakpoint function at an appropriate time.
Depending on the complexity of the debugger's rendezvous breakpoint implementation, this can provide significant performance improvements in cases where many debug objects are added in sequence.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D147310
This commit is contained in:
Stefan Gränitz
2023-04-03 11:41:37 +02:00
parent e20331cec0
commit 4c7f53b99c
9 changed files with 43 additions and 28 deletions

View File

@@ -37,8 +37,8 @@ Error addDebugSupport(ObjectLayer &ObjLayer) {
return createStringError(inconvertibleErrorCode(),
"No debug support for given object layer type");
ObjLinkingLayer->addPlugin(
std::make_unique<DebugObjectManagerPlugin>(ES, std::move(*Registrar)));
ObjLinkingLayer->addPlugin(std::make_unique<DebugObjectManagerPlugin>(
ES, std::move(*Registrar), true, true));
return Error::success();
}

View File

@@ -60,9 +60,15 @@ public:
/// names. Note that this may cause significant memory and transport
/// overhead for objects built with a release configuration.
///
/// AutoRegisterCode:
/// Notify the debugger for each new debug object. This is a good default
/// mode, but it may cause significant overhead when adding many modules in
/// sequence. When turning this off, the user has to issue the call to
/// __jit_debug_register_code() on the executor side manually.
///
DebugObjectManagerPlugin(ExecutionSession &ES,
std::unique_ptr<DebugObjectRegistrar> Target,
bool RequireDebugSections);
bool RequireDebugSections, bool AutoRegisterCode);
~DebugObjectManagerPlugin();
void notifyMaterializing(MaterializationResponsibility &MR,
@@ -92,6 +98,7 @@ private:
std::unique_ptr<DebugObjectRegistrar> Target;
bool RequireDebugSections;
bool AutoRegisterCode;
};
} // namespace orc

View File

@@ -31,7 +31,8 @@ class ExecutionSession;
/// Abstract interface for registering debug objects in the executor process.
class DebugObjectRegistrar {
public:
virtual Error registerDebugObject(ExecutorAddrRange TargetMem) = 0;
virtual Error registerDebugObject(ExecutorAddrRange TargetMem,
bool AutoRegisterCode) = 0;
virtual ~DebugObjectRegistrar() = default;
};
@@ -42,7 +43,8 @@ public:
EPCDebugObjectRegistrar(ExecutionSession &ES, ExecutorAddr RegisterFn)
: ES(ES), RegisterFn(RegisterFn) {}
Error registerDebugObject(ExecutorAddrRange TargetMem) override;
Error registerDebugObject(ExecutorAddrRange TargetMem,
bool AutoRegisterCode) override;
private:
ExecutionSession &ES;

View File

@@ -389,13 +389,14 @@ createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target,
bool RequireDebugSections)
bool RequireDebugSections, bool AutoRegisterCode)
: ES(ES), Target(std::move(Target)),
RequireDebugSections(RequireDebugSections) {}
RequireDebugSections(RequireDebugSections),
AutoRegisterCode(AutoRegisterCode) {}
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
: DebugObjectManagerPlugin(ES, std::move(Target), true) {}
: DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}
DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
@@ -464,7 +465,8 @@ Error DebugObjectManagerPlugin::notifyEmitted(
FinalizePromise.set_value(TargetMem.takeError());
return;
}
if (Error Err = Target->registerDebugObject(*TargetMem)) {
if (Error Err =
Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {
FinalizePromise.set_value(std::move(Err));
return;
}

View File

@@ -348,11 +348,12 @@ public:
Writer.write(SecCmd);
}
static constexpr bool AutoRegisterCode = true;
SectionRange R(MachOContainerBlock->getSection());
G.allocActions().push_back(
{cantFail(shared::WrapperFunctionCall::Create<
shared::SPSArgList<shared::SPSExecutorAddrRange>>(
RegisterActionAddr, R.getRange())),
shared::SPSArgList<shared::SPSExecutorAddrRange, bool>>(
RegisterActionAddr, R.getRange(), AutoRegisterCode)),
{}});
return Error::success();
}

View File

@@ -48,10 +48,10 @@ Expected<std::unique_ptr<EPCDebugObjectRegistrar>> createJITLoaderGDBRegistrar(
return std::make_unique<EPCDebugObjectRegistrar>(ES, (*Result)[0][0]);
}
Error EPCDebugObjectRegistrar::registerDebugObject(
ExecutorAddrRange TargetMem) {
return ES.callSPSWrapper<void(shared::SPSExecutorAddrRange)>(RegisterFn,
TargetMem);
Error EPCDebugObjectRegistrar::registerDebugObject(ExecutorAddrRange TargetMem,
bool AutoRegisterCode) {
return ES.callSPSWrapper<void(shared::SPSExecutorAddrRange, bool)>(
RegisterFn, TargetMem, AutoRegisterCode);
}
} // namespace orc

View File

@@ -67,9 +67,9 @@ using namespace llvm;
using namespace llvm::orc;
// Register debug object, return error message or null for success.
static void registerJITLoaderGDBImpl(const char *ObjAddr, size_t Size) {
static void appendJITDebugDescriptor(const char *ObjAddr, size_t Size) {
LLVM_DEBUG({
dbgs() << "Registering debug object with GDB JIT interface "
dbgs() << "Adding debug object to GDB JIT interface "
<< formatv("([{0:x16} -- {1:x16}])",
reinterpret_cast<uintptr_t>(ObjAddr),
reinterpret_cast<uintptr_t>(ObjAddr + Size))
@@ -94,20 +94,20 @@ static void registerJITLoaderGDBImpl(const char *ObjAddr, size_t Size) {
__jit_debug_descriptor.first_entry = E;
__jit_debug_descriptor.relevant_entry = E;
// Run into the rendezvous breakpoint.
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
__jit_debug_register_code();
}
extern "C" orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size) {
using namespace orc::shared;
return WrapperFunction<SPSError(SPSExecutorAddrRange)>::handle(
return WrapperFunction<SPSError(SPSExecutorAddrRange, bool)>::handle(
Data, Size,
[](ExecutorAddrRange R) {
registerJITLoaderGDBImpl(R.Start.toPtr<const char *>(),
[](ExecutorAddrRange R, bool AutoRegisterCode) {
appendJITDebugDescriptor(R.Start.toPtr<const char *>(),
R.size());
// Run into the rendezvous breakpoint.
if (AutoRegisterCode)
__jit_debug_register_code();
return Error::success();
})
.release();
@@ -116,11 +116,14 @@ llvm_orc_registerJITLoaderGDBAllocAction(const char *Data, size_t Size) {
extern "C" orc::shared::CWrapperFunctionResult
llvm_orc_registerJITLoaderGDBWrapper(const char *Data, uint64_t Size) {
using namespace orc::shared;
return WrapperFunction<SPSError(SPSExecutorAddrRange)>::handle(
return WrapperFunction<SPSError(SPSExecutorAddrRange, bool)>::handle(
Data, Size,
[](ExecutorAddrRange R) {
registerJITLoaderGDBImpl(R.Start.toPtr<const char *>(),
[](ExecutorAddrRange R, bool AutoRegisterCode) {
appendJITDebugDescriptor(R.Start.toPtr<const char *>(),
R.size());
// Run into the rendezvous breakpoint.
if (AutoRegisterCode)
__jit_debug_register_code();
return Error::success();
})
.release();

View File

@@ -938,7 +938,7 @@ int runOrcJIT(const char *ProgName) {
L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>(
ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES))));
L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>(
ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES))));
ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES)), true, true));
}
return L;
});

View File

@@ -1027,7 +1027,7 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES))));
if (DebuggerSupport)
ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES))));
ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES)), true, true));
}
ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));