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>> 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 #if LLVM_ENABLE_THREADS 74 ListenerThread.join(); 75 #endif 76 } 77 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 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 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 133 static Error makeUnexpectedEOFError() { 134 return make_error<StringError>("Unexpected end-of-file", 135 inconvertibleErrorCode()); 136 } 137 138 Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size, 139 bool *IsEOF) { 140 assert(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 169 int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) { 170 assert(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 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