brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.3 KiB · 1f69415 Raw
223 lines · cpp
1//===--- LLJITWithRemoteDebugging.cpp - LLJIT targeting a child process ---===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This example shows how to use LLJIT and JITLink for out-of-process execution10// with debug support.  A few notes beforehand:11//12//  * Debuggers must implement the GDB JIT interface (gdb, udb, lldb 12+).13//  * Debug support is currently limited to ELF on x86-64 platforms that run14//    Unix-like systems.15//  * There is a test for this example and it ships an IR file that is prepared16//    for the instructions below.17//18//19// The following command line session provides a complete walkthrough of the20// feature using LLDB 12:21//22// [Terminal 1] Prepare a debuggable out-of-process JIT session:23//24//    > cd llvm-project/build25//    > ninja LLJITWithRemoteDebugging llvm-jitlink-executor26//    > cp ../llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll .27//    > bin/LLJITWithRemoteDebugging --wait-for-debugger argc_sub1_elf.ll28//    Found out-of-process executor: bin/llvm-jitlink-executor29//    Launched executor in subprocess: 6553530//    Attach a debugger and press any key to continue.31//32//33// [Terminal 2] Attach a debugger to the child process:34//35//    (lldb) log enable lldb jit36//    (lldb) settings set plugin.jit-loader.gdb.enable on37//    (lldb) settings set target.source-map Inputs/ \38//             /path/to/llvm-project/llvm/test/Examples/OrcV2Examples/Inputs/39//    (lldb) attach -p 6553540//     JITLoaderGDB::SetJITBreakpoint looking for JIT register hook41//     JITLoaderGDB::SetJITBreakpoint setting JIT breakpoint42//    Process 65535 stopped43//    (lldb) b sub144//    Breakpoint 1: no locations (pending).45//    WARNING:  Unable to resolve breakpoint to any actual locations.46//    (lldb) c47//    Process 65535 resuming48//49//50// [Terminal 1] Press a key to start code generation and execution:51//52//    Parsed input IR code from: argc_sub1_elf.ll53//    Initialized LLJIT for remote executor54//    Running: argc_sub1_elf.ll55//56//57// [Terminal 2] Breakpoint hits; we change the argc value from 1 to 42:58//59//    (lldb)  JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint60//     JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x106b3400061//    1 location added to breakpoint 162//    Process 65535 stopped63//    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.164//        frame #0: JIT(0x106b34000)`sub1(x=1) at argc_sub1.c:1:2865//    -> 1   	int sub1(int x) { return x - 1; }66//       2   	int main(int argc, char **argv) { return sub1(argc); }67//    (lldb) p x68//    (int) $0 = 169//    (lldb) expr x = 4270//    (int) $1 = 4271//    (lldb) c72//73//74// [Terminal 1] Example output reflects the modified value:75//76//    Exit code: 4177//78//===----------------------------------------------------------------------===//79 80#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"81#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"82#include "llvm/ExecutionEngine/Orc/LLJIT.h"83#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"84#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"85#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"86#include "llvm/Support/CommandLine.h"87#include "llvm/Support/Error.h"88#include "llvm/Support/FormatVariadic.h"89#include "llvm/Support/InitLLVM.h"90#include "llvm/Support/TargetSelect.h"91#include "llvm/Support/raw_ostream.h"92#include "llvm/TargetParser/Host.h"93 94#include "../ExampleModules.h"95#include "RemoteJITUtils.h"96 97#include <memory>98#include <string>99 100using namespace llvm;101using namespace llvm::orc;102 103// The LLVM IR file to run.104static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,105                                        cl::desc("<input files>"));106 107// Command line arguments to pass to the JITed main function.108static cl::list<std::string> InputArgv("args", cl::Positional,109                                       cl::desc("<program arguments>..."),110                                       cl::PositionalEatsArgs);111 112// Given paths must exist on the remote target.113static cl::list<std::string>114    Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),115           cl::value_desc("filename"));116 117// File path of the executable to launch for execution in a child process.118// Inter-process communication will go through stdin/stdout pipes.119static cl::opt<std::string>120    OOPExecutor("executor", cl::desc("Set the out-of-process executor"),121                cl::value_desc("filename"));122 123// Network address of a running executor process that we can connect via TCP. It124// may run locally or on a remote machine.125static cl::opt<std::string> OOPExecutorConnectTCP(126    "connect",127    cl::desc("Connect to an out-of-process executor through a TCP socket"),128    cl::value_desc("<hostname>:<port>"));129 130// Give the user a chance to connect a debugger. Once we connected the executor131// process, wait for the user to press a key (and print out its PID if it's a132// child process).133static cl::opt<bool>134    WaitForDebugger("wait-for-debugger",135                    cl::desc("Wait for user input before entering JITed code"),136                    cl::init(false));137 138ExitOnError ExitOnErr;139 140int main(int argc, char *argv[]) {141  InitLLVM X(argc, argv);142 143  InitializeNativeTarget();144  InitializeNativeTargetAsmPrinter();145 146  ExitOnErr.setBanner(std::string(argv[0]) + ": ");147  cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");148 149  std::unique_ptr<SimpleRemoteEPC> EPC;150  if (OOPExecutorConnectTCP.getNumOccurrences() > 0) {151    // Connect to a running out-of-process executor through a TCP socket.152    EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));153    outs() << "Connected to executor at " << OOPExecutorConnectTCP << "\n";154  } else {155    // Launch an out-of-process executor locally in a child process.156    std::string Path =157        OOPExecutor.empty() ? findLocalExecutor(argv[0]) : OOPExecutor;158    outs() << "Found out-of-process executor: " << Path << "\n";159 160    uint64_t PID;161    std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(Path));162    outs() << "Launched executor in subprocess: " << PID << "\n";163  }164 165  if (WaitForDebugger) {166    outs() << "Attach a debugger and press any key to continue.\n";167    fflush(stdin);168    getchar();169  }170 171  // Load the given IR files.172  std::vector<ThreadSafeModule> TSMs;173  for (const std::string &Path : InputFiles) {174    outs() << "Parsing input IR code from: " << Path << "\n";175    TSMs.push_back(ExitOnErr(parseExampleModuleFromFile(Path)));176  }177 178  // Create LLJIT and destroy it before disconnecting the target process.179  outs() << "Initializing LLJIT for remote executor\n";180  auto J = ExitOnErr(181      LLJITBuilder().setExecutorProcessControl(std::move(EPC)).create());182 183  // Add plugin for debug support.184  ExitOnErr(enableDebuggerSupport(*J));185 186  // Load required shared libraries on the remote target and add a generator187  // for each of it, so the compiler can lookup their symbols.188  for (const std::string &Path : Dylibs)189    J->getMainJITDylib().addGenerator(190        ExitOnErr(loadDylib(J->getExecutionSession(), Path)));191 192  // Add the loaded IR module to the JIT. This will set up symbol tables and193  // prepare for materialization.194  for (ThreadSafeModule &TSM : TSMs)195    ExitOnErr(J->addIRModule(std::move(TSM)));196 197  // The example uses a non-lazy JIT for simplicity. Thus, looking up the main198  // function will materialize all reachable code. It also triggers debug199  // registration in the remote target process.200  auto MainAddr = ExitOnErr(J->lookup("main"));201 202  outs() << "Running: main(";203  int Pos = 0;204  std::vector<std::string> ActualArgv{"LLJITWithRemoteDebugging"};205  for (const std::string &Arg : InputArgv) {206    outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";207    ActualArgv.push_back(Arg);208  }209  outs() << ")\n";210 211  // Execute the code in the remote target process and dump the result. With212  // the debugger attached to the target, it should be possible to inspect the213  // JITed code as if it was compiled statically.214  {215    ExecutorProcessControl &EPC =216        J->getExecutionSession().getExecutorProcessControl();217    int Result = ExitOnErr(EPC.runAsMain(MainAddr, ActualArgv));218    outs() << "Exit code: " << Result << "\n";219  }220 221  return 0;222}223