Skip to content

Commit ac2daac

Browse files
[ORC][examples] Port LLJITWithRemoteDebugging to SimpleRemoteEPC
Though this is a full port of the example, it is not yet fully functional due to a threading issue in the SimpleRemoteEPC implementation. The issue was discussed in D110530, but it needs a more thorough solution. For now we are dropping the dependency to the old `OrcRPC` here (it's been the last use-case in-tree). The test for the example is under review in ... and will be re-enabled once the threading issue is solved.
1 parent 1380eae commit ac2daac

File tree

3 files changed

+124
-346
lines changed

3 files changed

+124
-346
lines changed

llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp

+70-77
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979

8080
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
8181
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
82+
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
83+
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
8284
#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
8385
#include "llvm/Support/CommandLine.h"
8486
#include "llvm/Support/Error.h"
@@ -116,9 +118,9 @@ static cl::opt<std::string>
116118
OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
117119
cl::value_desc("filename"));
118120

119-
// Network address of a running executor process that we can connected through a
120-
// TCP socket. It may run locally or on a remote machine.
121-
static cl::opt<std::string> OOPExecutorConnect(
121+
// Network address of a running executor process that we can connect via TCP. It
122+
// may run locally or on a remote machine.
123+
static cl::opt<std::string> OOPExecutorConnectTCP(
122124
"connect",
123125
cl::desc("Connect to an out-of-process executor through a TCP socket"),
124126
cl::value_desc("<hostname>:<port>"));
@@ -133,41 +135,6 @@ static cl::opt<bool>
133135

134136
ExitOnError ExitOnErr;
135137

136-
static std::unique_ptr<JITLinkExecutor> connectExecutor(const char *Argv0) {
137-
// Connect to a running out-of-process executor through a TCP socket.
138-
if (!OOPExecutorConnect.empty()) {
139-
std::unique_ptr<TCPSocketJITLinkExecutor> Exec =
140-
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
141-
std::ref(ExitOnErr)));
142-
143-
outs() << "Connected to executor at " << OOPExecutorConnect << "\n";
144-
if (WaitForDebugger) {
145-
outs() << "Attach a debugger and press any key to continue.\n";
146-
fflush(stdin);
147-
getchar();
148-
}
149-
150-
return std::move(Exec);
151-
}
152-
153-
// Launch a out-of-process executor locally in a child process.
154-
std::unique_ptr<ChildProcessJITLinkExecutor> Exec = ExitOnErr(
155-
OOPExecutor.empty() ? JITLinkExecutor::FindLocal(Argv0)
156-
: JITLinkExecutor::CreateLocal(OOPExecutor));
157-
158-
outs() << "Found out-of-process executor: " << Exec->getPath() << "\n";
159-
160-
ExitOnErr(Exec->launch(std::ref(ExitOnErr)));
161-
if (WaitForDebugger) {
162-
outs() << "Launched executor in subprocess: " << Exec->getPID() << "\n"
163-
<< "Attach a debugger and press any key to continue.\n";
164-
fflush(stdin);
165-
getchar();
166-
}
167-
168-
return std::move(Exec);
169-
}
170-
171138
int main(int argc, char *argv[]) {
172139
InitLLVM X(argc, argv);
173140

@@ -177,8 +144,27 @@ int main(int argc, char *argv[]) {
177144
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
178145
cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");
179146

180-
// Launch/connect the out-of-process executor.
181-
std::unique_ptr<JITLinkExecutor> Executor = connectExecutor(argv[0]);
147+
std::unique_ptr<SimpleRemoteEPC> EPC;
148+
if (OOPExecutorConnectTCP.getNumOccurrences() > 0) {
149+
// Connect to a running out-of-process executor through a TCP socket.
150+
EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
151+
outs() << "Connected to executor at " << OOPExecutorConnectTCP << "\n";
152+
} else {
153+
// Launch an out-of-process executor locally in a child process.
154+
std::string Path =
155+
OOPExecutor.empty() ? findLocalExecutor(argv[0]) : OOPExecutor;
156+
outs() << "Found out-of-process executor: " << Path << "\n";
157+
158+
uint64_t PID;
159+
std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(Path));
160+
outs() << "Launched executor in subprocess: " << PID << "\n";
161+
}
162+
163+
if (WaitForDebugger) {
164+
outs() << "Attach a debugger and press any key to continue.\n";
165+
fflush(stdin);
166+
getchar();
167+
}
182168

183169
// Load the given IR files.
184170
std::vector<ThreadSafeModule> TSMs;
@@ -211,44 +197,51 @@ int main(int argc, char *argv[]) {
211197
JTMB.setRelocationModel(Reloc::PIC_);
212198

213199
// Create LLJIT and destroy it before disconnecting the target process.
214-
{
215-
std::unique_ptr<ExecutionSession> ES = Executor->startSession();
216-
217-
outs() << "Initializing LLJIT for remote executor\n";
218-
auto J = ExitOnErr(LLJITBuilder()
219-
.setExecutionSession(std::move(ES))
220-
.setJITTargetMachineBuilder(std::move(JTMB))
221-
.setObjectLinkingLayerCreator(std::ref(*Executor))
222-
.create());
223-
224-
// Add plugin for debug support.
225-
ExitOnErr(Executor->addDebugSupport(J->getObjLinkingLayer()));
226-
227-
// Load required shared libraries on the remote target and add a generator
228-
// for each of it, so the compiler can lookup their symbols.
229-
for (const std::string &Path : Dylibs)
230-
J->getMainJITDylib().addGenerator(ExitOnErr(Executor->loadDylib(Path)));
231-
232-
// Add the loaded IR module to the JIT. This will set up symbol tables and
233-
// prepare for materialization.
234-
for (ThreadSafeModule &TSM : TSMs)
235-
ExitOnErr(J->addIRModule(std::move(TSM)));
236-
237-
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
238-
// function will materialize all reachable code. It also triggers debug
239-
// registration in the remote target process.
240-
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
241-
242-
outs() << "Running: main(";
243-
int Pos = 0;
244-
for (const std::string &Arg : InputArgv)
245-
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
246-
outs() << ")\n";
200+
outs() << "Initializing LLJIT for remote executor\n";
201+
auto J = ExitOnErr(LLJITBuilder()
202+
.setExecutorProcessControl(std::move(EPC))
203+
.setJITTargetMachineBuilder(std::move(JTMB))
204+
.setObjectLinkingLayerCreator([&](auto &ES, const auto &TT) {
205+
return std::make_unique<ObjectLinkingLayer>(ES);
206+
})
207+
.create());
208+
209+
// Add plugin for debug support.
210+
ExitOnErr(addDebugSupport(J->getObjLinkingLayer()));
211+
212+
// Load required shared libraries on the remote target and add a generator
213+
// for each of it, so the compiler can lookup their symbols.
214+
for (const std::string &Path : Dylibs)
215+
J->getMainJITDylib().addGenerator(
216+
ExitOnErr(loadDylib(J->getExecutionSession(), Path)));
217+
218+
// Add the loaded IR module to the JIT. This will set up symbol tables and
219+
// prepare for materialization.
220+
for (ThreadSafeModule &TSM : TSMs)
221+
ExitOnErr(J->addIRModule(std::move(TSM)));
222+
223+
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
224+
// function will materialize all reachable code. It also triggers debug
225+
// registration in the remote target process.
226+
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
227+
228+
outs() << "Running: main(";
229+
int Pos = 0;
230+
std::vector<std::string> ActualArgv{"LLJITWithRemoteDebugging"};
231+
for (const std::string &Arg : InputArgv) {
232+
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
233+
ActualArgv.push_back(Arg);
234+
}
235+
outs() << ")\n";
247236

248-
// Execute the code in the remote target process and dump the result. With
249-
// the debugger attached to the target, it should be possible to inspect the
250-
// JITed code as if it was compiled statically.
251-
int Result = ExitOnErr(Executor->runAsMain(MainFn, InputArgv));
237+
// Execute the code in the remote target process and dump the result. With
238+
// the debugger attached to the target, it should be possible to inspect the
239+
// JITed code as if it was compiled statically.
240+
{
241+
JITTargetAddress MainFnAddr = MainFn.getAddress();
242+
ExecutorProcessControl &EPC =
243+
J->getExecutionSession().getExecutorProcessControl();
244+
int Result = ExitOnErr(EPC.runAsMain(ExecutorAddr(MainFnAddr), ActualArgv));
252245
outs() << "Exit code: " << Result << "\n";
253246
}
254247

0 commit comments

Comments
 (0)