1 //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===// 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 // FIXME: Unify this code with similar functionality in llvm-jitlink. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/Interpreter/RemoteJITUtils.h" 14 15 #include "llvm/ADT/StringExtras.h" 16 #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" 17 #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" 18 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" 19 #include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h" 20 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" 21 #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" 22 #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" 23 #include "llvm/Support/FileSystem.h" 24 #include "llvm/Support/Path.h" 25 26 #ifdef LLVM_ON_UNIX 27 #include <netdb.h> 28 #include <netinet/in.h> 29 #include <sys/socket.h> 30 #include <unistd.h> 31 #endif // LLVM_ON_UNIX 32 33 using namespace llvm; 34 using namespace llvm::orc; 35 36 Expected<uint64_t> getSlabAllocSize(StringRef SizeString) { 37 SizeString = SizeString.trim(); 38 39 uint64_t Units = 1024; 40 41 if (SizeString.ends_with_insensitive("kb")) 42 SizeString = SizeString.drop_back(2).rtrim(); 43 else if (SizeString.ends_with_insensitive("mb")) { 44 Units = 1024 * 1024; 45 SizeString = SizeString.drop_back(2).rtrim(); 46 } else if (SizeString.ends_with_insensitive("gb")) { 47 Units = 1024 * 1024 * 1024; 48 SizeString = SizeString.drop_back(2).rtrim(); 49 } 50 51 uint64_t SlabSize = 0; 52 if (SizeString.getAsInteger(10, SlabSize)) 53 return make_error<StringError>("Invalid numeric format for slab size", 54 inconvertibleErrorCode()); 55 56 return SlabSize * Units; 57 } 58 59 Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>> 60 createSharedMemoryManager(SimpleRemoteEPC &SREPC, 61 StringRef SlabAllocateSizeString) { 62 SharedMemoryMapper::SymbolAddrs SAs; 63 if (auto Err = SREPC.getBootstrapSymbols( 64 {{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName}, 65 {SAs.Reserve, 66 rt::ExecutorSharedMemoryMapperServiceReserveWrapperName}, 67 {SAs.Initialize, 68 rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName}, 69 {SAs.Deinitialize, 70 rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName}, 71 {SAs.Release, 72 rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName}})) 73 return std::move(Err); 74 75 #ifdef _WIN32 76 size_t SlabSize = 1024 * 1024; 77 #else 78 size_t SlabSize = 1024 * 1024 * 1024; 79 #endif 80 81 if (!SlabAllocateSizeString.empty()) { 82 if (Expected<uint64_t> S = getSlabAllocSize(SlabAllocateSizeString)) 83 SlabSize = *S; 84 else 85 return S.takeError(); 86 } 87 88 return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>( 89 SlabSize, SREPC, SAs); 90 } 91 92 Expected<std::unique_ptr<SimpleRemoteEPC>> 93 launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, 94 llvm::StringRef SlabAllocateSizeString) { 95 #ifndef LLVM_ON_UNIX 96 // FIXME: Add support for Windows. 97 return make_error<StringError>("-" + ExecutablePath + 98 " not supported on non-unix platforms", 99 inconvertibleErrorCode()); 100 #elif !LLVM_ENABLE_THREADS 101 // Out of process mode using SimpleRemoteEPC depends on threads. 102 return make_error<StringError>( 103 "-" + ExecutablePath + 104 " requires threads, but LLVM was built with " 105 "LLVM_ENABLE_THREADS=Off", 106 inconvertibleErrorCode()); 107 #else 108 109 if (!sys::fs::can_execute(ExecutablePath)) 110 return make_error<StringError>( 111 formatv("Specified executor invalid: {0}", ExecutablePath), 112 inconvertibleErrorCode()); 113 114 constexpr int ReadEnd = 0; 115 constexpr int WriteEnd = 1; 116 117 // Pipe FDs. 118 int ToExecutor[2]; 119 int FromExecutor[2]; 120 121 pid_t ChildPID; 122 123 // Create pipes to/from the executor.. 124 if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0) 125 return make_error<StringError>("Unable to create pipe for executor", 126 inconvertibleErrorCode()); 127 128 ChildPID = fork(); 129 130 if (ChildPID == 0) { 131 // In the child... 132 133 // Close the parent ends of the pipes 134 close(ToExecutor[WriteEnd]); 135 close(FromExecutor[ReadEnd]); 136 137 // Execute the child process. 138 std::unique_ptr<char[]> ExecutorPath, FDSpecifier; 139 { 140 ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1); 141 strcpy(ExecutorPath.get(), ExecutablePath.data()); 142 143 std::string FDSpecifierStr("filedescs="); 144 FDSpecifierStr += utostr(ToExecutor[ReadEnd]); 145 FDSpecifierStr += ','; 146 FDSpecifierStr += utostr(FromExecutor[WriteEnd]); 147 FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1); 148 strcpy(FDSpecifier.get(), FDSpecifierStr.c_str()); 149 } 150 151 char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr}; 152 int RC = execvp(ExecutorPath.get(), Args); 153 if (RC != 0) { 154 errs() << "unable to launch out-of-process executor \"" 155 << ExecutorPath.get() << "\"\n"; 156 exit(1); 157 } 158 } 159 // else we're the parent... 160 161 // Close the child ends of the pipes 162 close(ToExecutor[ReadEnd]); 163 close(FromExecutor[WriteEnd]); 164 165 SimpleRemoteEPC::Setup S = SimpleRemoteEPC::Setup(); 166 if (UseSharedMemory) 167 S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) { 168 return createSharedMemoryManager(EPC, SlabAllocateSizeString); 169 }; 170 171 return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( 172 std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), 173 std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]); 174 #endif 175 } 176 177 #if LLVM_ON_UNIX && LLVM_ENABLE_THREADS 178 179 static Expected<int> connectTCPSocketImpl(std::string Host, 180 std::string PortStr) { 181 addrinfo *AI; 182 addrinfo Hints{}; 183 Hints.ai_family = AF_INET; 184 Hints.ai_socktype = SOCK_STREAM; 185 Hints.ai_flags = AI_NUMERICSERV; 186 187 if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI)) 188 return make_error<StringError>( 189 formatv("address resolution failed ({0})", gai_strerror(EC)), 190 inconvertibleErrorCode()); 191 // Cycle through the returned addrinfo structures and connect to the first 192 // reachable endpoint. 193 int SockFD; 194 addrinfo *Server; 195 for (Server = AI; Server != nullptr; Server = Server->ai_next) { 196 // socket might fail, e.g. if the address family is not supported. Skip to 197 // the next addrinfo structure in such a case. 198 if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) 199 continue; 200 201 // If connect returns null, we exit the loop with a working socket. 202 if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) 203 break; 204 205 close(SockFD); 206 } 207 freeaddrinfo(AI); 208 209 // If we reached the end of the loop without connecting to a valid endpoint, 210 // dump the last error that was logged in socket() or connect(). 211 if (Server == nullptr) 212 return make_error<StringError>("invalid hostname", 213 inconvertibleErrorCode()); 214 215 return SockFD; 216 } 217 #endif 218 219 Expected<std::unique_ptr<SimpleRemoteEPC>> 220 connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, 221 llvm::StringRef SlabAllocateSizeString) { 222 #ifndef LLVM_ON_UNIX 223 // FIXME: Add TCP support for Windows. 224 return make_error<StringError>("-" + NetworkAddress + 225 " not supported on non-unix platforms", 226 inconvertibleErrorCode()); 227 #elif !LLVM_ENABLE_THREADS 228 // Out of process mode using SimpleRemoteEPC depends on threads. 229 return make_error<StringError>( 230 "-" + NetworkAddress + 231 " requires threads, but LLVM was built with " 232 "LLVM_ENABLE_THREADS=Off", 233 inconvertibleErrorCode()); 234 #else 235 236 auto CreateErr = [NetworkAddress](Twine Details) { 237 return make_error<StringError>( 238 formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress, 239 Details), 240 inconvertibleErrorCode()); 241 }; 242 243 StringRef Host, PortStr; 244 std::tie(Host, PortStr) = NetworkAddress.split(':'); 245 if (Host.empty()) 246 return CreateErr("Host name for -" + NetworkAddress + " can not be empty"); 247 if (PortStr.empty()) 248 return CreateErr("Port number in -" + NetworkAddress + " can not be empty"); 249 int Port = 0; 250 if (PortStr.getAsInteger(10, Port)) 251 return CreateErr("Port number '" + PortStr + "' is not a valid integer"); 252 253 Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); 254 if (!SockFD) 255 return SockFD.takeError(); 256 257 SimpleRemoteEPC::Setup S = SimpleRemoteEPC::Setup(); 258 if (UseSharedMemory) 259 S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) { 260 return createSharedMemoryManager(EPC, SlabAllocateSizeString); 261 }; 262 263 return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>( 264 std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), 265 std::move(S), *SockFD, *SockFD); 266 #endif 267 } 268