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