xref: /llvm-project/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp (revision e57548387000071562f44bfd66644480c8e6542d)
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 #include "RemoteJITUtils.h"
10 
11 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
13 #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
14 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
15 #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
16 #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 
20 #ifdef LLVM_ON_UNIX
21 #include <netdb.h>
22 #include <netinet/in.h>
23 #include <sys/socket.h>
24 #include <unistd.h>
25 #endif // LLVM_ON_UNIX
26 
27 using namespace llvm;
28 using namespace llvm::orc;
29 
30 Expected<std::unique_ptr<DefinitionGenerator>>
31 loadDylib(ExecutionSession &ES, StringRef RemotePath) {
32   if (auto Handle = ES.getExecutorProcessControl().getDylibMgr().loadDylib(
33           RemotePath.data()))
34     return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
35   else
36     return Handle.takeError();
37 }
38 
39 static void findLocalExecutorHelper() {}
40 std::string findLocalExecutor(const char *HostArgv0) {
41   // This just needs to be some static symbol in the binary; C++ doesn't
42   // allow taking the address of ::main however.
43   uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
44   void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
45   SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
46   sys::path::remove_filename(FullName);
47   sys::path::append(FullName, "llvm-jitlink-executor");
48   return FullName.str().str();
49 }
50 
51 #ifndef LLVM_ON_UNIX
52 
53 // FIXME: Add support for Windows.
54 Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
55 launchLocalExecutor(StringRef ExecutablePath) {
56   return make_error<StringError>(
57       "Remote JITing not yet supported on non-unix platforms",
58       inconvertibleErrorCode());
59 }
60 
61 // FIXME: Add support for Windows.
62 Expected<std::unique_ptr<SimpleRemoteEPC>>
63 connectTCPSocket(StringRef NetworkAddress) {
64   return make_error<StringError>(
65       "Remote JITing not yet supported on non-unix platforms",
66       inconvertibleErrorCode());
67 }
68 
69 #else
70 
71 Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
72 launchLocalExecutor(StringRef ExecutablePath) {
73   constexpr int ReadEnd = 0;
74   constexpr int WriteEnd = 1;
75 
76   if (!sys::fs::can_execute(ExecutablePath))
77     return make_error<StringError>(
78         formatv("Specified executor invalid: {0}", ExecutablePath),
79         inconvertibleErrorCode());
80 
81   // Pipe FDs.
82   int ToExecutor[2];
83   int FromExecutor[2];
84 
85   // Create pipes to/from the executor..
86   if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
87     return make_error<StringError>("Unable to create pipe for executor",
88                                    inconvertibleErrorCode());
89 
90   pid_t ProcessID = fork();
91   if (ProcessID == 0) {
92     // In the child...
93 
94     // Close the parent ends of the pipes
95     close(ToExecutor[WriteEnd]);
96     close(FromExecutor[ReadEnd]);
97 
98     // Execute the child process.
99     std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag;
100     {
101       ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
102       strcpy(ExecPath.get(), ExecutablePath.data());
103 
104       const char *TestOutputFlagStr = "test-jitloadergdb";
105       TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1);
106       strcpy(TestOutputFlag.get(), TestOutputFlagStr);
107 
108       std::string FDSpecifierStr("filedescs=");
109       FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
110       FDSpecifierStr += ',';
111       FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
112       FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
113       strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
114     }
115 
116     char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),
117                           FDSpecifier.get(), nullptr};
118     int RC = execvp(ExecPath.get(), Args);
119     if (RC != 0)
120       return make_error<StringError>(
121           "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
122           inconvertibleErrorCode());
123 
124     llvm_unreachable("Fork won't return in success case");
125   }
126   // else we're the parent...
127 
128   // Close the child ends of the pipes
129   close(ToExecutor[ReadEnd]);
130   close(FromExecutor[WriteEnd]);
131 
132   auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
133       std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
134       SimpleRemoteEPC::Setup(),
135       FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
136   if (!EPC)
137     return EPC.takeError();
138 
139   return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
140 }
141 
142 static Expected<int> connectTCPSocketImpl(std::string Host,
143                                           std::string PortStr) {
144   addrinfo *AI;
145   addrinfo Hints{};
146   Hints.ai_family = AF_INET;
147   Hints.ai_socktype = SOCK_STREAM;
148   Hints.ai_flags = AI_NUMERICSERV;
149 
150   if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
151     return make_error<StringError>(
152         formatv("address resolution failed ({0})", gai_strerror(EC)),
153         inconvertibleErrorCode());
154 
155   // Cycle through the returned addrinfo structures and connect to the first
156   // reachable endpoint.
157   int SockFD;
158   addrinfo *Server;
159   for (Server = AI; Server != nullptr; Server = Server->ai_next) {
160     // If socket fails, maybe it's because the address family is not supported.
161     // Skip to the next addrinfo structure.
162     if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
163       continue;
164 
165     // If connect works, we exit the loop with a working socket.
166     if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
167       break;
168 
169     close(SockFD);
170   }
171   freeaddrinfo(AI);
172 
173   // Did we reach the end of the loop without connecting to a valid endpoint?
174   if (Server == nullptr)
175     return make_error<StringError>("invalid hostname",
176                                    inconvertibleErrorCode());
177 
178   return SockFD;
179 }
180 
181 Expected<std::unique_ptr<SimpleRemoteEPC>>
182 connectTCPSocket(StringRef NetworkAddress) {
183   auto CreateErr = [NetworkAddress](StringRef Details) {
184     return make_error<StringError>(
185         formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
186                 Details),
187         inconvertibleErrorCode());
188   };
189 
190   StringRef Host, PortStr;
191   std::tie(Host, PortStr) = NetworkAddress.split(':');
192   if (Host.empty())
193     return CreateErr("host name cannot be empty");
194   if (PortStr.empty())
195     return CreateErr("port cannot be empty");
196   int Port = 0;
197   if (PortStr.getAsInteger(10, Port))
198     return CreateErr("port number is not a valid integer");
199 
200   Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
201   if (!SockFD)
202     return CreateErr(toString(SockFD.takeError()));
203 
204   return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
205       std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
206       SimpleRemoteEPC::Setup(), *SockFD);
207 }
208 
209 #endif
210