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
getSlabAllocSize(StringRef SizeString)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>>
createSharedMemoryManager(SimpleRemoteEPC & SREPC,StringRef SlabAllocateSizeString)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>>
launchExecutor(StringRef ExecutablePath,bool UseSharedMemory,llvm::StringRef SlabAllocateSizeString)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
connectTCPSocketImpl(std::string Host,std::string PortStr)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>>
connectTCPSocket(StringRef NetworkAddress,bool UseSharedMemory,llvm::StringRef SlabAllocateSizeString)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