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
24namespace {
25
26struct 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
36namespace llvm {
37namespace orc {
38namespace SimpleRemoteEPCDefaultBootstrapSymbolNames {
39
40const char *ExecutorSessionObjectName =
41 "__llvm_orc_SimpleRemoteEPC_dispatch_ctx";
42const char *DispatchFnName = "__llvm_orc_SimpleRemoteEPC_dispatch_fn";
43
44} // end namespace SimpleRemoteEPCDefaultBootstrapSymbolNames
45
46SimpleRemoteEPCTransportClient::~SimpleRemoteEPCTransportClient() = default;
47SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() = default;
48
49Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
50FDSimpleRemoteEPCTransport::Create(SimpleRemoteEPCTransportClient &C, int InFD,
51 int OutFD) {
52#if LLVM_ENABLE_THREADS
53 if (InFD == -1)
54 return make_error<StringError>(Args: "Invalid input file descriptor " +
55 Twine(InFD),
56 Args: inconvertibleErrorCode());
57 if (OutFD == -1)
58 return make_error<StringError>(Args: "Invalid output file descriptor " +
59 Twine(OutFD),
60 Args: 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
72FDSimpleRemoteEPCTransport::~FDSimpleRemoteEPCTransport() {
73#if LLVM_ENABLE_THREADS
74 ListenerThread.join();
75#endif
76}
77
78Error 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
86Error 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>(Args: "FD-transport disconnected",
103 Args: inconvertibleErrorCode());
104 if (int ErrNo = writeBytes(Src: HeaderBuffer, Size: FDMsgHeader::Size))
105 return errorCodeToError(EC: std::error_code(ErrNo, std::generic_category()));
106 if (int ErrNo = writeBytes(Src: ArgBytes.data(), Size: ArgBytes.size()))
107 return errorCodeToError(EC: std::error_code(ErrNo, std::generic_category()));
108 return Error::success();
109}
110
111void 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(fd: InFD) == -1) {
120 if (errno == EBADF)
121 break;
122 }
123
124 // Close OutFD.
125 if (CloseOutFD) {
126 while (close(fd: OutFD) == -1) {
127 if (errno == EBADF)
128 break;
129 }
130 }
131}
132
133static Error makeUnexpectedEOFError() {
134 return make_error<StringError>(Args: "Unexpected end-of-file",
135 Args: inconvertibleErrorCode());
136}
137
138Error 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(fd: InFD, buf: Dst + Completed, nbytes: 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 EC: std::error_code(ErrNo, std::generic_category()));
162 }
163 }
164 Completed += Read;
165 }
166 return Error::success();
167}
168
169int 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(fd: OutFD, buf: Src + Completed, n: 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
186void 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(Dst: HeaderBuffer, Size: FDMsgHeader::Size, IsEOF: &IsEOF)) {
195 Err = joinErrors(E1: std::move(Err), E2: 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(E1: std::move(Err),
219 E2: make_error<StringError>(Args: "Message size too small",
220 Args: inconvertibleErrorCode()));
221 break;
222 }
223
224 // Read the argument bytes.
225 SimpleRemoteEPCArgBytesVector ArgBytes;
226 ArgBytes.resize(N: MsgSize - FDMsgHeader::Size);
227 if (auto Err2 = readBytes(Dst: ArgBytes.data(), Size: ArgBytes.size())) {
228 Err = joinErrors(E1: std::move(Err), E2: 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(E1: std::move(Err), E2: 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(Err: std::move(Err));
247}
248
249} // end namespace orc
250} // end namespace llvm
251