[llvm] 258f055 - [Orc][examples] Add LLJITWithRemoteDebugging example
David Blaikie via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 30 13:05:06 PDT 2021
This broke the build with examples enabled (LLVM_BUILD_EXAMPLES=ON) I
think. My second attempt at fixing this is
in ae217bf1f3277b8c14590a130ee5e63cc1664443
On Sun, Mar 28, 2021 at 8:25 AM Stefan Gränitz via llvm-commits <
llvm-commits at lists.llvm.org> wrote:
>
> Author: Stefan Gränitz
> Date: 2021-03-28T17:25:09+02:00
> New Revision: 258f055ed93661900bc568350e09f467c0950486
>
> URL:
> https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486
> DIFF:
> https://github.com/llvm/llvm-project/commit/258f055ed93661900bc568350e09f467c0950486.diff
>
> LOG: [Orc][examples] Add LLJITWithRemoteDebugging example
>
> Added:
> llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
>
> llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
> llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
> llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
> llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c
> llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll
> llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
>
> Modified:
> llvm/examples/OrcV2Examples/CMakeLists.txt
> llvm/examples/OrcV2Examples/ExampleModules.h
>
> Removed:
>
>
>
>
> ################################################################################
> diff --git a/llvm/examples/OrcV2Examples/CMakeLists.txt
> b/llvm/examples/OrcV2Examples/CMakeLists.txt
> index bed277e59e0b..f6a11d6f0ef2 100644
> --- a/llvm/examples/OrcV2Examples/CMakeLists.txt
> +++ b/llvm/examples/OrcV2Examples/CMakeLists.txt
> @@ -12,3 +12,7 @@ add_subdirectory(OrcV2CBindingsAddObjectFile)
> add_subdirectory(OrcV2CBindingsBasicUsage)
> add_subdirectory(OrcV2CBindingsReflectProcessSymbols)
> add_subdirectory(OrcV2CBindingsRemovableCode)
> +
> +if(CMAKE_HOST_UNIX)
> + add_subdirectory(LLJITWithRemoteDebugging)
> +endif()
>
> diff --git a/llvm/examples/OrcV2Examples/ExampleModules.h
> b/llvm/examples/OrcV2Examples/ExampleModules.h
> index c88609fae769..53da756e15f7 100644
> --- a/llvm/examples/OrcV2Examples/ExampleModules.h
> +++ b/llvm/examples/OrcV2Examples/ExampleModules.h
> @@ -52,4 +52,16 @@ parseExampleModule(llvm::StringRef Source,
> llvm::StringRef Name) {
> return createSMDiagnosticError(Err);
> }
>
> +inline llvm::Expected<llvm::orc::ThreadSafeModule>
> +parseExampleModuleFromFile(llvm::StringRef FileName) {
> + using namespace llvm;
> + auto Ctx = std::make_unique<LLVMContext>();
> + SMDiagnostic Err;
> +
> + if (auto M = parseIRFile(FileName, Err, *Ctx))
> + return orc::ThreadSafeModule(std::move(M), std::move(Ctx));
> +
> + return createSMDiagnosticError(Err);
> +}
> +
> #endif // LLVM_EXAMPLES_ORCV2EXAMPLES_EXAMPLEMODULES_H
>
> diff --git
> a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
> new file mode 100644
> index 000000000000..b93dd4924b05
> --- /dev/null
> +++ b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/CMakeLists.txt
> @@ -0,0 +1,18 @@
> +set(LLVM_LINK_COMPONENTS
> + Core
> + ExecutionEngine
> + IRReader
> + JITLink
> + OrcJIT
> + OrcTargetProcess
> + Support
> + nativecodegen
> + )
> +
> +add_llvm_example(LLJITWithRemoteDebugging
> + LLJITWithRemoteDebugging.cpp
> + RemoteJITUtils.cpp
> +
> + DEPENDS
> + llvm-jitlink-executor
> + )
>
> diff --git
> a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
> new file mode 100644
> index 000000000000..6da30f608206
> --- /dev/null
> +++
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/LLJITWithRemoteDebugging.cpp
> @@ -0,0 +1,258 @@
> +//===--- LLJITWithRemoteDebugging.cpp - LLJIT targeting a child process
> ---===//
> +//
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM
> Exceptions.
> +// See https://llvm.org/LICENSE.txt for license information.
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +// This example shows how to use LLJIT and JITLink for out-of-process
> execution
> +// with debug support. A few notes beforehand:
> +//
> +// * Debuggers must implement the GDB JIT interface (gdb, udb, lldb 12+).
> +// * Debug support is currently limited to ELF on x86-64 platforms that
> run
> +// Unix-like systems.
> +// * There is a test for this example and it ships an IR file that is
> prepared
> +// for the instructions below.
> +//
> +//
> +// The following command line session provides a complete walkthrough of
> the
> +// feature using LLDB 12:
> +//
> +// [Terminal 1] Prepare a debuggable out-of-process JIT session:
> +//
> +// > cd llvm-project/build
> +// > ninja LLJITWithRemoteDebugging llvm-jitlink-executor
> +// > cp ../llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll .
> +// > bin/LLJITWithRemoteDebugging --wait-for-debugger argc_sub1_elf.ll
> +// Found out-of-process executor: bin/llvm-jitlink-executor
> +// Launched executor in subprocess: 65535
> +// Attach a debugger and press any key to continue.
> +//
> +//
> +// [Terminal 2] Attach a debugger to the child process:
> +//
> +// (lldb) log enable lldb jit
> +// (lldb) settings set plugin.jit-loader.gdb.enable on
> +// (lldb) settings set target.source-map Inputs/ \
> +//
> /path/to/llvm-project/llvm/test/Examples/OrcV2Examples/Inputs/
> +// (lldb) attach -p 65535
> +// JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
> +// JITLoaderGDB::SetJITBreakpoint setting JIT breakpoint
> +// Process 65535 stopped
> +// (lldb) b sub1
> +// Breakpoint 1: no locations (pending).
> +// WARNING: Unable to resolve breakpoint to any actual locations.
> +// (lldb) c
> +// Process 65535 resuming
> +//
> +//
> +// [Terminal 1] Press a key to start code generation and execution:
> +//
> +// Parsed input IR code from: argc_sub1_elf.ll
> +// Initialized LLJIT for remote executor
> +// Running: argc_sub1_elf.ll
> +//
> +//
> +// [Terminal 2] Breakpoint hits; we change the argc value from 1 to 42:
> +//
> +// (lldb) JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
> +// JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at
> 0x106b34000
> +// 1 location added to breakpoint 1
> +// Process 65535 stopped
> +// * thread #1, queue = 'com.apple.main-thread', stop reason =
> breakpoint 1.1
> +// frame #0: JIT(0x106b34000)`sub1(x=1) at argc_sub1.c:1:28
> +// -> 1 int sub1(int x) { return x - 1; }
> +// 2 int main(int argc, char **argv) { return sub1(argc); }
> +// (lldb) p x
> +// (int) $0 = 1
> +// (lldb) expr x = 42
> +// (int) $1 = 42
> +// (lldb) c
> +//
> +//
> +// [Terminal 1] Example output reflects the modified value:
> +//
> +// Exit code: 41
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
> +#include "llvm/ExecutionEngine/Orc/LLJIT.h"
> +#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
> +#include "llvm/Support/CommandLine.h"
> +#include "llvm/Support/Error.h"
> +#include "llvm/Support/FormatVariadic.h"
> +#include "llvm/Support/InitLLVM.h"
> +#include "llvm/Support/TargetSelect.h"
> +#include "llvm/Support/raw_ostream.h"
> +
> +#include "../ExampleModules.h"
> +#include "RemoteJITUtils.h"
> +
> +#include <memory>
> +#include <string>
> +
> +using namespace llvm;
> +using namespace llvm::orc;
> +
> +// The LLVM IR file to run.
> +static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
> + cl::desc("<input files>"));
> +
> +// Command line arguments to pass to the JITed main function.
> +static cl::list<std::string> InputArgv("args", cl::Positional,
> + cl::desc("<program arguments>..."),
> + cl::ZeroOrMore,
> cl::PositionalEatsArgs);
> +
> +// Given paths must exist on the remote target.
> +static cl::list<std::string>
> + Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
> + cl::value_desc("filename"), cl::ZeroOrMore);
> +
> +// File path of the executable to launch for execution in a child process.
> +// Inter-process communication will go through stdin/stdout pipes.
> +static cl::opt<std::string>
> + OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
> + cl::value_desc("filename"));
> +
> +// Network address of a running executor process that we can connected
> through a
> +// TCP socket. It may run locally or on a remote machine.
> +static cl::opt<std::string> OOPExecutorConnect(
> + "connect",
> + cl::desc("Connect to an out-of-process executor through a TCP
> socket"),
> + cl::value_desc("<hostname>:<port>"));
> +
> +// Give the user a chance to connect a debugger. Once we connected the
> executor
> +// process, wait for the user to press a key (and print out its PID if
> it's a
> +// child process).
> +static cl::opt<bool>
> + WaitForDebugger("wait-for-debugger",
> + cl::desc("Wait for user input before entering JITed
> code"),
> + cl::init(false));
> +
> +ExitOnError ExitOnErr;
> +
> +static std::unique_ptr<JITLinkExecutor> connectExecutor(const char *Argv0,
> + ExecutionSession
> &ES) {
> + // Connect to a running out-of-process executor through a TCP socket.
> + if (!OOPExecutorConnect.empty()) {
> + std::unique_ptr<TCPSocketJITLinkExecutor> Exec =
> + ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect,
> ES));
> +
> + outs() << "Connected to executor at " << OOPExecutorConnect << "\n";
> + if (WaitForDebugger) {
> + outs() << "Attach a debugger and press any key to continue.\n";
> + fflush(stdin);
> + getchar();
> + }
> +
> + return std::move(Exec);
> + }
> +
> + // Launch a out-of-process executor locally in a child process.
> + std::unique_ptr<ChildProcessJITLinkExecutor> Exec = ExitOnErr(
> + OOPExecutor.empty() ? JITLinkExecutor::FindLocal(Argv0)
> + : JITLinkExecutor::CreateLocal(OOPExecutor));
> +
> + outs() << "Found out-of-process executor: " << Exec->getPath() << "\n";
> +
> + ExitOnErr(Exec->launch(ES));
> + if (WaitForDebugger) {
> + outs() << "Launched executor in subprocess: " << Exec->getPID() <<
> "\n"
> + << "Attach a debugger and press any key to continue.\n";
> + fflush(stdin);
> + getchar();
> + }
> +
> + return std::move(Exec);
> +}
> +
> +int main(int argc, char *argv[]) {
> + InitLLVM X(argc, argv);
> +
> + InitializeNativeTarget();
> + InitializeNativeTargetAsmPrinter();
> +
> + ExitOnErr.setBanner(std::string(argv[0]) + ": ");
> + cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");
> +
> + auto ES = std::make_unique<ExecutionSession>();
> + ES->setErrorReporter([&](Error Err) { ExitOnErr(std::move(Err)); });
> +
> + // Launch/connect the out-of-process executor.
> + std::unique_ptr<JITLinkExecutor> Executor = connectExecutor(argv[0],
> *ES);
> +
> + // Load the given IR files.
> + std::vector<ThreadSafeModule> TSMs;
> + for (const std::string &Path : InputFiles) {
> + outs() << "Parsing input IR code from: " << Path << "\n";
> + TSMs.push_back(ExitOnErr(parseExampleModuleFromFile(Path)));
> + }
> +
> + StringRef TT;
> + StringRef MainModuleName;
> + TSMs.front().withModuleDo([&MainModuleName, &TT](Module &M) {
> + MainModuleName = M.getName();
> + TT = M.getTargetTriple();
> + });
> +
> + for (const ThreadSafeModule &TSM : TSMs)
> + ExitOnErr(TSM.withModuleDo([TT, MainModuleName](Module &M) -> Error {
> + if (M.getTargetTriple() != TT)
> + return make_error<StringError>(
> + formatv("Different target triples in input files:\n"
> + " '{0}' in '{1}'\n '{2}' in '{3}'",
> + TT, MainModuleName, M.getTargetTriple(), M.getName()),
> + inconvertibleErrorCode());
> + return Error::success();
> + }));
> +
> + // Create a target machine that matches the input triple.
> + JITTargetMachineBuilder JTMB((Triple(TT)));
> + JTMB.setCodeModel(CodeModel::Small);
> + JTMB.setRelocationModel(Reloc::PIC_);
> +
> + // Create LLJIT and destroy it before disconnecting the target process.
> + {
> + outs() << "Initializing LLJIT for remote executor\n";
> + auto J = ExitOnErr(LLJITBuilder()
> + .setExecutionSession(std::move(ES))
> + .setJITTargetMachineBuilder(std::move(JTMB))
> +
> .setObjectLinkingLayerCreator(std::ref(*Executor))
> + .create());
> +
> + // Add plugin for debug support.
> + ExitOnErr(Executor->addDebugSupport(J->getObjLinkingLayer()));
> +
> + // Load required shared libraries on the remote target and add a
> generator
> + // for each of it, so the compiler can lookup their symbols.
> + for (const std::string &Path : Dylibs)
> +
> J->getMainJITDylib().addGenerator(ExitOnErr(Executor->loadDylib(Path)));
> +
> + // Add the loaded IR module to the JIT. This will set up symbol
> tables and
> + // prepare for materialization.
> + for (ThreadSafeModule &TSM : TSMs)
> + ExitOnErr(J->addIRModule(std::move(TSM)));
> +
> + // The example uses a non-lazy JIT for simplicity. Thus, looking up
> the main
> + // function will materialize all reachable code. It also triggers
> debug
> + // registration in the remote target process.
> + JITEvaluatedSymbol MainFn = ExitOnErr(J->lookup("main"));
> +
> + outs() << "Running: main(";
> + int Pos = 0;
> + for (const std::string &Arg : InputArgv)
> + outs() << (Pos++ == 0 ? "" : ", ") << Arg;
> + outs() << ")\n";
> +
> + // Execute the code in the remote target process and dump the result.
> With
> + // the debugger attached to the target, it should be possible to
> inspect the
> + // JITed code as if it was compiled statically.
> + int Result = ExitOnErr(Executor->runAsMain(MainFn, InputArgv));
> + outs() << "Exit code: " << Result << "\n";
> + }
> +
> + ExitOnErr(Executor->disconnect());
> + return 0;
> +}
>
> diff --git
> a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
> new file mode 100644
> index 000000000000..abce14de0fe0
> --- /dev/null
> +++
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
> @@ -0,0 +1,347 @@
> +//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++
> -*-===//
> +//
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM
> Exceptions.
> +// See https://llvm.org/LICENSE.txt for license information.
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "RemoteJITUtils.h"
> +
> +#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
> +#include "llvm/ExecutionEngine/Orc/OrcRPCTargetProcessControl.h"
> +#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
> +#include "llvm/ExecutionEngine/Orc/TPCDebugObjectRegistrar.h"
> +#include "llvm/ExecutionEngine/Orc/TPCDynamicLibrarySearchGenerator.h"
> +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
> +#include "llvm/Support/FileSystem.h"
> +#include "llvm/Support/Path.h"
> +#include "llvm/Support/ToolOutputFile.h"
> +
> +#ifdef LLVM_ON_UNIX
> +#include <netdb.h>
> +#include <netinet/in.h>
> +#include <sys/socket.h>
> +#include <unistd.h>
> +#endif // LLVM_ON_UNIX
> +
> +using namespace llvm;
> +using namespace llvm::orc;
> +
> +namespace llvm {
> +namespace orc {
> +
> +class RemoteTargetProcessControl
> + : public OrcRPCTargetProcessControlBase<
> + shared::MultiThreadedRPCEndpoint<JITLinkExecutor::RPCChannel>> {
> +public:
> + using RPCChannel = JITLinkExecutor::RPCChannel;
> + using RPCEndpoint = shared::MultiThreadedRPCEndpoint<RPCChannel>;
> +
> +private:
> + using ThisT = RemoteTargetProcessControl;
> + using BaseT = OrcRPCTargetProcessControlBase<RPCEndpoint>;
> + using MemoryAccess = OrcRPCTPCMemoryAccess<ThisT>;
> + using MemoryManager = OrcRPCTPCJITLinkMemoryManager<ThisT>;
> +
> +public:
> + using BaseT::initializeORCRPCTPCBase;
> +
> + RemoteTargetProcessControl(ExecutionSession &ES,
> + std::unique_ptr<RPCChannel> Channel,
> + std::unique_ptr<RPCEndpoint> Endpoint);
> +
> + void initializeMemoryManagement();
> + Error disconnect() override;
> +
> +private:
> + std::unique_ptr<RPCChannel> Channel;
> + std::unique_ptr<RPCEndpoint> Endpoint;
> + std::unique_ptr<MemoryAccess> OwnedMemAccess;
> + std::unique_ptr<MemoryManager> OwnedMemMgr;
> + std::atomic<bool> Finished{false};
> + std::thread ListenerThread;
> +};
> +
> +RemoteTargetProcessControl::RemoteTargetProcessControl(
> + ExecutionSession &ES, std::unique_ptr<RPCChannel> Channel,
> + std::unique_ptr<RPCEndpoint> Endpoint)
> + : BaseT(ES.getSymbolStringPool(), *Endpoint,
> + [&ES](Error Err) { ES.reportError(std::move(Err)); }),
> + Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) {
> +
> + ListenerThread = std::thread([&]() {
> + while (!Finished) {
> + if (auto Err = this->Endpoint->handleOne()) {
> + reportError(std::move(Err));
> + return;
> + }
> + }
> + });
> +}
> +
> +void RemoteTargetProcessControl::initializeMemoryManagement() {
> + OwnedMemAccess = std::make_unique<MemoryAccess>(*this);
> + OwnedMemMgr = std::make_unique<MemoryManager>(*this);
> +
> + // Base class needs non-owning access.
> + MemAccess = OwnedMemAccess.get();
> + MemMgr = OwnedMemMgr.get();
> +}
> +
> +Error RemoteTargetProcessControl::disconnect() {
> + std::promise<MSVCPError> P;
> + auto F = P.get_future();
> + auto Err = closeConnection([&](Error Err) -> Error {
> + P.set_value(std::move(Err));
> + Finished = true;
> + return Error::success();
> + });
> + ListenerThread.join();
> + return joinErrors(std::move(Err), F.get());
> +}
> +
> +} // namespace orc
> +} // namespace llvm
> +
> +JITLinkExecutor::JITLinkExecutor() = default;
> +JITLinkExecutor::~JITLinkExecutor() = default;
> +
> +Expected<std::unique_ptr<ObjectLayer>>
> +JITLinkExecutor::operator()(ExecutionSession &ES, const Triple &TT) {
> + return std::make_unique<ObjectLinkingLayer>(ES, TPC->getMemMgr());
> +}
> +
> +Error JITLinkExecutor::addDebugSupport(ObjectLayer &ObjLayer) {
> + auto Registrar = createJITLoaderGDBRegistrar(*TPC);
> + if (!Registrar)
> + return Registrar.takeError();
> +
> + cast<ObjectLinkingLayer>(&ObjLayer)->addPlugin(
> +
> std::make_unique<DebugObjectManagerPlugin>(ObjLayer.getExecutionSession(),
> + std::move(*Registrar)));
> +
> + return Error::success();
> +}
> +
> +Expected<std::unique_ptr<DefinitionGenerator>>
> +JITLinkExecutor::loadDylib(StringRef RemotePath) {
> + if (auto Handle = TPC->loadDylib(RemotePath.data()))
> + return std::make_unique<TPCDynamicLibrarySearchGenerator>(*TPC,
> *Handle);
> + else
> + return Handle.takeError();
> +}
> +
> +Expected<int> JITLinkExecutor::runAsMain(JITEvaluatedSymbol MainSym,
> + ArrayRef<std::string> Args) {
> + return TPC->runAsMain(MainSym.getAddress(), Args);
> +}
> +
> +Error JITLinkExecutor::disconnect() { return TPC->disconnect(); }
> +
> +static std::string defaultPath(const char *HostArgv0, StringRef
> ExecutorName) {
> + // This just needs to be some symbol in the binary; C++ doesn't
> + // allow taking the address of ::main however.
> + void *P = (void *)(intptr_t)defaultPath;
> + SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, P));
> + sys::path::remove_filename(FullName);
> + sys::path::append(FullName, ExecutorName);
> + return FullName.str().str();
> +}
> +
> +Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
> +JITLinkExecutor::FindLocal(const char *HostArgv) {
> + std::string BestGuess = defaultPath(HostArgv, "llvm-jitlink-executor");
> + auto Executor = CreateLocal(BestGuess);
> + if (!Executor) {
> + consumeError(Executor.takeError());
> + return make_error<StringError>(
> + formatv("Unable to find usable executor: {0}", BestGuess),
> + inconvertibleErrorCode());
> + }
> + return Executor;
> +}
> +
> +Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
> +JITLinkExecutor::CreateLocal(std::string ExecutablePath) {
> + if (!sys::fs::can_execute(ExecutablePath))
> + return make_error<StringError>(
> + formatv("Specified executor invalid: {0}", ExecutablePath),
> + inconvertibleErrorCode());
> + return std::unique_ptr<ChildProcessJITLinkExecutor>(
> + new ChildProcessJITLinkExecutor(std::move(ExecutablePath)));
> +}
> +
> +TCPSocketJITLinkExecutor::TCPSocketJITLinkExecutor(
> + std::unique_ptr<RemoteTargetProcessControl> TPC) {
> + this->TPC = std::move(TPC);
> +}
> +
> +#ifndef LLVM_ON_UNIX
> +
> +// FIXME: Add support for Windows.
> +Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
> + return make_error<StringError>(
> + "Remote JITing not yet supported on non-unix platforms",
> + inconvertibleErrorCode());
> +}
> +
> +// FIXME: Add support for Windows.
> +Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
> +JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
> + ExecutionSession &ES) {
> + return make_error<StringError>(
> + "Remote JITing not yet supported on non-unix platforms",
> + inconvertibleErrorCode());
> +}
> +
> +#else
> +
> +Error ChildProcessJITLinkExecutor::launch(ExecutionSession &ES) {
> + constexpr int ReadEnd = 0;
> + constexpr int WriteEnd = 1;
> +
> + // Pipe FDs.
> + int ToExecutor[2];
> + int FromExecutor[2];
> +
> + // Create pipes to/from the executor..
> + if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
> + return make_error<StringError>("Unable to create pipe for executor",
> + inconvertibleErrorCode());
> +
> + ProcessID = fork();
> + if (ProcessID == 0) {
> + // In the child...
> +
> + // Close the parent ends of the pipes
> + close(ToExecutor[WriteEnd]);
> + close(FromExecutor[ReadEnd]);
> +
> + // Execute the child process.
> + std::unique_ptr<char[]> ExecPath, FDSpecifier;
> + {
> + ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
> + strcpy(ExecPath.get(), ExecutablePath.data());
> +
> + std::string FDSpecifierStr("filedescs=");
> + FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
> + FDSpecifierStr += ',';
> + FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
> + FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
> + strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
> + }
> +
> + char *const Args[] = {ExecPath.get(), FDSpecifier.get(), nullptr};
> + int RC = execvp(ExecPath.get(), Args);
> + if (RC != 0)
> + return make_error<StringError>(
> + "Unable to launch out-of-process executor '" + ExecutablePath +
> "'\n",
> + inconvertibleErrorCode());
> +
> + llvm_unreachable("Fork won't return in success case");
> + }
> + // else we're the parent...
> +
> + // Close the child ends of the pipes
> + close(ToExecutor[ReadEnd]);
> + close(FromExecutor[WriteEnd]);
> +
> + auto Channel =
> + std::make_unique<RPCChannel>(FromExecutor[ReadEnd],
> ToExecutor[WriteEnd]);
> + auto Endpoint =
> + std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel,
> true);
> +
> + TPC = std::make_unique<RemoteTargetProcessControl>(ES,
> std::move(Channel),
> + std::move(Endpoint));
> +
> + if (auto Err = TPC->initializeORCRPCTPCBase())
> + return joinErrors(std::move(Err), TPC->disconnect());
> +
> + TPC->initializeMemoryManagement();
> +
> + shared::registerStringError<RPCChannel>();
> + return Error::success();
> +}
> +
> +static Expected<int> connectTCPSocketImpl(std::string Host,
> + std::string PortStr) {
> + addrinfo *AI;
> + addrinfo Hints{};
> + Hints.ai_family = AF_INET;
> + Hints.ai_socktype = SOCK_STREAM;
> + Hints.ai_flags = AI_NUMERICSERV;
> +
> + if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
> + return make_error<StringError>(
> + formatv("address resolution failed ({0})", gai_strerror(EC)),
> + inconvertibleErrorCode());
> +
> + // Cycle through the returned addrinfo structures and connect to the
> first
> + // reachable endpoint.
> + int SockFD;
> + addrinfo *Server;
> + for (Server = AI; Server != nullptr; Server = Server->ai_next) {
> + // If socket fails, maybe it's because the address family is not
> supported.
> + // Skip to the next addrinfo structure.
> + if ((SockFD = socket(AI->ai_family, AI->ai_socktype,
> AI->ai_protocol)) < 0)
> + continue;
> +
> + // If connect works, we exit the loop with a working socket.
> + if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
> + break;
> +
> + close(SockFD);
> + }
> + freeaddrinfo(AI);
> +
> + // Did we reach the end of the loop without connecting to a valid
> endpoint?
> + if (Server == nullptr)
> + return make_error<StringError>("invalid hostname",
> + inconvertibleErrorCode());
> +
> + return SockFD;
> +}
> +
> +Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
> +JITLinkExecutor::ConnectTCPSocket(StringRef NetworkAddress,
> + ExecutionSession &ES) {
> + auto CreateErr = [NetworkAddress](StringRef Details) {
> + return make_error<StringError>(
> + formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
> + Details),
> + inconvertibleErrorCode());
> + };
> +
> + StringRef Host, PortStr;
> + std::tie(Host, PortStr) = NetworkAddress.split(':');
> + if (Host.empty())
> + return CreateErr("host name cannot be empty");
> + if (PortStr.empty())
> + return CreateErr("port cannot be empty");
> + int Port = 0;
> + if (PortStr.getAsInteger(10, Port))
> + return CreateErr("port number is not a valid integer");
> +
> + Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
> + if (!SockFD)
> + return CreateErr(toString(SockFD.takeError()));
> +
> + auto Channel = std::make_unique<RPCChannel>(*SockFD, *SockFD);
> + auto Endpoint =
> + std::make_unique<RemoteTargetProcessControl::RPCEndpoint>(*Channel,
> true);
> +
> + auto TPC = std::make_unique<RemoteTargetProcessControl>(
> + ES, std::move(Channel), std::move(Endpoint));
> +
> + if (auto Err = TPC->initializeORCRPCTPCBase())
> + return joinErrors(std::move(Err), TPC->disconnect());
> +
> + TPC->initializeMemoryManagement();
> + shared::registerStringError<RPCChannel>();
> +
> + return std::unique_ptr<TCPSocketJITLinkExecutor>(
> + new TCPSocketJITLinkExecutor(std::move(TPC)));
> +}
> +
> +#endif
>
> diff --git
> a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
> b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
> new file mode 100644
> index 000000000000..e629c0e036f5
> --- /dev/null
> +++ b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.h
> @@ -0,0 +1,111 @@
> +//===-- RemoteJITUtils.h - Utilities for remote-JITing ----------*- C++
> -*-===//
> +//
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM
> Exceptions.
> +// See https://llvm.org/LICENSE.txt for license information.
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +// Utilities for TargetProcessControl-based remote JITing with Orc and
> JITLink.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef
> LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
> +#define
> LLVM_EXAMPLES_ORCV2EXAMPLES_LLJITWITHREMOTEDEBUGGING_REMOTEJITUTILS_H
> +
> +#include "llvm/ADT/ArrayRef.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/ADT/Triple.h"
> +#include "llvm/ExecutionEngine/JITSymbol.h"
> +#include "llvm/ExecutionEngine/Orc/Core.h"
> +#include "llvm/ExecutionEngine/Orc/Layer.h"
> +#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
> +#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
> +#include "llvm/Support/Error.h"
> +
> +#include <memory>
> +#include <string>
> +
> +#if !defined(_MSC_VER) && !defined(__MINGW32__)
> +#include <unistd.h>
> +#else
> +#include <io.h>
> +#endif
> +
> +namespace llvm {
> +namespace orc {
> +
> +class ChildProcessJITLinkExecutor;
> +class RemoteTargetProcessControl;
> +class TCPSocketJITLinkExecutor;
> +
> +class JITLinkExecutor {
> +public:
> + using RPCChannel = shared::FDRawByteChannel;
> +
> + /// Create a JITLinkExecutor for the given exectuable on disk.
> + static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
> + CreateLocal(std::string ExecutablePath);
> +
> + /// Find the default exectuable on disk and create a JITLinkExecutor
> for it.
> + static Expected<std::unique_ptr<ChildProcessJITLinkExecutor>>
> + FindLocal(const char *JITArgv0);
> +
> + /// Create a JITLinkExecutor that connects to the given network address
> + /// through a TCP socket. A valid NetworkAddress provides hostname and
> port,
> + /// e.g. localhost:20000.
> + static Expected<std::unique_ptr<TCPSocketJITLinkExecutor>>
> + ConnectTCPSocket(StringRef NetworkAddress, ExecutionSession &ES);
> +
> + // Implement ObjectLinkingLayerCreator
> + Expected<std::unique_ptr<ObjectLayer>> operator()(ExecutionSession &,
> + const Triple &);
> +
> + Error addDebugSupport(ObjectLayer &ObjLayer);
> +
> + Expected<std::unique_ptr<DefinitionGenerator>>
> + loadDylib(StringRef RemotePath);
> +
> + Expected<int> runAsMain(JITEvaluatedSymbol MainSym,
> + ArrayRef<std::string> Args);
> + Error disconnect();
> +
> + virtual ~JITLinkExecutor();
> +
> +protected:
> + std::unique_ptr<RemoteTargetProcessControl> TPC;
> +
> + JITLinkExecutor();
> +};
> +
> +/// JITLinkExecutor that runs in a child process on the local machine.
> +class ChildProcessJITLinkExecutor : public JITLinkExecutor {
> +public:
> + Error launch(ExecutionSession &ES);
> +
> + pid_t getPID() const { return ProcessID; }
> + StringRef getPath() const { return ExecutablePath; }
> +
> +private:
> + std::string ExecutablePath;
> + pid_t ProcessID;
> +
> + ChildProcessJITLinkExecutor(std::string ExecutablePath)
> + : ExecutablePath(std::move(ExecutablePath)) {}
> +
> + static std::string defaultPath(const char *HostArgv0, StringRef
> ExecutorName);
> + friend class JITLinkExecutor;
> +};
> +
> +/// JITLinkExecutor connected through a TCP socket.
> +class TCPSocketJITLinkExecutor : public JITLinkExecutor {
> +private:
> + TCPSocketJITLinkExecutor(std::unique_ptr<RemoteTargetProcessControl>
> TPC);
> +
> + friend class JITLinkExecutor;
> +};
> +
> +} // namespace orc
> +} // namespace llvm
> +
> +#endif
>
> diff --git a/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c
> b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c
> new file mode 100644
> index 000000000000..3a5c2bcefee1
> --- /dev/null
> +++ b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1.c
> @@ -0,0 +1,2 @@
> +int sub1(int x) { return x - 1; }
> +int main(int argc, char **argv) { return sub1(argc); }
>
> diff --git a/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll
> b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll
> new file mode 100644
> index 000000000000..659dbe109ec6
> --- /dev/null
> +++ b/llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll
> @@ -0,0 +1,52 @@
> +; ModuleID = 'argc_sub1.c'
> +target triple = "x86_64-unknown-unknown-elf"
> +
> +define i32 @sub1(i32) !dbg !8 {
> + call void @llvm.dbg.value(metadata i32 %0, metadata !13, metadata
> !DIExpression()), !dbg !14
> + %2 = add nsw i32 %0, -1, !dbg !15
> + ret i32 %2, !dbg !16
> +}
> +
> +define i32 @main(i32, i8** nocapture readnone) !dbg !17 {
> + call void @llvm.dbg.value(metadata i32 %0, metadata !24, metadata
> !DIExpression()), !dbg !26
> + call void @llvm.dbg.value(metadata i8** %1, metadata !25, metadata
> !DIExpression()), !dbg !27
> + %3 = tail call i32 @sub1(i32 %0), !dbg !28
> + ret i32 %3, !dbg !29
> +}
> +
> +declare void @llvm.dbg.value(metadata, metadata, metadata)
> +
> +!llvm.dbg.cu = !{!0}
> +!llvm.module.flags = !{!3, !4, !5, !6}
> +!llvm.ident = !{!7}
> +
> +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer:
> "clang version 7.0.1-8+deb10u2 (tags/RELEASE_701/final)", isOptimized:
> true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
> +!1 = !DIFile(filename: "argc_sub1.c", directory: "Inputs/")
> +!2 = !{}
> +!3 = !{i32 2, !"Dwarf Version", i32 4}
> +!4 = !{i32 2, !"Debug Info Version", i32 3}
> +!5 = !{i32 1, !"wchar_size", i32 4}
> +!6 = !{i32 7, !"PIC Level", i32 2}
> +!7 = !{!"clang version 7.0.1-8+deb10u2 (tags/RELEASE_701/final)"}
> +!8 = distinct !DISubprogram(name: "sub1", scope: !1, file: !1, line: 1,
> type: !9, isLocal: false, isDefinition: true, scopeLine: 1, flags:
> DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !12)
> +!9 = !DISubroutineType(types: !10)
> +!10 = !{!11, !11}
> +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
> +!12 = !{!13}
> +!13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 1,
> type: !11)
> +!14 = !DILocation(line: 1, column: 14, scope: !8)
> +!15 = !DILocation(line: 1, column: 28, scope: !8)
> +!16 = !DILocation(line: 1, column: 19, scope: !8)
> +!17 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 2,
> type: !18, isLocal: false, isDefinition: true, scopeLine: 2, flags:
> DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !23)
> +!18 = !DISubroutineType(types: !19)
> +!19 = !{!11, !11, !20}
> +!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64)
> +!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64)
> +!22 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
> +!23 = !{!24, !25}
> +!24 = !DILocalVariable(name: "argc", arg: 1, scope: !17, file: !1, line:
> 2, type: !11)
> +!25 = !DILocalVariable(name: "argv", arg: 2, scope: !17, file: !1, line:
> 2, type: !20)
> +!26 = !DILocation(line: 2, column: 14, scope: !17)
> +!27 = !DILocation(line: 2, column: 27, scope: !17)
> +!28 = !DILocation(line: 2, column: 42, scope: !17)
> +!29 = !DILocation(line: 2, column: 35, scope: !17)
>
> diff --git
> a/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
> b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
> new file mode 100644
> index 000000000000..a9c3946b55e2
> --- /dev/null
> +++ b/llvm/test/Examples/OrcV2Examples/lljit-with-remote-debugging.test
> @@ -0,0 +1,10 @@
> +# This test makes sure that the example builds and executes as expected.
> +# Instructions for debugging can be found in LLJITWithRemoteDebugging.cpp
> +
> +# RUN: LLJITWithRemoteDebugging %p/Inputs/argc_sub1_elf.ll
> +# CHECK: Running: {{.*}}/Inputs/argc_sub1_elf.ll
> +# CHECK: Exit code: 0
> +
> +# RUN: LLJITWithRemoteDebugging %p/Inputs/argc_sub1_elf.ll --args 2nd 3rd
> 4th
> +# CHECK: Running: {{.*}}/Inputs/argc_sub1_elf.ll 2nd 3rd 4th
> +# CHECK: Exit code: 3
>
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20210330/80a4dae2/attachment.html>
More information about the llvm-commits
mailing list