xref: /openbsd-src/gnu/llvm/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
173471bf0Spatrick //===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
273471bf0Spatrick //
373471bf0Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
473471bf0Spatrick // See https://llvm.org/LICENSE.txt for license information.
573471bf0Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
673471bf0Spatrick //
773471bf0Spatrick //===----------------------------------------------------------------------===//
873471bf0Spatrick 
973471bf0Spatrick #include "RemoteJITUtils.h"
1073471bf0Spatrick 
1173471bf0Spatrick #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
1273471bf0Spatrick #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
1373471bf0Spatrick #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
14*d415bd75Srobert #include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
1573471bf0Spatrick #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
1673471bf0Spatrick #include "llvm/Support/FileSystem.h"
1773471bf0Spatrick #include "llvm/Support/Path.h"
1873471bf0Spatrick 
1973471bf0Spatrick #ifdef LLVM_ON_UNIX
2073471bf0Spatrick #include <netdb.h>
2173471bf0Spatrick #include <netinet/in.h>
2273471bf0Spatrick #include <sys/socket.h>
2373471bf0Spatrick #include <unistd.h>
2473471bf0Spatrick #endif // LLVM_ON_UNIX
2573471bf0Spatrick 
2673471bf0Spatrick using namespace llvm;
2773471bf0Spatrick using namespace llvm::orc;
2873471bf0Spatrick 
addDebugSupport(ObjectLayer & ObjLayer)29*d415bd75Srobert Error addDebugSupport(ObjectLayer &ObjLayer) {
30*d415bd75Srobert   ExecutionSession &ES = ObjLayer.getExecutionSession();
31*d415bd75Srobert   auto Registrar = createJITLoaderGDBRegistrar(ES);
3273471bf0Spatrick   if (!Registrar)
3373471bf0Spatrick     return Registrar.takeError();
3473471bf0Spatrick 
35*d415bd75Srobert   auto *ObjLinkingLayer = cast<ObjectLinkingLayer>(&ObjLayer);
36*d415bd75Srobert   if (!ObjLinkingLayer)
37*d415bd75Srobert     return createStringError(inconvertibleErrorCode(),
38*d415bd75Srobert                              "No debug support for given object layer type");
3973471bf0Spatrick 
40*d415bd75Srobert   ObjLinkingLayer->addPlugin(
41*d415bd75Srobert       std::make_unique<DebugObjectManagerPlugin>(ES, std::move(*Registrar)));
4273471bf0Spatrick   return Error::success();
4373471bf0Spatrick }
4473471bf0Spatrick 
4573471bf0Spatrick Expected<std::unique_ptr<DefinitionGenerator>>
loadDylib(ExecutionSession & ES,StringRef RemotePath)46*d415bd75Srobert loadDylib(ExecutionSession &ES, StringRef RemotePath) {
47*d415bd75Srobert   if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
48*d415bd75Srobert     return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
4973471bf0Spatrick   else
5073471bf0Spatrick     return Handle.takeError();
5173471bf0Spatrick }
5273471bf0Spatrick 
findLocalExecutorHelper()53*d415bd75Srobert static void findLocalExecutorHelper() {}
findLocalExecutor(const char * HostArgv0)54*d415bd75Srobert std::string findLocalExecutor(const char *HostArgv0) {
55*d415bd75Srobert   // This just needs to be some static symbol in the binary; C++ doesn't
5673471bf0Spatrick   // allow taking the address of ::main however.
57*d415bd75Srobert   uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
58*d415bd75Srobert   void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
59*d415bd75Srobert   SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
6073471bf0Spatrick   sys::path::remove_filename(FullName);
61*d415bd75Srobert   sys::path::append(FullName, "llvm-jitlink-executor");
6273471bf0Spatrick   return FullName.str().str();
6373471bf0Spatrick }
6473471bf0Spatrick 
6573471bf0Spatrick #ifndef LLVM_ON_UNIX
6673471bf0Spatrick 
6773471bf0Spatrick // FIXME: Add support for Windows.
68*d415bd75Srobert Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath)69*d415bd75Srobert launchLocalExecutor(StringRef ExecutablePath) {
7073471bf0Spatrick   return make_error<StringError>(
7173471bf0Spatrick       "Remote JITing not yet supported on non-unix platforms",
7273471bf0Spatrick       inconvertibleErrorCode());
7373471bf0Spatrick }
7473471bf0Spatrick 
7573471bf0Spatrick // FIXME: Add support for Windows.
76*d415bd75Srobert Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress)77*d415bd75Srobert connectTCPSocket(StringRef NetworkAddress) {
7873471bf0Spatrick   return make_error<StringError>(
7973471bf0Spatrick       "Remote JITing not yet supported on non-unix platforms",
8073471bf0Spatrick       inconvertibleErrorCode());
8173471bf0Spatrick }
8273471bf0Spatrick 
8373471bf0Spatrick #else
8473471bf0Spatrick 
85*d415bd75Srobert Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
launchLocalExecutor(StringRef ExecutablePath)86*d415bd75Srobert launchLocalExecutor(StringRef ExecutablePath) {
8773471bf0Spatrick   constexpr int ReadEnd = 0;
8873471bf0Spatrick   constexpr int WriteEnd = 1;
8973471bf0Spatrick 
90*d415bd75Srobert   if (!sys::fs::can_execute(ExecutablePath))
91*d415bd75Srobert     return make_error<StringError>(
92*d415bd75Srobert         formatv("Specified executor invalid: {0}", ExecutablePath),
93*d415bd75Srobert         inconvertibleErrorCode());
94*d415bd75Srobert 
9573471bf0Spatrick   // Pipe FDs.
9673471bf0Spatrick   int ToExecutor[2];
9773471bf0Spatrick   int FromExecutor[2];
9873471bf0Spatrick 
9973471bf0Spatrick   // Create pipes to/from the executor..
10073471bf0Spatrick   if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
10173471bf0Spatrick     return make_error<StringError>("Unable to create pipe for executor",
10273471bf0Spatrick                                    inconvertibleErrorCode());
10373471bf0Spatrick 
104*d415bd75Srobert   pid_t ProcessID = fork();
10573471bf0Spatrick   if (ProcessID == 0) {
10673471bf0Spatrick     // In the child...
10773471bf0Spatrick 
10873471bf0Spatrick     // Close the parent ends of the pipes
10973471bf0Spatrick     close(ToExecutor[WriteEnd]);
11073471bf0Spatrick     close(FromExecutor[ReadEnd]);
11173471bf0Spatrick 
11273471bf0Spatrick     // Execute the child process.
11373471bf0Spatrick     std::unique_ptr<char[]> ExecPath, FDSpecifier;
11473471bf0Spatrick     {
11573471bf0Spatrick       ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
11673471bf0Spatrick       strcpy(ExecPath.get(), ExecutablePath.data());
11773471bf0Spatrick 
11873471bf0Spatrick       std::string FDSpecifierStr("filedescs=");
11973471bf0Spatrick       FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
12073471bf0Spatrick       FDSpecifierStr += ',';
12173471bf0Spatrick       FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
12273471bf0Spatrick       FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
12373471bf0Spatrick       strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
12473471bf0Spatrick     }
12573471bf0Spatrick 
12673471bf0Spatrick     char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr};
12773471bf0Spatrick     int RC = execvp(ExecPath.get(), Args);
12873471bf0Spatrick     if (RC != 0)
12973471bf0Spatrick       return make_error<StringError>(
13073471bf0Spatrick           "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
13173471bf0Spatrick           inconvertibleErrorCode());
13273471bf0Spatrick 
13373471bf0Spatrick     llvm_unreachable("Fork won't return in success case");
13473471bf0Spatrick   }
13573471bf0Spatrick   // else we're the parent...
13673471bf0Spatrick 
13773471bf0Spatrick   // Close the child ends of the pipes
13873471bf0Spatrick   close(ToExecutor[ReadEnd]);
13973471bf0Spatrick   close(FromExecutor[WriteEnd]);
14073471bf0Spatrick 
141*d415bd75Srobert   auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
142*d415bd75Srobert       std::make_unique<DynamicThreadPoolTaskDispatcher>(),
143*d415bd75Srobert       SimpleRemoteEPC::Setup(),
144*d415bd75Srobert       FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
145*d415bd75Srobert   if (!EPC)
146*d415bd75Srobert     return EPC.takeError();
14773471bf0Spatrick 
148*d415bd75Srobert   return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
14973471bf0Spatrick }
15073471bf0Spatrick 
connectTCPSocketImpl(std::string Host,std::string PortStr)15173471bf0Spatrick static Expected<int> connectTCPSocketImpl(std::string Host,
15273471bf0Spatrick                                           std::string PortStr) {
15373471bf0Spatrick   addrinfo *AI;
15473471bf0Spatrick   addrinfo Hints{};
15573471bf0Spatrick   Hints.ai_family = AF_INET;
15673471bf0Spatrick   Hints.ai_socktype = SOCK_STREAM;
15773471bf0Spatrick   Hints.ai_flags = AI_NUMERICSERV;
15873471bf0Spatrick 
15973471bf0Spatrick   if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
16073471bf0Spatrick     return make_error<StringError>(
16173471bf0Spatrick         formatv("address resolution failed ({0})", gai_strerror(EC)),
16273471bf0Spatrick         inconvertibleErrorCode());
16373471bf0Spatrick 
16473471bf0Spatrick   // Cycle through the returned addrinfo structures and connect to the first
16573471bf0Spatrick   // reachable endpoint.
16673471bf0Spatrick   int SockFD;
16773471bf0Spatrick   addrinfo *Server;
16873471bf0Spatrick   for (Server = AI; Server != nullptr; Server = Server->ai_next) {
16973471bf0Spatrick     // If socket fails, maybe it's because the address family is not supported.
17073471bf0Spatrick     // Skip to the next addrinfo structure.
17173471bf0Spatrick     if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
17273471bf0Spatrick       continue;
17373471bf0Spatrick 
17473471bf0Spatrick     // If connect works, we exit the loop with a working socket.
17573471bf0Spatrick     if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
17673471bf0Spatrick       break;
17773471bf0Spatrick 
17873471bf0Spatrick     close(SockFD);
17973471bf0Spatrick   }
18073471bf0Spatrick   freeaddrinfo(AI);
18173471bf0Spatrick 
18273471bf0Spatrick   // Did we reach the end of the loop without connecting to a valid endpoint?
18373471bf0Spatrick   if (Server == nullptr)
18473471bf0Spatrick     return make_error<StringError>("invalid hostname",
18573471bf0Spatrick                                    inconvertibleErrorCode());
18673471bf0Spatrick 
18773471bf0Spatrick   return SockFD;
18873471bf0Spatrick }
18973471bf0Spatrick 
190*d415bd75Srobert Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress)191*d415bd75Srobert connectTCPSocket(StringRef NetworkAddress) {
19273471bf0Spatrick   auto CreateErr = [NetworkAddress](StringRef Details) {
19373471bf0Spatrick     return make_error<StringError>(
19473471bf0Spatrick         formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
19573471bf0Spatrick                 Details),
19673471bf0Spatrick         inconvertibleErrorCode());
19773471bf0Spatrick   };
19873471bf0Spatrick 
19973471bf0Spatrick   StringRef Host, PortStr;
20073471bf0Spatrick   std::tie(Host, PortStr) = NetworkAddress.split(':');
20173471bf0Spatrick   if (Host.empty())
20273471bf0Spatrick     return CreateErr("host name cannot be empty");
20373471bf0Spatrick   if (PortStr.empty())
20473471bf0Spatrick     return CreateErr("port cannot be empty");
20573471bf0Spatrick   int Port = 0;
20673471bf0Spatrick   if (PortStr.getAsInteger(10, Port))
20773471bf0Spatrick     return CreateErr("port number is not a valid integer");
20873471bf0Spatrick 
20973471bf0Spatrick   Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
21073471bf0Spatrick   if (!SockFD)
21173471bf0Spatrick     return CreateErr(toString(SockFD.takeError()));
21273471bf0Spatrick 
213*d415bd75Srobert   return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
214*d415bd75Srobert       std::make_unique<DynamicThreadPoolTaskDispatcher>(),
215*d415bd75Srobert       SimpleRemoteEPC::Setup(), *SockFD);
21673471bf0Spatrick }
21773471bf0Spatrick 
21873471bf0Spatrick #endif
219