forked from OSchip/llvm-project
219 lines
7.3 KiB
C++
219 lines
7.3 KiB
C++
//===-- 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/EPCDebugObjectRegistrar.h"
|
|
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.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"
|
|
|
|
#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;
|
|
|
|
Error addDebugSupport(ObjectLayer &ObjLayer) {
|
|
ExecutionSession &ES = ObjLayer.getExecutionSession();
|
|
auto Registrar = createJITLoaderGDBRegistrar(ES);
|
|
if (!Registrar)
|
|
return Registrar.takeError();
|
|
|
|
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>>
|
|
loadDylib(ExecutionSession &ES, StringRef RemotePath) {
|
|
if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
|
|
return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
|
|
else
|
|
return Handle.takeError();
|
|
}
|
|
|
|
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.
|
|
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, "llvm-jitlink-executor");
|
|
return FullName.str().str();
|
|
}
|
|
|
|
#ifndef LLVM_ON_UNIX
|
|
|
|
// FIXME: Add support for Windows.
|
|
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<SimpleRemoteEPC>>
|
|
connectTCPSocket(StringRef NetworkAddress) {
|
|
return make_error<StringError>(
|
|
"Remote JITing not yet supported on non-unix platforms",
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
#else
|
|
|
|
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];
|
|
|
|
// Create pipes to/from the executor..
|
|
if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
|
|
return make_error<StringError>("Unable to create pipe for executor",
|
|
inconvertibleErrorCode());
|
|
|
|
pid_t 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 EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(),
|
|
SimpleRemoteEPC::Setup(),
|
|
FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
|
|
if (!EPC)
|
|
return EPC.takeError();
|
|
|
|
return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
|
|
}
|
|
|
|
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<SimpleRemoteEPC>>
|
|
connectTCPSocket(StringRef NetworkAddress) {
|
|
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()));
|
|
|
|
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(),
|
|
SimpleRemoteEPC::Setup(), *SockFD);
|
|
}
|
|
|
|
#endif
|