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