Preliminary patch for GDB support for JIT

Right now, GDB has no way to be told about JITed code. I'm working on
adding such an interface, and the LLVM JIT would be the first client.
The interface is evolving, and I'll admit that right now it's
downright terrible. Here's how it works right now:

- The JIT generates the machine code and DWARF call frame info
(.eh_frame/.debug_frame) for a function into memory.
- The JIT copies that info into a temporary ELF file with a symbol for
the function.
- The JIT stuffs the filename and text address (function start) into a
global struct at a special symbol that GDB knows about.
- The JIT calls a function marked noinline that GDB knows about and
has put an internal breakpoint in.
- GDB takes control when the breakpoint fires, reads the filename and
addr from the struct, and does the equivalent of an 'add-symbol-file'
command from the prompt with those two arguments.
- The JIT continues, and the next time we stop the program, we are
able to produce a proper backtrace.

There is a lot of room for improvement, but I have to start somewhere,
and this works. Here is a GDB session debugging a simple JITed
program that makes three nested calls and segfaults without debug
info, and then with debug info:

[rnk@knuckles llvm-gdb]$ ../gdb-install/bin/gdb ../llvm-gdb/Debug/bin/lli
GNU gdb (GDB) 6.8.50.20090609-cvs
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <The GNU General Public License v3.0 - GNU Project - Free Software Foundation>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
For bug reporting instructions, please see:
<Bugs in GDB>...
(gdb) run t.bc
Starting program: /home/rnk/llvm-gdb/Debug/bin/lli t.bc
[Thread debugging using libthread_db enabled]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff5d310b8 in ?? ()
(gdb) bt
#0 0x00007ffff5d310b8 in ?? ()
#1 0x0000000000000003 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00037ffff5d30fd0 in ?? ()
#4 0x00007ffff5d3108c in ?? ()
#5 0x00027fff00000003 in ?? ()
#6 0x00007ffff5d3105a in ?? ()
#7 0x01000002f5d30ff0 in ?? ()
#8 0x00007ffff5d3102c in ?? ()
#9 0x0100000000000001 in ?? ()
#10 0x0000000001430220 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b8692c in llvm::JIT::runFunction (this=0x13fdb20,
F=0x13fa520, ArgValues=...) at JIT.cpp:394
#13 0x0000000000ba9a69 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x13fdb20, Fn=0x13fa520, argv=..., envp=0x7fffffffe410)
    at ExecutionEngine.cpp:376
#14 0x00000000007eb665 in main (argc=2, argv=0x7fffffffe3f8,
envp=0x7fffffffe410) at lli.cpp:205
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) run --jit-emit-debug t.bc
Starting program: /home/rnk/llvm-gdb/Debug/bin/lli --jit-emit-debug t.bc
[Thread debugging using libthread_db enabled]
symbol_file: /tmp/llvm_zp32Cb/llvm_function_0x7ffff5d31010_main.o
symbol_file: /tmp/llvm_zp32Cb/llvm_function_0x7ffff5d31090_foo.o
symbol_file: /tmp/llvm_zp32Cb/llvm_function_0x7ffff5d31110_bar.o
symbol_file: /tmp/llvm_zp32Cb/llvm_function_0x7ffff5d31190_baz.o

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff5d311a8 in baz ()
(gdb) bt
#0 0x00007ffff5d311a8 in baz ()
#1 0x00007ffff5d3112c in bar ()
#2 0x00007ffff5d310aa in foo ()
#3 0x00007ffff5d3102c in main ()
#4 0x0000000000b8692c in llvm::JIT::runFunction (this=0x13fdb20,
F=0x13fa520, ArgValues=...) at JIT.cpp:394
#5 0x0000000000ba9a69 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x13fdb20, Fn=0x13fa520, argv=..., envp=0x7fffffffe3f8)
    at ExecutionEngine.cpp:376
#6 0x00000000007eb665 in main (argc=3, argv=0x7fffffffe3d8,
envp=0x7fffffffe3f8) at lli.cpp:205
(gdb)

For reference, here is the corresponding patch against GDB:
http://web.mit.edu/rnk/www/jit-patch.diff

And finally, the patch to LLVM is attached and uploaded to Rietveld:
http://codereview.appspot.com/91042/show

I know this code is sketchy and preliminary, but it's useful for us,
and I have plans to improve it:
http://wiki.llvm.org/HowTo:_Tell_GDB_about_JITted_code

Please review!

Thanks,
Reid

gdb-elf-patch.diff (16.4 KB)

Right now, GDB has no way to be told about JITed code. I'm working on
adding such an interface, and the LLVM JIT would be the first client.
The interface is evolving, and I'll admit that right now it's
downright terrible. Here's how it works right now:

- The JIT generates the machine code and DWARF call frame info
(.eh_frame/.debug_frame) for a function into memory.
- The JIT copies that info into a temporary ELF file with a symbol for
the function.
- The JIT stuffs the filename and text address (function start) into a
global struct at a special symbol that GDB knows about.
- The JIT calls a function marked noinline that GDB knows about and
has put an internal breakpoint in.
- GDB takes control when the breakpoint fires, reads the filename and
addr from the struct, and does the equivalent of an 'add-symbol-file'
command from the prompt with those two arguments.
- The JIT continues, and the next time we stop the program, we are
able to produce a proper backtrace.

I've been thinking about this lately myself and this is along the lines I
was going as well.

For reference, here is the corresponding patch against GDB:
http://web.mit.edu/rnk/www/jit-patch.diff

This appears to be missing some hunks...

And finally, the patch to LLVM is attached and uploaded to Rietveld:
http://codereview.appspot.com/91042/show

In general I'd like to see the ELF support separated from the main
interface. It makes any other platform support require a complete
refactor :slight_smile:

I know this code is sketchy and preliminary, but it's useful for us,
and I have plans to improve it:
http://wiki.llvm.org/HowTo:_Tell_GDB_about_JITted_code

I'm not entirely sure about the lazy debug symbols part, but
as far as I can tell neither are you :slight_smile:

The rest of it seems reasonable to me.

Note that I can't approve the patch anyhow :slight_smile:

-eric

Have you considered how this might be made to work for embedded
targets where remote debugging is required?

deep

For reference, here is the corresponding patch against GDB:
http://web.mit.edu/rnk/www/jit-patch.diff

This appears to be missing some hunks...

Ah, crap, there's a gdb/jit.[ch] but I don't know how to get cvs diff
to include them. 'cvs add' wants write access on the main server.
Supposedly 'cvs diff -upN' will do it, but it's not working for me...
Expect to hear back.

And finally, the patch to LLVM is attached and uploaded to Rietveld:
http://codereview.appspot.com/91042/show

In general I'd like to see the ELF support separated from the main
interface. It makes any other platform support require a complete
refactor :slight_smile:

That's a goal I have for the summer. One dumb way to approach it
would be to push the ELF creation to the GDB side of the interface,
and create the ELF there. That would clean up the interface and make
it much nicer.

But once you've done that, then it's probably just as much work to
figure out the in memory objfile data structures and modify those
directly. This was just the path that yielded results most quickly.

I know this code is sketchy and preliminary, but it's useful for us,
and I have plans to improve it:
http://wiki.llvm.org/HowTo:_Tell_GDB_about_JITted_code

I'm not entirely sure about the lazy debug symbols part, but
as far as I can tell neither are you :slight_smile:

Yeah, that's seeming less and less likely to happen.

The rest of it seems reasonable to me.

Great!

And finally, the patch to LLVM is attached and uploaded to Rietveld:
http://codereview.appspot.com/91042/show

In general I'd like to see the ELF support separated from the main
interface. It makes any other platform support require a complete
refactor :slight_smile:

That's a goal I have for the summer. One dumb way to approach it
would be to push the ELF creation to the GDB side of the interface,
and create the ELF there. That would clean up the interface and make
it much nicer.

True. I wonder what it would take to just mark it as a section or
perhaps start a blank section in the executable for more debug info...

just thoughts.

But once you've done that, then it's probably just as much work to
figure out the in memory objfile data structures and modify those
directly. This was just the path that yielded results most quickly.

Yeah. Might be best to keep it in llvm since it will already know how
to emit the files.

-eric