LLVM 23.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
15
16#define DEBUG_TYPE "orc"
17
18namespace llvm {
19namespace orc {
20
22#ifndef NDEBUG
23 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
24 assert(Disconnected && "Destroyed without disconnection");
25#endif // NDEBUG
26}
27
30 int64_t Result = 0;
32 RunAsMainAddr, Result, MainFnAddr, Args))
33 return std::move(Err);
34 return Result;
35}
36
38 int32_t Result = 0;
40 RunAsVoidFunctionAddr, Result, VoidFnAddr))
41 return std::move(Err);
42 return Result;
43}
44
46 int Arg) {
47 int32_t Result = 0;
49 RunAsIntFunctionAddr, Result, IntFnAddr, Arg))
50 return std::move(Err);
51 return Result;
52}
53
55 IncomingWFRHandler OnComplete,
56 ArrayRef<char> ArgBuffer) {
57 uint64_t SeqNo;
58 {
59 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
60 SeqNo = getNextSeqNo();
61 assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use");
62 PendingCallWrapperResults[SeqNo] = std::move(OnComplete);
63 }
64
65 if (auto Err = sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
66 WrapperFnAddr, ArgBuffer)) {
68
69 // We just registered OnComplete, but there may be a race between this
70 // thread returning from sendMessage and handleDisconnect being called from
71 // the transport's listener thread. If handleDisconnect gets there first
72 // then it will have failed 'H' for us. If we get there first (or if
73 // handleDisconnect already ran) then we need to take care of it.
74 {
75 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
76 auto I = PendingCallWrapperResults.find(SeqNo);
77 if (I != PendingCallWrapperResults.end()) {
78 H = std::move(I->second);
79 PendingCallWrapperResults.erase(I);
80 }
81 }
82
83 if (H)
85
86 getExecutionSession().reportError(std::move(Err));
87 }
88}
89
94
98 if (!DM)
99 return DM.takeError();
100 return std::make_unique<EPCGenericDylibManager>(std::move(*DM));
101}
102
123
125 T->disconnect();
126 D->shutdown();
127 std::unique_lock<std::mutex> Lock(SimpleRemoteEPCMutex);
128 DisconnectCV.wait(Lock, [this] { return Disconnected; });
129 return std::move(DisconnectErr);
130}
131
134 ExecutorAddr TagAddr,
136
137 LLVM_DEBUG({
138 dbgs() << "SimpleRemoteEPC::handleMessage: opc = ";
139 switch (OpC) {
141 dbgs() << "Setup";
142 assert(SeqNo == 0 && "Non-zero SeqNo for Setup?");
143 assert(!TagAddr && "Non-zero TagAddr for Setup?");
144 break;
146 dbgs() << "Hangup";
147 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
148 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
149 break;
151 dbgs() << "Result";
152 assert(!TagAddr && "Non-zero TagAddr for Result?");
153 break;
155 dbgs() << "CallWrapper";
156 break;
157 }
158 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
159 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
160 << " bytes\n";
161 });
162
163 using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
164 if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
165 return make_error<StringError>("Unexpected opcode",
167
168 switch (OpC) {
170 if (auto Err = handleSetup(SeqNo, TagAddr, std::move(ArgBytes)))
171 return std::move(Err);
172 break;
174 T->disconnect();
175 if (auto Err = handleHangup(std::move(ArgBytes)))
176 return std::move(Err);
177 return EndSession;
179 if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
180 return std::move(Err);
181 break;
183 handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
184 break;
185 }
186 return ContinueSession;
187}
188
190 LLVM_DEBUG({
191 dbgs() << "SimpleRemoteEPC::handleDisconnect: "
192 << (Err ? "failure" : "success") << "\n";
193 });
194
195 PendingCallWrapperResultsMap TmpPending;
196
197 {
198 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
199 std::swap(TmpPending, PendingCallWrapperResults);
200 }
201
202 for (auto &KV : TmpPending)
203 KV.second(
205
206 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
207 DisconnectErr = joinErrors(std::move(DisconnectErr), std::move(Err));
208 Disconnected = true;
209 DisconnectCV.notify_all();
210}
211
215 if (auto Err = SREPC.getBootstrapSymbols(
216 {{SAs.Allocator, rt::SimpleExecutorMemoryManagerInstanceName},
217 {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName},
218 {SAs.Initialize,
219 rt::SimpleExecutorMemoryManagerInitializeWrapperName},
220 {SAs.Release, rt::SimpleExecutorMemoryManagerReleaseWrapperName}}))
221 return std::move(Err);
222
223 return std::make_unique<EPCGenericJITLinkMemoryManager>(SREPC, SAs);
224}
225
226Error SimpleRemoteEPC::sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
227 ExecutorAddr TagAddr,
228 ArrayRef<char> ArgBytes) {
229 assert(OpC != SimpleRemoteEPCOpcode::Setup &&
230 "SimpleRemoteEPC sending Setup message? That's the wrong direction.");
231
232 LLVM_DEBUG({
233 dbgs() << "SimpleRemoteEPC::sendMessage: opc = ";
234 switch (OpC) {
235 case SimpleRemoteEPCOpcode::Hangup:
236 dbgs() << "Hangup";
237 assert(SeqNo == 0 && "Non-zero SeqNo for Hangup?");
238 assert(!TagAddr && "Non-zero TagAddr for Hangup?");
239 break;
240 case SimpleRemoteEPCOpcode::Result:
241 dbgs() << "Result";
242 assert(!TagAddr && "Non-zero TagAddr for Result?");
243 break;
244 case SimpleRemoteEPCOpcode::CallWrapper:
245 dbgs() << "CallWrapper";
246 break;
247 default:
248 llvm_unreachable("Invalid opcode");
249 }
250 dbgs() << ", seqno = " << SeqNo << ", tag-addr = " << TagAddr
251 << ", arg-buffer = " << formatv("{0:x}", ArgBytes.size())
252 << " bytes\n";
253 });
254 auto Err = T->sendMessage(OpC, SeqNo, TagAddr, ArgBytes);
255 LLVM_DEBUG({
256 if (Err)
257 dbgs() << " \\--> SimpleRemoteEPC::sendMessage failed\n";
258 });
259 return Err;
260}
261
262Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddr TagAddr,
263 shared::WrapperFunctionBuffer ArgBytes) {
264 if (SeqNo != 0)
265 return make_error<StringError>("Setup packet SeqNo not zero",
267
268 if (TagAddr)
269 return make_error<StringError>("Setup packet TagAddr not zero",
271
272 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
273 auto I = PendingCallWrapperResults.find(0);
274 assert(PendingCallWrapperResults.size() == 1 &&
275 I != PendingCallWrapperResults.end() &&
276 "Setup message handler not connectly set up");
277 auto SetupMsgHandler = std::move(I->second);
278 PendingCallWrapperResults.erase(I);
279
280 auto WFR =
281 shared::WrapperFunctionBuffer::copyFrom(ArgBytes.data(), ArgBytes.size());
282 SetupMsgHandler(std::move(WFR));
283 return Error::success();
284}
285
286Error SimpleRemoteEPC::setup() {
287 using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
288
289 std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> EIP;
290 auto EIF = EIP.get_future();
291
292 // Prepare a handler for the setup packet.
293 PendingCallWrapperResults[0] =
294 RunInPlace()(
295 [&](shared::WrapperFunctionBuffer SetupMsgBytes) {
296 if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
297 EIP.set_value(
299 return;
300 }
301 using SPSSerialize =
302 shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
303 shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size());
304 SimpleRemoteEPCExecutorInfo EI;
305 if (SPSSerialize::deserialize(IB, EI))
306 EIP.set_value(EI);
307 else
308 EIP.set_value(make_error<StringError>(
309 "Could not deserialize setup message", inconvertibleErrorCode()));
310 });
311
312 // Start the transport.
313 if (auto Err = T->start())
314 return Err;
315
316 // Wait for setup packet to arrive.
317 auto EI = EIF.get();
318 if (!EI) {
319 T->disconnect();
320 return EI.takeError();
321 }
322
323 LLVM_DEBUG({
324 dbgs() << "SimpleRemoteEPC received setup message:\n"
325 << " Triple: " << EI->TargetTriple << "\n"
326 << " Page size: " << EI->PageSize << "\n"
327 << " Bootstrap map" << (EI->BootstrapMap.empty() ? " empty" : ":")
328 << "\n";
329 for (const auto &KV : EI->BootstrapMap)
330 dbgs() << " " << KV.first() << ": " << KV.second.size()
331 << "-byte SPS encoded buffer\n";
332 dbgs() << " Bootstrap symbols"
333 << (EI->BootstrapSymbols.empty() ? " empty" : ":") << "\n";
334 for (const auto &KV : EI->BootstrapSymbols)
335 dbgs() << " " << KV.first() << ": " << KV.second << "\n";
336 });
337 TargetTriple = Triple(EI->TargetTriple);
338 PageSize = EI->PageSize;
339 BootstrapMap = std::move(EI->BootstrapMap);
340 BootstrapSymbols = std::move(EI->BootstrapSymbols);
341
342 if (auto Err = getBootstrapSymbols(
343 {{JDI.JITDispatchContext, ExecutorSessionObjectName},
344 {JDI.JITDispatchFunction, DispatchFnName},
345 {RunAsMainAddr, rt::RunAsMainWrapperName},
346 {RunAsVoidFunctionAddr, rt::RunAsVoidFunctionWrapperName},
347 {RunAsIntFunctionAddr, rt::RunAsIntFunctionWrapperName}}))
348 return Err;
349
350 return Error::success();
351}
352
353Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddr TagAddr,
354 shared::WrapperFunctionBuffer ArgBytes) {
355 IncomingWFRHandler SendResult;
356
357 if (TagAddr)
358 return make_error<StringError>("Unexpected TagAddr in result message",
360
361 {
362 std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
363 auto I = PendingCallWrapperResults.find(SeqNo);
364 if (I == PendingCallWrapperResults.end())
365 return make_error<StringError>("No call for sequence number " +
366 Twine(SeqNo),
368 SendResult = std::move(I->second);
369 PendingCallWrapperResults.erase(I);
370 releaseSeqNo(SeqNo);
371 }
372
373 auto WFR =
374 shared::WrapperFunctionBuffer::copyFrom(ArgBytes.data(), ArgBytes.size());
375 SendResult(std::move(WFR));
376 return Error::success();
377}
378
379void SimpleRemoteEPC::handleCallWrapper(
380 uint64_t RemoteSeqNo, ExecutorAddr TagAddr,
381 shared::WrapperFunctionBuffer ArgBytes) {
382 assert(ES && "No ExecutionSession attached");
383 D->dispatch(makeGenericNamedTask(
384 [this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() mutable {
385 ES->runJITDispatchHandler(
386 [this, RemoteSeqNo](shared::WrapperFunctionBuffer WFR) {
387 if (auto Err =
388 sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
389 ExecutorAddr(), {WFR.data(), WFR.size()}))
390 getExecutionSession().reportError(std::move(Err));
391 },
392 TagAddr, std::move(ArgBytes));
393 },
394 "callWrapper task"));
395}
396
397Error SimpleRemoteEPC::handleHangup(shared::WrapperFunctionBuffer ArgBytes) {
398 using namespace llvm::orc::shared;
399 auto WFR = WrapperFunctionBuffer::copyFrom(ArgBytes.data(), ArgBytes.size());
400 if (const char *ErrMsg = WFR.getOutOfBandError())
402
404 SPSInputBuffer IB(WFR.data(), WFR.size());
405 if (!SPSArgList<SPSError>::deserialize(IB, Info))
406 return make_error<StringError>("Could not deserialize hangup info",
408 return fromSPSSerializable(std::move(Info));
409}
410
411} // end namespace orc
412} // end namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static RegisterPass< DebugifyModulePass > DM("debugify", "Attach debug info to everything")
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
#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:114
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
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
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:47
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
static LLVM_ABI Expected< EPCGenericDylibManager > CreateWithDefaultBootstrapSymbols(ExecutorProcessControl &EPC)
Create an EPCGenericMemoryAccess instance from a given set of function addrs.
static Expected< std::unique_ptr< EPCGenericJITLinkMemoryManager > > Create(JITDylib &JD, rt::SimpleExecutorMemoryManagerSymbolNames SNs=rt::orc_rt_SimpleNativeMemoryMapSPSSymbols)
Create an EPCGenericJITLinkMemoryManager using the given implementation symbol names.
void reportError(Error Err)
Report a error for this execution session.
Definition Core.h:1506
Represents an address in the executor process.
A handler or incoming WrapperFunctionBuffers – either return values from callWrapper* calls,...
std::unique_ptr< TaskDispatcher > D
Error callSPSWrapper(ExecutorAddr WrapperFnAddr, WrapperCallArgTs &&...WrapperCallArgs)
Run a wrapper function using SPS to serialize the arguments and deserialize the results.
Error getBootstrapSymbols(ArrayRef< std::pair< ExecutorAddr &, StringRef > > Pairs) const
For each (ExecutorAddr&, StringRef) pair, looks up the string in the bootstrap symbols map and writes...
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.
Expected< int32_t > runAsVoidFunction(ExecutorAddr VoidFnAddr) override
Run function with a int (*)(void) signature.
void callWrapperAsync(ExecutorAddr WrapperFnAddr, IncomingWFRHandler OnComplete, ArrayRef< char > ArgBuffer) override
Run a wrapper function in the executor.
Expected< int32_t > runAsIntFunction(ExecutorAddr IntFnAddr, int Arg) override
Run function with a int (*)(int) signature.
C++ wrapper function buffer: Same as CWrapperFunctionBuffer but auto-releases memory.
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 * MemoryReadUInt64sWrapperName
LLVM_ABI const char * MemoryWriteUInt16sWrapperName
LLVM_ABI const char * MemoryReadStringsWrapperName
LLVM_ABI const char * MemoryReadUInt16sWrapperName
LLVM_ABI const char * MemoryReadUInt32sWrapperName
LLVM_ABI const char * MemoryWriteUInt64sWrapperName
LLVM_ABI const char * MemoryWriteUInt8sWrapperName
LLVM_ABI const char * MemoryWritePointersWrapperName
LLVM_ABI const char * MemoryWriteUInt32sWrapperName
LLVM_ABI const char * MemoryWriteBuffersWrapperName
LLVM_ABI const char * MemoryReadBuffersWrapperName
LLVM_ABI const char * MemoryReadUInt8sWrapperName
Error fromSPSSerializable(SPSSerializableError BSE)
std::unique_ptr< GenericNamedTask > makeGenericNamedTask(FnT &&Fn, std::string Desc)
Create a generic named task from a std::string description.
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:207
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:872
Symbol addresses for memory management implementation.
Function addresses for memory access.