Skip to content

Commit 4268e67

Browse files
[ORC] Update ELF debugger support plugin to use AllocActions (#167866)
The `DebugObjectManagerPlugin` implements debugger support for ELF platforms with the GDB JIT Interface. It emits a separate debug object allocation in addition to the LinkGraph's own allocation. This used to happen in the plugin's `notifyEmitted()` callback, i.e. after the LinkGraph's allocation was finalized. In the meantime, it had to block finalization of the corresponding materialization unit to make sure that the debugger can register the object before the code runs. This patch switches the plugin to use an allocation action instead. We can remove the `notifyEmitted()` hook and implement all steps as JITLink passes.
1 parent 95aa70c commit 4268e67

File tree

8 files changed

+108
-79
lines changed

8 files changed

+108
-79
lines changed

llvm/include/llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
1717
#include "llvm/ExecutionEngine/Orc/Core.h"
18-
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
1918
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
19+
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
2020
#include "llvm/Support/Compiler.h"
2121
#include "llvm/Support/Error.h"
2222
#include "llvm/Support/Memory.h"
@@ -48,10 +48,6 @@ class DebugObject;
4848
///
4949
class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
5050
public:
51-
// DEPRECATED - Please specify options explicitly
52-
DebugObjectManagerPlugin(ExecutionSession &ES,
53-
std::unique_ptr<DebugObjectRegistrar> Target);
54-
5551
/// Create the plugin to submit DebugObjects for JITLink artifacts. For all
5652
/// options the recommended setting is true.
5753
///
@@ -67,16 +63,14 @@ class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
6763
/// sequence. When turning this off, the user has to issue the call to
6864
/// __jit_debug_register_code() on the executor side manually.
6965
///
70-
DebugObjectManagerPlugin(ExecutionSession &ES,
71-
std::unique_ptr<DebugObjectRegistrar> Target,
72-
bool RequireDebugSections, bool AutoRegisterCode);
66+
DebugObjectManagerPlugin(ExecutionSession &ES, bool RequireDebugSections,
67+
bool AutoRegisterCode, Error &Err);
7368
~DebugObjectManagerPlugin() override;
7469

7570
void notifyMaterializing(MaterializationResponsibility &MR,
7671
jitlink::LinkGraph &G, jitlink::JITLinkContext &Ctx,
7772
MemoryBufferRef InputObject) override;
7873

79-
Error notifyEmitted(MaterializationResponsibility &MR) override;
8074
Error notifyFailed(MaterializationResponsibility &MR) override;
8175
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override;
8276

@@ -97,7 +91,7 @@ class LLVM_ABI DebugObjectManagerPlugin : public ObjectLinkingLayer::Plugin {
9791
std::mutex PendingObjsLock;
9892
std::mutex RegisteredObjsLock;
9993

100-
std::unique_ptr<DebugObjectRegistrar> Target;
94+
ExecutorAddr RegistrationAction;
10195
bool RequireDebugSections;
10296
bool AutoRegisterCode;
10397
};

llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ LLVM_ABI extern const char *MemoryReadStringsWrapperName;
5959
LLVM_ABI extern const char *RegisterEHFrameSectionAllocActionName;
6060
LLVM_ABI extern const char *DeregisterEHFrameSectionAllocActionName;
6161

62+
LLVM_ABI extern const char *RegisterJITLoaderGDBAllocActionName;
63+
6264
LLVM_ABI extern const char *RunAsMainWrapperName;
6365
LLVM_ABI extern const char *RunAsVoidFunctionWrapperName;
6466
LLVM_ABI extern const char *RunAsIntFunctionWrapperName;

llvm/lib/ExecutionEngine/Orc/DebugObjectManagerPlugin.cpp

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/BinaryFormat/ELF.h"
2020
#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
2121
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
22+
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
2223
#include "llvm/Object/ELFObjectFile.h"
2324
#include "llvm/Support/Errc.h"
2425
#include "llvm/Support/MSVCErrorWorkarounds.h"
@@ -115,7 +116,9 @@ class DebugObject {
115116
public:
116117
DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
117118
ExecutionSession &ES)
118-
: MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {}
119+
: MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {
120+
FinalizeFuture = FinalizePromise.get_future();
121+
}
119122

120123
bool hasFlags(DebugObjectFlags F) const { return Flags & F; }
121124
void setFlags(DebugObjectFlags F) {
@@ -126,8 +129,17 @@ class DebugObject {
126129
}
127130

128131
using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;
132+
void finalizeAsync(FinalizeContinuation OnAsync);
133+
134+
void failMaterialization(Error Err) {
135+
FinalizePromise.set_value(std::move(Err));
136+
}
129137

130-
void finalizeAsync(FinalizeContinuation OnFinalize);
138+
void reportTargetMem(ExecutorAddrRange TargetMem) {
139+
FinalizePromise.set_value(TargetMem);
140+
}
141+
142+
Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }
131143

132144
virtual ~DebugObject() {
133145
if (Alloc) {
@@ -151,6 +163,9 @@ class DebugObject {
151163
const JITLinkDylib *JD = nullptr;
152164
ExecutionSession &ES;
153165

166+
std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;
167+
std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;
168+
154169
private:
155170
DebugObjectFlags Flags;
156171
FinalizedAlloc Alloc;
@@ -160,22 +175,27 @@ class DebugObject {
160175
// copying memory over to the target and pass on the result once we're done.
161176
// Ownership of the allocation remains with us for the rest of our lifetime.
162177
void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {
163-
assert(!Alloc && "Cannot finalize more than once");
164-
178+
assert(!this->Alloc && "Cannot finalize more than once");
165179
if (auto SimpleSegAlloc = finalizeWorkingMemory()) {
166180
auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);
167181
ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());
168182
SimpleSegAlloc->finalize(
169183
[this, DebugObjRange,
170184
OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {
171185
if (FA) {
172-
Alloc = std::move(*FA);
186+
// Note: FA->getAddress() is supposed to be the address of the
187+
// memory range on the target, but InProcessMemoryManager returns
188+
// the address of a FinalizedAllocInfo helper instead.
189+
this->Alloc = std::move(*FA);
173190
OnFinalize(DebugObjRange);
174191
} else
175192
OnFinalize(FA.takeError());
176193
});
177-
} else
194+
} else {
195+
// We could report this error synchronously, but it's easier this way,
196+
// because the FinalizePromise will be triggered unconditionally.
178197
OnFinalize(SimpleSegAlloc.takeError());
198+
}
179199
}
180200

181201
/// The current implementation of ELFDebugObject replicates the approach used in
@@ -386,16 +406,17 @@ createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,
386406
}
387407
}
388408

389-
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
390-
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target,
391-
bool RequireDebugSections, bool AutoRegisterCode)
392-
: ES(ES), Target(std::move(Target)),
393-
RequireDebugSections(RequireDebugSections),
394-
AutoRegisterCode(AutoRegisterCode) {}
395-
396-
DebugObjectManagerPlugin::DebugObjectManagerPlugin(
397-
ExecutionSession &ES, std::unique_ptr<DebugObjectRegistrar> Target)
398-
: DebugObjectManagerPlugin(ES, std::move(Target), true, true) {}
409+
DebugObjectManagerPlugin::DebugObjectManagerPlugin(ExecutionSession &ES,
410+
bool RequireDebugSections,
411+
bool AutoRegisterCode,
412+
Error &Err)
413+
: ES(ES), RequireDebugSections(RequireDebugSections),
414+
AutoRegisterCode(AutoRegisterCode) {
415+
// Pass bootstrap symbol for registration function to enable debugging
416+
ErrorAsOutParameter _(&Err);
417+
Err = ES.getExecutorProcessControl().getBootstrapSymbols(
418+
{{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});
419+
}
399420

400421
DebugObjectManagerPlugin::~DebugObjectManagerPlugin() = default;
401422

@@ -440,48 +461,50 @@ void DebugObjectManagerPlugin::modifyPassConfig(
440461
SectionRange(GraphSection));
441462
return Error::success();
442463
});
443-
}
444-
}
445464

446-
Error DebugObjectManagerPlugin::notifyEmitted(
447-
MaterializationResponsibility &MR) {
448-
std::lock_guard<std::mutex> Lock(PendingObjsLock);
449-
auto It = PendingObjs.find(&MR);
450-
if (It == PendingObjs.end())
451-
return Error::success();
452-
453-
// During finalization the debug object is registered with the target.
454-
// Materialization must wait for this process to finish. Otherwise we might
455-
// start running code before the debugger processed the corresponding debug
456-
// info.
457-
std::promise<MSVCPError> FinalizePromise;
458-
std::future<MSVCPError> FinalizeErr = FinalizePromise.get_future();
459-
460-
It->second->finalizeAsync(
461-
[this, &FinalizePromise, &MR](Expected<ExecutorAddrRange> TargetMem) {
462-
// Any failure here will fail materialization.
463-
if (!TargetMem) {
464-
FinalizePromise.set_value(TargetMem.takeError());
465-
return;
466-
}
467-
if (Error Err =
468-
Target->registerDebugObject(*TargetMem, AutoRegisterCode)) {
469-
FinalizePromise.set_value(std::move(Err));
470-
return;
471-
}
472-
473-
// Once our tracking info is updated, notifyEmitted() can return and
474-
// finish materialization.
475-
FinalizePromise.set_value(MR.withResourceKeyDo([&](ResourceKey K) {
476-
assert(PendingObjs.count(&MR) && "We still hold PendingObjsLock");
477-
std::lock_guard<std::mutex> Lock(RegisteredObjsLock);
478-
auto It = PendingObjs.find(&MR);
479-
RegisteredObjs[K].push_back(std::move(It->second));
480-
PendingObjs.erase(It);
481-
}));
482-
});
483-
484-
return FinalizeErr.get();
465+
PassConfig.PreFixupPasses.push_back(
466+
[this, &DebugObj, &MR](LinkGraph &G) -> Error {
467+
DebugObj.finalizeAsync([this, &DebugObj,
468+
&MR](Expected<ExecutorAddrRange> TargetMem) {
469+
if (!TargetMem) {
470+
DebugObj.failMaterialization(TargetMem.takeError());
471+
return;
472+
}
473+
// Update tracking info
474+
Error Err = MR.withResourceKeyDo([&](ResourceKey K) {
475+
std::lock_guard<std::mutex> LockPending(PendingObjsLock);
476+
std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);
477+
auto It = PendingObjs.find(&MR);
478+
RegisteredObjs[K].push_back(std::move(It->second));
479+
PendingObjs.erase(It);
480+
});
481+
482+
if (Err)
483+
DebugObj.failMaterialization(std::move(Err));
484+
485+
// Unblock post-fixup pass
486+
DebugObj.reportTargetMem(*TargetMem);
487+
});
488+
return Error::success();
489+
});
490+
491+
PassConfig.PostFixupPasses.push_back(
492+
[this, &DebugObj](LinkGraph &G) -> Error {
493+
Expected<ExecutorAddrRange> R = DebugObj.awaitTargetMem();
494+
if (!R)
495+
return R.takeError();
496+
if (R->empty())
497+
return Error::success();
498+
499+
using namespace shared;
500+
G.allocActions().push_back(
501+
{cantFail(WrapperFunctionCall::Create<
502+
SPSArgList<SPSExecutorAddrRange, bool>>(
503+
RegistrationAction, *R, AutoRegisterCode)),
504+
{/* no deregistration */}});
505+
return Error::success();
506+
});
507+
}
485508
}
486509

487510
Error DebugObjectManagerPlugin::notifyFailed(

llvm/lib/ExecutionEngine/Orc/Debugging/DebuggerSupport.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,10 @@ Error enableDebuggerSupport(LLJIT &J) {
3535

3636
switch (TT.getObjectFormat()) {
3737
case Triple::ELF: {
38-
auto Registrar = createJITLoaderGDBRegistrar(ES);
39-
if (!Registrar)
40-
return Registrar.takeError();
38+
Error TargetSymErr = Error::success();
4139
ObjLinkingLayer->addPlugin(std::make_unique<DebugObjectManagerPlugin>(
42-
ES, std::move(*Registrar), false, true));
43-
return Error::success();
40+
ES, false, true, TargetSymErr));
41+
return TargetSymErr;
4442
}
4543
case Triple::MachO: {
4644
auto DS = GDBJITDebugInfoRegistrationPlugin::Create(ES, *ProcessSymsJD, TT);

llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ const char *RegisterEHFrameSectionAllocActionName =
7474
const char *DeregisterEHFrameSectionAllocActionName =
7575
"llvm_orc_deregisterEHFrameAllocAction";
7676

77+
const char *RegisterJITLoaderGDBAllocActionName =
78+
"llvm_orc_registerJITLoaderGDBAllocAction";
79+
7780
const char *RunAsMainWrapperName = "__llvm_orc_bootstrap_run_as_main_wrapper";
7881
const char *RunAsVoidFunctionWrapperName =
7982
"__llvm_orc_bootstrap_run_as_void_function_wrapper";

llvm/lib/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
1010

1111
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12+
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
1213
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
1314

1415
#ifdef __APPLE__
@@ -27,6 +28,9 @@ void addDefaultBootstrapValuesForHostProcess(
2728
BootstrapSymbols[rt::DeregisterEHFrameSectionAllocActionName] =
2829
ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionAllocAction);
2930

31+
BootstrapSymbols[rt::RegisterJITLoaderGDBAllocActionName] =
32+
ExecutorAddr::fromPtr(&llvm_orc_registerJITLoaderGDBAllocAction);
33+
3034
#ifdef __APPLE__
3135
if (!dlsym(RTLD_DEFAULT, "__unw_add_find_dynamic_unwind_sections"))
3236
BootstrapMap["darwin-use-ehframes-only"].push_back(1);

llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
1010

1111
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
12+
#include "llvm/ExecutionEngine/Orc/TargetProcess/DefaultHostBootstrapValues.h"
1213
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
1314
#include "llvm/Support/FormatVariadic.h"
1415
#include "llvm/Support/Process.h"
@@ -206,10 +207,7 @@ Error SimpleRemoteEPCServer::sendSetupMessage(
206207
"Dispatch function name should not be set");
207208
EI.BootstrapSymbols[ExecutorSessionObjectName] = ExecutorAddr::fromPtr(this);
208209
EI.BootstrapSymbols[DispatchFnName] = ExecutorAddr::fromPtr(jitDispatchEntry);
209-
EI.BootstrapSymbols[rt::RegisterEHFrameSectionAllocActionName] =
210-
ExecutorAddr::fromPtr(&llvm_orc_registerEHFrameSectionAllocAction);
211-
EI.BootstrapSymbols[rt::DeregisterEHFrameSectionAllocActionName] =
212-
ExecutorAddr::fromPtr(&llvm_orc_deregisterEHFrameSectionAllocAction);
210+
addDefaultBootstrapValuesForHostProcess(EI.BootstrapMap, EI.BootstrapSymbols);
213211

214212
using SPSSerialize =
215213
shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;

llvm/tools/llvm-jitlink/llvm-jitlink.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,9 +1297,16 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
12971297
} else if (TT.isOSBinFormatELF()) {
12981298
if (!NoExec)
12991299
ObjLayer.addPlugin(ExitOnErr(EHFrameRegistrationPlugin::Create(ES)));
1300-
if (DebuggerSupport)
1301-
ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
1302-
ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES)), true, true));
1300+
if (DebuggerSupport) {
1301+
Error TargetSymErr = Error::success();
1302+
auto Plugin = std::make_unique<DebugObjectManagerPlugin>(ES, true, true,
1303+
TargetSymErr);
1304+
if (!TargetSymErr)
1305+
ObjLayer.addPlugin(std::move(Plugin));
1306+
else
1307+
logAllUnhandledErrors(std::move(TargetSymErr), errs(),
1308+
"Debugger support not available: ");
1309+
}
13031310
}
13041311

13051312
if (auto MainJDOrErr = ES.createJITDylib("main"))

0 commit comments

Comments
 (0)