[ORC] Remove OrcRPCExecutorProcessControl ad OrcRPCTPCServer.

All in-tree tools have moved to SimpleRemoteEPC.
This commit is contained in:
Lang Hames
2021-10-01 09:34:16 -07:00
parent 4f0225f6d2
commit 6aeed7b19c
2 changed files with 0 additions and 1054 deletions

View File

@@ -1,436 +0,0 @@
//===-- OrcRPCExecutorProcessControl.h - Remote target control --*- 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
//
//===----------------------------------------------------------------------===//
//
// Executor control via ORC RPC.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H
#define LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
namespace llvm {
namespace orc {
/// JITLinkMemoryManager implementation for a process connected via an ORC RPC
/// endpoint.
template <typename OrcRPCEPCImplT>
class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
private:
struct HostAlloc {
std::unique_ptr<char[]> Mem;
uint64_t Size;
};
struct TargetAlloc {
JITTargetAddress Address = 0;
uint64_t AllocatedSize = 0;
};
using HostAllocMap = DenseMap<int, HostAlloc>;
using TargetAllocMap = DenseMap<int, TargetAlloc>;
public:
class OrcRPCAllocation : public Allocation {
public:
OrcRPCAllocation(OrcRPCEPCJITLinkMemoryManager<OrcRPCEPCImplT> &Parent,
HostAllocMap HostAllocs, TargetAllocMap TargetAllocs)
: Parent(Parent), HostAllocs(std::move(HostAllocs)),
TargetAllocs(std::move(TargetAllocs)) {
assert(HostAllocs.size() == TargetAllocs.size() &&
"HostAllocs size should match TargetAllocs");
}
~OrcRPCAllocation() override {
assert(TargetAllocs.empty() && "failed to deallocate");
}
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
auto I = HostAllocs.find(Seg);
assert(I != HostAllocs.end() && "No host allocation for segment");
auto &HA = I->second;
return {HA.Mem.get(), static_cast<size_t>(HA.Size)};
}
JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
auto I = TargetAllocs.find(Seg);
assert(I != TargetAllocs.end() && "No target allocation for segment");
return I->second.Address;
}
void finalizeAsync(FinalizeContinuation OnFinalize) override {
std::vector<tpctypes::BufferWrite> BufferWrites;
orcrpctpc::ReleaseOrFinalizeMemRequest FMR;
for (auto &KV : HostAllocs) {
assert(TargetAllocs.count(KV.first) &&
"No target allocation for buffer");
auto &HA = KV.second;
auto &TA = TargetAllocs[KV.first];
BufferWrites.push_back({TA.Address, StringRef(HA.Mem.get(), HA.Size)});
FMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
TA.Address, TA.AllocatedSize});
}
DEBUG_WITH_TYPE("orc", {
dbgs() << "finalizeAsync " << (void *)this << ":\n";
auto FMRI = FMR.begin();
for (auto &B : BufferWrites) {
auto Prot = FMRI->Prot;
++FMRI;
dbgs() << " Writing " << formatv("{0:x16}", B.Buffer.size())
<< " bytes to " << tpctypes::getWireProtectionFlagsStr(Prot)
<< " segment: local " << (const void *)B.Buffer.data()
<< " -> target " << formatv("{0:x16}", B.Address) << "\n";
}
});
if (auto Err =
Parent.Parent.getMemoryAccess().writeBuffers(BufferWrites)) {
OnFinalize(std::move(Err));
return;
}
DEBUG_WITH_TYPE("orc", dbgs() << " Applying permissions...\n");
if (auto Err =
Parent.getEndpoint().template callAsync<orcrpctpc::FinalizeMem>(
[OF = std::move(OnFinalize)](Error Err2) {
// FIXME: Dispatch to work queue.
std::thread([OF = std::move(OF),
Err3 = std::move(Err2)]() mutable {
DEBUG_WITH_TYPE(
"orc", { dbgs() << " finalizeAsync complete\n"; });
OF(std::move(Err3));
}).detach();
return Error::success();
},
FMR)) {
DEBUG_WITH_TYPE("orc", dbgs() << " failed.\n");
Parent.getEndpoint().abandonPendingResponses();
Parent.reportError(std::move(Err));
}
DEBUG_WITH_TYPE("orc", {
dbgs() << "Leaving finalizeAsync (finalization may continue in "
"background)\n";
});
}
Error deallocate() override {
orcrpctpc::ReleaseOrFinalizeMemRequest RMR;
for (auto &KV : TargetAllocs)
RMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.Address, KV.second.AllocatedSize});
TargetAllocs.clear();
return Parent.getEndpoint().template callB<orcrpctpc::ReleaseMem>(RMR);
}
private:
OrcRPCEPCJITLinkMemoryManager<OrcRPCEPCImplT> &Parent;
HostAllocMap HostAllocs;
TargetAllocMap TargetAllocs;
};
OrcRPCEPCJITLinkMemoryManager(OrcRPCEPCImplT &Parent) : Parent(Parent) {}
Expected<std::unique_ptr<Allocation>>
allocate(const jitlink::JITLinkDylib *JD,
const SegmentsRequestMap &Request) override {
orcrpctpc::ReserveMemRequest RMR;
HostAllocMap HostAllocs;
for (auto &KV : Request) {
assert(KV.second.getContentSize() <= std::numeric_limits<size_t>::max() &&
"Content size is out-of-range for host");
RMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.getContentSize() + KV.second.getZeroFillSize(),
KV.second.getAlignment()});
HostAllocs[KV.first] = {
std::make_unique<char[]>(KV.second.getContentSize()),
KV.second.getContentSize()};
}
DEBUG_WITH_TYPE("orc", {
dbgs() << "Orc remote memmgr got request:\n";
for (auto &KV : Request)
dbgs() << " permissions: "
<< ((KV.first & sys::Memory::MF_READ) ? 'R' : '-')
<< ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-')
<< ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-')
<< ", content size: "
<< formatv("{0:x16}", KV.second.getContentSize())
<< " + zero-fill-size: "
<< formatv("{0:x16}", KV.second.getZeroFillSize())
<< ", align: " << KV.second.getAlignment() << "\n";
});
// FIXME: LLVM RPC needs to be fixed to support alt
// serialization/deserialization on return types. For now just
// translate from std::map to DenseMap manually.
auto TmpTargetAllocs =
Parent.getEndpoint().template callB<orcrpctpc::ReserveMem>(RMR);
if (!TmpTargetAllocs)
return TmpTargetAllocs.takeError();
if (TmpTargetAllocs->size() != RMR.size())
return make_error<StringError>(
"Number of target allocations does not match request",
inconvertibleErrorCode());
TargetAllocMap TargetAllocs;
for (auto &E : *TmpTargetAllocs)
TargetAllocs[tpctypes::fromWireProtectionFlags(E.Prot)] = {
E.Address, E.AllocatedSize};
DEBUG_WITH_TYPE("orc", {
auto HAI = HostAllocs.begin();
for (auto &KV : TargetAllocs)
dbgs() << " permissions: "
<< ((KV.first & sys::Memory::MF_READ) ? 'R' : '-')
<< ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-')
<< ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-')
<< " assigned local " << (void *)HAI->second.Mem.get()
<< ", target " << formatv("{0:x16}", KV.second.Address) << "\n";
});
return std::make_unique<OrcRPCAllocation>(*this, std::move(HostAllocs),
std::move(TargetAllocs));
}
private:
void reportError(Error Err) { Parent.reportError(std::move(Err)); }
decltype(std::declval<OrcRPCEPCImplT>().getEndpoint()) getEndpoint() {
return Parent.getEndpoint();
}
OrcRPCEPCImplT &Parent;
};
/// ExecutorProcessControl::MemoryAccess implementation for a process connected
/// via an ORC RPC endpoint.
template <typename OrcRPCEPCImplT>
class OrcRPCEPCMemoryAccess : public ExecutorProcessControl::MemoryAccess {
public:
OrcRPCEPCMemoryAccess(OrcRPCEPCImplT &Parent) : Parent(Parent) {}
void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
WriteResultFn OnWriteComplete) override {
writeViaRPC<orcrpctpc::WriteUInt8s>(Ws, std::move(OnWriteComplete));
}
void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
WriteResultFn OnWriteComplete) override {
writeViaRPC<orcrpctpc::WriteUInt16s>(Ws, std::move(OnWriteComplete));
}
void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
WriteResultFn OnWriteComplete) override {
writeViaRPC<orcrpctpc::WriteUInt32s>(Ws, std::move(OnWriteComplete));
}
void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
WriteResultFn OnWriteComplete) override {
writeViaRPC<orcrpctpc::WriteUInt64s>(Ws, std::move(OnWriteComplete));
}
void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
WriteResultFn OnWriteComplete) override {
writeViaRPC<orcrpctpc::WriteBuffers>(Ws, std::move(OnWriteComplete));
}
private:
template <typename WriteRPCFunction, typename WriteElementT>
void writeViaRPC(ArrayRef<WriteElementT> Ws, WriteResultFn OnWriteComplete) {
if (auto Err = Parent.getEndpoint().template callAsync<WriteRPCFunction>(
[OWC = std::move(OnWriteComplete)](Error Err2) mutable -> Error {
OWC(std::move(Err2));
return Error::success();
},
Ws)) {
Parent.reportError(std::move(Err));
Parent.getEndpoint().abandonPendingResponses();
}
}
OrcRPCEPCImplT &Parent;
};
// ExecutorProcessControl for a process connected via an ORC RPC Endpoint.
template <typename RPCEndpointT>
class OrcRPCExecutorProcessControlBase : public ExecutorProcessControl {
public:
using ErrorReporter = unique_function<void(Error)>;
using OnCloseConnectionFunction = unique_function<Error(Error)>;
OrcRPCExecutorProcessControlBase(std::shared_ptr<SymbolStringPool> SSP,
RPCEndpointT &EP, ErrorReporter ReportError)
: ExecutorProcessControl(std::move(SSP)),
ReportError(std::move(ReportError)), EP(EP) {
using ThisT = OrcRPCExecutorProcessControlBase<RPCEndpointT>;
EP.template addAsyncHandler<orcrpctpc::RunWrapper>(*this,
&ThisT::runWrapperInJIT);
}
void reportError(Error Err) { ReportError(std::move(Err)); }
RPCEndpointT &getEndpoint() { return EP; }
Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override {
DEBUG_WITH_TYPE("orc", {
dbgs() << "Loading dylib \"" << (DylibPath ? DylibPath : "") << "\" ";
if (!DylibPath)
dbgs() << "(process symbols)";
dbgs() << "\n";
});
if (!DylibPath)
DylibPath = "";
auto H = EP.template callB<orcrpctpc::LoadDylib>(DylibPath);
DEBUG_WITH_TYPE("orc", {
if (H)
dbgs() << " got handle " << formatv("{0:x16}", *H) << "\n";
else
dbgs() << " error, unable to load\n";
});
return H;
}
Expected<std::vector<tpctypes::LookupResult>>
lookupSymbols(ArrayRef<LookupRequest> Request) override {
std::vector<orcrpctpc::RemoteLookupRequest> RR;
for (auto &E : Request) {
RR.push_back({});
RR.back().first = E.Handle;
for (auto &KV : E.Symbols)
RR.back().second.push_back(
{(*KV.first).str(),
KV.second == SymbolLookupFlags::WeaklyReferencedSymbol});
}
DEBUG_WITH_TYPE("orc", {
dbgs() << "Compound lookup:\n";
for (auto &R : Request) {
dbgs() << " In " << formatv("{0:x16}", R.Handle) << ": {";
bool First = true;
for (auto &KV : R.Symbols) {
dbgs() << (First ? "" : ",") << " " << *KV.first;
First = false;
}
dbgs() << " }\n";
}
});
return EP.template callB<orcrpctpc::LookupSymbols>(RR);
}
Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
ArrayRef<std::string> Args) override {
DEBUG_WITH_TYPE("orc", {
dbgs() << "Running as main: " << formatv("{0:x16}", MainFnAddr.getValue())
<< ", args = [";
for (unsigned I = 0; I != Args.size(); ++I)
dbgs() << (I ? "," : "") << " \"" << Args[I] << "\"";
dbgs() << "]\n";
});
auto Result =
EP.template callB<orcrpctpc::RunMain>(MainFnAddr.getValue(), Args);
DEBUG_WITH_TYPE("orc", {
dbgs() << " call to " << formatv("{0:x16}", MainFnAddr.getValue());
if (Result)
dbgs() << " returned result " << *Result << "\n";
else
dbgs() << " failed\n";
});
return Result;
}
void callWrapperAsync(SendResultFunction OnComplete,
ExecutorAddr WrapperFnAddr,
ArrayRef<char> ArgBuffer) override {
DEBUG_WITH_TYPE("orc", {
dbgs() << "Running as wrapper function "
<< formatv("{0:x16}", WrapperFnAddr.getValue()) << " with "
<< formatv("{0:x16}", ArgBuffer.size()) << " argument buffer\n";
});
auto Result = EP.template callB<orcrpctpc::RunWrapper>(
WrapperFnAddr.getValue(),
ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(ArgBuffer.data()),
ArgBuffer.size()));
if (!Result)
OnComplete(shared::WrapperFunctionResult::createOutOfBandError(
toString(Result.takeError())));
OnComplete(std::move(*Result));
}
Error closeConnection(OnCloseConnectionFunction OnCloseConnection) {
DEBUG_WITH_TYPE("orc", dbgs() << "Closing connection to remote\n");
return EP.template callAsync<orcrpctpc::CloseConnection>(
std::move(OnCloseConnection));
}
Error closeConnectionAndWait() {
std::promise<MSVCPError> P;
auto F = P.get_future();
if (auto Err = closeConnection([&](Error Err2) -> Error {
P.set_value(std::move(Err2));
return Error::success();
})) {
EP.abandonAllPendingResponses();
return joinErrors(std::move(Err), F.get());
}
return F.get();
}
protected:
/// Subclasses must call this during construction to initialize the
/// TargetTriple and PageSize members.
Error initializeORCRPCEPCBase() {
if (auto EPI = EP.template callB<orcrpctpc::GetExecutorProcessInfo>()) {
this->TargetTriple = Triple(EPI->Triple);
this->PageSize = EPI->PageSize;
this->JDI = {ExecutorAddr(EPI->DispatchFuncAddr),
ExecutorAddr(EPI->DispatchCtxAddr)};
return Error::success();
} else
return EPI.takeError();
}
private:
Error runWrapperInJIT(
std::function<Error(Expected<shared::WrapperFunctionResult>)> SendResult,
JITTargetAddress FunctionTag, std::vector<uint8_t> ArgBuffer) {
getExecutionSession().runJITDispatchHandler(
[this, SendResult = std::move(SendResult)](
Expected<shared::WrapperFunctionResult> R) {
if (auto Err = SendResult(std::move(R)))
ReportError(std::move(Err));
},
FunctionTag,
{reinterpret_cast<const char *>(ArgBuffer.data()), ArgBuffer.size()});
return Error::success();
}
ErrorReporter ReportError;
RPCEndpointT &EP;
};
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H

View File

@@ -1,618 +0,0 @@
//===-- OrcRPCTPCServer.h -- OrcRPCTargetProcessControl Server --*- 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
//
//===----------------------------------------------------------------------===//
//
// OrcRPCTargetProcessControl server class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/Process.h"
#include <atomic>
namespace llvm {
namespace orc {
namespace orcrpctpc {
struct ExecutorProcessInfo {
std::string Triple;
unsigned PageSize;
JITTargetAddress DispatchFuncAddr;
JITTargetAddress DispatchCtxAddr;
};
struct ReserveMemRequestElement {
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
uint64_t Size = 0;
uint64_t Alignment = 0;
};
using ReserveMemRequest = std::vector<ReserveMemRequestElement>;
struct ReserveMemResultElement {
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
JITTargetAddress Address = 0;
uint64_t AllocatedSize = 0;
};
using ReserveMemResult = std::vector<ReserveMemResultElement>;
struct ReleaseOrFinalizeMemRequestElement {
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
JITTargetAddress Address = 0;
uint64_t Size = 0;
};
using ReleaseOrFinalizeMemRequest =
std::vector<ReleaseOrFinalizeMemRequestElement>;
} // end namespace orcrpctpc
namespace shared {
template <> class SerializationTypeName<WrapperFunctionResult> {
public:
static const char *getName() { return "WrapperFunctionResult"; }
};
template <typename ChannelT>
class SerializationTraits<
ChannelT, WrapperFunctionResult, WrapperFunctionResult,
std::enable_if_t<std::is_base_of<RawByteChannel, ChannelT>::value>> {
public:
static Error serialize(ChannelT &C, const WrapperFunctionResult &E) {
if (auto Err = serializeSeq(C, static_cast<uint64_t>(E.size())))
return Err;
if (E.size() == 0)
return Error::success();
return C.appendBytes(E.data(), E.size());
}
static Error deserialize(ChannelT &C, WrapperFunctionResult &E) {
uint64_t Size;
if (auto Err = deserializeSeq(C, Size))
return Err;
auto Tmp = WrapperFunctionResult::allocate(Size);
if (auto Err = C.readBytes(Tmp.data(), Tmp.size()))
return Err;
E = std::move(Tmp);
return Error::success();
}
};
template <> class SerializationTypeName<tpctypes::UInt8Write> {
public:
static const char *getName() { return "UInt8Write"; }
};
template <> class SerializationTypeName<tpctypes::UInt16Write> {
public:
static const char *getName() { return "UInt16Write"; }
};
template <> class SerializationTypeName<tpctypes::UInt32Write> {
public:
static const char *getName() { return "UInt32Write"; }
};
template <> class SerializationTypeName<tpctypes::UInt64Write> {
public:
static const char *getName() { return "UInt64Write"; }
};
template <> class SerializationTypeName<tpctypes::BufferWrite> {
public:
static const char *getName() { return "BufferWrite"; }
};
template <> class SerializationTypeName<orcrpctpc::ReserveMemRequestElement> {
public:
static const char *getName() { return "ReserveMemRequestElement"; }
};
template <> class SerializationTypeName<orcrpctpc::ReserveMemResultElement> {
public:
static const char *getName() { return "ReserveMemResultElement"; }
};
template <>
class SerializationTypeName<orcrpctpc::ReleaseOrFinalizeMemRequestElement> {
public:
static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; }
};
template <> class SerializationTypeName<orcrpctpc::ExecutorProcessInfo> {
public:
static const char *getName() { return "ExecutorProcessInfo"; }
};
template <typename ChannelT, typename WriteT>
class SerializationTraits<
ChannelT, WriteT, WriteT,
std::enable_if_t<std::is_same<WriteT, tpctypes::UInt8Write>::value ||
std::is_same<WriteT, tpctypes::UInt16Write>::value ||
std::is_same<WriteT, tpctypes::UInt32Write>::value ||
std::is_same<WriteT, tpctypes::UInt64Write>::value>> {
public:
static Error serialize(ChannelT &C, const WriteT &W) {
return serializeSeq(C, W.Address, W.Value);
}
static Error deserialize(ChannelT &C, WriteT &W) {
return deserializeSeq(C, W.Address, W.Value);
}
};
template <typename ChannelT>
class SerializationTraits<
ChannelT, tpctypes::BufferWrite, tpctypes::BufferWrite,
std::enable_if_t<std::is_base_of<RawByteChannel, ChannelT>::value>> {
public:
static Error serialize(ChannelT &C, const tpctypes::BufferWrite &W) {
uint64_t Size = W.Buffer.size();
if (auto Err = serializeSeq(C, W.Address, Size))
return Err;
return C.appendBytes(W.Buffer.data(), Size);
}
static Error deserialize(ChannelT &C, tpctypes::BufferWrite &W) {
JITTargetAddress Address;
uint64_t Size;
if (auto Err = deserializeSeq(C, Address, Size))
return Err;
char *Buffer = jitTargetAddressToPointer<char *>(Address);
if (auto Err = C.readBytes(Buffer, Size))
return Err;
W = {Address, StringRef(Buffer, Size)};
return Error::success();
}
};
template <typename ChannelT>
class SerializationTraits<ChannelT, orcrpctpc::ReserveMemRequestElement> {
public:
static Error serialize(ChannelT &C,
const orcrpctpc::ReserveMemRequestElement &E) {
return serializeSeq(C, static_cast<uint8_t>(E.Prot), E.Size, E.Alignment);
}
static Error deserialize(ChannelT &C,
orcrpctpc::ReserveMemRequestElement &E) {
return deserializeSeq(C, *reinterpret_cast<uint8_t *>(&E.Prot), E.Size,
E.Alignment);
}
};
template <typename ChannelT>
class SerializationTraits<ChannelT, orcrpctpc::ReserveMemResultElement> {
public:
static Error serialize(ChannelT &C,
const orcrpctpc::ReserveMemResultElement &E) {
return serializeSeq(C, static_cast<uint8_t>(E.Prot), E.Address,
E.AllocatedSize);
}
static Error deserialize(ChannelT &C, orcrpctpc::ReserveMemResultElement &E) {
return deserializeSeq(C, *reinterpret_cast<uint8_t *>(&E.Prot), E.Address,
E.AllocatedSize);
}
};
template <typename ChannelT>
class SerializationTraits<ChannelT,
orcrpctpc::ReleaseOrFinalizeMemRequestElement> {
public:
static Error
serialize(ChannelT &C,
const orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) {
return serializeSeq(C, static_cast<uint8_t>(E.Prot), E.Address, E.Size);
}
static Error deserialize(ChannelT &C,
orcrpctpc::ReleaseOrFinalizeMemRequestElement &E) {
return deserializeSeq(C, *reinterpret_cast<uint8_t *>(&E.Prot), E.Address,
E.Size);
}
};
template <typename ChannelT>
class SerializationTraits<ChannelT, orcrpctpc::ExecutorProcessInfo> {
public:
static Error serialize(ChannelT &C,
const orcrpctpc::ExecutorProcessInfo &EPI) {
return serializeSeq(C, EPI.Triple, EPI.PageSize, EPI.DispatchFuncAddr,
EPI.DispatchCtxAddr);
}
static Error deserialize(ChannelT &C, orcrpctpc::ExecutorProcessInfo &EPI) {
return deserializeSeq(C, EPI.Triple, EPI.PageSize, EPI.DispatchFuncAddr,
EPI.DispatchCtxAddr);
}
};
} // end namespace shared
namespace orcrpctpc {
using RemoteSymbolLookupSet = std::vector<std::pair<std::string, bool>>;
using RemoteLookupRequest =
std::pair<tpctypes::DylibHandle, RemoteSymbolLookupSet>;
class GetExecutorProcessInfo
: public shared::RPCFunction<GetExecutorProcessInfo,
orcrpctpc::ExecutorProcessInfo()> {
public:
static const char *getName() { return "GetJITDispatchInfo"; }
};
class ReserveMem
: public shared::RPCFunction<ReserveMem, Expected<ReserveMemResult>(
ReserveMemRequest)> {
public:
static const char *getName() { return "ReserveMem"; }
};
class FinalizeMem
: public shared::RPCFunction<FinalizeMem,
Error(ReleaseOrFinalizeMemRequest)> {
public:
static const char *getName() { return "FinalizeMem"; }
};
class ReleaseMem
: public shared::RPCFunction<ReleaseMem,
Error(ReleaseOrFinalizeMemRequest)> {
public:
static const char *getName() { return "ReleaseMem"; }
};
class WriteUInt8s
: public shared::RPCFunction<WriteUInt8s,
Error(std::vector<tpctypes::UInt8Write>)> {
public:
static const char *getName() { return "WriteUInt8s"; }
};
class WriteUInt16s
: public shared::RPCFunction<WriteUInt16s,
Error(std::vector<tpctypes::UInt16Write>)> {
public:
static const char *getName() { return "WriteUInt16s"; }
};
class WriteUInt32s
: public shared::RPCFunction<WriteUInt32s,
Error(std::vector<tpctypes::UInt32Write>)> {
public:
static const char *getName() { return "WriteUInt32s"; }
};
class WriteUInt64s
: public shared::RPCFunction<WriteUInt64s,
Error(std::vector<tpctypes::UInt64Write>)> {
public:
static const char *getName() { return "WriteUInt64s"; }
};
class WriteBuffers
: public shared::RPCFunction<WriteBuffers,
Error(std::vector<tpctypes::BufferWrite>)> {
public:
static const char *getName() { return "WriteBuffers"; }
};
class LoadDylib
: public shared::RPCFunction<LoadDylib, Expected<tpctypes::DylibHandle>(
std::string DylibPath)> {
public:
static const char *getName() { return "LoadDylib"; }
};
class LookupSymbols
: public shared::RPCFunction<LookupSymbols,
Expected<std::vector<tpctypes::LookupResult>>(
std::vector<RemoteLookupRequest>)> {
public:
static const char *getName() { return "LookupSymbols"; }
};
class RunMain
: public shared::RPCFunction<RunMain,
int64_t(JITTargetAddress MainAddr,
std::vector<std::string> Args)> {
public:
static const char *getName() { return "RunMain"; }
};
class RunWrapper
: public shared::RPCFunction<RunWrapper,
shared::WrapperFunctionResult(
JITTargetAddress, std::vector<uint8_t>)> {
public:
static const char *getName() { return "RunWrapper"; }
};
class CloseConnection : public shared::RPCFunction<CloseConnection, void()> {
public:
static const char *getName() { return "CloseConnection"; }
};
} // end namespace orcrpctpc
/// TargetProcessControl for a process connected via an ORC RPC Endpoint.
template <typename RPCEndpointT> class OrcRPCTPCServer {
private:
using ThisT = OrcRPCTPCServer<RPCEndpointT>;
public:
/// Create an OrcRPCTPCServer from the given endpoint.
OrcRPCTPCServer(RPCEndpointT &EP) : EP(EP) {
TripleStr = sys::getProcessTriple();
PageSize = sys::Process::getPageSizeEstimate();
EP.template addHandler<orcrpctpc::GetExecutorProcessInfo>(
*this, &ThisT::getExecutorProcessInfo);
EP.template addHandler<orcrpctpc::ReserveMem>(*this, &ThisT::reserveMemory);
EP.template addHandler<orcrpctpc::FinalizeMem>(*this,
&ThisT::finalizeMemory);
EP.template addHandler<orcrpctpc::ReleaseMem>(*this, &ThisT::releaseMemory);
EP.template addHandler<orcrpctpc::WriteUInt8s>(
handleWriteUInt<tpctypes::UInt8Write>);
EP.template addHandler<orcrpctpc::WriteUInt16s>(
handleWriteUInt<tpctypes::UInt16Write>);
EP.template addHandler<orcrpctpc::WriteUInt32s>(
handleWriteUInt<tpctypes::UInt32Write>);
EP.template addHandler<orcrpctpc::WriteUInt64s>(
handleWriteUInt<tpctypes::UInt64Write>);
EP.template addHandler<orcrpctpc::WriteBuffers>(handleWriteBuffer);
EP.template addHandler<orcrpctpc::LoadDylib>(*this, &ThisT::loadDylib);
EP.template addHandler<orcrpctpc::LookupSymbols>(*this,
&ThisT::lookupSymbols);
EP.template addHandler<orcrpctpc::RunMain>(*this, &ThisT::runMain);
EP.template addHandler<orcrpctpc::RunWrapper>(*this, &ThisT::runWrapper);
EP.template addHandler<orcrpctpc::CloseConnection>(*this,
&ThisT::closeConnection);
}
/// Set the ProgramName to be used as the first argv element when running
/// functions via runAsMain.
void setProgramName(Optional<std::string> ProgramName = None) {
this->ProgramName = std::move(ProgramName);
}
/// Get the RPC endpoint for this server.
RPCEndpointT &getEndpoint() { return EP; }
/// Run the server loop.
Error run() {
while (!Finished) {
if (auto Err = EP.handleOne())
return Err;
}
return Error::success();
}
Expected<shared::WrapperFunctionResult>
runWrapperInJIT(JITTargetAddress FunctionId, ArrayRef<char> ArgBuffer) {
return EP.template callB<orcrpctpc::RunWrapper>(
FunctionId,
ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(ArgBuffer.data()),
ArgBuffer.size()));
}
private:
static shared::detail::CWrapperFunctionResult
jitDispatchViaOrcRPCTPCServer(void *Ctx, const void *FnTag, const char *Data,
size_t Size) {
assert(Ctx && "Attempt to dispatch with null context ptr");
auto R = static_cast<ThisT *>(Ctx)->runWrapperInJIT(
pointerToJITTargetAddress(FnTag), {Data, Size});
if (!R) {
auto ErrMsg = toString(R.takeError());
return shared::WrapperFunctionResult::createOutOfBandError(ErrMsg.data())
.release();
}
return R->release();
}
orcrpctpc::ExecutorProcessInfo getExecutorProcessInfo() {
return {TripleStr, static_cast<uint32_t>(PageSize),
pointerToJITTargetAddress(jitDispatchViaOrcRPCTPCServer),
pointerToJITTargetAddress(this)};
}
template <typename WriteT>
static void handleWriteUInt(const std::vector<WriteT> &Ws) {
using ValueT = decltype(std::declval<WriteT>().Value);
for (auto &W : Ws)
*jitTargetAddressToPointer<ValueT *>(W.Address) = W.Value;
}
static void handleWriteBuffer(const std::vector<tpctypes::BufferWrite> &Ws) {
for (auto &W : Ws) {
memcpy(jitTargetAddressToPointer<char *>(W.Address), W.Buffer.data(),
W.Buffer.size());
}
}
Expected<orcrpctpc::ReserveMemResult>
reserveMemory(const orcrpctpc::ReserveMemRequest &Request) {
orcrpctpc::ReserveMemResult Allocs;
auto PF = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
uint64_t TotalSize = 0;
for (const auto &E : Request) {
uint64_t Size = alignTo(E.Size, PageSize);
uint16_t Align = E.Alignment;
if ((Align > PageSize) || (PageSize % Align))
return make_error<StringError>(
"Page alignmen does not satisfy requested alignment",
inconvertibleErrorCode());
TotalSize += Size;
}
// Allocate memory slab.
std::error_code EC;
auto MB = sys::Memory::allocateMappedMemory(TotalSize, nullptr, PF, EC);
if (EC)
return make_error<StringError>("Unable to allocate memory: " +
EC.message(),
inconvertibleErrorCode());
// Zero-fill the whole thing.
memset(MB.base(), 0, MB.allocatedSize());
// Carve up sections to return.
uint64_t SectionBase = 0;
for (const auto &E : Request) {
uint64_t SectionSize = alignTo(E.Size, PageSize);
Allocs.push_back({E.Prot,
pointerToJITTargetAddress(MB.base()) + SectionBase,
SectionSize});
SectionBase += SectionSize;
}
return Allocs;
}
Error finalizeMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &FMR) {
for (const auto &E : FMR) {
sys::MemoryBlock MB(jitTargetAddressToPointer<void *>(E.Address), E.Size);
auto PF = tpctypes::fromWireProtectionFlags(E.Prot);
if (auto EC =
sys::Memory::protectMappedMemory(MB, static_cast<unsigned>(PF)))
return make_error<StringError>("error protecting memory: " +
EC.message(),
inconvertibleErrorCode());
}
return Error::success();
}
Error releaseMemory(const orcrpctpc::ReleaseOrFinalizeMemRequest &RMR) {
for (const auto &E : RMR) {
sys::MemoryBlock MB(jitTargetAddressToPointer<void *>(E.Address), E.Size);
if (auto EC = sys::Memory::releaseMappedMemory(MB))
return make_error<StringError>("error release memory: " + EC.message(),
inconvertibleErrorCode());
}
return Error::success();
}
Expected<tpctypes::DylibHandle> loadDylib(const std::string &Path) {
std::string ErrMsg;
const char *DLPath = !Path.empty() ? Path.c_str() : nullptr;
auto DL = sys::DynamicLibrary::getPermanentLibrary(DLPath, &ErrMsg);
if (!DL.isValid())
return make_error<StringError>(std::move(ErrMsg),
inconvertibleErrorCode());
tpctypes::DylibHandle H = Dylibs.size();
Dylibs[H] = std::move(DL);
return H;
}
Expected<std::vector<tpctypes::LookupResult>>
lookupSymbols(const std::vector<orcrpctpc::RemoteLookupRequest> &Request) {
std::vector<tpctypes::LookupResult> Result;
for (const auto &E : Request) {
auto I = Dylibs.find(E.first);
if (I == Dylibs.end())
return make_error<StringError>("Unrecognized handle",
inconvertibleErrorCode());
auto &DL = I->second;
Result.push_back({});
for (const auto &KV : E.second) {
auto &SymString = KV.first;
bool WeakReference = KV.second;
const char *Sym = SymString.c_str();
#ifdef __APPLE__
if (*Sym == '_')
++Sym;
#endif
void *Addr = DL.getAddressOfSymbol(Sym);
if (!Addr && !WeakReference)
return make_error<StringError>(Twine("Missing definition for ") + Sym,
inconvertibleErrorCode());
Result.back().push_back(pointerToJITTargetAddress(Addr));
}
}
return Result;
}
int64_t runMain(JITTargetAddress MainFnAddr,
const std::vector<std::string> &Args) {
Optional<StringRef> ProgramNameOverride;
if (ProgramName)
ProgramNameOverride = *ProgramName;
return runAsMain(
jitTargetAddressToFunction<int (*)(int, char *[])>(MainFnAddr), Args,
ProgramNameOverride);
}
shared::WrapperFunctionResult
runWrapper(JITTargetAddress WrapperFnAddr,
const std::vector<uint8_t> &ArgBuffer) {
using WrapperFnTy = shared::detail::CWrapperFunctionResult (*)(
const char *Data, uint64_t Size);
auto *WrapperFn = jitTargetAddressToFunction<WrapperFnTy>(WrapperFnAddr);
return WrapperFn(reinterpret_cast<const char *>(ArgBuffer.data()),
ArgBuffer.size());
}
void closeConnection() { Finished = true; }
std::string TripleStr;
uint64_t PageSize = 0;
Optional<std::string> ProgramName;
RPCEndpointT &EP;
std::atomic<bool> Finished{false};
DenseMap<tpctypes::DylibHandle, sys::DynamicLibrary> Dylibs;
};
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_ORCRPCTPCSERVER_H