LLVM 24.0.0git
SimpleRemoteEPC.cpp
Go to the documentation of this file.
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
18
19#define DEBUG_TYPE "orc"
20
21namespace llvm {
22namespace orc {
23
25#ifndef NDEBUG
26 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
27 assert(Disconnected && "Destroyed without disconnection");
28#endif // NDEBUG
29}
30
33 if (!CallMain)
34 if (auto Err =
35 lookupAndApply(getExecutionSession().getBootstrapJITDylib(),
37 return Err;
38
39 auto Result = CallMain(getExecutionSession(), MainFnAddr, Args);
40 if (!Result)
41 return Result.takeError();
42 return *Result;
43}
44
46 IncomingWFRHandler OnComplete,
47 ArrayRef<char> ArgBuffer) {
48 uint64_t SeqNo;
49 {
50 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
51 SeqNo = getNextSeqNo();
52 assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use");
53 PendingCallWrapperResults[SeqNo] = std::move(OnComplete);
54 }
55
56 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
57 WrapperFnAddr, ArgBuffer)) {
59
60 // We just registered OnComplete, but there may be a race between this
61 // thread returning from sendMessage and handleDisconnect being called from
62 // the transport's listener thread. If handleDisconnect gets there first
63 // then it will have failed 'H' for us. If we get there first (or if
64 // handleDisconnect already ran) then we need to take care of it.
65 {
66 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
67 auto I = PendingCallWrapperResults.find(SeqNo);
68 if (I != PendingCallWrapperResults.end()) {
69 H = std::move(I->second);
70 PendingCallWrapperResults.erase(I);
71 }
72 }
73
74 if (H)
76
77 getExecutionSession().reportError(std::move(Err));
78 }
79}
80
85
90
95
97 // disconnect is idempotent, so the first caller owns the hangup. There is
98 // also nothing to announce to an executor that has already announced its own
99 // departure.
100 bool SendHangup = false;
101 {
102 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
103 SendHangup = !LocalHangup && !RemoteHangup;
104 LocalHangup = true;
105 }
106
107 // Tell the executor we're going away, so that it can distinguish this from
108 // losing us unexpectedly. Best-effort: if the send fails there is nothing to
109 // do but tear down anyway, and the executor will report the disconnection as
110 // unexpected. A locally requested disconnect is orderly, so the hangup
111 // carries a success value.
112 if (SendHangup) {
113 auto Payload = encodeHangupPayload(Error::success());
114 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::Hangup, 0, ExecutorAddr(),
115 {Payload.data(), Payload.size()}))
116 consumeError(std::move(Err));
117 }
118
119 T->disconnect();
120 D->shutdown();
121 std::unique_lock<std::mutex> Lock(SimpleRemoteEPCMutex);
122 DisconnectCV.wait(Lock, [this] { return Disconnected; });
123 return std::move(DisconnectErr);
124}
125
128 ExecutorAddr TagAddr,
130
131 LLVM_DEBUG({
132 dbgs() << "SimpleRemoteEPC::handleMessage: opc = ";
133 switch (OpC) {
135 dbgs() << "Setup";
136 assert(SeqNo == 0 && "Non-zero SeqNo for Setup?");
137 assert(!TagAddr && "Non-zero TagAddr for Setup?");
138 break;
140 dbgs() << "Hangup";
141 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
142 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
143 break;
145 dbgs() << "Result";
146 break;
148 dbgs() << "CallWrapper";
149 break;
150 }
151 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
152 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
153 << " bytes\n";
154 });
155
156 using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
157 if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
158 return make_error<StringError>("Unexpected opcode",
160
161 switch (OpC) {
163 if (auto Err = handleSetup(SeqNo, TagAddr, std::move(ArgBytes)))
164 return std::move(Err);
165 break;
167 T->disconnect();
168 {
169 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
170 RemoteHangup = true;
171 }
172 if (auto Err = handleHangup(std::move(ArgBytes)))
173 return std::move(Err);
174 return EndSession;
176 if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
177 return std::move(Err);
178 break;
180 handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
181 break;
182 }
183 return ContinueSession;
184}
185
187 LLVM_DEBUG({
188 dbgs() << "SimpleRemoteEPC::handleDisconnect: "
189 << (Err ? "failure" : "success") << "\n";
190 });
191
192 PendingCallWrapperResultsMap TmpPending;
193
194 {
195 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
196 std::swap(TmpPending, PendingCallWrapperResults);
197 }
198
199 for (auto &KV : TmpPending)
200 KV.second(
202
203 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
204
205 // If the transport reported no error, but neither side announced the end of
206 // the session, then the executor went away without telling us. The cause is
207 // not knowable from here -- it may have crashed, been killed, or become
208 // unreachable -- so report what was observed rather than a cause.
209 //
210 // A missing hangup is evidence, not proof: a hangup can also be lost in
211 // transit, since closing a TCP socket with unread data queued sends an RST,
212 // which can discard bytes the peer had already delivered. We accept that
213 // rather than draining the read side before closing -- the cost is a
214 // misleading diagnostic on a session that is ending regardless, whereas a
215 // drain risks stalling teardown on a peer that never closes.
216 Error DisconnectReason =
217 (!Err && !LocalHangup && !RemoteHangup)
218 ? make_error<StringError>("Connection closed without hangup",
220 : std::move(Err);
221
222 DisconnectErr =
223 joinErrors(std::move(DisconnectErr), std::move(DisconnectReason));
224 Disconnected = true;
225 DisconnectCV.notify_all();
226}
227
228Error SimpleRemoteEPC::sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
229 ExecutorAddr TagAddr,
230 ArrayRef<char> ArgBytes) {
232 "SimpleRemoteEPC sending Setup message? That's the wrong direction.");
233
234 LLVM_DEBUG({
235 dbgs() << "SimpleRemoteEPC::sendMessage: opc = ";
236 switch (OpC) {
238 dbgs() << "Hangup";
239 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
240 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
241 break;
243 dbgs() << "Result";
244 break;
246 dbgs() << "CallWrapper";
247 break;
248 default:
249 llvm_unreachable("Invalid opcode");
250 }
251 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
252 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
253 << " bytes\n";
254 });
255 auto Err = T->sendMessage(OpC, SeqNo, TagAddr, ArgBytes);
256 LLVM_DEBUG({
257 if (Err)
258 dbgs() << " \\--> SimpleRemoteEPC::sendMessage failed\n";
259 });
260 return Err;
261}
262
263Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddr TagAddr,
265 if (SeqNo != 0)
266 return make_error<StringError>("Setup packet SeqNo not zero",
268
269 if (TagAddr)
270 return make_error<StringError>("Setup packet TagAddr not zero",
272
273 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
274 auto I = PendingCallWrapperResults.find(0);
275 assert(PendingCallWrapperResults.size() == 1 &&
276 I != PendingCallWrapperResults.end() &&
277 "Setup message handler not connectly set up");
278 auto SetupMsgHandler = std::move(I->second);
279 PendingCallWrapperResults.erase(I);
280
281 auto WFR =
282 shared::WrapperFunctionBuffer::copyFrom(ArgBytes.data(), ArgBytes.size());
283 SetupMsgHandler(std::move(WFR));
284 return Error::success();
285}
286
287Error SimpleRemoteEPC::setup() {
288 using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
289
290 std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> EIP;
291 auto EIF = EIP.get_future();
292
293 // Prepare a handler for the setup packet.
294 PendingCallWrapperResults[0] =
295 RunInPlace()(
296 [&](shared::WrapperFunctionBuffer SetupMsgBytes) {
297 if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
298 EIP.set_value(
300 return;
301 }
302 using SPSSerialize =
303 shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
304 shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size());
305 SimpleRemoteEPCExecutorInfo EI;
306 if (SPSSerialize::deserialize(IB, EI))
307 EIP.set_value(EI);
308 else
309 EIP.set_value(make_error<StringError>(
310 "Could not deserialize setup message", inconvertibleErrorCode()));
311 });
312
313 // Start the transport.
314 if (auto Err = T->start())
315 return Err;
316
317 // Wait for setup packet to arrive.
318 auto EI = EIF.get();
319 if (!EI) {
320 T->disconnect();
321 return EI.takeError();
322 }
323
324 LLVM_DEBUG({
325 dbgs() << "SimpleRemoteEPC received setup message:\n"
326 << " Triple: " << EI->TargetTriple << "\n"
327 << " Page size: " << EI->PageSize << "\n"
328 << " Bootstrap map" << (EI->BootstrapMap.empty() ? " empty" : ":")
329 << "\n";
330 for (const auto &KV : EI->BootstrapMap)
331 dbgs() << " " << KV.first() << ": " << KV.second.size()
332 << "-byte SPS encoded buffer\n";
333 dbgs() << " Bootstrap symbols"
334 << (EI->BootstrapSymbols.empty() ? " empty" : ":") << "\n";
335 for (const auto &KV : EI->BootstrapSymbols)
336 dbgs() << " " << KV.first() << ": " << KV.second << "\n";
337 });
338 TargetTriple = Triple(EI->TargetTriple);
339 PageSize = EI->PageSize;
340 BootstrapMap = std::move(EI->BootstrapMap);
341 BootstrapSymbols = std::move(EI->BootstrapSymbols);
342
346
347 return Error::success();
348}
349
350Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddr TagAddr,
352 IncomingWFRHandler SendResult;
353
354 auto WFR = decodeResultMessage(TagAddr, std::move(ArgBytes));
355 if (!WFR)
356 return WFR.takeError();
357
358 {
359 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
360 auto I = PendingCallWrapperResults.find(SeqNo);
361 if (I == PendingCallWrapperResults.end())
362 return make_error<StringError>("No call for sequence number " +
363 Twine(SeqNo),
365 SendResult = std::move(I->second);
366 PendingCallWrapperResults.erase(I);
367 releaseSeqNo(SeqNo);
368 }
369
370 SendResult(std::move(*WFR));
371 return Error::success();
372}
373
374void SimpleRemoteEPC::handleCallWrapper(
375 uint64_t RemoteSeqNo, ExecutorAddr TagAddr,
377 assert(ES && "No ExecutionSession attached");
378 D->dispatch(makeGenericNamedTask(
379 [this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() mutable {
380 ES->runJITDispatchHandler(
381 [this, RemoteSeqNo](shared::WrapperFunctionBuffer WFR) {
382 auto [ResultTag, Payload] = encodeResultMessage(std::move(WFR));
383 if (auto Err =
384 sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
385 ResultTag, {Payload.data(), Payload.size()}))
386 getExecutionSession().reportError(std::move(Err));
387 },
388 TagAddr, std::move(ArgBytes));
389 },
390 "callWrapper task"));
391}
392
393Error SimpleRemoteEPC::handleHangup(shared::WrapperFunctionBuffer ArgBytes) {
394 return decodeHangupPayload(std::move(ArgBytes));
395}
396
397} // end namespace orc
398} // end namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
#define I(x, y, z)
Definition MD5.cpp:57
#define H(x, y, z)
Definition MD5.cpp:56
#define T
#define LLVM_DEBUG(...)
Definition Debug.h:119
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
void reportError(Error Err)
Report a error for this execution session.
Definition Core.h:1267
Represents an address in the executor process.
A handler or incoming WrapperFunctionBuffers – either return values from callWrapper* calls,...
Constructs an IncomingWFRHandler from a function object that is callable as void(shared::WrapperFunct...
std::unique_ptr< TaskDispatcher > D
StringMap< ExecutorAddr > BootstrapSymbols
StringMap< std::vector< char > > BootstrapMap
ExecutionSession & getExecutionSession()
Return the ExecutionSession associated with this instance.
void handleDisconnect(Error Err) override
Handle a disconnection from the underlying transport.
Expected< std::unique_ptr< MemoryAccess > > createDefaultMemoryAccess() override
Create a default MemoryAccess for the target process.
Expected< int32_t > runAsMain(ExecutorAddr MainFnAddr, ArrayRef< std::string > Args) override
Run function with a main-like signature.
Expected< std::unique_ptr< jitlink::JITLinkMemoryManager > > createDefaultMemoryManager() override
Create a default JITLinkMemoryManager for the target process.
Expected< HandleMessageAction > handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo, ExecutorAddr TagAddr, shared::WrapperFunctionBuffer ArgBytes) override
Handle receipt of a message.
Expected< std::unique_ptr< DylibManager > > createDefaultDylibMgr() override
Create a default DylibManager for the target process.
Error disconnect() override
Disconnect from the target process.
void callWrapperAsync(ExecutorAddr WrapperFnAddr, IncomingWFRHandler OnComplete, ArrayRef< char > ArgBuffer) override
Run a wrapper function in the executor.
C++ wrapper function buffer: Same as CWrapperFunctionBuffer but auto-releases memory.
static WrapperFunctionBuffer copyFrom(const char *Source, size_t Size)
Copy from the given char range.
size_t size() const
Returns the size of the data contained in this instance.
static WrapperFunctionBuffer createOutOfBandError(const char *Msg)
Create an out-of-band error by copying the given string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_ABI const char *const DispatchCtxName
LLVM_ABI const char *const DispatchName
LLVM_ABI Expected< std::unique_ptr< EPCGenericJITLinkMemoryManager > > createEPCGenericJITLinkMemoryManager(JITDylib &JD)
Create an EPCGenericJITLinkMemoryManager for the ORC runtime's SimpleNativeMemoryMap interface,...
LLVM_ABI Expected< std::unique_ptr< EPCGenericDylibManager > > createEPCGenericDylibManager(JITDylib &JD)
Create an EPCGenericDylibManager for the ORC runtime's NativeDylibManager interface,...
LLVM_ABI Expected< std::unique_ptr< EPCGenericMemoryAccess > > createEPCGenericMemoryAccess(JITDylib &JD)
Create an EPCGenericMemoryAccess that reaches the memory-access wrappers in the given JITDylib via th...
LLVM_ABI shared::WrapperFunctionBuffer encodeHangupPayload(Error Err)
Encode an Error as the payload of a Hangup message.
LLVM_ABI std::pair< ExecutorAddr, shared::WrapperFunctionBuffer > encodeResultMessage(shared::WrapperFunctionBuffer ResultBytes)
Encode a wrapper function result as the TagAddr and payload of a Result message.
std::unique_ptr< GenericNamedTask > makeGenericNamedTask(FnT &&Fn, std::string Desc)
Create a generic named task from a std::string description.
LLVM_ABI void lookupAndApply(unique_function< void(Error)> OnApplied, LookupKind K, const JITDylibSearchOrder &SearchOrder, ArrayRef< LookupPrepareFn > PrepareFns)
Resolve the symbols contributed by every prepare function with a single lookup, then let each of thei...
LookupPrepareFn recordProxy(Proxy< FnT > *P, typename Proxy< FnT >::DispatchFn Dispatch, StringRef Name, SymbolLookupFlags LF=SymbolLookupFlags::RequiredSymbol)
Builds P over the symbol with the given name, dispatching through Dispatch.
Definition RecordProxy.h:30
LLVM_ABI Error decodeHangupPayload(shared::WrapperFunctionBuffer Payload)
Decode a Hangup payload produced by encodeHangupPayload.
LLVM_ABI Expected< shared::WrapperFunctionBuffer > decodeResultMessage(ExecutorAddr TagAddr, shared::WrapperFunctionBuffer Payload)
Decode a Result message produced by encodeResultMessage, returning the result to complete the pending...
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition Error.h:442
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:880