xref: /freebsd/contrib/llvm-project/clang/lib/Interpreter/RemoteJITUtils.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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