123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- ExecutorProcessControl.h - Executor process control APIs -*- 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
- //
- //===----------------------------------------------------------------------===//
- //
- // Utilities for interacting with the executor processes.
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
- #define LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
- #include "llvm/ADT/StringRef.h"
- #include "llvm/ADT/Triple.h"
- #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
- #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
- #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
- #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
- #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
- #include "llvm/ExecutionEngine/Orc/TaskDispatch.h"
- #include "llvm/Support/DynamicLibrary.h"
- #include "llvm/Support/MSVCErrorWorkarounds.h"
- #include <future>
- #include <mutex>
- #include <vector>
- namespace llvm {
- namespace orc {
- class ExecutionSession;
- class SymbolLookupSet;
- /// ExecutorProcessControl supports interaction with a JIT target process.
- class ExecutorProcessControl {
- friend class ExecutionSession;
- public:
- /// A handler or incoming WrapperFunctionResults -- either return values from
- /// callWrapper* calls, or incoming JIT-dispatch requests.
- ///
- /// IncomingWFRHandlers are constructible from
- /// unique_function<void(shared::WrapperFunctionResult)>s using the
- /// runInPlace function or a RunWithDispatch object.
- class IncomingWFRHandler {
- friend class ExecutorProcessControl;
- public:
- IncomingWFRHandler() = default;
- explicit operator bool() const { return !!H; }
- void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); }
- private:
- template <typename FnT> IncomingWFRHandler(FnT &&Fn)
- : H(std::forward<FnT>(Fn)) {}
- unique_function<void(shared::WrapperFunctionResult)> H;
- };
- /// Constructs an IncomingWFRHandler from a function object that is callable
- /// as void(shared::WrapperFunctionResult). The function object will be called
- /// directly. This should be used with care as it may block listener threads
- /// in remote EPCs. It is only suitable for simple tasks (e.g. setting a
- /// future), or for performing some quick analysis before dispatching "real"
- /// work as a Task.
- class RunInPlace {
- public:
- template <typename FnT>
- IncomingWFRHandler operator()(FnT &&Fn) {
- return IncomingWFRHandler(std::forward<FnT>(Fn));
- }
- };
- /// Constructs an IncomingWFRHandler from a function object by creating a new
- /// function object that dispatches the original using a TaskDispatcher,
- /// wrapping the original as a GenericNamedTask.
- ///
- /// This is the default approach for running WFR handlers.
- class RunAsTask {
- public:
- RunAsTask(TaskDispatcher &D) : D(D) {}
- template <typename FnT>
- IncomingWFRHandler operator()(FnT &&Fn) {
- return IncomingWFRHandler(
- [&D = this->D, Fn = std::move(Fn)]
- (shared::WrapperFunctionResult WFR) mutable {
- D.dispatch(
- makeGenericNamedTask(
- [Fn = std::move(Fn), WFR = std::move(WFR)]() mutable {
- Fn(std::move(WFR));
- }, "WFR handler task"));
- });
- }
- private:
- TaskDispatcher &D;
- };
- /// APIs for manipulating memory in the target process.
- class MemoryAccess {
- public:
- /// Callback function for asynchronous writes.
- using WriteResultFn = unique_function<void(Error)>;
- virtual ~MemoryAccess();
- virtual void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
- WriteResultFn OnWriteComplete) = 0;
- virtual void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
- WriteResultFn OnWriteComplete) = 0;
- virtual void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
- WriteResultFn OnWriteComplete) = 0;
- virtual void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
- WriteResultFn OnWriteComplete) = 0;
- virtual void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
- WriteResultFn OnWriteComplete) = 0;
- Error writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws) {
- std::promise<MSVCPError> ResultP;
- auto ResultF = ResultP.get_future();
- writeUInt8sAsync(Ws,
- [&](Error Err) { ResultP.set_value(std::move(Err)); });
- return ResultF.get();
- }
- Error writeUInt16s(ArrayRef<tpctypes::UInt16Write> Ws) {
- std::promise<MSVCPError> ResultP;
- auto ResultF = ResultP.get_future();
- writeUInt16sAsync(Ws,
- [&](Error Err) { ResultP.set_value(std::move(Err)); });
- return ResultF.get();
- }
- Error writeUInt32s(ArrayRef<tpctypes::UInt32Write> Ws) {
- std::promise<MSVCPError> ResultP;
- auto ResultF = ResultP.get_future();
- writeUInt32sAsync(Ws,
- [&](Error Err) { ResultP.set_value(std::move(Err)); });
- return ResultF.get();
- }
- Error writeUInt64s(ArrayRef<tpctypes::UInt64Write> Ws) {
- std::promise<MSVCPError> ResultP;
- auto ResultF = ResultP.get_future();
- writeUInt64sAsync(Ws,
- [&](Error Err) { ResultP.set_value(std::move(Err)); });
- return ResultF.get();
- }
- Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) {
- std::promise<MSVCPError> ResultP;
- auto ResultF = ResultP.get_future();
- writeBuffersAsync(Ws,
- [&](Error Err) { ResultP.set_value(std::move(Err)); });
- return ResultF.get();
- }
- };
- /// A pair of a dylib and a set of symbols to be looked up.
- struct LookupRequest {
- LookupRequest(tpctypes::DylibHandle Handle, const SymbolLookupSet &Symbols)
- : Handle(Handle), Symbols(Symbols) {}
- tpctypes::DylibHandle Handle;
- const SymbolLookupSet &Symbols;
- };
- /// Contains the address of the dispatch function and context that the ORC
- /// runtime can use to call functions in the JIT.
- struct JITDispatchInfo {
- ExecutorAddr JITDispatchFunction;
- ExecutorAddr JITDispatchContext;
- };
- ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP,
- std::unique_ptr<TaskDispatcher> D)
- : SSP(std::move(SSP)), D(std::move(D)) {}
- virtual ~ExecutorProcessControl();
- /// Return the ExecutionSession associated with this instance.
- /// Not callable until the ExecutionSession has been associated.
- ExecutionSession &getExecutionSession() {
- assert(ES && "No ExecutionSession associated yet");
- return *ES;
- }
- /// Intern a symbol name in the SymbolStringPool.
- SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); }
- /// Return a shared pointer to the SymbolStringPool for this instance.
- std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; }
- TaskDispatcher &getDispatcher() { return *D; }
- /// Return the Triple for the target process.
- const Triple &getTargetTriple() const { return TargetTriple; }
- /// Get the page size for the target process.
- unsigned getPageSize() const { return PageSize; }
- /// Get the JIT dispatch function and context address for the executor.
- const JITDispatchInfo &getJITDispatchInfo() const { return JDI; }
- /// Return a MemoryAccess object for the target process.
- MemoryAccess &getMemoryAccess() const {
- assert(MemAccess && "No MemAccess object set.");
- return *MemAccess;
- }
- /// Return a JITLinkMemoryManager for the target process.
- jitlink::JITLinkMemoryManager &getMemMgr() const {
- assert(MemMgr && "No MemMgr object set");
- return *MemMgr;
- }
- /// Returns the bootstrap symbol map.
- const StringMap<ExecutorAddr> &getBootstrapSymbolsMap() const {
- return BootstrapSymbols;
- }
- /// For each (ExecutorAddr&, StringRef) pair, looks up the string in the
- /// bootstrap symbols map and writes its address to the ExecutorAddr if
- /// found. If any symbol is not found then the function returns an error.
- Error getBootstrapSymbols(
- ArrayRef<std::pair<ExecutorAddr &, StringRef>> Pairs) const {
- for (auto &KV : Pairs) {
- auto I = BootstrapSymbols.find(KV.second);
- if (I == BootstrapSymbols.end())
- return make_error<StringError>("Symbol \"" + KV.second +
- "\" not found "
- "in bootstrap symbols map",
- inconvertibleErrorCode());
- KV.first = I->second;
- }
- return Error::success();
- }
- /// Load the dynamic library at the given path and return a handle to it.
- /// If LibraryPath is null this function will return the global handle for
- /// the target process.
- virtual Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) = 0;
- /// Search for symbols in the target process.
- ///
- /// The result of the lookup is a 2-dimentional array of target addresses
- /// that correspond to the lookup order. If a required symbol is not
- /// found then this method will return an error. If a weakly referenced
- /// symbol is not found then it be assigned a '0' value.
- virtual Expected<std::vector<tpctypes::LookupResult>>
- lookupSymbols(ArrayRef<LookupRequest> Request) = 0;
- /// Run function with a main-like signature.
- virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
- ArrayRef<std::string> Args) = 0;
- /// Run a wrapper function in the executor. The given WFRHandler will be
- /// called on the result when it is returned.
- ///
- /// The wrapper function should be callable as:
- ///
- /// \code{.cpp}
- /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
- /// \endcode{.cpp}
- virtual void callWrapperAsync(ExecutorAddr WrapperFnAddr,
- IncomingWFRHandler OnComplete,
- ArrayRef<char> ArgBuffer) = 0;
- /// Run a wrapper function in the executor using the given Runner to dispatch
- /// OnComplete when the result is ready.
- template <typename RunPolicyT, typename FnT>
- void callWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
- FnT &&OnComplete, ArrayRef<char> ArgBuffer) {
- callWrapperAsync(
- WrapperFnAddr, Runner(std::forward<FnT>(OnComplete)), ArgBuffer);
- }
- /// Run a wrapper function in the executor. OnComplete will be dispatched
- /// as a GenericNamedTask using this instance's TaskDispatch object.
- template <typename FnT>
- void callWrapperAsync(ExecutorAddr WrapperFnAddr, FnT &&OnComplete,
- ArrayRef<char> ArgBuffer) {
- callWrapperAsync(RunAsTask(*D), WrapperFnAddr,
- std::forward<FnT>(OnComplete), ArgBuffer);
- }
- /// Run a wrapper function in the executor. The wrapper function should be
- /// callable as:
- ///
- /// \code{.cpp}
- /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
- /// \endcode{.cpp}
- shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr,
- ArrayRef<char> ArgBuffer) {
- std::promise<shared::WrapperFunctionResult> RP;
- auto RF = RP.get_future();
- callWrapperAsync(
- RunInPlace(), WrapperFnAddr,
- [&](shared::WrapperFunctionResult R) {
- RP.set_value(std::move(R));
- }, ArgBuffer);
- return RF.get();
- }
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- template <typename SPSSignature, typename RunPolicyT, typename SendResultT,
- typename... ArgTs>
- void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
- SendResultT &&SendResult, const ArgTs &...Args) {
- shared::WrapperFunction<SPSSignature>::callAsync(
- [this, WrapperFnAddr, Runner = std::move(Runner)]
- (auto &&SendResult, const char *ArgData, size_t ArgSize) mutable {
- this->callWrapperAsync(std::move(Runner), WrapperFnAddr,
- std::move(SendResult),
- ArrayRef<char>(ArgData, ArgSize));
- },
- std::forward<SendResultT>(SendResult), Args...);
- }
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- template <typename SPSSignature, typename SendResultT, typename... ArgTs>
- void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult,
- const ArgTs &...Args) {
- callSPSWrapperAsync<SPSSignature>(RunAsTask(*D), WrapperFnAddr,
- std::forward<SendResultT>(SendResult),
- Args...);
- }
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- ///
- /// If SPSSignature is a non-void function signature then the second argument
- /// (the first in the Args list) should be a reference to a return value.
- template <typename SPSSignature, typename... WrapperCallArgTs>
- Error callSPSWrapper(ExecutorAddr WrapperFnAddr,
- WrapperCallArgTs &&...WrapperCallArgs) {
- return shared::WrapperFunction<SPSSignature>::call(
- [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) {
- return callWrapper(WrapperFnAddr, ArrayRef<char>(ArgData, ArgSize));
- },
- std::forward<WrapperCallArgTs>(WrapperCallArgs)...);
- }
- /// Disconnect from the target process.
- ///
- /// This should be called after the JIT session is shut down.
- virtual Error disconnect() = 0;
- protected:
- std::shared_ptr<SymbolStringPool> SSP;
- std::unique_ptr<TaskDispatcher> D;
- ExecutionSession *ES = nullptr;
- Triple TargetTriple;
- unsigned PageSize = 0;
- JITDispatchInfo JDI;
- MemoryAccess *MemAccess = nullptr;
- jitlink::JITLinkMemoryManager *MemMgr = nullptr;
- StringMap<ExecutorAddr> BootstrapSymbols;
- };
- /// A ExecutorProcessControl instance that asserts if any of its methods are
- /// used. Suitable for use is unit tests, and by ORC clients who haven't moved
- /// to ExecutorProcessControl-based APIs yet.
- class UnsupportedExecutorProcessControl : public ExecutorProcessControl {
- public:
- UnsupportedExecutorProcessControl(
- std::shared_ptr<SymbolStringPool> SSP = nullptr,
- std::unique_ptr<TaskDispatcher> D = nullptr,
- const std::string &TT = "", unsigned PageSize = 0)
- : ExecutorProcessControl(SSP ? std::move(SSP)
- : std::make_shared<SymbolStringPool>(),
- D ? std::move(D)
- : std::make_unique<InPlaceTaskDispatcher>()) {
- this->TargetTriple = Triple(TT);
- this->PageSize = PageSize;
- }
- Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override {
- llvm_unreachable("Unsupported");
- }
- Expected<std::vector<tpctypes::LookupResult>>
- lookupSymbols(ArrayRef<LookupRequest> Request) override {
- llvm_unreachable("Unsupported");
- }
- Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
- ArrayRef<std::string> Args) override {
- llvm_unreachable("Unsupported");
- }
- void callWrapperAsync(ExecutorAddr WrapperFnAddr,
- IncomingWFRHandler OnComplete,
- ArrayRef<char> ArgBuffer) override {
- llvm_unreachable("Unsupported");
- }
- Error disconnect() override { return Error::success(); }
- };
- /// A ExecutorProcessControl implementation targeting the current process.
- class SelfExecutorProcessControl
- : public ExecutorProcessControl,
- private ExecutorProcessControl::MemoryAccess {
- public:
- SelfExecutorProcessControl(
- std::shared_ptr<SymbolStringPool> SSP, std::unique_ptr<TaskDispatcher> D,
- Triple TargetTriple, unsigned PageSize,
- std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr);
- /// Create a SelfExecutorProcessControl with the given symbol string pool and
- /// memory manager.
- /// If no symbol string pool is given then one will be created.
- /// If no memory manager is given a jitlink::InProcessMemoryManager will
- /// be created and used by default.
- static Expected<std::unique_ptr<SelfExecutorProcessControl>>
- Create(std::shared_ptr<SymbolStringPool> SSP = nullptr,
- std::unique_ptr<TaskDispatcher> D = nullptr,
- std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr = nullptr);
- Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override;
- Expected<std::vector<tpctypes::LookupResult>>
- lookupSymbols(ArrayRef<LookupRequest> Request) override;
- Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr,
- ArrayRef<std::string> Args) override;
- void callWrapperAsync(ExecutorAddr WrapperFnAddr,
- IncomingWFRHandler OnComplete,
- ArrayRef<char> ArgBuffer) override;
- Error disconnect() override;
- private:
- void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
- WriteResultFn OnWriteComplete) override;
- void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
- WriteResultFn OnWriteComplete) override;
- void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
- WriteResultFn OnWriteComplete) override;
- void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
- WriteResultFn OnWriteComplete) override;
- void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
- WriteResultFn OnWriteComplete) override;
- static shared::CWrapperFunctionResult
- jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag,
- const char *Data, size_t Size);
- std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
- char GlobalManglingPrefix = 0;
- std::vector<std::unique_ptr<sys::DynamicLibrary>> DynamicLibraries;
- };
- } // end namespace orc
- } // end namespace llvm
- #endif // LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|