1 //===------ SimpleRemoteEPCUtils.cpp - Utils for Simple Remote EPC --------===//
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 // Message definitions and other utilities for SimpleRemoteEPC and
10 // SimpleRemoteEPCServer.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
15 #include "llvm/Support/Endian.h"
16 #include "llvm/Support/FormatVariadic.h"
17 
18 #if !defined(_MSC_VER) && !defined(__MINGW32__)
19 #include <unistd.h>
20 #else
21 #include <io.h>
22 #endif
23 
24 namespace {
25 
26 struct FDMsgHeader {
27   static constexpr unsigned MsgSizeOffset = 0;
28   static constexpr unsigned OpCOffset = MsgSizeOffset + sizeof(uint64_t);
29   static constexpr unsigned SeqNoOffset = OpCOffset + sizeof(uint64_t);
30   static constexpr unsigned TagAddrOffset = SeqNoOffset + sizeof(uint64_t);
31   static constexpr unsigned Size = TagAddrOffset + sizeof(uint64_t);
32 };
33 
34 } // namespace
35 
36 namespace llvm {
37 namespace orc {
38 namespace SimpleRemoteEPCDefaultBootstrapSymbolNames {
39 
40 const char *ExecutorSessionObjectName =
41     "__llvm_orc_SimpleRemoteEPC_dispatch_ctx";
42 const char *DispatchFnName = "__llvm_orc_SimpleRemoteEPC_dispatch_fn";
43 
44 } // end namespace SimpleRemoteEPCDefaultBootstrapSymbolNames
45 
46 SimpleRemoteEPCTransportClient::~SimpleRemoteEPCTransportClient() = default;
47 SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() = default;
48 
49 Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
Create(SimpleRemoteEPCTransportClient & C,int InFD,int OutFD)50 FDSimpleRemoteEPCTransport::Create(SimpleRemoteEPCTransportClient &C, int InFD,
51                                    int OutFD) {
52 #if LLVM_ENABLE_THREADS
53   if (InFD == -1)
54     return make_error<StringError>("Invalid input file descriptor " +
55                                        Twine(InFD),
56                                    inconvertibleErrorCode());
57   if (OutFD == -1)
58     return make_error<StringError>("Invalid output file descriptor " +
59                                        Twine(OutFD),
60                                    inconvertibleErrorCode());
61   std::unique_ptr<FDSimpleRemoteEPCTransport> FDT(
62       new FDSimpleRemoteEPCTransport(C, InFD, OutFD));
63   return std::move(FDT);
64 #else
65   return make_error<StringError>("FD-based SimpleRemoteEPC transport requires "
66                                  "thread support, but llvm was built with "
67                                  "LLVM_ENABLE_THREADS=Off",
68                                  inconvertibleErrorCode());
69 #endif
70 }
71 
~FDSimpleRemoteEPCTransport()72 FDSimpleRemoteEPCTransport::~FDSimpleRemoteEPCTransport() {
73 #if LLVM_ENABLE_THREADS
74   ListenerThread.join();
75 #endif
76 }
77 
start()78 Error FDSimpleRemoteEPCTransport::start() {
79 #if LLVM_ENABLE_THREADS
80   ListenerThread = std::thread([this]() { listenLoop(); });
81   return Error::success();
82 #endif
83   llvm_unreachable("Should not be called with LLVM_ENABLE_THREADS=Off");
84 }
85 
sendMessage(SimpleRemoteEPCOpcode OpC,uint64_t SeqNo,ExecutorAddr TagAddr,ArrayRef<char> ArgBytes)86 Error FDSimpleRemoteEPCTransport::sendMessage(SimpleRemoteEPCOpcode OpC,
87                                               uint64_t SeqNo,
88                                               ExecutorAddr TagAddr,
89                                               ArrayRef<char> ArgBytes) {
90   char HeaderBuffer[FDMsgHeader::Size];
91 
92   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset)) =
93       FDMsgHeader::Size + ArgBytes.size();
94   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset)) =
95       static_cast<uint64_t>(OpC);
96   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset)) = SeqNo;
97   *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)) =
98       TagAddr.getValue();
99 
100   std::lock_guard<std::mutex> Lock(M);
101   if (Disconnected)
102     return make_error<StringError>("FD-transport disconnected",
103                                    inconvertibleErrorCode());
104   if (int ErrNo = writeBytes(HeaderBuffer, FDMsgHeader::Size))
105     return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
106   if (int ErrNo = writeBytes(ArgBytes.data(), ArgBytes.size()))
107     return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
108   return Error::success();
109 }
110 
disconnect()111 void FDSimpleRemoteEPCTransport::disconnect() {
112   if (Disconnected)
113     return; // Return if already disconnected.
114 
115   Disconnected = true;
116   bool CloseOutFD = InFD != OutFD;
117 
118   // Close InFD.
119   while (close(InFD) == -1) {
120     if (errno == EBADF)
121       break;
122   }
123 
124   // Close OutFD.
125   if (CloseOutFD) {
126     while (close(OutFD) == -1) {
127       if (errno == EBADF)
128         break;
129     }
130   }
131 }
132 
makeUnexpectedEOFError()133 static Error makeUnexpectedEOFError() {
134   return make_error<StringError>("Unexpected end-of-file",
135                                  inconvertibleErrorCode());
136 }
137 
readBytes(char * Dst,size_t Size,bool * IsEOF)138 Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size,
139                                             bool *IsEOF) {
140   assert((Size == 0 || Dst) && "Attempt to read into null.");
141   ssize_t Completed = 0;
142   while (Completed < static_cast<ssize_t>(Size)) {
143     ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed);
144     if (Read <= 0) {
145       auto ErrNo = errno;
146       if (Read == 0) {
147         if (Completed == 0 && IsEOF) {
148           *IsEOF = true;
149           return Error::success();
150         } else
151           return makeUnexpectedEOFError();
152       } else if (ErrNo == EAGAIN || ErrNo == EINTR)
153         continue;
154       else {
155         std::lock_guard<std::mutex> Lock(M);
156         if (Disconnected && IsEOF) { // disconnect called,  pretend this is EOF.
157           *IsEOF = true;
158           return Error::success();
159         }
160         return errorCodeToError(
161             std::error_code(ErrNo, std::generic_category()));
162       }
163     }
164     Completed += Read;
165   }
166   return Error::success();
167 }
168 
writeBytes(const char * Src,size_t Size)169 int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) {
170   assert((Size == 0 || Src) && "Attempt to append from null.");
171   ssize_t Completed = 0;
172   while (Completed < static_cast<ssize_t>(Size)) {
173     ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed);
174     if (Written < 0) {
175       auto ErrNo = errno;
176       if (ErrNo == EAGAIN || ErrNo == EINTR)
177         continue;
178       else
179         return ErrNo;
180     }
181     Completed += Written;
182   }
183   return 0;
184 }
185 
listenLoop()186 void FDSimpleRemoteEPCTransport::listenLoop() {
187   Error Err = Error::success();
188   do {
189 
190     char HeaderBuffer[FDMsgHeader::Size];
191     // Read the header buffer.
192     {
193       bool IsEOF = false;
194       if (auto Err2 = readBytes(HeaderBuffer, FDMsgHeader::Size, &IsEOF)) {
195         Err = joinErrors(std::move(Err), std::move(Err2));
196         break;
197       }
198       if (IsEOF)
199         break;
200     }
201 
202     // Decode header buffer.
203     uint64_t MsgSize;
204     SimpleRemoteEPCOpcode OpC;
205     uint64_t SeqNo;
206     ExecutorAddr TagAddr;
207 
208     MsgSize =
209         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset));
210     OpC = static_cast<SimpleRemoteEPCOpcode>(static_cast<uint64_t>(
211         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset))));
212     SeqNo =
213         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset));
214     TagAddr.setValue(
215         *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)));
216 
217     if (MsgSize < FDMsgHeader::Size) {
218       Err = joinErrors(std::move(Err),
219                        make_error<StringError>("Message size too small",
220                                                inconvertibleErrorCode()));
221       break;
222     }
223 
224     // Read the argument bytes.
225     SimpleRemoteEPCArgBytesVector ArgBytes;
226     ArgBytes.resize(MsgSize - FDMsgHeader::Size);
227     if (auto Err2 = readBytes(ArgBytes.data(), ArgBytes.size())) {
228       Err = joinErrors(std::move(Err), std::move(Err2));
229       break;
230     }
231 
232     if (auto Action = C.handleMessage(OpC, SeqNo, TagAddr, ArgBytes)) {
233       if (*Action == SimpleRemoteEPCTransportClient::EndSession)
234         break;
235     } else {
236       Err = joinErrors(std::move(Err), Action.takeError());
237       break;
238     }
239   } while (true);
240 
241   // Attempt to close FDs, set Disconnected to true so that subsequent
242   // sendMessage calls fail.
243   disconnect();
244 
245   // Call up to the client to handle the disconnection.
246   C.handleDisconnect(std::move(Err));
247 }
248 
249 } // end namespace orc
250 } // end namespace llvm
251