aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp')
-rw-r--r--llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
new file mode 100644
index 000000000000..abce14de0fe0
--- /dev/null
+++ b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
@@ -0,0 +1,347 @@
+//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemoteJITUtils.h"
+
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
+#include "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.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>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif // LLVM_ON_UNIX
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace llvm {
+namespace orc {
+
+class RemoteTargetProcessControl
+ : public OrcRPCTargetProcessControlBase<
+ shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> {
+public:
+ using RPCChannel = JITLinkExecutor::RPCChannel;
+ using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>;
+
+private:
+ using ThisT = RemoteTargetProcessControl;
+ using BaseT = OrcRPCTargetProcessControlBase<RPCEndpoint>;
+ using MemoryAccess = OrcRPCTPCMemoryAccess<ThisT>;
+ using MemoryManager = OrcRPCTPCJITLinkMemoryManager<ThisT>;
+
+public:
+ using BaseT::initializeORCRPCTPCBase;
+
+ RemoteTargetProcessControl(ExecutionSession &ES,
+ std::unique_ptr<RPCChannel> Channel,
+ std::unique_ptr<RPCEndpoint> Endpoint);
+
+ 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;
+};
+
+RemoteTargetProcessControl::RemoteTargetProcessControl(
+ ExecutionSession &ES, std::unique_ptr<RPCChannel> Channel,
+ std::unique_ptr<RPCEndpoint> Endpoint)
+ : BaseT(ES.getSymbolStringPool(), *Endpoint,
+ [&ES](Error Err) { ES.reportError(std::move(Err)); }),
+ 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 RemoteTargetProcessControl::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 RemoteTargetProcessControl::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) {
+ return std::make_unique<ObjectLinkingLayer>(ES, TPC->getMemMgr());
+}
+
+Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) {
+ auto Registrar = createJITLoaderGDBRegistrar(*TPC);
+ if (!Registrar)
+ return Registrar.takeError();
+
+ cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(
+ std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),
+ std::move(*Registrar)));
+
+ return Error::success();
+}
+
+Expected<std::unique_ptr<DefinitionGenerator>>
+JITLinkExecutor::loadDylib(StringRef RemotePath) {
+ if (auto Handle = TPC->loadDylib(RemotePath.data()))
+ return std::make_unique<TPCDynamicLibrarySearchGenerator>(*TPC, *Handle);
+ else
+ return Handle.takeError();
+}
+
+Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,
+ ArrayRef<std::string> Args) {
+ return TPC->runAsMain(MainSym.getAddress(), Args);
+}
+
+Error JITLinkExecutor::disconnect() { return TPC->disconnect(); }
+
+static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName) {
+ // This just needs to be some 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));
+ sys::path::remove_filename(FullName);
+ sys::path::append(FullName, ExecutorName);
+ 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<RemoteTargetProcessControl> TPC) {
+ this->TPC = std::move(TPC);
+}
+
+#ifndef LLVM_ON_UNIX
+
+// FIXME: Add support for Windows.
+Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
+ 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) {
+ return make_error<StringError>(
+ "Remote JITing not yet supported on non-unix platforms",
+ inconvertibleErrorCode());
+}
+
+#else
+
+Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
+ constexpr int ReadEnd = 0;
+ constexpr int WriteEnd = 1;
+
+ // Pipe FDs.
+ int ToExecutor[2];
+ int FromExecutor[2];
+
+ // Create pipes to/from the executor..
+ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+ return make_error<StringError>("Unable to create pipe for executor",
+ inconvertibleErrorCode());
+
+ ProcessID = fork();
+ if (ProcessID == 0) {
+ // In the child...
+
+ // Close the parent ends of the pipes
+ close(ToExecutor[WriteEnd]);
+ close(FromExecutor[ReadEnd]);
+
+ // Execute the child process.
+ std::unique_ptr<char[]> ExecPath, FDSpecifier;
+ {
+ ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
+ strcpy(ExecPath.get(), ExecutablePath.data());
+
+ std::string FDSpecifierStr("filedescs=");
+ FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
+ FDSpecifierStr += ',';
+ FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
+ FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+ strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+ }
+
+ char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr};
+ int RC = execvp(ExecPath.get(), Args);
+ if (RC != 0)
+ return make_error<StringError>(
+ "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
+ inconvertibleErrorCode());
+
+ llvm_unreachable("Fork won't return in success case");
+ }
+ // else we're the parent...
+
+ // Close the child ends of the pipes
+ close(ToExecutor[ReadEnd]);
+ close(FromExecutor[WriteEnd]);
+
+ auto Channel =
+ std::make_unique<RPCChannel>(FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+ auto Endpoint =
+ std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel, true);
+
+ TPC = std::make_unique<RemoteTargetProcessControl>(ES, std::move(Channel),
+ std::move(Endpoint));
+
+ if (auto Err = TPC->initializeORCRPCTPCBase())
+ return joinErrors(std::move(Err), TPC->disconnect());
+
+ TPC->initializeMemoryManagement();
+
+ shared::registerStringError<RPCChannel>();
+ return Error::success();
+}
+
+static Expected<int> connectTCPSocketImpl(std::string Host,
+ std::string PortStr) {
+ addrinfo *AI;
+ addrinfo Hints{};
+ Hints.ai_family = AF_INET;
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags = AI_NUMERICSERV;
+
+ if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
+ return make_error<StringError>(
+ formatv("address resolution failed ({0})", gai_strerror(EC)),
+ inconvertibleErrorCode());
+
+ // Cycle through the returned addrinfo structures and connect to the first
+ // reachable endpoint.
+ int SockFD;
+ addrinfo *Server;
+ for (Server = AI; Server != nullptr; Server = Server->ai_next) {
+ // If socket fails, maybe it's because the address family is not supported.
+ // Skip to the next addrinfo structure.
+ if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+ continue;
+
+ // If connect works, we exit the loop with a working socket.
+ if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+ break;
+
+ close(SockFD);
+ }
+ freeaddrinfo(AI);
+
+ // Did we reach the end of the loop without connecting to a valid endpoint?
+ if (Server == nullptr)
+ return make_error<StringError>("invalid hostname",
+ inconvertibleErrorCode());
+
+ return SockFD;
+}
+
+Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
+JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
+ ExecutionSession &ES) {
+ auto CreateErr = [NetworkAddress](StringRef Details) {
+ return make_error<StringError>(
+ formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
+ Details),
+ inconvertibleErrorCode());
+ };
+
+ StringRef Host, PortStr;
+ std::tie(Host, PortStr) = NetworkAddress.split(':');
+ if (Host.empty())
+ return CreateErr("host name cannot be empty");
+ if (PortStr.empty())
+ return CreateErr("port cannot be empty");
+ int Port = 0;
+ if (PortStr.getAsInteger(10, Port))
+ return CreateErr("port number is not a valid integer");
+
+ Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
+ if (!SockFD)
+ return CreateErr(toString(SockFD.takeError()));
+
+ auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD);
+ auto Endpoint =
+ std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel, true);
+
+ auto TPC = std::make_unique<RemoteTargetProcessControl>(
+ ES, std::move(Channel), std::move(Endpoint));
+
+ if (auto Err = TPC->initializeORCRPCTPCBase())
+ return joinErrors(std::move(Err), TPC->disconnect());
+
+ TPC->initializeMemoryManagement();
+ shared::registerStringError<RPCChannel>();
+
+ return std::unique_ptr<TCPSocketJITLinkExecutor>(
+ new TCPSocketJITLinkExecutor(std::move(TPC)));
+}
+
+#endif