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() {} 47 SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() {} 48 49 Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>> 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 72 FDSimpleRemoteEPCTransport::FDSimpleRemoteEPCTransport( 73 SimpleRemoteEPCTransportClient &C, int InFD, int OutFD) 74 : C(C), InFD(InFD), OutFD(OutFD) { 75 #if LLVM_ENABLE_THREADS 76 ListenerThread = std::thread([this]() { listenLoop(); }); 77 #endif 78 } 79 80 FDSimpleRemoteEPCTransport::~FDSimpleRemoteEPCTransport() { 81 #if LLVM_ENABLE_THREADS 82 ListenerThread.join(); 83 #endif 84 } 85 86 Error FDSimpleRemoteEPCTransport::sendMessage(SimpleRemoteEPCOpcode OpC, 87 uint64_t SeqNo, 88 ExecutorAddress 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 (OutFD == -1) 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 111 void FDSimpleRemoteEPCTransport::disconnect() { 112 int CloseInFD = -1, CloseOutFD = -1; 113 { 114 std::lock_guard<std::mutex> Lock(M); 115 std::swap(InFD, CloseInFD); 116 std::swap(OutFD, CloseOutFD); 117 } 118 119 // If CloseOutFD == CloseInFD then set CloseOutFD to -1 up-front so that we 120 // don't double-close. 121 if (CloseOutFD == CloseInFD) 122 CloseOutFD = -1; 123 124 // Close InFD. 125 if (CloseInFD != -1) 126 while (close(CloseInFD) == -1) { 127 if (errno == EBADF) 128 break; 129 } 130 131 // Close OutFD. 132 if (CloseOutFD != -1) { 133 while (close(CloseOutFD) == -1) { 134 if (errno == EBADF) 135 break; 136 } 137 } 138 } 139 140 static Error makeUnexpectedEOFError() { 141 return make_error<StringError>("Unexpected end-of-file", 142 inconvertibleErrorCode()); 143 } 144 145 Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size, 146 bool *IsEOF) { 147 assert(Dst && "Attempt to read into null."); 148 ssize_t Completed = 0; 149 while (Completed < static_cast<ssize_t>(Size)) { 150 ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed); 151 if (Read <= 0) { 152 auto ErrNo = errno; 153 if (Read == 0) { 154 if (Completed == 0 && IsEOF) { 155 *IsEOF = true; 156 return Error::success(); 157 } else 158 return makeUnexpectedEOFError(); 159 } else if (ErrNo == EAGAIN || ErrNo == EINTR) 160 continue; 161 else { 162 std::lock_guard<std::mutex> Lock(M); 163 if (InFD == -1 && IsEOF) { // Disconnected locally. Pretend this is EOF. 164 *IsEOF = true; 165 return Error::success(); 166 } 167 return errorCodeToError( 168 std::error_code(ErrNo, std::generic_category())); 169 } 170 } 171 Completed += Read; 172 } 173 return Error::success(); 174 } 175 176 int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) { 177 assert(Src && "Attempt to append from null."); 178 ssize_t Completed = 0; 179 while (Completed < static_cast<ssize_t>(Size)) { 180 ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed); 181 if (Written < 0) { 182 auto ErrNo = errno; 183 if (ErrNo == EAGAIN || ErrNo == EINTR) 184 continue; 185 else 186 return ErrNo; 187 } 188 Completed += Written; 189 } 190 return 0; 191 } 192 193 void FDSimpleRemoteEPCTransport::listenLoop() { 194 Error Err = Error::success(); 195 do { 196 197 char HeaderBuffer[FDMsgHeader::Size]; 198 // Read the header buffer. 199 { 200 bool IsEOF; 201 if (auto Err2 = readBytes(HeaderBuffer, FDMsgHeader::Size, &IsEOF)) { 202 Err = joinErrors(std::move(Err), std::move(Err2)); 203 break; 204 } 205 if (IsEOF) 206 break; 207 } 208 209 // Decode header buffer. 210 uint64_t MsgSize; 211 SimpleRemoteEPCOpcode OpC; 212 uint64_t SeqNo; 213 ExecutorAddress TagAddr; 214 215 MsgSize = 216 *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset)); 217 OpC = static_cast<SimpleRemoteEPCOpcode>(static_cast<uint64_t>( 218 *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset)))); 219 SeqNo = 220 *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset)); 221 TagAddr.setValue( 222 *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset))); 223 224 if (MsgSize < FDMsgHeader::Size) { 225 Err = joinErrors(std::move(Err), 226 make_error<StringError>("Message size too small", 227 inconvertibleErrorCode())); 228 break; 229 } 230 231 // Read the argument bytes. 232 SimpleRemoteEPCArgBytesVector ArgBytes; 233 ArgBytes.resize(MsgSize - FDMsgHeader::Size); 234 if (auto Err2 = readBytes(ArgBytes.data(), ArgBytes.size())) { 235 Err = joinErrors(std::move(Err), std::move(Err2)); 236 break; 237 } 238 239 if (auto Action = C.handleMessage(OpC, SeqNo, TagAddr, ArgBytes)) { 240 if (*Action == SimpleRemoteEPCTransportClient::EndSession) 241 break; 242 } else { 243 Err = joinErrors(std::move(Err), Action.takeError()); 244 break; 245 } 246 } while (true); 247 248 C.handleDisconnect(std::move(Err)); 249 } 250 251 } // end namespace orc 252 } // end namespace llvm 253