xref: /llvm-project/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp (revision e57548387000071562f44bfd66644480c8e6542d)
1258f055eSStefan Gränitz //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
2258f055eSStefan Gränitz //
3258f055eSStefan Gränitz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4258f055eSStefan Gränitz // See https://llvm.org/LICENSE.txt for license information.
5258f055eSStefan Gränitz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6258f055eSStefan Gränitz //
7258f055eSStefan Gränitz //===----------------------------------------------------------------------===//
8258f055eSStefan Gränitz 
9258f055eSStefan Gränitz #include "RemoteJITUtils.h"
10258f055eSStefan Gränitz 
11b0abd489SElliot Goodrich #include "llvm/ADT/StringExtras.h"
12258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
13662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
14662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
15ac2daacbSStefan Gränitz #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
16258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
17258f055eSStefan Gränitz #include "llvm/Support/FileSystem.h"
18258f055eSStefan Gränitz #include "llvm/Support/Path.h"
19258f055eSStefan Gränitz 
20258f055eSStefan Gränitz #ifdef LLVM_ON_UNIX
21258f055eSStefan Gränitz #include <netdb.h>
22258f055eSStefan Gränitz #include <netinet/in.h>
23258f055eSStefan Gränitz #include <sys/socket.h>
24258f055eSStefan Gränitz #include <unistd.h>
25258f055eSStefan Gränitz #endif // LLVM_ON_UNIX
26258f055eSStefan Gränitz 
27258f055eSStefan Gränitz using namespace llvm;
28258f055eSStefan Gränitz using namespace llvm::orc;
29258f055eSStefan Gränitz 
30258f055eSStefan Gränitz Expected<std::unique_ptr<DefinitionGenerator>>
31ac2daacbSStefan Gränitz loadDylib(ExecutionSession &ES, StringRef RemotePath) {
32*e5754838SLang Hames   if (auto Handle = ES.getExecutorProcessControl().getDylibMgr().loadDylib(
33*e5754838SLang Hames           RemotePath.data()))
34ac2daacbSStefan Gränitz     return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
35258f055eSStefan Gränitz   else
36258f055eSStefan Gränitz     return Handle.takeError();
37258f055eSStefan Gränitz }
38258f055eSStefan Gränitz 
39ac2daacbSStefan Gränitz static void findLocalExecutorHelper() {}
40ac2daacbSStefan Gränitz std::string findLocalExecutor(const char *HostArgv0) {
41ac2daacbSStefan Gränitz   // This just needs to be some static symbol in the binary; C++ doesn't
42258f055eSStefan Gränitz   // allow taking the address of ::main however.
43ac2daacbSStefan Gränitz   uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
44ac2daacbSStefan Gränitz   void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
45ac2daacbSStefan Gränitz   SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
46258f055eSStefan Gränitz   sys::path::remove_filename(FullName);
47ac2daacbSStefan Gränitz   sys::path::append(FullName, "llvm-jitlink-executor");
48258f055eSStefan Gränitz   return FullName.str().str();
49258f055eSStefan Gränitz }
50258f055eSStefan Gränitz 
51258f055eSStefan Gränitz #ifndef LLVM_ON_UNIX
52258f055eSStefan Gränitz 
53258f055eSStefan Gränitz // FIXME: Add support for Windows.
54ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
55ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) {
56258f055eSStefan Gränitz   return make_error<StringError>(
57258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
58258f055eSStefan Gränitz       inconvertibleErrorCode());
59258f055eSStefan Gränitz }
60258f055eSStefan Gränitz 
61258f055eSStefan Gränitz // FIXME: Add support for Windows.
62ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>>
63ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) {
64258f055eSStefan Gränitz   return make_error<StringError>(
65258f055eSStefan Gränitz       "Remote JITing not yet supported on non-unix platforms",
66258f055eSStefan Gränitz       inconvertibleErrorCode());
67258f055eSStefan Gränitz }
68258f055eSStefan Gränitz 
69258f055eSStefan Gränitz #else
70258f055eSStefan Gränitz 
71ac2daacbSStefan Gränitz Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
72ac2daacbSStefan Gränitz launchLocalExecutor(StringRef ExecutablePath) {
73258f055eSStefan Gränitz   constexpr int ReadEnd = 0;
74258f055eSStefan Gränitz   constexpr int WriteEnd = 1;
75258f055eSStefan Gränitz 
76ac2daacbSStefan Gränitz   if (!sys::fs::can_execute(ExecutablePath))
77ac2daacbSStefan Gränitz     return make_error<StringError>(
78ac2daacbSStefan Gränitz         formatv("Specified executor invalid: {0}", ExecutablePath),
79ac2daacbSStefan Gränitz         inconvertibleErrorCode());
80ac2daacbSStefan Gränitz 
81258f055eSStefan Gränitz   // Pipe FDs.
82258f055eSStefan Gränitz   int ToExecutor[2];
83258f055eSStefan Gränitz   int FromExecutor[2];
84258f055eSStefan Gränitz 
85258f055eSStefan Gränitz   // Create pipes to/from the executor..
86258f055eSStefan Gränitz   if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
87258f055eSStefan Gränitz     return make_error<StringError>("Unable to create pipe for executor",
88258f055eSStefan Gränitz                                    inconvertibleErrorCode());
89258f055eSStefan Gränitz 
90ac2daacbSStefan Gränitz   pid_t ProcessID = fork();
91258f055eSStefan Gränitz   if (ProcessID == 0) {
92258f055eSStefan Gränitz     // In the child...
93258f055eSStefan Gränitz 
94258f055eSStefan Gränitz     // Close the parent ends of the pipes
95258f055eSStefan Gränitz     close(ToExecutor[WriteEnd]);
96258f055eSStefan Gränitz     close(FromExecutor[ReadEnd]);
97258f055eSStefan Gränitz 
98258f055eSStefan Gränitz     // Execute the child process.
99b7d5b0d0SStefan Gränitz     std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag;
100258f055eSStefan Gränitz     {
101258f055eSStefan Gränitz       ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
102258f055eSStefan Gränitz       strcpy(ExecPath.get(), ExecutablePath.data());
103258f055eSStefan Gränitz 
104b7d5b0d0SStefan Gränitz       const char *TestOutputFlagStr = "test-jitloadergdb";
105b7d5b0d0SStefan Gränitz       TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1);
106b7d5b0d0SStefan Gränitz       strcpy(TestOutputFlag.get(), TestOutputFlagStr);
107b7d5b0d0SStefan Gränitz 
108258f055eSStefan Gränitz       std::string FDSpecifierStr("filedescs=");
109258f055eSStefan Gränitz       FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
110258f055eSStefan Gränitz       FDSpecifierStr += ',';
111258f055eSStefan Gränitz       FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
112258f055eSStefan Gränitz       FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
113258f055eSStefan Gränitz       strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
114258f055eSStefan Gränitz     }
115258f055eSStefan Gränitz 
116b7d5b0d0SStefan Gränitz     char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),
117b7d5b0d0SStefan Gränitz                           FDSpecifier.get(), nullptr};
118258f055eSStefan Gränitz     int RC = execvp(ExecPath.get(), Args);
119258f055eSStefan Gränitz     if (RC != 0)
120258f055eSStefan Gränitz       return make_error<StringError>(
121258f055eSStefan Gränitz           "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
122258f055eSStefan Gränitz           inconvertibleErrorCode());
123258f055eSStefan Gränitz 
124258f055eSStefan Gränitz     llvm_unreachable("Fork won't return in success case");
125258f055eSStefan Gränitz   }
126258f055eSStefan Gränitz   // else we're the parent...
127258f055eSStefan Gränitz 
128258f055eSStefan Gränitz   // Close the child ends of the pipes
129258f055eSStefan Gränitz   close(ToExecutor[ReadEnd]);
130258f055eSStefan Gränitz   close(FromExecutor[WriteEnd]);
131258f055eSStefan Gränitz 
132ac2daacbSStefan Gränitz   auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
13369703b1fSLang Hames       std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
134abdb82b2SLang Hames       SimpleRemoteEPC::Setup(),
135ac2daacbSStefan Gränitz       FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
136ac2daacbSStefan Gränitz   if (!EPC)
137ac2daacbSStefan Gränitz     return EPC.takeError();
138258f055eSStefan Gränitz 
139ac2daacbSStefan Gränitz   return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
140258f055eSStefan Gränitz }
141258f055eSStefan Gränitz 
142258f055eSStefan Gränitz static Expected<int> connectTCPSocketImpl(std::string Host,
143258f055eSStefan Gränitz                                           std::string PortStr) {
144258f055eSStefan Gränitz   addrinfo *AI;
145258f055eSStefan Gränitz   addrinfo Hints{};
146258f055eSStefan Gränitz   Hints.ai_family = AF_INET;
147258f055eSStefan Gränitz   Hints.ai_socktype = SOCK_STREAM;
148258f055eSStefan Gränitz   Hints.ai_flags = AI_NUMERICSERV;
149258f055eSStefan Gränitz 
150258f055eSStefan Gränitz   if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
151258f055eSStefan Gränitz     return make_error<StringError>(
152258f055eSStefan Gränitz         formatv("address resolution failed ({0})", gai_strerror(EC)),
153258f055eSStefan Gränitz         inconvertibleErrorCode());
154258f055eSStefan Gränitz 
155258f055eSStefan Gränitz   // Cycle through the returned addrinfo structures and connect to the first
156258f055eSStefan Gränitz   // reachable endpoint.
157258f055eSStefan Gränitz   int SockFD;
158258f055eSStefan Gränitz   addrinfo *Server;
159258f055eSStefan Gränitz   for (Server = AI; Server != nullptr; Server = Server->ai_next) {
160258f055eSStefan Gränitz     // If socket fails, maybe it's because the address family is not supported.
161258f055eSStefan Gränitz     // Skip to the next addrinfo structure.
162258f055eSStefan Gränitz     if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
163258f055eSStefan Gränitz       continue;
164258f055eSStefan Gränitz 
165258f055eSStefan Gränitz     // If connect works, we exit the loop with a working socket.
166258f055eSStefan Gränitz     if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
167258f055eSStefan Gränitz       break;
168258f055eSStefan Gränitz 
169258f055eSStefan Gränitz     close(SockFD);
170258f055eSStefan Gränitz   }
171258f055eSStefan Gränitz   freeaddrinfo(AI);
172258f055eSStefan Gränitz 
173258f055eSStefan Gränitz   // Did we reach the end of the loop without connecting to a valid endpoint?
174258f055eSStefan Gränitz   if (Server == nullptr)
175258f055eSStefan Gränitz     return make_error<StringError>("invalid hostname",
176258f055eSStefan Gränitz                                    inconvertibleErrorCode());
177258f055eSStefan Gränitz 
178258f055eSStefan Gränitz   return SockFD;
179258f055eSStefan Gränitz }
180258f055eSStefan Gränitz 
181ac2daacbSStefan Gränitz Expected<std::unique_ptr<SimpleRemoteEPC>>
182ac2daacbSStefan Gränitz connectTCPSocket(StringRef NetworkAddress) {
183258f055eSStefan Gränitz   auto CreateErr = [NetworkAddress](StringRef Details) {
184258f055eSStefan Gränitz     return make_error<StringError>(
185258f055eSStefan Gränitz         formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
186258f055eSStefan Gränitz                 Details),
187258f055eSStefan Gränitz         inconvertibleErrorCode());
188258f055eSStefan Gränitz   };
189258f055eSStefan Gränitz 
190258f055eSStefan Gränitz   StringRef Host, PortStr;
191258f055eSStefan Gränitz   std::tie(Host, PortStr) = NetworkAddress.split(':');
192258f055eSStefan Gränitz   if (Host.empty())
193258f055eSStefan Gränitz     return CreateErr("host name cannot be empty");
194258f055eSStefan Gränitz   if (PortStr.empty())
195258f055eSStefan Gränitz     return CreateErr("port cannot be empty");
196258f055eSStefan Gränitz   int Port = 0;
197258f055eSStefan Gränitz   if (PortStr.getAsInteger(10, Port))
198258f055eSStefan Gränitz     return CreateErr("port number is not a valid integer");
199258f055eSStefan Gränitz 
200258f055eSStefan Gränitz   Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
201258f055eSStefan Gränitz   if (!SockFD)
202258f055eSStefan Gränitz     return CreateErr(toString(SockFD.takeError()));
203258f055eSStefan Gränitz 
2042e6c92c5SLang Hames   return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
20569703b1fSLang Hames       std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
206abdb82b2SLang Hames       SimpleRemoteEPC::Setup(), *SockFD);
207258f055eSStefan Gränitz }
208258f055eSStefan Gränitz 
209258f055eSStefan Gränitz #endif
210