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 11258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" 12*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" 13*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" 14*662c5544SLang Hames #include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h" 15258f055eSStefan Gränitz #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.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 #include "llvm/Support/ToolOutputFile.h" 20258f055eSStefan Gränitz 21258f055eSStefan Gränitz #ifdef LLVM_ON_UNIX 22258f055eSStefan Gränitz #include <netdb.h> 23258f055eSStefan Gränitz #include <netinet/in.h> 24258f055eSStefan Gränitz #include <sys/socket.h> 25258f055eSStefan Gränitz #include <unistd.h> 26258f055eSStefan Gränitz #endif // LLVM_ON_UNIX 27258f055eSStefan Gränitz 28258f055eSStefan Gränitz using namespace llvm; 29258f055eSStefan Gränitz using namespace llvm::orc; 30258f055eSStefan Gränitz 31258f055eSStefan Gränitz namespace llvm { 32258f055eSStefan Gränitz namespace orc { 33258f055eSStefan Gränitz 34*662c5544SLang Hames class RemoteExecutorProcessControl 35*662c5544SLang Hames : public OrcRPCExecutorProcessControlBase< 36258f055eSStefan Gränitz shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> { 37258f055eSStefan Gränitz public: 38258f055eSStefan Gränitz using RPCChannel = JITLinkExecutor::RPCChannel; 39258f055eSStefan Gränitz using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>; 40258f055eSStefan Gränitz 41258f055eSStefan Gränitz private: 42*662c5544SLang Hames using ThisT = RemoteExecutorProcessControl; 43*662c5544SLang Hames using BaseT = OrcRPCExecutorProcessControlBase<RPCEndpoint>; 44*662c5544SLang Hames using MemoryAccess = OrcRPCEPCMemoryAccess<ThisT>; 45*662c5544SLang Hames using MemoryManager = OrcRPCEPCJITLinkMemoryManager<ThisT>; 46258f055eSStefan Gränitz 47258f055eSStefan Gränitz public: 48*662c5544SLang Hames using BaseT::initializeORCRPCEPCBase; 49258f055eSStefan Gränitz 50*662c5544SLang Hames RemoteExecutorProcessControl(ExecutionSession &ES, 51258f055eSStefan Gränitz std::unique_ptr<RPCChannel> Channel, 52258f055eSStefan Gränitz std::unique_ptr<RPCEndpoint> Endpoint); 53258f055eSStefan Gränitz 54258f055eSStefan Gränitz void initializeMemoryManagement(); 55258f055eSStefan Gränitz Error disconnect() override; 56258f055eSStefan Gränitz 57258f055eSStefan Gränitz private: 58258f055eSStefan Gränitz std::unique_ptr<RPCChannel> Channel; 59258f055eSStefan Gränitz std::unique_ptr<RPCEndpoint> Endpoint; 60258f055eSStefan Gränitz std::unique_ptr<MemoryAccess> OwnedMemAccess; 61258f055eSStefan Gränitz std::unique_ptr<MemoryManager> OwnedMemMgr; 62258f055eSStefan Gränitz std::atomic<bool> Finished{false}; 63258f055eSStefan Gränitz std::thread ListenerThread; 64258f055eSStefan Gränitz }; 65258f055eSStefan Gränitz 66*662c5544SLang Hames RemoteExecutorProcessControl::RemoteExecutorProcessControl( 67258f055eSStefan Gränitz ExecutionSession &ES, std::unique_ptr<RPCChannel> Channel, 68258f055eSStefan Gränitz std::unique_ptr<RPCEndpoint> Endpoint) 69258f055eSStefan Gränitz : BaseT(ES.getSymbolStringPool(), *Endpoint, 70258f055eSStefan Gränitz [&ES](Error Err) { ES.reportError(std::move(Err)); }), 71258f055eSStefan Gränitz Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) { 72258f055eSStefan Gränitz 73258f055eSStefan Gränitz ListenerThread = std::thread([&]() { 74258f055eSStefan Gränitz while (!Finished) { 75258f055eSStefan Gränitz if (auto Err = this->Endpoint->handleOne()) { 76258f055eSStefan Gränitz reportError(std::move(Err)); 77258f055eSStefan Gränitz return; 78258f055eSStefan Gränitz } 79258f055eSStefan Gränitz } 80258f055eSStefan Gränitz }); 81258f055eSStefan Gränitz } 82258f055eSStefan Gränitz 83*662c5544SLang Hames void RemoteExecutorProcessControl::initializeMemoryManagement() { 84258f055eSStefan Gränitz OwnedMemAccess = std::make_unique<MemoryAccess>(*this); 85258f055eSStefan Gränitz OwnedMemMgr = std::make_unique<MemoryManager>(*this); 86258f055eSStefan Gränitz 87258f055eSStefan Gränitz // Base class needs non-owning access. 88258f055eSStefan Gränitz MemAccess = OwnedMemAccess.get(); 89258f055eSStefan Gränitz MemMgr = OwnedMemMgr.get(); 90258f055eSStefan Gränitz } 91258f055eSStefan Gränitz 92*662c5544SLang Hames Error RemoteExecutorProcessControl::disconnect() { 93258f055eSStefan Gränitz std::promise<MSVCPError> P; 94258f055eSStefan Gränitz auto F = P.get_future(); 95258f055eSStefan Gränitz auto Err = closeConnection([&](Error Err) -> Error { 96258f055eSStefan Gränitz P.set_value(std::move(Err)); 97258f055eSStefan Gränitz Finished = true; 98258f055eSStefan Gränitz return Error::success(); 99258f055eSStefan Gränitz }); 100258f055eSStefan Gränitz ListenerThread.join(); 101258f055eSStefan Gränitz return joinErrors(std::move(Err), F.get()); 102258f055eSStefan Gränitz } 103258f055eSStefan Gränitz 104258f055eSStefan Gränitz } // namespace orc 105258f055eSStefan Gränitz } // namespace llvm 106258f055eSStefan Gränitz 107258f055eSStefan Gränitz JITLinkExecutor::JITLinkExecutor() = default; 108258f055eSStefan Gränitz JITLinkExecutor::~JITLinkExecutor() = default; 109258f055eSStefan Gränitz 110258f055eSStefan Gränitz Expected<std::unique_ptr<ObjectLayer>> 111258f055eSStefan Gränitz JITLinkExecutor::operator()(ExecutionSession &ES, const Triple &TT) { 112*662c5544SLang Hames return std::make_unique<ObjectLinkingLayer>(ES, EPC->getMemMgr()); 113258f055eSStefan Gränitz } 114258f055eSStefan Gränitz 115258f055eSStefan Gränitz Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) { 116*662c5544SLang Hames auto Registrar = createJITLoaderGDBRegistrar(*EPC); 117258f055eSStefan Gränitz if (!Registrar) 118258f055eSStefan Gränitz return Registrar.takeError(); 119258f055eSStefan Gränitz 120258f055eSStefan Gränitz cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin( 121258f055eSStefan Gränitz std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(), 122258f055eSStefan Gränitz std::move(*Registrar))); 123258f055eSStefan Gränitz 124258f055eSStefan Gränitz return Error::success(); 125258f055eSStefan Gränitz } 126258f055eSStefan Gränitz 127258f055eSStefan Gränitz Expected<std::unique_ptr<DefinitionGenerator>> 128258f055eSStefan Gränitz JITLinkExecutor::loadDylib(StringRef RemotePath) { 129*662c5544SLang Hames if (auto Handle = EPC->loadDylib(RemotePath.data())) 130*662c5544SLang Hames return std::make_unique<EPCDynamicLibrarySearchGenerator>(*EPC, *Handle); 131258f055eSStefan Gränitz else 132258f055eSStefan Gränitz return Handle.takeError(); 133258f055eSStefan Gränitz } 134258f055eSStefan Gränitz 135258f055eSStefan Gränitz Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym, 136258f055eSStefan Gränitz ArrayRef<std::string> Args) { 137*662c5544SLang Hames return EPC->runAsMain(MainSym.getAddress(), Args); 138258f055eSStefan Gränitz } 139258f055eSStefan Gränitz 140*662c5544SLang Hames Error JITLinkExecutor::disconnect() { return EPC->disconnect(); } 141258f055eSStefan Gränitz 142258f055eSStefan Gränitz static std::string defaultPath(const char *HostArgv0, StringRef ExecutorName) { 143258f055eSStefan Gränitz // This just needs to be some symbol in the binary; C++ doesn't 144258f055eSStefan Gränitz // allow taking the address of ::main however. 145258f055eSStefan Gränitz void *P = (void *)(intptr_t)defaultPath; 146258f055eSStefan Gränitz SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, P)); 147258f055eSStefan Gränitz sys::path::remove_filename(FullName); 148258f055eSStefan Gränitz sys::path::append(FullName, ExecutorName); 149258f055eSStefan Gränitz return FullName.str().str(); 150258f055eSStefan Gränitz } 151258f055eSStefan Gränitz 152258f055eSStefan Gränitz Expected<std::unique_ptr<ChildProcessJITLinkExecutor>> 153258f055eSStefan Gränitz JITLinkExecutor::FindLocal(const char *HostArgv) { 154258f055eSStefan Gränitz std::string BestGuess = defaultPath(HostArgv, "llvm-jitlink-executor"); 155258f055eSStefan Gränitz auto Executor = CreateLocal(BestGuess); 156258f055eSStefan Gränitz if (!Executor) { 157258f055eSStefan Gränitz consumeError(Executor.takeError()); 158258f055eSStefan Gränitz return make_error<StringError>( 159258f055eSStefan Gränitz formatv("Unable to find usable executor: {0}", BestGuess), 160258f055eSStefan Gränitz inconvertibleErrorCode()); 161258f055eSStefan Gränitz } 162258f055eSStefan Gränitz return Executor; 163258f055eSStefan Gränitz } 164258f055eSStefan Gränitz 165258f055eSStefan Gränitz Expected<std::unique_ptr<ChildProcessJITLinkExecutor>> 166258f055eSStefan Gränitz JITLinkExecutor::CreateLocal(std::string ExecutablePath) { 167258f055eSStefan Gränitz if (!sys::fs::can_execute(ExecutablePath)) 168258f055eSStefan Gränitz return make_error<StringError>( 169258f055eSStefan Gränitz formatv("Specified executor invalid: {0}", ExecutablePath), 170258f055eSStefan Gränitz inconvertibleErrorCode()); 171258f055eSStefan Gränitz return std::unique_ptr<ChildProcessJITLinkExecutor>( 172258f055eSStefan Gränitz new ChildProcessJITLinkExecutor(std::move(ExecutablePath))); 173258f055eSStefan Gränitz } 174258f055eSStefan Gränitz 175258f055eSStefan Gränitz TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor( 176*662c5544SLang Hames std::unique_ptr<RemoteExecutorProcessControl> EPC) { 177*662c5544SLang Hames this->EPC = std::move(EPC); 178258f055eSStefan Gränitz } 179258f055eSStefan Gränitz 180258f055eSStefan Gränitz #ifndef LLVM_ON_UNIX 181258f055eSStefan Gränitz 182258f055eSStefan Gränitz // FIXME: Add support for Windows. 183258f055eSStefan Gränitz Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) { 184258f055eSStefan Gränitz return make_error<StringError>( 185258f055eSStefan Gränitz "Remote JITing not yet supported on non-unix platforms", 186258f055eSStefan Gränitz inconvertibleErrorCode()); 187258f055eSStefan Gränitz } 188258f055eSStefan Gränitz 189258f055eSStefan Gränitz // FIXME: Add support for Windows. 190258f055eSStefan Gränitz Expected<std::unique_ptr<TCPSocketJITLinkExecutor>> 191258f055eSStefan Gränitz JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress, 192258f055eSStefan Gränitz ExecutionSession &ES) { 193258f055eSStefan Gränitz return make_error<StringError>( 194258f055eSStefan Gränitz "Remote JITing not yet supported on non-unix platforms", 195258f055eSStefan Gränitz inconvertibleErrorCode()); 196258f055eSStefan Gränitz } 197258f055eSStefan Gränitz 198258f055eSStefan Gränitz #else 199258f055eSStefan Gränitz 200258f055eSStefan Gränitz Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) { 201258f055eSStefan Gränitz constexpr int ReadEnd = 0; 202258f055eSStefan Gränitz constexpr int WriteEnd = 1; 203258f055eSStefan Gränitz 204258f055eSStefan Gränitz // Pipe FDs. 205258f055eSStefan Gränitz int ToExecutor[2]; 206258f055eSStefan Gränitz int FromExecutor[2]; 207258f055eSStefan Gränitz 208258f055eSStefan Gränitz // Create pipes to/from the executor.. 209258f055eSStefan Gränitz if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0) 210258f055eSStefan Gränitz return make_error<StringError>("Unable to create pipe for executor", 211258f055eSStefan Gränitz inconvertibleErrorCode()); 212258f055eSStefan Gränitz 213258f055eSStefan Gränitz ProcessID = fork(); 214258f055eSStefan Gränitz if (ProcessID == 0) { 215258f055eSStefan Gränitz // In the child... 216258f055eSStefan Gränitz 217258f055eSStefan Gränitz // Close the parent ends of the pipes 218258f055eSStefan Gränitz close(ToExecutor[WriteEnd]); 219258f055eSStefan Gränitz close(FromExecutor[ReadEnd]); 220258f055eSStefan Gränitz 221258f055eSStefan Gränitz // Execute the child process. 222258f055eSStefan Gränitz std::unique_ptr<char[]> ExecPath, FDSpecifier; 223258f055eSStefan Gränitz { 224258f055eSStefan Gränitz ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1); 225258f055eSStefan Gränitz strcpy(ExecPath.get(), ExecutablePath.data()); 226258f055eSStefan Gränitz 227258f055eSStefan Gränitz std::string FDSpecifierStr("filedescs="); 228258f055eSStefan Gränitz FDSpecifierStr += utostr(ToExecutor[ReadEnd]); 229258f055eSStefan Gränitz FDSpecifierStr += ','; 230258f055eSStefan Gränitz FDSpecifierStr += utostr(FromExecutor[WriteEnd]); 231258f055eSStefan Gränitz FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1); 232258f055eSStefan Gränitz strcpy(FDSpecifier.get(), FDSpecifierStr.c_str()); 233258f055eSStefan Gränitz } 234258f055eSStefan Gränitz 235258f055eSStefan Gränitz char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr}; 236258f055eSStefan Gränitz int RC = execvp(ExecPath.get(), Args); 237258f055eSStefan Gränitz if (RC != 0) 238258f055eSStefan Gränitz return make_error<StringError>( 239258f055eSStefan Gränitz "Unable to launch out-of-process executor '" + ExecutablePath + "'\n", 240258f055eSStefan Gränitz inconvertibleErrorCode()); 241258f055eSStefan Gränitz 242258f055eSStefan Gränitz llvm_unreachable("Fork won't return in success case"); 243258f055eSStefan Gränitz } 244258f055eSStefan Gränitz // else we're the parent... 245258f055eSStefan Gränitz 246258f055eSStefan Gränitz // Close the child ends of the pipes 247258f055eSStefan Gränitz close(ToExecutor[ReadEnd]); 248258f055eSStefan Gränitz close(FromExecutor[WriteEnd]); 249258f055eSStefan Gränitz 250258f055eSStefan Gränitz auto Channel = 251258f055eSStefan Gränitz std::make_unique<RPCChannel>(FromExecutor[ReadEnd], ToExecutor[WriteEnd]); 252*662c5544SLang Hames auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>( 253*662c5544SLang Hames *Channel, true); 254258f055eSStefan Gränitz 255*662c5544SLang Hames EPC = std::make_unique<RemoteExecutorProcessControl>(ES, std::move(Channel), 256258f055eSStefan Gränitz std::move(Endpoint)); 257258f055eSStefan Gränitz 258*662c5544SLang Hames if (auto Err = EPC->initializeORCRPCEPCBase()) 259*662c5544SLang Hames return joinErrors(std::move(Err), EPC->disconnect()); 260258f055eSStefan Gränitz 261*662c5544SLang Hames EPC->initializeMemoryManagement(); 262258f055eSStefan Gränitz 263258f055eSStefan Gränitz shared::registerStringError<RPCChannel>(); 264258f055eSStefan Gränitz return Error::success(); 265258f055eSStefan Gränitz } 266258f055eSStefan Gränitz 267258f055eSStefan Gränitz static Expected<int> connectTCPSocketImpl(std::string Host, 268258f055eSStefan Gränitz std::string PortStr) { 269258f055eSStefan Gränitz addrinfo *AI; 270258f055eSStefan Gränitz addrinfo Hints{}; 271258f055eSStefan Gränitz Hints.ai_family = AF_INET; 272258f055eSStefan Gränitz Hints.ai_socktype = SOCK_STREAM; 273258f055eSStefan Gränitz Hints.ai_flags = AI_NUMERICSERV; 274258f055eSStefan Gränitz 275258f055eSStefan Gränitz if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI)) 276258f055eSStefan Gränitz return make_error<StringError>( 277258f055eSStefan Gränitz formatv("address resolution failed ({0})", gai_strerror(EC)), 278258f055eSStefan Gränitz inconvertibleErrorCode()); 279258f055eSStefan Gränitz 280258f055eSStefan Gränitz // Cycle through the returned addrinfo structures and connect to the first 281258f055eSStefan Gränitz // reachable endpoint. 282258f055eSStefan Gränitz int SockFD; 283258f055eSStefan Gränitz addrinfo *Server; 284258f055eSStefan Gränitz for (Server = AI; Server != nullptr; Server = Server->ai_next) { 285258f055eSStefan Gränitz // If socket fails, maybe it's because the address family is not supported. 286258f055eSStefan Gränitz // Skip to the next addrinfo structure. 287258f055eSStefan Gränitz if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) 288258f055eSStefan Gränitz continue; 289258f055eSStefan Gränitz 290258f055eSStefan Gränitz // If connect works, we exit the loop with a working socket. 291258f055eSStefan Gränitz if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) 292258f055eSStefan Gränitz break; 293258f055eSStefan Gränitz 294258f055eSStefan Gränitz close(SockFD); 295258f055eSStefan Gränitz } 296258f055eSStefan Gränitz freeaddrinfo(AI); 297258f055eSStefan Gränitz 298258f055eSStefan Gränitz // Did we reach the end of the loop without connecting to a valid endpoint? 299258f055eSStefan Gränitz if (Server == nullptr) 300258f055eSStefan Gränitz return make_error<StringError>("invalid hostname", 301258f055eSStefan Gränitz inconvertibleErrorCode()); 302258f055eSStefan Gränitz 303258f055eSStefan Gränitz return SockFD; 304258f055eSStefan Gränitz } 305258f055eSStefan Gränitz 306258f055eSStefan Gränitz Expected<std::unique_ptr<TCPSocketJITLinkExecutor>> 307258f055eSStefan Gränitz JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress, 308258f055eSStefan Gränitz ExecutionSession &ES) { 309258f055eSStefan Gränitz auto CreateErr = [NetworkAddress](StringRef Details) { 310258f055eSStefan Gränitz return make_error<StringError>( 311258f055eSStefan Gränitz formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress, 312258f055eSStefan Gränitz Details), 313258f055eSStefan Gränitz inconvertibleErrorCode()); 314258f055eSStefan Gränitz }; 315258f055eSStefan Gränitz 316258f055eSStefan Gränitz StringRef Host, PortStr; 317258f055eSStefan Gränitz std::tie(Host, PortStr) = NetworkAddress.split(':'); 318258f055eSStefan Gränitz if (Host.empty()) 319258f055eSStefan Gränitz return CreateErr("host name cannot be empty"); 320258f055eSStefan Gränitz if (PortStr.empty()) 321258f055eSStefan Gränitz return CreateErr("port cannot be empty"); 322258f055eSStefan Gränitz int Port = 0; 323258f055eSStefan Gränitz if (PortStr.getAsInteger(10, Port)) 324258f055eSStefan Gränitz return CreateErr("port number is not a valid integer"); 325258f055eSStefan Gränitz 326258f055eSStefan Gränitz Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); 327258f055eSStefan Gränitz if (!SockFD) 328258f055eSStefan Gränitz return CreateErr(toString(SockFD.takeError())); 329258f055eSStefan Gränitz 330258f055eSStefan Gränitz auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD); 331*662c5544SLang Hames auto Endpoint = std::make_unique<RemoteExecutorProcessControl::RPCEndpoint>( 332*662c5544SLang Hames *Channel, true); 333258f055eSStefan Gränitz 334*662c5544SLang Hames auto EPC = std::make_unique<RemoteExecutorProcessControl>( 335258f055eSStefan Gränitz ES, std::move(Channel), std::move(Endpoint)); 336258f055eSStefan Gränitz 337*662c5544SLang Hames if (auto Err = EPC->initializeORCRPCEPCBase()) 338*662c5544SLang Hames return joinErrors(std::move(Err), EPC->disconnect()); 339258f055eSStefan Gränitz 340*662c5544SLang Hames EPC->initializeMemoryManagement(); 341258f055eSStefan Gränitz shared::registerStringError<RPCChannel>(); 342258f055eSStefan Gränitz 343258f055eSStefan Gränitz return std::unique_ptr<TCPSocketJITLinkExecutor>( 344*662c5544SLang Hames new TCPSocketJITLinkExecutor(std::move(EPC))); 345258f055eSStefan Gränitz } 346258f055eSStefan Gränitz 347258f055eSStefan Gränitz #endif 348