forked from OSchip/llvm-project
[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.
This commit is contained in:
parent
1380eae590
commit
ac2daacb31
|
@ -79,6 +79,8 @@
|
|||
|
||||
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
|
||||
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
@ -116,9 +118,9 @@ static cl::opt<std::string>
|
|||
OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
|
||||
cl::value_desc("filename"));
|
||||
|
||||
// Network address of a running executor process that we can connected through a
|
||||
// TCP socket. It may run locally or on a remote machine.
|
||||
static cl::opt<std::string> OOPExecutorConnect(
|
||||
// Network address of a running executor process that we can connect via TCP. It
|
||||
// may run locally or on a remote machine.
|
||||
static cl::opt<std::string> OOPExecutorConnectTCP(
|
||||
"connect",
|
||||
cl::desc("Connect to an out-of-process executor through a TCP socket"),
|
||||
cl::value_desc("<hostname>:<port>"));
|
||||
|
@ -133,41 +135,6 @@ static cl::opt<bool>
|
|||
|
||||
ExitOnError ExitOnErr;
|
||||
|
||||
static std::unique_ptr<JITLinkExecutor> connectExecutor(const char *Argv0) {
|
||||
// Connect to a running out-of-process executor through a TCP socket.
|
||||
if (!OOPExecutorConnect.empty()) {
|
||||
std::unique_ptr<TCPSocketJITLinkExecutor> Exec =
|
||||
ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
|
||||
std::ref(ExitOnErr)));
|
||||
|
||||
outs() << "Connected to executor at " << OOPExecutorConnect << "\n";
|
||||
if (WaitForDebugger) {
|
||||
outs() << "Attach a debugger and press any key to continue.\n";
|
||||
fflush(stdin);
|
||||
getchar();
|
||||
}
|
||||
|
||||
return std::move(Exec);
|
||||
}
|
||||
|
||||
// Launch a out-of-process executor locally in a child process.
|
||||
std::unique_ptr<ChildProcessJITLinkExecutor> Exec = ExitOnErr(
|
||||
OOPExecutor.empty() ? JITLinkExecutor::FindLocal(Argv0)
|
||||
: JITLinkExecutor::CreateLocal(OOPExecutor));
|
||||
|
||||
outs() << "Found out-of-process executor: " << Exec->getPath() << "\n";
|
||||
|
||||
ExitOnErr(Exec->launch(std::ref(ExitOnErr)));
|
||||
if (WaitForDebugger) {
|
||||
outs() << "Launched executor in subprocess: " << Exec->getPID() << "\n"
|
||||
<< "Attach a debugger and press any key to continue.\n";
|
||||
fflush(stdin);
|
||||
getchar();
|
||||
}
|
||||
|
||||
return std::move(Exec);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
InitLLVM X(argc, argv);
|
||||
|
||||
|
@ -177,8 +144,27 @@ int main(int argc, char *argv[]) {
|
|||
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
|
||||
cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");
|
||||
|
||||
// Launch/connect the out-of-process executor.
|
||||
std::unique_ptr<JITLinkExecutor> Executor = connectExecutor(argv[0]);
|
||||
std::unique_ptr<SimpleRemoteEPC> EPC;
|
||||
if (OOPExecutorConnectTCP.getNumOccurrences() > 0) {
|
||||
// Connect to a running out-of-process executor through a TCP socket.
|
||||
EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
|
||||
outs() << "Connected to executor at " << OOPExecutorConnectTCP << "\n";
|
||||
} else {
|
||||
// Launch an out-of-process executor locally in a child process.
|
||||
std::string Path =
|
||||
OOPExecutor.empty() ? findLocalExecutor(argv[0]) : OOPExecutor;
|
||||
outs() << "Found out-of-process executor: " << Path << "\n";
|
||||
|
||||
uint64_t PID;
|
||||
std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(Path));
|
||||
outs() << "Launched executor in subprocess: " << PID << "\n";
|
||||
}
|
||||
|
||||
if (WaitForDebugger) {
|
||||
outs() << "Attach a debugger and press any key to continue.\n";
|
||||
fflush(stdin);
|
||||
getchar();
|
||||
}
|
||||
|
||||
// Load the given IR files.
|
||||
std::vector<ThreadSafeModule> TSMs;
|
||||
|
@ -211,44 +197,51 @@ int main(int argc, char *argv[]) {
|
|||
JTMB.setRelocationModel(Reloc::PIC_);
|
||||
|
||||
// Create LLJIT and destroy it before disconnecting the target process.
|
||||
outs() << "Initializing LLJIT for remote executor\n";
|
||||
auto J = ExitOnErr(LLJITBuilder()
|
||||
.setExecutorProcessControl(std::move(EPC))
|
||||
.setJITTargetMachineBuilder(std::move(JTMB))
|
||||
.setObjectLinkingLayerCreator([&](auto &ES, const auto &TT) {
|
||||
return std::make_unique<ObjectLinkingLayer>(ES);
|
||||
})
|
||||
.create());
|
||||
|
||||
// Add plugin for debug support.
|
||||
ExitOnErr(addDebugSupport(J->getObjLinkingLayer()));
|
||||
|
||||
// Load required shared libraries on the remote target and add a generator
|
||||
// for each of it, so the compiler can lookup their symbols.
|
||||
for (const std::string &Path : Dylibs)
|
||||
J->getMainJITDylib().addGenerator(
|
||||
ExitOnErr(loadDylib(J->getExecutionSession(), Path)));
|
||||
|
||||
// Add the loaded IR module to the JIT. This will set up symbol tables and
|
||||
// prepare for materialization.
|
||||
for (ThreadSafeModule &TSM : TSMs)
|
||||
ExitOnErr(J->addIRModule(std::move(TSM)));
|
||||
|
||||
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
|
||||
// function will materialize all reachable code. It also triggers debug
|
||||
// registration in the remote target process.
|
||||
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
|
||||
|
||||
outs() << "Running: main(";
|
||||
int Pos = 0;
|
||||
std::vector<std::string> ActualArgv{"LLJITWithRemoteDebugging"};
|
||||
for (const std::string &Arg : InputArgv) {
|
||||
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
|
||||
ActualArgv.push_back(Arg);
|
||||
}
|
||||
outs() << ")\n";
|
||||
|
||||
// Execute the code in the remote target process and dump the result. With
|
||||
// the debugger attached to the target, it should be possible to inspect the
|
||||
// JITed code as if it was compiled statically.
|
||||
{
|
||||
std::unique_ptr<ExecutionSession> ES = Executor->startSession();
|
||||
|
||||
outs() << "Initializing LLJIT for remote executor\n";
|
||||
auto J = ExitOnErr(LLJITBuilder()
|
||||
.setExecutionSession(std::move(ES))
|
||||
.setJITTargetMachineBuilder(std::move(JTMB))
|
||||
.setObjectLinkingLayerCreator(std::ref(*Executor))
|
||||
.create());
|
||||
|
||||
// Add plugin for debug support.
|
||||
ExitOnErr(Executor->addDebugSupport(J->getObjLinkingLayer()));
|
||||
|
||||
// Load required shared libraries on the remote target and add a generator
|
||||
// for each of it, so the compiler can lookup their symbols.
|
||||
for (const std::string &Path : Dylibs)
|
||||
J->getMainJITDylib().addGenerator(ExitOnErr(Executor->loadDylib(Path)));
|
||||
|
||||
// Add the loaded IR module to the JIT. This will set up symbol tables and
|
||||
// prepare for materialization.
|
||||
for (ThreadSafeModule &TSM : TSMs)
|
||||
ExitOnErr(J->addIRModule(std::move(TSM)));
|
||||
|
||||
// The example uses a non-lazy JIT for simplicity. Thus, looking up the main
|
||||
// function will materialize all reachable code. It also triggers debug
|
||||
// registration in the remote target process.
|
||||
JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
|
||||
|
||||
outs() << "Running: main(";
|
||||
int Pos = 0;
|
||||
for (const std::string &Arg : InputArgv)
|
||||
outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
|
||||
outs() << ")\n";
|
||||
|
||||
// Execute the code in the remote target process and dump the result. With
|
||||
// the debugger attached to the target, it should be possible to inspect the
|
||||
// JITed code as if it was compiled statically.
|
||||
int Result = ExitOnErr(Executor->runAsMain(MainFn, InputArgv));
|
||||
JITTargetAddress MainFnAddr = MainFn.getAddress();
|
||||
ExecutorProcessControl &EPC =
|
||||
J->getExecutionSession().getExecutorProcessControl();
|
||||
int Result = ExitOnErr(EPC.runAsMain(ExecutorAddr(MainFnAddr), ActualArgv));
|
||||
outs() << "Exit code: " << Result << "\n";
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,10 @@
|
|||
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
||||
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
|
||||
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
|
||||
#include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
|
||||
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
||||
#ifdef LLVM_ON_UNIX
|
||||
#include <netdb.h>
|
||||
|
@ -28,176 +26,55 @@
|
|||
using namespace llvm;
|
||||
using namespace llvm::orc;
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
class RemoteExecutorProcessControl
|
||||
: public OrcRPCExecutorProcessControlBase<
|
||||
shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> {
|
||||
public:
|
||||
using RPCChannel = JITLinkExecutor::RPCChannel;
|
||||
using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>;
|
||||
|
||||
private:
|
||||
using ThisT = RemoteExecutorProcessControl;
|
||||
using BaseT = OrcRPCExecutorProcessControlBase<RPCEndpoint>;
|
||||
using MemoryAccess = OrcRPCEPCMemoryAccess<ThisT>;
|
||||
using MemoryManager = OrcRPCEPCJITLinkMemoryManager<ThisT>;
|
||||
|
||||
public:
|
||||
using BaseT::initializeORCRPCEPCBase;
|
||||
|
||||
RemoteExecutorProcessControl(std::unique_ptr<RPCChannel> Channel,
|
||||
std::unique_ptr<RPCEndpoint> Endpoint,
|
||||
BaseT::ErrorReporter ReportError);
|
||||
|
||||
void initializeMemoryManagement();
|
||||
Error disconnect() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RPCChannel> Channel;
|
||||
std::unique_ptr<RPCEndpoint> Endpoint;
|
||||
std::unique_ptr<MemoryAccess> OwnedMemAccess;
|
||||
std::unique_ptr<MemoryManager> OwnedMemMgr;
|
||||
std::atomic<bool> Finished{false};
|
||||
std::thread ListenerThread;
|
||||
};
|
||||
|
||||
RemoteExecutorProcessControl::RemoteExecutorProcessControl(
|
||||
std::unique_ptr<RPCChannel> Channel, std::unique_ptr<RPCEndpoint> Endpoint,
|
||||
BaseT::ErrorReporter ReportError)
|
||||
: BaseT(std::make_shared<SymbolStringPool>(), *Endpoint,
|
||||
std::move(ReportError)),
|
||||
Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) {
|
||||
|
||||
ListenerThread = std::thread([&]() {
|
||||
while (!Finished) {
|
||||
if (auto Err = this->Endpoint->handleOne()) {
|
||||
reportError(std::move(Err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RemoteExecutorProcessControl::initializeMemoryManagement() {
|
||||
OwnedMemAccess = std::make_unique<MemoryAccess>(*this);
|
||||
OwnedMemMgr = std::make_unique<MemoryManager>(*this);
|
||||
|
||||
// Base class needs non-owning access.
|
||||
MemAccess = OwnedMemAccess.get();
|
||||
MemMgr = OwnedMemMgr.get();
|
||||
}
|
||||
|
||||
Error RemoteExecutorProcessControl::disconnect() {
|
||||
std::promise<MSVCPError> P;
|
||||
auto F = P.get_future();
|
||||
auto Err = closeConnection([&](Error Err) -> Error {
|
||||
P.set_value(std::move(Err));
|
||||
Finished = true;
|
||||
return Error::success();
|
||||
});
|
||||
ListenerThread.join();
|
||||
return joinErrors(std::move(Err), F.get());
|
||||
}
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
|
||||
JITLinkExecutor::JITLinkExecutor() = default;
|
||||
JITLinkExecutor::~JITLinkExecutor() = default;
|
||||
|
||||
Expected<std::unique_ptr<ObjectLayer>>
|
||||
JITLinkExecutor::operator()(ExecutionSession &ES, const Triple &TT) {
|
||||
assert(EPC && "RemoteExecutorProcessControl must be initialized");
|
||||
return std::make_unique<ObjectLinkingLayer>(ES, EPC->getMemMgr());
|
||||
}
|
||||
|
||||
std::unique_ptr<ExecutionSession> JITLinkExecutor::startSession() {
|
||||
assert(OwnedEPC && "RemoteExecutorProcessControl must be initialized");
|
||||
return std::make_unique<ExecutionSession>(std::move(OwnedEPC));
|
||||
}
|
||||
|
||||
Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) {
|
||||
auto Registrar = createJITLoaderGDBRegistrar(EPC->getExecutionSession());
|
||||
Error addDebugSupport(ObjectLayer &ObjLayer) {
|
||||
ExecutionSession &ES = ObjLayer.getExecutionSession();
|
||||
auto Registrar = createJITLoaderGDBRegistrar(ES);
|
||||
if (!Registrar)
|
||||
return Registrar.takeError();
|
||||
|
||||
cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(
|
||||
std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),
|
||||
std::move(*Registrar)));
|
||||
auto *ObjLinkingLayer = cast<ObjectLinkingLayer>(&ObjLayer);
|
||||
if (!ObjLinkingLayer)
|
||||
return createStringError(inconvertibleErrorCode(),
|
||||
"No debug support for given object layer type");
|
||||
|
||||
ObjLinkingLayer->addPlugin(
|
||||
std::make_unique<DebugObjectManagerPlugin>(ES, std::move(*Registrar)));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<DefinitionGenerator>>
|
||||
JITLinkExecutor::loadDylib(StringRef RemotePath) {
|
||||
if (auto Handle = EPC->loadDylib(RemotePath.data()))
|
||||
return std::make_unique<EPCDynamicLibrarySearchGenerator>(
|
||||
EPC->getExecutionSession(), *Handle);
|
||||
loadDylib(ExecutionSession &ES, StringRef RemotePath) {
|
||||
if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
|
||||
return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
|
||||
else
|
||||
return Handle.takeError();
|
||||
}
|
||||
|
||||
Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,
|
||||
ArrayRef<std::string> Args) {
|
||||
return EPC->runAsMain(ExecutorAddr(MainSym.getAddress()), Args);
|
||||
}
|
||||
|
||||
Error JITLinkExecutor::disconnect() { return EPC->disconnect(); }
|
||||
|
||||
static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName) {
|
||||
// This just needs to be some symbol in the binary; C++ doesn't
|
||||
static void findLocalExecutorHelper() {}
|
||||
std::string findLocalExecutor(const char *HostArgv0) {
|
||||
// This just needs to be some static symbol in the binary; C++ doesn't
|
||||
// allow taking the address of ::main however.
|
||||
void *P = (void *)(intptr_t)defaultPath;
|
||||
SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, P));
|
||||
uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
|
||||
void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
|
||||
SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
|
||||
sys::path::remove_filename(FullName);
|
||||
sys::path::append(FullName, ExecutorName);
|
||||
sys::path::append(FullName, "llvm-jitlink-executor");
|
||||
return FullName.str().str();
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
|
||||
JITLinkExecutor::FindLocal(const char *HostArgv) {
|
||||
std::string BestGuess = defaultPath(HostArgv, "llvm-jitlink-executor");
|
||||
auto Executor = CreateLocal(BestGuess);
|
||||
if (!Executor) {
|
||||
consumeError(Executor.takeError());
|
||||
return make_error<StringError>(
|
||||
formatv("Unable to find usable executor: {0}", BestGuess),
|
||||
inconvertibleErrorCode());
|
||||
}
|
||||
return Executor;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
|
||||
JITLinkExecutor::CreateLocal(std::string ExecutablePath) {
|
||||
if (!sys::fs::can_execute(ExecutablePath))
|
||||
return make_error<StringError>(
|
||||
formatv("Specified executor invalid: {0}", ExecutablePath),
|
||||
inconvertibleErrorCode());
|
||||
return std::unique_ptr<ChildProcessJITLinkExecutor>(
|
||||
new ChildProcessJITLinkExecutor(std::move(ExecutablePath)));
|
||||
}
|
||||
|
||||
TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(
|
||||
std::unique_ptr<RemoteExecutorProcessControl> EPC) {
|
||||
this->OwnedEPC = std::move(EPC);
|
||||
this->EPC = this->OwnedEPC.get();
|
||||
}
|
||||
|
||||
#ifndef LLVM_ON_UNIX
|
||||
|
||||
// FIXME: Add support for Windows.
|
||||
Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
|
||||
Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
|
||||
launchLocalExecutor(StringRef ExecutablePath) {
|
||||
return make_error<StringError>(
|
||||
"Remote JITing not yet supported on non-unix platforms",
|
||||
inconvertibleErrorCode());
|
||||
}
|
||||
|
||||
// FIXME: Add support for Windows.
|
||||
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
|
||||
JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
|
||||
ExecutionSession &ES) {
|
||||
Expected<std::unique_ptr<SimpleRemoteEPC>>
|
||||
connectTCPSocket(StringRef NetworkAddress) {
|
||||
return make_error<StringError>(
|
||||
"Remote JITing not yet supported on non-unix platforms",
|
||||
inconvertibleErrorCode());
|
||||
|
@ -205,11 +82,16 @@ JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
|
|||
|
||||
#else
|
||||
|
||||
Error ChildProcessJITLinkExecutor::launch(
|
||||
unique_function<void(Error)> ErrorReporter) {
|
||||
Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
|
||||
launchLocalExecutor(StringRef ExecutablePath) {
|
||||
constexpr int ReadEnd = 0;
|
||||
constexpr int WriteEnd = 1;
|
||||
|
||||
if (!sys::fs::can_execute(ExecutablePath))
|
||||
return make_error<StringError>(
|
||||
formatv("Specified executor invalid: {0}", ExecutablePath),
|
||||
inconvertibleErrorCode());
|
||||
|
||||
// Pipe FDs.
|
||||
int ToExecutor[2];
|
||||
int FromExecutor[2];
|
||||
|
@ -219,7 +101,7 @@ Error ChildProcessJITLinkExecutor::launch(
|
|||
return make_error<StringError>("Unable to create pipe for executor",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
ProcessID = fork();
|
||||
pid_t ProcessID = fork();
|
||||
if (ProcessID == 0) {
|
||||
// In the child...
|
||||
|
||||
|
@ -256,22 +138,12 @@ Error ChildProcessJITLinkExecutor::launch(
|
|||
close(ToExecutor[ReadEnd]);
|
||||
close(FromExecutor[WriteEnd]);
|
||||
|
||||
auto Channel =
|
||||
std::make_unique<RPCChannel>(FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
|
||||
auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
|
||||
*Channel, true);
|
||||
auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
||||
FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
|
||||
if (!EPC)
|
||||
return EPC.takeError();
|
||||
|
||||
OwnedEPC = std::make_unique<RemoteExecutorProcessControl>(
|
||||
std::move(Channel), std::move(Endpoint), std::move(ErrorReporter));
|
||||
|
||||
if (auto Err = OwnedEPC->initializeORCRPCEPCBase())
|
||||
return joinErrors(std::move(Err), OwnedEPC->disconnect());
|
||||
|
||||
OwnedEPC->initializeMemoryManagement();
|
||||
EPC = OwnedEPC.get();
|
||||
|
||||
shared::registerStringError<RPCChannel>();
|
||||
return Error::success();
|
||||
return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
|
||||
}
|
||||
|
||||
static Expected<int> connectTCPSocketImpl(std::string Host,
|
||||
|
@ -313,9 +185,8 @@ static Expected<int> connectTCPSocketImpl(std::string Host,
|
|||
return SockFD;
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
|
||||
JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
|
||||
unique_function<void(Error)> ErrorReporter) {
|
||||
Expected<std::unique_ptr<SimpleRemoteEPC>>
|
||||
connectTCPSocket(StringRef NetworkAddress) {
|
||||
auto CreateErr = [NetworkAddress](StringRef Details) {
|
||||
return make_error<StringError>(
|
||||
formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
|
||||
|
@ -337,21 +208,7 @@ JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
|
|||
if (!SockFD)
|
||||
return CreateErr(toString(SockFD.takeError()));
|
||||
|
||||
auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD);
|
||||
auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>(
|
||||
*Channel, true);
|
||||
|
||||
auto EPC = std::make_unique<RemoteExecutorProcessControl>(
|
||||
std::move(Channel), std::move(Endpoint), std::move(ErrorReporter));
|
||||
|
||||
if (auto Err = EPC->initializeORCRPCEPCBase())
|
||||
return joinErrors(std::move(Err), EPC->disconnect());
|
||||
|
||||
EPC->initializeMemoryManagement();
|
||||
shared::registerStringError<RPCChannel>();
|
||||
|
||||
return std::unique_ptr<TCPSocketJITLinkExecutor>(
|
||||
new TCPSocketJITLinkExecutor(std::move(EPC)));
|
||||
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(*SockFD);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,103 +14,31 @@
|
|||
#ifndef LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
|
||||
#define LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Layer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
|
||||
#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
/// Find the default exectuable on disk and create a JITLinkExecutor for it.
|
||||
std::string findLocalExecutor(const char *HostArgv0);
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
llvm::Expected<std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint64_t>>
|
||||
launchLocalExecutor(llvm::StringRef ExecutablePath);
|
||||
|
||||
class ChildProcessJITLinkExecutor;
|
||||
class RemoteExecutorProcessControl;
|
||||
class TCPSocketJITLinkExecutor;
|
||||
/// Create a JITLinkExecutor that connects to the given network address
|
||||
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
|
||||
/// e.g. localhost:20000.
|
||||
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
|
||||
connectTCPSocket(llvm::StringRef NetworkAddress);
|
||||
|
||||
class JITLinkExecutor {
|
||||
public:
|
||||
using RPCChannel = shared::FDRawByteChannel;
|
||||
llvm::Error addDebugSupport(llvm::orc::ObjectLayer &ObjLayer);
|
||||
|
||||
/// Create a JITLinkExecutor for the given exectuable on disk.
|
||||
static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
|
||||
CreateLocal(std::string ExecutablePath);
|
||||
|
||||
/// Find the default exectuable on disk and create a JITLinkExecutor for it.
|
||||
static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
|
||||
FindLocal(const char *JITArgv0);
|
||||
|
||||
/// Create a JITLinkExecutor that connects to the given network address
|
||||
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
|
||||
/// e.g. localhost:20000.
|
||||
static Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
|
||||
ConnectTCPSocket(StringRef NetworkAddress,
|
||||
unique_function<void(Error)> ErrorReporter);
|
||||
|
||||
// Implement ObjectLinkingLayerCreator
|
||||
Expected<std::unique_ptr<ObjectLayer>> operator()(ExecutionSession &,
|
||||
const Triple &);
|
||||
|
||||
std::unique_ptr<ExecutionSession> startSession();
|
||||
Error disconnect();
|
||||
|
||||
Error addDebugSupport(ObjectLayer &ObjLayer);
|
||||
|
||||
Expected<std::unique_ptr<DefinitionGenerator>>
|
||||
loadDylib(StringRef RemotePath);
|
||||
|
||||
Expected<int> runAsMain(JITEvaluatedSymbol MainSym,
|
||||
ArrayRef<std::string> Args);
|
||||
|
||||
virtual ~JITLinkExecutor();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<RemoteExecutorProcessControl> OwnedEPC;
|
||||
RemoteExecutorProcessControl *EPC{nullptr};
|
||||
|
||||
JITLinkExecutor();
|
||||
};
|
||||
|
||||
/// JITLinkExecutor that runs in a child process on the local machine.
|
||||
class ChildProcessJITLinkExecutor : public JITLinkExecutor {
|
||||
public:
|
||||
Error launch(unique_function<void(Error)> ErrorReporter);
|
||||
|
||||
pid_t getPID() const { return ProcessID; }
|
||||
StringRef getPath() const { return ExecutablePath; }
|
||||
|
||||
private:
|
||||
std::string ExecutablePath;
|
||||
pid_t ProcessID;
|
||||
|
||||
ChildProcessJITLinkExecutor(std::string ExecutablePath)
|
||||
: ExecutablePath(std::move(ExecutablePath)) {}
|
||||
|
||||
static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName);
|
||||
friend class JITLinkExecutor;
|
||||
};
|
||||
|
||||
/// JITLinkExecutor connected through a TCP socket.
|
||||
class TCPSocketJITLinkExecutor : public JITLinkExecutor {
|
||||
private:
|
||||
TCPSocketJITLinkExecutor(std::unique_ptr<RemoteExecutorProcessControl> EPC);
|
||||
|
||||
friend class JITLinkExecutor;
|
||||
};
|
||||
|
||||
} // namespace orc
|
||||
} // namespace llvm
|
||||
llvm::Expected<std::unique_ptr<llvm::orc::DefinitionGenerator>>
|
||||
loadDylib(llvm::orc::ExecutionSession &ES, llvm::StringRef RemotePath);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue