1 //===--- LLJITWithRemoteDebugging.cpp - LLJIT targeting a child process ---===//
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
7 //===----------------------------------------------------------------------===//
9 // This example shows how to use LLJIT and JITLink for out-of-process execution
10 // with debug support. A few notes beforehand:
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
15 // * There is a test for this example and it ships an IR file that is prepared
16 // for the instructions below.
19 // The following command line session provides a complete walkthrough of the
20 // feature using LLDB 12:
22 // [Terminal 1] Prepare a debuggable out-of-process JIT session:
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.
33 // [Terminal 2] Attach a debugger to the child process:
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
44 // Breakpoint 1: no locations (pending).
45 // WARNING: Unable to resolve breakpoint to any actual locations.
47 // Process 65535 resuming
50 // [Terminal 1] Press a key to start code generation and execution:
52 // Parsed input IR code from: argc_sub1_elf.ll
53 // Initialized LLJIT for remote executor
54 // Running: argc_sub1_elf.ll
57 // [Terminal 2] Breakpoint hits; we change the argc value from 1 to 42:
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); }
74 // [Terminal 1] Example output reflects the modified value:
78 //===----------------------------------------------------------------------===//
80 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
81 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
82 #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
83 #include "llvm/Support/CommandLine.h"
84 #include "llvm/Support/Error.h"
85 #include "llvm/Support/FormatVariadic.h"
86 #include "llvm/Support/InitLLVM.h"
87 #include "llvm/Support/TargetSelect.h"
88 #include "llvm/Support/raw_ostream.h"
90 #include "../ExampleModules.h"
91 #include "RemoteJITUtils.h"
97 using namespace llvm::orc
;
99 // The LLVM IR file to run.
100 static cl::list
<std::string
> InputFiles(cl::Positional
, cl::OneOrMore
,
101 cl::desc("<input files>"));
103 // Command line arguments to pass to the JITed main function.
104 static cl::list
<std::string
> InputArgv("args", cl::Positional
,
105 cl::desc("<program arguments>..."),
106 cl::ZeroOrMore
, cl::PositionalEatsArgs
);
108 // Given paths must exist on the remote target.
109 static cl::list
<std::string
>
110 Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
111 cl::value_desc("filename"), cl::ZeroOrMore
);
113 // File path of the executable to launch for execution in a child process.
114 // Inter-process communication will go through stdin/stdout pipes.
115 static cl::opt
<std::string
>
116 OOPExecutor("executor", cl::desc("Set the out-of-process executor"),
117 cl::value_desc("filename"));
119 // Network address of a running executor process that we can connected through a
120 // TCP socket. It may run locally or on a remote machine.
121 static cl::opt
<std::string
> OOPExecutorConnect(
123 cl::desc("Connect to an out-of-process executor through a TCP socket"),
124 cl::value_desc("<hostname>:<port>"));
126 // Give the user a chance to connect a debugger. Once we connected the executor
127 // process, wait for the user to press a key (and print out its PID if it's a
130 WaitForDebugger("wait-for-debugger",
131 cl::desc("Wait for user input before entering JITed code"),
134 ExitOnError ExitOnErr
;
136 static std::unique_ptr
<JITLinkExecutor
> connectExecutor(const char *Argv0
) {
137 // Connect to a running out-of-process executor through a TCP socket.
138 if (!OOPExecutorConnect
.empty()) {
139 std::unique_ptr
<TCPSocketJITLinkExecutor
> Exec
=
140 ExitOnErr(JITLinkExecutor::ConnectTCPSocket(OOPExecutorConnect
,
141 std::ref(ExitOnErr
)));
143 outs() << "Connected to executor at " << OOPExecutorConnect
<< "\n";
144 if (WaitForDebugger
) {
145 outs() << "Attach a debugger and press any key to continue.\n";
150 return std::move(Exec
);
153 // Launch a out-of-process executor locally in a child process.
154 std::unique_ptr
<ChildProcessJITLinkExecutor
> Exec
= ExitOnErr(
155 OOPExecutor
.empty() ? JITLinkExecutor::FindLocal(Argv0
)
156 : JITLinkExecutor::CreateLocal(OOPExecutor
));
158 outs() << "Found out-of-process executor: " << Exec
->getPath() << "\n";
160 ExitOnErr(Exec
->launch(std::ref(ExitOnErr
)));
161 if (WaitForDebugger
) {
162 outs() << "Launched executor in subprocess: " << Exec
->getPID() << "\n"
163 << "Attach a debugger and press any key to continue.\n";
168 return std::move(Exec
);
171 int main(int argc
, char *argv
[]) {
172 InitLLVM
X(argc
, argv
);
174 InitializeNativeTarget();
175 InitializeNativeTargetAsmPrinter();
177 ExitOnErr
.setBanner(std::string(argv
[0]) + ": ");
178 cl::ParseCommandLineOptions(argc
, argv
, "LLJITWithRemoteDebugging");
180 // Launch/connect the out-of-process executor.
181 std::unique_ptr
<JITLinkExecutor
> Executor
= connectExecutor(argv
[0]);
183 // Load the given IR files.
184 std::vector
<ThreadSafeModule
> TSMs
;
185 for (const std::string
&Path
: InputFiles
) {
186 outs() << "Parsing input IR code from: " << Path
<< "\n";
187 TSMs
.push_back(ExitOnErr(parseExampleModuleFromFile(Path
)));
191 StringRef MainModuleName
;
192 TSMs
.front().withModuleDo([&MainModuleName
, &TT
](Module
&M
) {
193 MainModuleName
= M
.getName();
194 TT
= M
.getTargetTriple();
197 for (const ThreadSafeModule
&TSM
: TSMs
)
198 ExitOnErr(TSM
.withModuleDo([TT
, MainModuleName
](Module
&M
) -> Error
{
199 if (M
.getTargetTriple() != TT
)
200 return make_error
<StringError
>(
201 formatv("Different target triples in input files:\n"
202 " '{0}' in '{1}'\n '{2}' in '{3}'",
203 TT
, MainModuleName
, M
.getTargetTriple(), M
.getName()),
204 inconvertibleErrorCode());
205 return Error::success();
208 // Create a target machine that matches the input triple.
209 JITTargetMachineBuilder
JTMB((Triple(TT
)));
210 JTMB
.setCodeModel(CodeModel::Small
);
211 JTMB
.setRelocationModel(Reloc::PIC_
);
213 // Create LLJIT and destroy it before disconnecting the target process.
215 std::unique_ptr
<ExecutionSession
> ES
= Executor
->startSession();
217 outs() << "Initializing LLJIT for remote executor\n";
218 auto J
= ExitOnErr(LLJITBuilder()
219 .setExecutionSession(std::move(ES
))
220 .setJITTargetMachineBuilder(std::move(JTMB
))
221 .setObjectLinkingLayerCreator(std::ref(*Executor
))
224 // Add plugin for debug support.
225 ExitOnErr(Executor
->addDebugSupport(J
->getObjLinkingLayer()));
227 // Load required shared libraries on the remote target and add a generator
228 // for each of it, so the compiler can lookup their symbols.
229 for (const std::string
&Path
: Dylibs
)
230 J
->getMainJITDylib().addGenerator(ExitOnErr(Executor
->loadDylib(Path
)));
232 // Add the loaded IR module to the JIT. This will set up symbol tables and
233 // prepare for materialization.
234 for (ThreadSafeModule
&TSM
: TSMs
)
235 ExitOnErr(J
->addIRModule(std::move(TSM
)));
237 // The example uses a non-lazy JIT for simplicity. Thus, looking up the main
238 // function will materialize all reachable code. It also triggers debug
239 // registration in the remote target process.
240 JITEvaluatedSymbol MainFn
= ExitOnErr(J
->lookup("main"));
242 outs() << "Running: main(";
244 for (const std::string
&Arg
: InputArgv
)
245 outs() << (Pos
++ == 0 ? "" : ", ") << "\"" << Arg
<< "\"";
248 // Execute the code in the remote target process and dump the result. With
249 // the debugger attached to the target, it should be possible to inspect the
250 // JITed code as if it was compiled statically.
251 int Result
= ExitOnErr(Executor
->runAsMain(MainFn
, InputArgv
));
252 outs() << "Exit code: " << Result
<< "\n";