1 //===- ExecutorProcessControl.h - Executor process control APIs -*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Utilities for interacting with the executor processes. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H 14 #define LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H 15 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" 18 #include "llvm/ExecutionEngine/Orc/DylibManager.h" 19 #include "llvm/ExecutionEngine/Orc/MemoryAccess.h" 20 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" 21 #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" 22 #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" 23 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" 24 #include "llvm/ExecutionEngine/Orc/TargetProcess/UnwindInfoManager.h" 25 #include "llvm/ExecutionEngine/Orc/TaskDispatch.h" 26 #include "llvm/Support/Compiler.h" 27 #include "llvm/TargetParser/Triple.h" 28 29 #include <future> 30 #include <mutex> 31 #include <vector> 32 33 namespace llvm::orc { 34 35 class ExecutionSession; 36 37 /// ExecutorProcessControl supports interaction with a JIT target process. 38 class LLVM_ABI ExecutorProcessControl { 39 friend class ExecutionSession; 40 public: 41 42 /// A handler or incoming WrapperFunctionResults -- either return values from 43 /// callWrapper* calls, or incoming JIT-dispatch requests. 44 /// 45 /// IncomingWFRHandlers are constructible from 46 /// unique_function<void(shared::WrapperFunctionResult)>s using the 47 /// runInPlace function or a RunWithDispatch object. 48 class IncomingWFRHandler { 49 friend class ExecutorProcessControl; 50 public: 51 IncomingWFRHandler() = default; 52 explicit operator bool() const { return !!H; } operator()53 void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); } 54 private: IncomingWFRHandler(FnT && Fn)55 template <typename FnT> IncomingWFRHandler(FnT &&Fn) 56 : H(std::forward<FnT>(Fn)) {} 57 58 unique_function<void(shared::WrapperFunctionResult)> H; 59 }; 60 61 /// Constructs an IncomingWFRHandler from a function object that is callable 62 /// as void(shared::WrapperFunctionResult). The function object will be called 63 /// directly. This should be used with care as it may block listener threads 64 /// in remote EPCs. It is only suitable for simple tasks (e.g. setting a 65 /// future), or for performing some quick analysis before dispatching "real" 66 /// work as a Task. 67 class RunInPlace { 68 public: 69 template <typename FnT> operator()70 IncomingWFRHandler operator()(FnT &&Fn) { 71 return IncomingWFRHandler(std::forward<FnT>(Fn)); 72 } 73 }; 74 75 /// Constructs an IncomingWFRHandler from a function object by creating a new 76 /// function object that dispatches the original using a TaskDispatcher, 77 /// wrapping the original as a GenericNamedTask. 78 /// 79 /// This is the default approach for running WFR handlers. 80 class RunAsTask { 81 public: RunAsTask(TaskDispatcher & D)82 RunAsTask(TaskDispatcher &D) : D(D) {} 83 84 template <typename FnT> operator()85 IncomingWFRHandler operator()(FnT &&Fn) { 86 return IncomingWFRHandler( 87 [&D = this->D, Fn = std::move(Fn)] 88 (shared::WrapperFunctionResult WFR) mutable { 89 D.dispatch( 90 makeGenericNamedTask( 91 [Fn = std::move(Fn), WFR = std::move(WFR)]() mutable { 92 Fn(std::move(WFR)); 93 }, "WFR handler task")); 94 }); 95 } 96 private: 97 TaskDispatcher &D; 98 }; 99 100 /// Contains the address of the dispatch function and context that the ORC 101 /// runtime can use to call functions in the JIT. 102 struct JITDispatchInfo { 103 ExecutorAddr JITDispatchFunction; 104 ExecutorAddr JITDispatchContext; 105 }; 106 ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP,std::unique_ptr<TaskDispatcher> D)107 ExecutorProcessControl(std::shared_ptr<SymbolStringPool> SSP, 108 std::unique_ptr<TaskDispatcher> D) 109 : SSP(std::move(SSP)), D(std::move(D)) {} 110 111 virtual ~ExecutorProcessControl(); 112 113 /// Return the ExecutionSession associated with this instance. 114 /// Not callable until the ExecutionSession has been associated. getExecutionSession()115 ExecutionSession &getExecutionSession() { 116 assert(ES && "No ExecutionSession associated yet"); 117 return *ES; 118 } 119 120 /// Intern a symbol name in the SymbolStringPool. intern(StringRef SymName)121 SymbolStringPtr intern(StringRef SymName) { return SSP->intern(SymName); } 122 123 /// Return a shared pointer to the SymbolStringPool for this instance. getSymbolStringPool()124 std::shared_ptr<SymbolStringPool> getSymbolStringPool() const { return SSP; } 125 getDispatcher()126 TaskDispatcher &getDispatcher() { return *D; } 127 128 /// Return the Triple for the target process. getTargetTriple()129 const Triple &getTargetTriple() const { return TargetTriple; } 130 131 /// Get the page size for the target process. getPageSize()132 unsigned getPageSize() const { return PageSize; } 133 134 /// Get the JIT dispatch function and context address for the executor. getJITDispatchInfo()135 const JITDispatchInfo &getJITDispatchInfo() const { return JDI; } 136 137 /// Return a MemoryAccess object for the target process. getMemoryAccess()138 MemoryAccess &getMemoryAccess() const { 139 assert(MemAccess && "No MemAccess object set."); 140 return *MemAccess; 141 } 142 143 /// Return a JITLinkMemoryManager for the target process. getMemMgr()144 jitlink::JITLinkMemoryManager &getMemMgr() const { 145 assert(MemMgr && "No MemMgr object set"); 146 return *MemMgr; 147 } 148 149 /// Return the DylibManager for the target process. getDylibMgr()150 DylibManager &getDylibMgr() const { 151 assert(DylibMgr && "No DylibMgr object set"); 152 return *DylibMgr; 153 } 154 155 /// Returns the bootstrap map. getBootstrapMap()156 const StringMap<std::vector<char>> &getBootstrapMap() const { 157 return BootstrapMap; 158 } 159 160 /// Look up and SPS-deserialize a bootstrap map value. 161 template <typename T, typename SPSTagT> getBootstrapMapValue(StringRef Key,std::optional<T> & Val)162 Error getBootstrapMapValue(StringRef Key, std::optional<T> &Val) const { 163 Val = std::nullopt; 164 165 auto I = BootstrapMap.find(Key); 166 if (I == BootstrapMap.end()) 167 return Error::success(); 168 169 T Tmp; 170 shared::SPSInputBuffer IB(I->second.data(), I->second.size()); 171 if (!shared::SPSArgList<SPSTagT>::deserialize(IB, Tmp)) 172 return make_error<StringError>("Could not deserialize value for key " + 173 Key, 174 inconvertibleErrorCode()); 175 176 Val = std::move(Tmp); 177 return Error::success(); 178 } 179 180 /// Returns the bootstrap symbol map. getBootstrapSymbolsMap()181 const StringMap<ExecutorAddr> &getBootstrapSymbolsMap() const { 182 return BootstrapSymbols; 183 } 184 185 /// For each (ExecutorAddr&, StringRef) pair, looks up the string in the 186 /// bootstrap symbols map and writes its address to the ExecutorAddr if 187 /// found. If any symbol is not found then the function returns an error. getBootstrapSymbols(ArrayRef<std::pair<ExecutorAddr &,StringRef>> Pairs)188 Error getBootstrapSymbols( 189 ArrayRef<std::pair<ExecutorAddr &, StringRef>> Pairs) const { 190 for (const auto &KV : Pairs) { 191 auto I = BootstrapSymbols.find(KV.second); 192 if (I == BootstrapSymbols.end()) 193 return make_error<StringError>("Symbol \"" + KV.second + 194 "\" not found " 195 "in bootstrap symbols map", 196 inconvertibleErrorCode()); 197 198 KV.first = I->second; 199 } 200 return Error::success(); 201 } 202 203 /// Run function with a main-like signature. 204 virtual Expected<int32_t> runAsMain(ExecutorAddr MainFnAddr, 205 ArrayRef<std::string> Args) = 0; 206 207 // TODO: move this to ORC runtime. 208 /// Run function with a int (*)(void) signature. 209 virtual Expected<int32_t> runAsVoidFunction(ExecutorAddr VoidFnAddr) = 0; 210 211 // TODO: move this to ORC runtime. 212 /// Run function with a int (*)(int) signature. 213 virtual Expected<int32_t> runAsIntFunction(ExecutorAddr IntFnAddr, 214 int Arg) = 0; 215 216 /// Run a wrapper function in the executor. The given WFRHandler will be 217 /// called on the result when it is returned. 218 /// 219 /// The wrapper function should be callable as: 220 /// 221 /// \code{.cpp} 222 /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); 223 /// \endcode{.cpp} 224 virtual void callWrapperAsync(ExecutorAddr WrapperFnAddr, 225 IncomingWFRHandler OnComplete, 226 ArrayRef<char> ArgBuffer) = 0; 227 228 /// Run a wrapper function in the executor using the given Runner to dispatch 229 /// OnComplete when the result is ready. 230 template <typename RunPolicyT, typename FnT> callWrapperAsync(RunPolicyT && Runner,ExecutorAddr WrapperFnAddr,FnT && OnComplete,ArrayRef<char> ArgBuffer)231 void callWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr, 232 FnT &&OnComplete, ArrayRef<char> ArgBuffer) { 233 callWrapperAsync( 234 WrapperFnAddr, Runner(std::forward<FnT>(OnComplete)), ArgBuffer); 235 } 236 237 /// Run a wrapper function in the executor. OnComplete will be dispatched 238 /// as a GenericNamedTask using this instance's TaskDispatch object. 239 template <typename FnT> callWrapperAsync(ExecutorAddr WrapperFnAddr,FnT && OnComplete,ArrayRef<char> ArgBuffer)240 void callWrapperAsync(ExecutorAddr WrapperFnAddr, FnT &&OnComplete, 241 ArrayRef<char> ArgBuffer) { 242 callWrapperAsync(RunAsTask(*D), WrapperFnAddr, 243 std::forward<FnT>(OnComplete), ArgBuffer); 244 } 245 246 /// Run a wrapper function in the executor. The wrapper function should be 247 /// callable as: 248 /// 249 /// \code{.cpp} 250 /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); 251 /// \endcode{.cpp} callWrapper(ExecutorAddr WrapperFnAddr,ArrayRef<char> ArgBuffer)252 shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr, 253 ArrayRef<char> ArgBuffer) { 254 std::promise<shared::WrapperFunctionResult> RP; 255 auto RF = RP.get_future(); 256 callWrapperAsync( 257 RunInPlace(), WrapperFnAddr, 258 [&](shared::WrapperFunctionResult R) { 259 RP.set_value(std::move(R)); 260 }, ArgBuffer); 261 return RF.get(); 262 } 263 264 /// Run a wrapper function using SPS to serialize the arguments and 265 /// deserialize the results. 266 template <typename SPSSignature, typename RunPolicyT, typename SendResultT, 267 typename... ArgTs> callSPSWrapperAsync(RunPolicyT && Runner,ExecutorAddr WrapperFnAddr,SendResultT && SendResult,const ArgTs &...Args)268 void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr, 269 SendResultT &&SendResult, const ArgTs &...Args) { 270 shared::WrapperFunction<SPSSignature>::callAsync( 271 [this, WrapperFnAddr, Runner = std::move(Runner)] 272 (auto &&SendResult, const char *ArgData, size_t ArgSize) mutable { 273 this->callWrapperAsync(std::move(Runner), WrapperFnAddr, 274 std::move(SendResult), 275 ArrayRef<char>(ArgData, ArgSize)); 276 }, 277 std::forward<SendResultT>(SendResult), Args...); 278 } 279 280 /// Run a wrapper function using SPS to serialize the arguments and 281 /// deserialize the results. 282 template <typename SPSSignature, typename SendResultT, typename... ArgTs> callSPSWrapperAsync(ExecutorAddr WrapperFnAddr,SendResultT && SendResult,const ArgTs &...Args)283 void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult, 284 const ArgTs &...Args) { 285 callSPSWrapperAsync<SPSSignature>(RunAsTask(*D), WrapperFnAddr, 286 std::forward<SendResultT>(SendResult), 287 Args...); 288 } 289 290 /// Run a wrapper function using SPS to serialize the arguments and 291 /// deserialize the results. 292 /// 293 /// If SPSSignature is a non-void function signature then the second argument 294 /// (the first in the Args list) should be a reference to a return value. 295 template <typename SPSSignature, typename... WrapperCallArgTs> callSPSWrapper(ExecutorAddr WrapperFnAddr,WrapperCallArgTs &&...WrapperCallArgs)296 Error callSPSWrapper(ExecutorAddr WrapperFnAddr, 297 WrapperCallArgTs &&...WrapperCallArgs) { 298 return shared::WrapperFunction<SPSSignature>::call( 299 [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) { 300 return callWrapper(WrapperFnAddr, ArrayRef<char>(ArgData, ArgSize)); 301 }, 302 std::forward<WrapperCallArgTs>(WrapperCallArgs)...); 303 } 304 305 /// Disconnect from the target process. 306 /// 307 /// This should be called after the JIT session is shut down. 308 virtual Error disconnect() = 0; 309 310 protected: 311 312 std::shared_ptr<SymbolStringPool> SSP; 313 std::unique_ptr<TaskDispatcher> D; 314 ExecutionSession *ES = nullptr; 315 Triple TargetTriple; 316 unsigned PageSize = 0; 317 JITDispatchInfo JDI; 318 MemoryAccess *MemAccess = nullptr; 319 jitlink::JITLinkMemoryManager *MemMgr = nullptr; 320 DylibManager *DylibMgr = nullptr; 321 StringMap<std::vector<char>> BootstrapMap; 322 StringMap<ExecutorAddr> BootstrapSymbols; 323 }; 324 325 } // namespace llvm::orc 326 327 #endif // LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H 328