cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
183 lines
6.9 KiB
ReStructuredText
183 lines
6.9 KiB
ReStructuredText
=====================
|
|
Debugging JIT-ed Code
|
|
=====================
|
|
|
|
Background
|
|
==========
|
|
|
|
Without special runtime support, debugging dynamically generated code can be
|
|
quite painful. Debuggers generally read debug information from object files on
|
|
disk, but for JITed code there is no such file to look for.
|
|
|
|
In order to hand over the necessary debug info, `GDB established an
|
|
interface <https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html>`_
|
|
for registering JITed code with debuggers. LLDB implements it in the
|
|
JITLoaderGDB plugin. On the JIT side, LLVM MCJIT does implement the interface
|
|
for ELF object files.
|
|
|
|
At a high level, whenever MCJIT generates new machine code, it does so in an
|
|
in-memory object file that contains the debug information in DWARF format.
|
|
MCJIT then adds this in-memory object file to a global list of dynamically
|
|
generated object files and calls a special function
|
|
``__jit_debug_register_code`` that the debugger knows about. When the debugger
|
|
attaches to a process, it puts a breakpoint in this function and associates a
|
|
special handler with it. Once MCJIT calls the registration function, the
|
|
debugger catches the breakpoint signal, loads the new object file from the
|
|
inferior's memory and resumes execution. This way it can obtain debug
|
|
information for pure in-memory object files.
|
|
|
|
|
|
GDB Version
|
|
===========
|
|
|
|
In order to debug code JIT-ed by LLVM, you need GDB 7.0 or newer, which is
|
|
available on most modern distributions of Linux. The version of GDB that
|
|
Apple ships with Xcode has been frozen at 6.3 for a while.
|
|
|
|
|
|
LLDB Version
|
|
============
|
|
|
|
Due to a regression in release 6.0, LLDB didn't support JITed code debugging for
|
|
a while. The bug was fixed in mainline recently, so that debugging JITed ELF
|
|
objects should be possible again from the upcoming release 12.0 on. On macOS the
|
|
feature must be enabled explicitly using the ``plugin.jit-loader.gdb.enable``
|
|
setting.
|
|
|
|
|
|
Debugging MCJIT-ed code
|
|
=======================
|
|
|
|
The emerging MCJIT component of LLVM allows full debugging of JIT-ed code with
|
|
GDB. This is due to MCJIT's ability to use the MC emitter to provide full
|
|
DWARF debugging information to GDB.
|
|
|
|
Note that lli has to be passed the ``--jit-kind=mcjit`` flag to JIT the code
|
|
with MCJIT instead of the newer ORC JIT.
|
|
|
|
Example
|
|
-------
|
|
|
|
Consider the following C code (with line numbers added to make the example
|
|
easier to follow):
|
|
|
|
..
|
|
FIXME:
|
|
Sphinx has the ability to automatically number these lines by adding
|
|
:linenos: on the line immediately following the `.. code-block:: c`, but
|
|
it looks like garbage; the line numbers don't even line up with the
|
|
lines. Is this a Sphinx bug, or is it a CSS problem?
|
|
|
|
.. code-block:: c
|
|
|
|
1 int compute_factorial(int n)
|
|
2 {
|
|
3 if (n <= 1)
|
|
4 return 1;
|
|
5
|
|
6 int f = n;
|
|
7 while (--n > 1)
|
|
8 f *= n;
|
|
9 return f;
|
|
10 }
|
|
11
|
|
12
|
|
13 int main(int argc, char** argv)
|
|
14 {
|
|
15 if (argc < 2)
|
|
16 return -1;
|
|
17 char firstletter = argv[1][0];
|
|
18 int result = compute_factorial(firstletter - '0');
|
|
19
|
|
20 // Returned result is clipped at 255...
|
|
21 return result;
|
|
22 }
|
|
|
|
Here is a sample command line session that shows how to build and run this
|
|
code via ``lli`` inside LLDB:
|
|
|
|
.. code-block:: bash
|
|
|
|
> export BINPATH=/workspaces/llvm-project/build/bin
|
|
> $BINPATH/clang -g -S -emit-llvm --target=x86_64-unknown-unknown-elf showdebug.c
|
|
> lldb $BINPATH/lli
|
|
(lldb) target create "/workspaces/llvm-project/build/bin/lli"
|
|
Current executable set to '/workspaces/llvm-project/build/bin/lli' (x86_64).
|
|
(lldb) settings set plugin.jit-loader.gdb.enable on
|
|
(lldb) b compute_factorial
|
|
Breakpoint 1: no locations (pending).
|
|
WARNING: Unable to resolve breakpoint to any actual locations.
|
|
(lldb) run --jit-kind=mcjit showdebug.ll 5
|
|
1 location added to breakpoint 1
|
|
Process 21340 stopped
|
|
* thread #1, name = 'lli', stop reason = breakpoint 1.1
|
|
frame #0: 0x00007ffff7fd0007 JIT(0x45c2cb0)`compute_factorial(n=5) at showdebug.c:3:11
|
|
1 int compute_factorial(int n)
|
|
2 {
|
|
-> 3 if (n <= 1)
|
|
4 return 1;
|
|
5 int f = n;
|
|
6 while (--n > 1)
|
|
7 f *= n;
|
|
(lldb) p n
|
|
(int) $0 = 5
|
|
(lldb) b showdebug.c:9
|
|
Breakpoint 2: where = JIT(0x45c2cb0)`compute_factorial + 60 at showdebug.c:9:1, address = 0x00007ffff7fd003c
|
|
(lldb) c
|
|
Process 21340 resuming
|
|
Process 21340 stopped
|
|
* thread #1, name = 'lli', stop reason = breakpoint 2.1
|
|
frame #0: 0x00007ffff7fd003c JIT(0x45c2cb0)`compute_factorial(n=1) at showdebug.c:9:1
|
|
6 while (--n > 1)
|
|
7 f *= n;
|
|
8 return f;
|
|
-> 9 }
|
|
10
|
|
11 int main(int argc, char** argv)
|
|
12 {
|
|
(lldb) p f
|
|
(int) $1 = 120
|
|
(lldb) bt
|
|
* thread #1, name = 'lli', stop reason = breakpoint 2.1
|
|
* frame #0: 0x00007ffff7fd003c JIT(0x45c2cb0)`compute_factorial(n=1) at showdebug.c:9:1
|
|
frame #1: 0x00007ffff7fd0095 JIT(0x45c2cb0)`main(argc=2, argv=0x00000000046122f0) at showdebug.c:16:18
|
|
frame #2: 0x0000000002a8306e lli`llvm::MCJIT::runFunction(this=0x000000000458ed10, F=0x0000000004589ff8, ArgValues=ArrayRef<llvm::GenericValue> @ 0x00007fffffffc798) at MCJIT.cpp:554:31
|
|
frame #3: 0x00000000029bdb45 lli`llvm::ExecutionEngine::runFunctionAsMain(this=0x000000000458ed10, Fn=0x0000000004589ff8, argv=size=0, envp=0x00007fffffffe140) at ExecutionEngine.cpp:467:10
|
|
frame #4: 0x0000000001f2fc2f lli`main(argc=4, argv=0x00007fffffffe118, envp=0x00007fffffffe140) at lli.cpp:643:18
|
|
frame #5: 0x00007ffff788c09b libc.so.6`__libc_start_main(main=(lli`main at lli.cpp:387), argc=4, argv=0x00007fffffffe118, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffe108) at libc-start.c:308:16
|
|
frame #6: 0x0000000001f2dc7a lli`_start + 42
|
|
(lldb) finish
|
|
Process 21340 stopped
|
|
* thread #1, name = 'lli', stop reason = step out
|
|
Return value: (int) $2 = 120
|
|
|
|
frame #0: 0x00007ffff7fd0095 JIT(0x45c2cb0)`main(argc=2, argv=0x00000000046122f0) at showdebug.c:16:9
|
|
13 if (argc < 2)
|
|
14 return -1;
|
|
15 char firstletter = argv[1][0];
|
|
-> 16 int result = compute_factorial(firstletter - '0');
|
|
17
|
|
18 // Returned result is clipped at 255...
|
|
19 return result;
|
|
(lldb) p result
|
|
(int) $3 = 73670648
|
|
(lldb) n
|
|
Process 21340 stopped
|
|
* thread #1, name = 'lli', stop reason = step over
|
|
frame #0: 0x00007ffff7fd0098 JIT(0x45c2cb0)`main(argc=2, argv=0x00000000046122f0) at showdebug.c:19:12
|
|
16 int result = compute_factorial(firstletter - '0');
|
|
17
|
|
18 // Returned result is clipped at 255...
|
|
-> 19 return result;
|
|
20 }
|
|
(lldb) p result
|
|
(int) $4 = 120
|
|
(lldb) expr result=42
|
|
(int) $5 = 42
|
|
(lldb) p result
|
|
(int) $6 = 42
|
|
(lldb) c
|
|
Process 21340 resuming
|
|
Process 21340 exited with status = 42 (0x0000002a)
|
|
(lldb) exit
|