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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This example shows how to use LLJIT and JITLink for out-of-process execution
10 // 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 run
14 // Unix-like systems.
15 // * There is a test for this example and it ships an IR file that is prepared
16 // for the instructions below.
17 //
18 //
19 // The following command line session provides a complete walkthrough of the
20 // feature using LLDB 12:
21 //
22 // [Terminal 1] Prepare a debuggable out-of-process JIT session:
23 //
24 // > cd llvm-project/build
25 // > ninja LLJITWithRemoteDebugging llvm-jitlink-executor
26 // > cp ../llvm/test/Examples/OrcV2Examples/Inputs/argc_sub1_elf.ll .
27 // > bin/LLJITWithRemoteDebugging --wait-for-debugger argc_sub1_elf.ll
28 // Found out-of-process executor: bin/llvm-jitlink-executor
29 // Launched executor in subprocess: 65535
30 // 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 jit
36 // (lldb) settings set plugin.jit-loader.gdb.enable on
37 // (lldb) settings set target.source-map Inputs/ \
38 // /path/to/llvm-project/llvm/test/Examples/OrcV2Examples/Inputs/
39 // (lldb) attach -p 65535
40 // JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
41 // JITLoaderGDB::SetJITBreakpoint setting JIT breakpoint
42 // Process 65535 stopped
43 // (lldb) b sub1
44 // Breakpoint 1: no locations (pending).
45 // WARNING: Unable to resolve breakpoint to any actual locations.
46 // (lldb) c
47 // Process 65535 resuming
48 //
49 //
50 // [Terminal 1] Press a key to start code generation and execution:
51 //
52 // Parsed input IR code from: argc_sub1_elf.ll
53 // Initialized LLJIT for remote executor
54 // Running: argc_sub1_elf.ll
55 //
56 //
57 // [Terminal 2] Breakpoint hits; we change the argc value from 1 to 42:
58 //
59 // (lldb) JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
60 // JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x106b34000
61 // 1 location added to breakpoint 1
62 // Process 65535 stopped
63 // * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
64 // frame #0: JIT(0x106b34000)`sub1(x=1) at argc_sub1.c:1:28
65 // -> 1 int sub1(int x) { return x - 1; }
66 // 2 int main(int argc, char **argv) { return sub1(argc); }
67 // (lldb) p x
68 // (int) $0 = 1
69 // (lldb) expr x = 42
70 // (int) $1 = 42
71 // (lldb) c
72 //
73 //
74 // [Terminal 1] Example output reflects the modified value:
75 //
76 // Exit code: 41
77 //
78 //===----------------------------------------------------------------------===//
79
80 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
81 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
82 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
83 #include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
84 #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
85 #include "llvm/Support/CommandLine.h"
86 #include "llvm/Support/Error.h"
87 #include "llvm/Support/FormatVariadic.h"
88 #include "llvm/Support/InitLLVM.h"
89 #include "llvm/Support/TargetSelect.h"
90 #include "llvm/Support/raw_ostream.h"
91
92 #include "../ExampleModules.h"
93 #include "RemoteJITUtils.h"
94
95 #include <memory>
96 #include <string>
97
98 using namespace llvm;
99 using namespace llvm::orc;
100
101 // The LLVM IR file to run.
102 static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
103 cl::desc("<input files>"));
104
105 // Command line arguments to pass to the JITed main function.
106 static cl::list<std::string> InputArgv("args", cl::Positional,
107 cl::desc("<program arguments>..."),
108 cl::PositionalEatsArgs);
109
110 // Given paths must exist on the remote target.
111 static cl::list<std::string>
112 Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
113 cl::value_desc("filename"));
114
115 // File path of the executable to launch for execution in a child process.
116 // Inter-process communication will go through stdin/stdout pipes.
117 static cl::opt<std::string>
118 OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
119 cl::value_desc("filename"));
120
121 // Network address of a running executor process that we can connect via TCP. It
122 // may run locally or on a remote machine.
123 static cl::opt<std::string> OOPExecutorConnectTCP(
124 "connect",
125 cl::desc("Connect to an out-of-process executor through a TCP socket"),
126 cl::value_desc("<hostname>:<port>"));
127
128 // Give the user a chance to connect a debugger. Once we connected the executor
129 // process, wait for the user to press a key (and print out its PID if it's a
130 // child process).
131 static cl::opt<bool>
132 WaitForDebugger("wait-for-debugger",
133 cl::desc("Wait for user input before entering JITed code"),
134 cl::init(false));
135
136 ExitOnError ExitOnErr;
137
main(int argc,char * argv[])138 int main(int argc, char *argv[]) {
139 InitLLVM X(argc, argv);
140
141 InitializeNativeTarget();
142 InitializeNativeTargetAsmPrinter();
143
144 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
145 cl::ParseCommandLineOptions(argc, argv, "LLJITWithRemoteDebugging");
146
147 std::unique_ptr<SimpleRemoteEPC> EPC;
148 if (OOPExecutorConnectTCP.getNumOccurrences() > 0) {
149 // Connect to a running out-of-process executor through a TCP socket.
150 EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
151 outs() << "Connected to executor at " << OOPExecutorConnectTCP << "\n";
152 } else {
153 // Launch an out-of-process executor locally in a child process.
154 std::string Path =
155 OOPExecutor.empty() ? findLocalExecutor(argv[0]) : OOPExecutor;
156 outs() << "Found out-of-process executor: " << Path << "\n";
157
158 uint64_t PID;
159 std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(Path));
160 outs() << "Launched executor in subprocess: " << PID << "\n";
161 }
162
163 if (WaitForDebugger) {
164 outs() << "Attach a debugger and press any key to continue.\n";
165 fflush(stdin);
166 getchar();
167 }
168
169 // Load the given IR files.
170 std::vector<ThreadSafeModule> TSMs;
171 for (const std::string &Path : InputFiles) {
172 outs() << "Parsing input IR code from: " << Path << "\n";
173 TSMs.push_back(ExitOnErr(parseExampleModuleFromFile(Path)));
174 }
175
176 StringRef TT;
177 StringRef MainModuleName;
178 TSMs.front().withModuleDo([&MainModuleName, &TT](Module &M) {
179 MainModuleName = M.getName();
180 TT = M.getTargetTriple();
181 });
182
183 for (const ThreadSafeModule &TSM : TSMs)
184 ExitOnErr(TSM.withModuleDo([TT, MainModuleName](Module &M) -> Error {
185 if (M.getTargetTriple() != TT)
186 return make_error<StringError>(
187 formatv("Different target triples in input files:\n"
188 " '{0}' in '{1}'\n '{2}' in '{3}'",
189 TT, MainModuleName, M.getTargetTriple(), M.getName()),
190 inconvertibleErrorCode());
191 return Error::success();
192 }));
193
194 // Create a target machine that matches the input triple.
195 JITTargetMachineBuilder JTMB((Triple(TT)));
196 JTMB.setCodeModel(CodeModel::Small);
197 JTMB.setRelocationModel(Reloc::PIC_);
198
199 // Create LLJIT and destroy it before disconnecting the target process.
200 outs() << "Initializing LLJIT for remote executor\n";
201 auto J = ExitOnErr(LLJITBuilder()
202 .setExecutorProcessControl(std::move(EPC))
203 .setJITTargetMachineBuilder(std::move(JTMB))
204 .setObjectLinkingLayerCreator([&](auto &ES, const auto &TT) {
205 return std::make_unique<ObjectLinkingLayer>(ES);
206 })
207 .create());
208
209 // Add plugin for debug support.
210 ExitOnErr(addDebugSupport(J->getObjLinkingLayer()));
211
212 // Load required shared libraries on the remote target and add a generator
213 // for each of it, so the compiler can lookup their symbols.
214 for (const std::string &Path : Dylibs)
215 J->getMainJITDylib().addGenerator(
216 ExitOnErr(loadDylib(J->getExecutionSession(), Path)));
217
218 // Add the loaded IR module to the JIT. This will set up symbol tables and
219 // prepare for materialization.
220 for (ThreadSafeModule &TSM : TSMs)
221 ExitOnErr(J->addIRModule(std::move(TSM)));
222
223 // The example uses a non-lazy JIT for simplicity. Thus, looking up the main
224 // function will materialize all reachable code. It also triggers debug
225 // registration in the remote target process.
226 auto MainAddr = ExitOnErr(J->lookup("main"));
227
228 outs() << "Running: main(";
229 int Pos = 0;
230 std::vector<std::string> ActualArgv{"LLJITWithRemoteDebugging"};
231 for (const std::string &Arg : InputArgv) {
232 outs() << (Pos++ == 0 ? "" : ", ") << "\"" << Arg << "\"";
233 ActualArgv.push_back(Arg);
234 }
235 outs() << ")\n";
236
237 // Execute the code in the remote target process and dump the result. With
238 // the debugger attached to the target, it should be possible to inspect the
239 // JITed code as if it was compiled statically.
240 {
241 ExecutorProcessControl &EPC =
242 J->getExecutionSession().getExecutorProcessControl();
243 int Result = ExitOnErr(EPC.runAsMain(MainAddr, ActualArgv));
244 outs() << "Exit code: " << Result << "\n";
245 }
246
247 return 0;
248 }
249