1 | //===------- SimpleRemoteEPC.cpp -- Simple remote executor control --------===// |
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 | #include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h" |
10 | #include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h" |
11 | #include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h" |
12 | #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" |
13 | #include "llvm/Support/FormatVariadic.h" |
14 | |
15 | #define DEBUG_TYPE "orc" |
16 | |
17 | namespace llvm { |
18 | namespace orc { |
19 | |
20 | SimpleRemoteEPC::~SimpleRemoteEPC() { |
21 | #ifndef NDEBUG |
22 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
23 | assert(Disconnected && "Destroyed without disconnection" ); |
24 | #endif // NDEBUG |
25 | } |
26 | |
27 | Expected<tpctypes::DylibHandle> |
28 | SimpleRemoteEPC::loadDylib(const char *DylibPath) { |
29 | return DylibMgr->open(Path: DylibPath, Mode: 0); |
30 | } |
31 | |
32 | /// Async helper to chain together calls to DylibMgr::lookupAsync to fulfill all |
33 | /// all the requests. |
34 | /// FIXME: The dylib manager should support multiple LookupRequests natively. |
35 | static void |
36 | lookupSymbolsAsyncHelper(EPCGenericDylibManager &DylibMgr, |
37 | ArrayRef<SimpleRemoteEPC::LookupRequest> Request, |
38 | std::vector<tpctypes::LookupResult> Result, |
39 | SimpleRemoteEPC::SymbolLookupCompleteFn Complete) { |
40 | if (Request.empty()) |
41 | return Complete(std::move(Result)); |
42 | |
43 | auto &Element = Request.front(); |
44 | DylibMgr.lookupAsync(H: Element.Handle, Lookup: Element.Symbols, |
45 | Complete: [&DylibMgr, Request, Complete = std::move(Complete), |
46 | Result = std::move(Result)](auto R) mutable { |
47 | if (!R) |
48 | return Complete(R.takeError()); |
49 | Result.push_back(x: {}); |
50 | Result.back().reserve(n: R->size()); |
51 | for (auto Addr : *R) |
52 | Result.back().push_back(Addr); |
53 | |
54 | lookupSymbolsAsyncHelper( |
55 | DylibMgr, Request: Request.drop_front(), Result: std::move(Result), |
56 | Complete: std::move(Complete)); |
57 | }); |
58 | } |
59 | |
60 | void SimpleRemoteEPC::lookupSymbolsAsync(ArrayRef<LookupRequest> Request, |
61 | SymbolLookupCompleteFn Complete) { |
62 | lookupSymbolsAsyncHelper(DylibMgr&: *DylibMgr, Request, Result: {}, Complete: std::move(Complete)); |
63 | } |
64 | |
65 | Expected<int32_t> SimpleRemoteEPC::runAsMain(ExecutorAddr MainFnAddr, |
66 | ArrayRef<std::string> Args) { |
67 | int64_t Result = 0; |
68 | if (auto Err = callSPSWrapper<rt::SPSRunAsMainSignature>( |
69 | WrapperFnAddr: RunAsMainAddr, WrapperCallArgs&: Result, WrapperCallArgs&: MainFnAddr, WrapperCallArgs&: Args)) |
70 | return std::move(Err); |
71 | return Result; |
72 | } |
73 | |
74 | Expected<int32_t> SimpleRemoteEPC::runAsVoidFunction(ExecutorAddr VoidFnAddr) { |
75 | int32_t Result = 0; |
76 | if (auto Err = callSPSWrapper<rt::SPSRunAsVoidFunctionSignature>( |
77 | WrapperFnAddr: RunAsVoidFunctionAddr, WrapperCallArgs&: Result, WrapperCallArgs&: VoidFnAddr)) |
78 | return std::move(Err); |
79 | return Result; |
80 | } |
81 | |
82 | Expected<int32_t> SimpleRemoteEPC::runAsIntFunction(ExecutorAddr IntFnAddr, |
83 | int Arg) { |
84 | int32_t Result = 0; |
85 | if (auto Err = callSPSWrapper<rt::SPSRunAsIntFunctionSignature>( |
86 | WrapperFnAddr: RunAsIntFunctionAddr, WrapperCallArgs&: Result, WrapperCallArgs&: IntFnAddr, WrapperCallArgs&: Arg)) |
87 | return std::move(Err); |
88 | return Result; |
89 | } |
90 | |
91 | void SimpleRemoteEPC::callWrapperAsync(ExecutorAddr WrapperFnAddr, |
92 | IncomingWFRHandler OnComplete, |
93 | ArrayRef<char> ArgBuffer) { |
94 | uint64_t SeqNo; |
95 | { |
96 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
97 | SeqNo = getNextSeqNo(); |
98 | assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use" ); |
99 | PendingCallWrapperResults[SeqNo] = std::move(OnComplete); |
100 | } |
101 | |
102 | if (auto Err = sendMessage(OpC: SimpleRemoteEPCOpcode::CallWrapper, SeqNo, |
103 | TagAddr: WrapperFnAddr, ArgBytes: ArgBuffer)) { |
104 | IncomingWFRHandler H; |
105 | |
106 | // We just registered OnComplete, but there may be a race between this |
107 | // thread returning from sendMessage and handleDisconnect being called from |
108 | // the transport's listener thread. If handleDisconnect gets there first |
109 | // then it will have failed 'H' for us. If we get there first (or if |
110 | // handleDisconnect already ran) then we need to take care of it. |
111 | { |
112 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
113 | auto I = PendingCallWrapperResults.find(Val: SeqNo); |
114 | if (I != PendingCallWrapperResults.end()) { |
115 | H = std::move(I->second); |
116 | PendingCallWrapperResults.erase(I); |
117 | } |
118 | } |
119 | |
120 | if (H) |
121 | H(shared::WrapperFunctionResult::createOutOfBandError(Msg: "disconnecting" )); |
122 | |
123 | getExecutionSession().reportError(Err: std::move(Err)); |
124 | } |
125 | } |
126 | |
127 | Error SimpleRemoteEPC::disconnect() { |
128 | T->disconnect(); |
129 | D->shutdown(); |
130 | std::unique_lock<std::mutex> Lock(SimpleRemoteEPCMutex); |
131 | DisconnectCV.wait(lock&: Lock, p: [this] { return Disconnected; }); |
132 | return std::move(DisconnectErr); |
133 | } |
134 | |
135 | Expected<SimpleRemoteEPCTransportClient::HandleMessageAction> |
136 | SimpleRemoteEPC::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo, |
137 | ExecutorAddr TagAddr, |
138 | SimpleRemoteEPCArgBytesVector ArgBytes) { |
139 | |
140 | LLVM_DEBUG({ |
141 | dbgs() << "SimpleRemoteEPC::handleMessage: opc = " ; |
142 | switch (OpC) { |
143 | case SimpleRemoteEPCOpcode::Setup: |
144 | dbgs() << "Setup" ; |
145 | assert(SeqNo == 0 && "Non-zero SeqNo for Setup?" ); |
146 | assert(!TagAddr && "Non-zero TagAddr for Setup?" ); |
147 | break; |
148 | case SimpleRemoteEPCOpcode::Hangup: |
149 | dbgs() << "Hangup" ; |
150 | assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?" ); |
151 | assert(!TagAddr && "Non-zero TagAddr for Hangup?" ); |
152 | break; |
153 | case SimpleRemoteEPCOpcode::Result: |
154 | dbgs() << "Result" ; |
155 | assert(!TagAddr && "Non-zero TagAddr for Result?" ); |
156 | break; |
157 | case SimpleRemoteEPCOpcode::CallWrapper: |
158 | dbgs() << "CallWrapper" ; |
159 | break; |
160 | } |
161 | dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr |
162 | << ", arg-buffer = " << formatv("{0:x}" , ArgBytes.size()) |
163 | << " bytes\n" ; |
164 | }); |
165 | |
166 | using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>; |
167 | if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC)) |
168 | return make_error<StringError>(Args: "Unexpected opcode" , |
169 | Args: inconvertibleErrorCode()); |
170 | |
171 | switch (OpC) { |
172 | case SimpleRemoteEPCOpcode::Setup: |
173 | if (auto Err = handleSetup(SeqNo, TagAddr, ArgBytes: std::move(ArgBytes))) |
174 | return std::move(Err); |
175 | break; |
176 | case SimpleRemoteEPCOpcode::Hangup: |
177 | T->disconnect(); |
178 | if (auto Err = handleHangup(ArgBytes: std::move(ArgBytes))) |
179 | return std::move(Err); |
180 | return EndSession; |
181 | case SimpleRemoteEPCOpcode::Result: |
182 | if (auto Err = handleResult(SeqNo, TagAddr, ArgBytes: std::move(ArgBytes))) |
183 | return std::move(Err); |
184 | break; |
185 | case SimpleRemoteEPCOpcode::CallWrapper: |
186 | handleCallWrapper(RemoteSeqNo: SeqNo, TagAddr, ArgBytes: std::move(ArgBytes)); |
187 | break; |
188 | } |
189 | return ContinueSession; |
190 | } |
191 | |
192 | void SimpleRemoteEPC::handleDisconnect(Error Err) { |
193 | LLVM_DEBUG({ |
194 | dbgs() << "SimpleRemoteEPC::handleDisconnect: " |
195 | << (Err ? "failure" : "success" ) << "\n" ; |
196 | }); |
197 | |
198 | PendingCallWrapperResultsMap TmpPending; |
199 | |
200 | { |
201 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
202 | std::swap(a&: TmpPending, b&: PendingCallWrapperResults); |
203 | } |
204 | |
205 | for (auto &KV : TmpPending) |
206 | KV.second( |
207 | shared::WrapperFunctionResult::createOutOfBandError(Msg: "disconnecting" )); |
208 | |
209 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
210 | DisconnectErr = joinErrors(E1: std::move(DisconnectErr), E2: std::move(Err)); |
211 | Disconnected = true; |
212 | DisconnectCV.notify_all(); |
213 | } |
214 | |
215 | Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>> |
216 | SimpleRemoteEPC::createDefaultMemoryManager(SimpleRemoteEPC &SREPC) { |
217 | EPCGenericJITLinkMemoryManager::SymbolAddrs SAs; |
218 | if (auto Err = SREPC.getBootstrapSymbols( |
219 | Pairs: {{SAs.Allocator, rt::SimpleExecutorMemoryManagerInstanceName}, |
220 | {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName}, |
221 | {SAs.Finalize, rt::SimpleExecutorMemoryManagerFinalizeWrapperName}, |
222 | {SAs.Deallocate, |
223 | rt::SimpleExecutorMemoryManagerDeallocateWrapperName}})) |
224 | return std::move(Err); |
225 | |
226 | return std::make_unique<EPCGenericJITLinkMemoryManager>(args&: SREPC, args&: SAs); |
227 | } |
228 | |
229 | Expected<std::unique_ptr<ExecutorProcessControl::MemoryAccess>> |
230 | SimpleRemoteEPC::createDefaultMemoryAccess(SimpleRemoteEPC &SREPC) { |
231 | return nullptr; |
232 | } |
233 | |
234 | Error SimpleRemoteEPC::sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo, |
235 | ExecutorAddr TagAddr, |
236 | ArrayRef<char> ArgBytes) { |
237 | assert(OpC != SimpleRemoteEPCOpcode::Setup && |
238 | "SimpleRemoteEPC sending Setup message? That's the wrong direction." ); |
239 | |
240 | LLVM_DEBUG({ |
241 | dbgs() << "SimpleRemoteEPC::sendMessage: opc = " ; |
242 | switch (OpC) { |
243 | case SimpleRemoteEPCOpcode::Hangup: |
244 | dbgs() << "Hangup" ; |
245 | assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?" ); |
246 | assert(!TagAddr && "Non-zero TagAddr for Hangup?" ); |
247 | break; |
248 | case SimpleRemoteEPCOpcode::Result: |
249 | dbgs() << "Result" ; |
250 | assert(!TagAddr && "Non-zero TagAddr for Result?" ); |
251 | break; |
252 | case SimpleRemoteEPCOpcode::CallWrapper: |
253 | dbgs() << "CallWrapper" ; |
254 | break; |
255 | default: |
256 | llvm_unreachable("Invalid opcode" ); |
257 | } |
258 | dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr |
259 | << ", arg-buffer = " << formatv("{0:x}" , ArgBytes.size()) |
260 | << " bytes\n" ; |
261 | }); |
262 | auto Err = T->sendMessage(OpC, SeqNo, TagAddr, ArgBytes); |
263 | LLVM_DEBUG({ |
264 | if (Err) |
265 | dbgs() << " \\--> SimpleRemoteEPC::sendMessage failed\n" ; |
266 | }); |
267 | return Err; |
268 | } |
269 | |
270 | Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddr TagAddr, |
271 | SimpleRemoteEPCArgBytesVector ArgBytes) { |
272 | if (SeqNo != 0) |
273 | return make_error<StringError>(Args: "Setup packet SeqNo not zero" , |
274 | Args: inconvertibleErrorCode()); |
275 | |
276 | if (TagAddr) |
277 | return make_error<StringError>(Args: "Setup packet TagAddr not zero" , |
278 | Args: inconvertibleErrorCode()); |
279 | |
280 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
281 | auto I = PendingCallWrapperResults.find(Val: 0); |
282 | assert(PendingCallWrapperResults.size() == 1 && |
283 | I != PendingCallWrapperResults.end() && |
284 | "Setup message handler not connectly set up" ); |
285 | auto SetupMsgHandler = std::move(I->second); |
286 | PendingCallWrapperResults.erase(I); |
287 | |
288 | auto WFR = |
289 | shared::WrapperFunctionResult::copyFrom(Source: ArgBytes.data(), Size: ArgBytes.size()); |
290 | SetupMsgHandler(std::move(WFR)); |
291 | return Error::success(); |
292 | } |
293 | |
294 | Error SimpleRemoteEPC::setup(Setup S) { |
295 | using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames; |
296 | |
297 | std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> EIP; |
298 | auto EIF = EIP.get_future(); |
299 | |
300 | // Prepare a handler for the setup packet. |
301 | PendingCallWrapperResults[0] = |
302 | RunInPlace()( |
303 | [&](shared::WrapperFunctionResult SetupMsgBytes) { |
304 | if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) { |
305 | EIP.set_value( |
306 | make_error<StringError>(Args&: ErrMsg, Args: inconvertibleErrorCode())); |
307 | return; |
308 | } |
309 | using SPSSerialize = |
310 | shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>; |
311 | shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size()); |
312 | SimpleRemoteEPCExecutorInfo EI; |
313 | if (SPSSerialize::deserialize(IB, Arg&: EI)) |
314 | EIP.set_value(EI); |
315 | else |
316 | EIP.set_value(make_error<StringError>( |
317 | Args: "Could not deserialize setup message" , Args: inconvertibleErrorCode())); |
318 | }); |
319 | |
320 | // Start the transport. |
321 | if (auto Err = T->start()) |
322 | return Err; |
323 | |
324 | // Wait for setup packet to arrive. |
325 | auto EI = EIF.get(); |
326 | if (!EI) { |
327 | T->disconnect(); |
328 | return EI.takeError(); |
329 | } |
330 | |
331 | LLVM_DEBUG({ |
332 | dbgs() << "SimpleRemoteEPC received setup message:\n" |
333 | << " Triple: " << EI->TargetTriple << "\n" |
334 | << " Page size: " << EI->PageSize << "\n" |
335 | << " Bootstrap map" << (EI->BootstrapMap.empty() ? " empty" : ":" ) |
336 | << "\n" ; |
337 | for (const auto &KV : EI->BootstrapMap) |
338 | dbgs() << " " << KV.first() << ": " << KV.second.size() |
339 | << "-byte SPS encoded buffer\n" ; |
340 | dbgs() << " Bootstrap symbols" |
341 | << (EI->BootstrapSymbols.empty() ? " empty" : ":" ) << "\n" ; |
342 | for (const auto &KV : EI->BootstrapSymbols) |
343 | dbgs() << " " << KV.first() << ": " << KV.second << "\n" ; |
344 | }); |
345 | TargetTriple = Triple(EI->TargetTriple); |
346 | PageSize = EI->PageSize; |
347 | BootstrapMap = std::move(EI->BootstrapMap); |
348 | BootstrapSymbols = std::move(EI->BootstrapSymbols); |
349 | |
350 | if (auto Err = getBootstrapSymbols( |
351 | Pairs: {{JDI.JITDispatchContext, ExecutorSessionObjectName}, |
352 | {JDI.JITDispatchFunction, DispatchFnName}, |
353 | {RunAsMainAddr, rt::RunAsMainWrapperName}, |
354 | {RunAsVoidFunctionAddr, rt::RunAsVoidFunctionWrapperName}, |
355 | {RunAsIntFunctionAddr, rt::RunAsIntFunctionWrapperName}})) |
356 | return Err; |
357 | |
358 | if (auto DM = |
359 | EPCGenericDylibManager::CreateWithDefaultBootstrapSymbols(EPC&: *this)) |
360 | DylibMgr = std::make_unique<EPCGenericDylibManager>(args: std::move(*DM)); |
361 | else |
362 | return DM.takeError(); |
363 | |
364 | // Set a default CreateMemoryManager if none is specified. |
365 | if (!S.CreateMemoryManager) |
366 | S.CreateMemoryManager = createDefaultMemoryManager; |
367 | |
368 | if (auto MemMgr = S.CreateMemoryManager(*this)) { |
369 | OwnedMemMgr = std::move(*MemMgr); |
370 | this->MemMgr = OwnedMemMgr.get(); |
371 | } else |
372 | return MemMgr.takeError(); |
373 | |
374 | // Set a default CreateMemoryAccess if none is specified. |
375 | if (!S.CreateMemoryAccess) |
376 | S.CreateMemoryAccess = createDefaultMemoryAccess; |
377 | |
378 | if (auto MemAccess = S.CreateMemoryAccess(*this)) { |
379 | OwnedMemAccess = std::move(*MemAccess); |
380 | this->MemAccess = OwnedMemAccess.get(); |
381 | } else |
382 | return MemAccess.takeError(); |
383 | |
384 | return Error::success(); |
385 | } |
386 | |
387 | Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddr TagAddr, |
388 | SimpleRemoteEPCArgBytesVector ArgBytes) { |
389 | IncomingWFRHandler SendResult; |
390 | |
391 | if (TagAddr) |
392 | return make_error<StringError>(Args: "Unexpected TagAddr in result message" , |
393 | Args: inconvertibleErrorCode()); |
394 | |
395 | { |
396 | std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex); |
397 | auto I = PendingCallWrapperResults.find(Val: SeqNo); |
398 | if (I == PendingCallWrapperResults.end()) |
399 | return make_error<StringError>(Args: "No call for sequence number " + |
400 | Twine(SeqNo), |
401 | Args: inconvertibleErrorCode()); |
402 | SendResult = std::move(I->second); |
403 | PendingCallWrapperResults.erase(I); |
404 | releaseSeqNo(SeqNo); |
405 | } |
406 | |
407 | auto WFR = |
408 | shared::WrapperFunctionResult::copyFrom(Source: ArgBytes.data(), Size: ArgBytes.size()); |
409 | SendResult(std::move(WFR)); |
410 | return Error::success(); |
411 | } |
412 | |
413 | void SimpleRemoteEPC::handleCallWrapper( |
414 | uint64_t RemoteSeqNo, ExecutorAddr TagAddr, |
415 | SimpleRemoteEPCArgBytesVector ArgBytes) { |
416 | assert(ES && "No ExecutionSession attached" ); |
417 | D->dispatch(T: makeGenericNamedTask( |
418 | Fn: [this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() { |
419 | ES->runJITDispatchHandler( |
420 | SendResult: [this, RemoteSeqNo](shared::WrapperFunctionResult WFR) { |
421 | if (auto Err = |
422 | sendMessage(OpC: SimpleRemoteEPCOpcode::Result, SeqNo: RemoteSeqNo, |
423 | TagAddr: ExecutorAddr(), ArgBytes: {WFR.data(), WFR.size()})) |
424 | getExecutionSession().reportError(Err: std::move(Err)); |
425 | }, |
426 | HandlerFnTagAddr: TagAddr, ArgBuffer: ArgBytes); |
427 | }, |
428 | Desc: "callWrapper task" )); |
429 | } |
430 | |
431 | Error SimpleRemoteEPC::handleHangup(SimpleRemoteEPCArgBytesVector ArgBytes) { |
432 | using namespace llvm::orc::shared; |
433 | auto WFR = WrapperFunctionResult::copyFrom(Source: ArgBytes.data(), Size: ArgBytes.size()); |
434 | if (const char *ErrMsg = WFR.getOutOfBandError()) |
435 | return make_error<StringError>(Args&: ErrMsg, Args: inconvertibleErrorCode()); |
436 | |
437 | detail::SPSSerializableError Info; |
438 | SPSInputBuffer IB(WFR.data(), WFR.size()); |
439 | if (!SPSArgList<SPSError>::deserialize(IB, Arg&: Info)) |
440 | return make_error<StringError>(Args: "Could not deserialize hangup info" , |
441 | Args: inconvertibleErrorCode()); |
442 | return fromSPSSerializable(BSE: std::move(Info)); |
443 | } |
444 | |
445 | } // end namespace orc |
446 | } // end namespace llvm |
447 | |