* [RFA] Add interface for registering JITed code
@ 2009-07-23 1:58 Reid Kleckner
2009-07-23 12:08 ` Reid Kleckner
2009-07-23 23:21 ` Tom Tromey
0 siblings, 2 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-07-23 1:58 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 4449 bytes --]
Hi,
I'm working on unladen swallow, and we're trying to speed up Python by
using the LLVM JIT. However, when we merge back to mainline, we want
developers to be able to debug CPython with GDB. So that means we
need LLVM to generate dwarf debug info, and we need to register it
with GDB. After talking with some GDB developers here at Google, we
decided the best way to do this was to mirror the dlopen/dlclose
interface.
On the LLVM side, for each JITed function we create a small ELF in
memory with the debug info and symbol. LLVM then writes a little code
entry struct describing the ELF, adds it to a linked list, and calls
__jit_debug_register_code.
I've added a breakpoint at __jit_debug_register_code and a
corresponding event handler on the GDB side, which then reads the code
entry out of another special global symbol (__jit_debug_descriptor).
GDB then copies over the ELF and creates a BFD with it in memory, as
is done in symbol-file-from-memory. Then it can call
add_symbol_file_from_bfd with the BFD, and life is good.
If GDB attaches while the program is running, it reads the linked list
of code entries from the descriptor and registers each ELF as above.
If LLVM frees machine code, then it sets the action enum in the
descriptor to JIT_UNREGISTER, points the descriptor at the relevant
code entry, and calls __jit_debug_register_code again. This way, GDB
can turn around and free the corresponding object file. It's a nicer
interface than the shared library interface because it actually passes
the relevant entry, so you don't have to iterate over the linked list
in the inferior.
Finally, if the inferior exits, GDB goes through and tosses out all
the JITed object files.
One nice thing about this interface is that we don't have to reinvent
another "file" format to encode the debug information, but it is
annoying that it requires the JIT to link in an object file writer.
Right now LLVM only has call frame information support (which they use
for dwarf exception handling), but they have plans to add more after
this summer. With this interface, we don't have to change anything on
the GDB side when that happens.
Here is a demo of what this does on x86_64, which relies on call frame
information to produce a backtrace.
Without the interface, the backtrace is totally garbled (it has way
too many frames) in addition to not having symbols:
[rnk@knuckles llvm-gdb-64]$ gdb Debug/bin/lli
GNU gdb 6.8-gg16
...
(gdb) run t.bc
...
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7fd86f0 (LWP 15017)]
(gdb) bt
#0 0x00007ffff61441a4 in ?? ()
#1 0x0000000000000003 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00037ffff5f43fd0 in ?? ()
#4 0x00007ffff614411c in ?? ()
#5 0x00027fff00000003 in ?? ()
#6 0x00007ffff61440aa in ?? ()
#7 0x01000002f5f43ff0 in ?? ()
#8 0x00007ffff614402c in ?? ()
#9 0x0100000000000001 in ?? ()
#10 0x0000000001438a40 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
ArgValues=@0x7fffffffdfd0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
#13 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
this=0x1405900, Fn=0x1402e10, argv=@0x13efab8, envp=0x7fffffffe3a0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
#14 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe388,
envp=0x7fffffffe3a0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
(gdb)
With the interface, it has symbols and no extra frames:
[rnk@knuckles llvm-gdb-64]$ ../gdb-jit-64/gdb/gdb Debug/bin/lli
...
(gdb) run t.bc
...
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff61441a4 in baz ()
(gdb) bt
#0 0x00007ffff61441a4 in baz ()
#1 0x00007ffff614411c in bar ()
#2 0x00007ffff61440aa in foo ()
#3 0x00007ffff614402c in main ()
#4 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
ArgValues=...) at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
#5 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
this=0x1405900, Fn=0x1402e10, argv=..., envp=0x7fffffffe390)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
#6 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe378,
envp=0x7fffffffe390) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
(gdb)
I've tested this on x86_64 debugging both 64-bit and 32-bit inferiors.
Please review!
Thanks,
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 31372 bytes --]
2009-07-22 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
* symfile.h:
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Added declaration.
(symbol_file_guess_template): New.
* symfile-mem.c:
(bfd_open_from_memory): New.
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Modified to use new common code.
(symbol_file_add_from_memory_common): New.
(symbol_file_guess_template): New.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1086
diff -u -p -u -r1.1086 Makefile.in
--- gdb/Makefile.in 28 May 2009 01:05:13 -0000 1.1086
+++ gdb/Makefile.in 22 Jul 2009 22:26:07 -0000
@@ -673,7 +673,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -744,7 +745,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -825,7 +826,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.403
diff -u -p -u -r1.403 breakpoint.c
--- gdb/breakpoint.c 8 Jun 2009 16:05:14 -0000 1.403
+++ gdb/breakpoint.c 22 Jul 2009 22:26:08 -0000
@@ -2510,6 +2510,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3232,6 +3233,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3247,6 +3251,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3267,12 +3272,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3290,28 +3296,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3324,6 +3320,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3394,6 +3391,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
bs_class = bp_nostop;
@@ -3524,6 +3524,7 @@ print_one_breakpoint_location (struct br
{bp_overlay_event, "overlay events"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3651,6 +3652,7 @@ print_one_breakpoint_location (struct br
case bp_thread_event:
case bp_overlay_event:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4266,6 +4268,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -4530,6 +4533,16 @@ struct lang_and_radix
};
+struct breakpoint *
+create_jit_event_breakpoint (CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
+
void
remove_solib_event_breakpoints (void)
{
@@ -5155,6 +5168,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
break;
}
@@ -7398,6 +7412,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy &&
b->type != bp_shlib_event &&
+ b->type != bp_jit_event &&
b->type != bp_thread_event &&
b->type != bp_overlay_event &&
b->number >= 0)
@@ -7415,6 +7430,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy &&
b->type != bp_shlib_event &&
+ b->type != bp_jit_event &&
b->type != bp_thread_event &&
b->type != bp_overlay_event &&
b->number >= 0)
@@ -7736,6 +7752,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.92
diff -u -p -u -r1.92 breakpoint.h
--- gdb/breakpoint.h 24 May 2009 18:00:08 -0000 1.92
+++ gdb/breakpoint.h 22 Jul 2009 22:26:08 -0000
@@ -113,6 +113,9 @@ enum bptype
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -535,6 +538,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -827,6 +833,8 @@ extern void mark_breakpoints_out (void);
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (CORE_ADDR);
extern struct breakpoint *create_thread_event_breakpoint (CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.385
diff -u -p -u -r1.385 infrun.c
--- gdb/infrun.c 7 Jun 2009 16:46:48 -0000 1.385
+++ gdb/infrun.c 22 Jul 2009 22:26:08 -0000
@@ -49,6 +49,7 @@
#include "mi/mi-common.h"
#include "event-top.h"
#include "record.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -3462,6 +3463,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 22 Jul 2009 22:26:08 -0000
@@ -0,0 +1,300 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr;
+
+/* This is a linked list mapping code entry addresses to objfiles. */
+
+static struct jit_obj_link *jit_objfiles = NULL;
+
+/* Helper method for reading the global JIT descriptor from remote memory. */
+
+static int
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ {
+ error (_("Unable to read JIT descriptor from remote memory!\n"));
+ return err;
+ }
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4);
+ descriptor->action_flag = extract_unsigned_integer (&desc_buf[4], 4);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry = extract_typed_address
+ (&desc_buf[8 + ptr_size], ptr_type);
+
+ return 0;
+}
+
+/* Helper method for reading a JITed code entry from remote memory. */
+
+static int
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ {
+ error (_("Unable to read JIT code entry from remote memory!\n"));
+ return err;
+ }
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry = extract_typed_address
+ (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr = extract_typed_address
+ (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size = extract_unsigned_integer
+ (&entry_buf[3 * ptr_size], 8);
+
+ return 0;
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *templ;
+ bfd *nbfd;
+ bfd_byte *buffer;
+ bfd_size_type size;
+ int err;
+ struct section_addr_info *sai;
+ struct objfile *objfile;
+ struct jit_obj_link *link;
+
+ /* We need some representative bfd to know the target we are looking at. */
+ templ = symbol_file_guess_template ();
+ if (templ == NULL)
+ error (_("\
+Cannot add symbol file for JITed code without an object file template."));
+
+ /* Read the symfile from remote memory. */
+ size = code_entry->symfile_size;
+ buffer = xmalloc (size);
+ err = target_read_memory (code_entry->symfile_addr, buffer, size);
+ if (err)
+ {
+ free (buffer);
+ error (_("Unable to read JITed symfile from remote memory."));
+ return;
+ }
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ objfile = symbol_file_add_from_local_memory (templ, buffer, size);
+ if (objfile == NULL)
+ free (buffer);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ link = xmalloc (sizeof (struct jit_obj_link));
+ link->next = jit_objfiles;
+ link->entry_addr = entry_addr;
+ link->objfile = objfile;
+ jit_objfiles = link;
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct jit_obj_link **last_ptr, struct jit_obj_link *link)
+{
+ /* Cut the link from the chain, and free both the link and the objfile. */
+ *last_ptr = link->next;
+ free_objfile (link->objfile);
+ xfree (link);
+}
+
+/* This function reads any existing code entries in the linked list and inserts
+ the breakpoint so we can listen for any more. */
+
+static void
+jit_inferior_created_hook (struct target_ops *objfile, int from_tty)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+ int err;
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == (CORE_ADDR)NULL)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == (CORE_ADDR)NULL)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ err = jit_read_descriptor (&descriptor);
+ if (err) return;
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ {
+ error (_("Unsupported JIT protocol version in descriptor!\n"));
+ return;
+ }
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ err = jit_read_code_entry (cur_entry_addr, &cur_entry);
+ if (err) return;
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ while (jit_objfiles != NULL)
+ {
+ jit_unregister_code (&jit_objfiles, jit_objfiles);
+ }
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ int err;
+ CORE_ADDR entry_addr;
+ struct jit_obj_link *link;
+ struct jit_obj_link **last_ptr;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ err = jit_read_code_entry (entry_addr, &code_entry);
+ if (err) return;
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the link with this code entry address. */
+ for (last_ptr = &jit_objfiles, link = *last_ptr; link != NULL;
+ last_ptr = &link->next, link = *last_ptr)
+ {
+ if (link->entry_addr == entry_addr)
+ {
+ jit_unregister_code (last_ptr, link);
+ return;
+ }
+ }
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void*)entry_addr);
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!\n"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 22 Jul 2009 22:26:08 -0000
@@ -0,0 +1,81 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ The list is singly linked. For the first element prev_entry should be NULL,
+ and for the last element next_entry should be NULL. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* This struct is used to form a linked list mapping the address of the JIT code
+ entry in the inferior's address space to the corresponding object file in
+ GDB. These are used to unregister objfiles for code that has been freed. */
+
+struct jit_obj_link
+{
+ struct jit_obj_link *next;
+ CORE_ADDR entry_addr;
+ struct objfile *objfile;
+};
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.83
diff -u -p -u -r1.83 objfiles.c
--- gdb/objfiles.c 14 May 2009 23:33:08 -0000 1.83
+++ gdb/objfiles.c 22 Jul 2009 22:26:08 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -430,9 +431,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
Index: gdb/symfile-mem.c
===================================================================
RCS file: /cvs/src/src/gdb/symfile-mem.c,v
retrieving revision 1.17
diff -u -p -u -r1.17 symfile-mem.c
--- gdb/symfile-mem.c 21 Feb 2009 16:14:49 -0000 1.17
+++ gdb/symfile-mem.c 22 Jul 2009 22:26:08 -0000
@@ -54,37 +54,23 @@
#include "observer.h"
#include "auxv.h"
#include "elf/common.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include <time.h>
-/* Read inferior memory at ADDR to find the header of a loaded object file
- and read its in-core symbols out of inferior memory. TEMPL is a bfd
- representing the target's format. NAME is the name to use for this
- symbol file in messages; it can be NULL or a malloc-allocated string
- which will be attached to the BFD. */
+/* Common code for symbol_file_add_from_memory and
+ symbol_file_add_from_local_memory. */
+
static struct objfile *
-symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
- int from_tty)
+symbol_file_add_from_memory_common (struct bfd *nbfd, bfd_vma loadbase,
+ int from_tty)
{
struct objfile *objf;
- struct bfd *nbfd;
struct bfd_section *sec;
- bfd_vma loadbase;
struct section_addr_info *sai;
unsigned int i;
- if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
- error (_("add-symbol-file-from-memory not supported for this target"));
-
- nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
- target_read_memory);
- if (nbfd == NULL)
- error (_("Failed to read a valid object file image from memory."));
-
- if (name == NULL)
- nbfd->filename = xstrdup ("shared object read from target memory");
- else
- nbfd->filename = name;
-
if (!bfd_check_format (nbfd, bfd_object))
{
/* FIXME: should be checking for errors from bfd_close (for one thing,
@@ -116,6 +102,121 @@ symbol_file_add_from_memory (struct bfd
return objf;
}
+/* Read inferior memory at ADDR to find the header of a loaded object file
+ and read its in-core symbols out of inferior memory. TEMPL is a bfd
+ representing the target's format. NAME is the name to use for this
+ symbol file in messages; it can be NULL or a malloc-allocated string
+ which will be attached to the BFD. */
+
+struct objfile *
+symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
+ int from_tty)
+{
+ struct bfd *nbfd;
+ bfd_vma loadbase;
+
+ if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
+ error (_("add-symbol-file-from-memory not supported for this target"));
+
+ nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
+ target_read_memory);
+ if (nbfd == NULL)
+ error (_("Failed to read a valid object file image from memory."));
+
+ if (name == NULL)
+ nbfd->filename = xstrdup ("shared object read from target memory");
+ else
+ nbfd->filename = name;
+
+ return symbol_file_add_from_memory_common (nbfd, loadbase, from_tty);
+}
+
+/* Create a new BFD for a file that is already loaded into memory. This does
+ not take ownership of buffer or filename, and the caller must free them.
+ This code is based on bfd_from_remote_memory in bfd/elfcode.h, and should
+ probably live inside of the bfd library. */
+
+static bfd *
+bfd_open_from_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size, const char *filename)
+{
+ bfd *nbfd;
+ struct bfd_in_memory *bim;
+
+ bim = bfd_malloc (sizeof (struct bfd_in_memory));
+ if (bim == NULL)
+ return NULL;
+ bim->buffer = buffer;
+ bim->size = size;
+
+ nbfd = _bfd_new_bfd ();
+ if (nbfd == NULL)
+ {
+ free (bim);
+ return NULL;
+ }
+ nbfd->filename = filename;
+ nbfd->xvec = templ->xvec;
+ nbfd->iostream = bim;
+ nbfd->flags = BFD_IN_MEMORY;
+ nbfd->direction = read_direction;
+ nbfd->mtime = time (NULL);
+ nbfd->mtime_set = TRUE;
+
+ return nbfd;
+}
+
+/* Add a symbol file from local memory. This differs from
+ symbol_file_add_from_memory in that it assumes that we have the entire
+ symbol file laid out in memory and we don't need to use
+ bfd_elf_bfd_from_remote_memory to reconstruct the ELF from the program
+ headers. */
+
+struct objfile *
+symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size)
+{
+ bfd *nbfd;
+ int err;
+ struct section_addr_info *sai;
+ bfd_vma loadbase;
+ const char *filename;
+
+ /* The filename must be strdup'd or GDB will crash when freeing the object
+ file. */
+ filename = xstrdup ("<in-memory>");
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ nbfd = bfd_open_from_memory (templ, buffer, size, filename);
+ if (nbfd == NULL)
+ {
+ error (_("Unable to create BFD from local memory: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ return NULL;
+ }
+
+ /* We set loadbase to 0 and assume that the symbol file we're loading has the
+ absolute addresses set in the ELF. */
+ loadbase = 0;
+ return symbol_file_add_from_memory_common (nbfd, loadbase, 0);
+}
+
+/* Use the BFD of the main executable to guess what kind of object file we want
+ to add. This is used for adding symbol files from memory, where we don't
+ know the format of the file. */
+
+struct bfd *
+symbol_file_guess_template (void)
+{
+ struct bfd *templ;
+
+ if (symfile_objfile != NULL)
+ templ = symfile_objfile->obfd;
+ else
+ templ = exec_bfd;
+
+ return templ;
+}
static void
add_symbol_file_from_memory_command (char *args, int from_tty)
@@ -129,10 +230,7 @@ add_symbol_file_from_memory_command (cha
addr = parse_and_eval_address (args);
/* We need some representative bfd to know the target we are looking at. */
- if (symfile_objfile != NULL)
- templ = symfile_objfile->obfd;
- else
- templ = exec_bfd;
+ templ = symbol_file_guess_template ();
if (templ == NULL)
error (_("\
Must use symbol-file or exec-file before add-symbol-file-from-memory."));
Index: gdb/symfile.h
===================================================================
RCS file: /cvs/src/src/gdb/symfile.h,v
retrieving revision 1.50
diff -u -p -u -r1.50 symfile.h
--- gdb/symfile.h 22 May 2009 23:49:14 -0000 1.50
+++ gdb/symfile.h 22 Jul 2009 22:26:08 -0000
@@ -392,4 +392,17 @@ extern void elfmdebug_build_psymtabs (st
const struct ecoff_debug_swap *,
asection *);
+/* From symfile-mem.c */
+
+extern struct bfd *symbol_file_guess_template (void);
+
+extern struct objfile *symbol_file_add_from_memory (struct bfd *templ,
+ CORE_ADDR addr,
+ char *name,
+ int from_tty);
+
+extern struct objfile *symbol_file_add_from_local_memory (struct bfd *templ,
+ bfd_byte *buffer,
+ bfd_size_type size);
+
#endif /* !defined(SYMFILE_H) */
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-23 1:58 [RFA] Add interface for registering JITed code Reid Kleckner
@ 2009-07-23 12:08 ` Reid Kleckner
2009-07-23 23:21 ` Tom Tromey
1 sibling, 0 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-07-23 12:08 UTC (permalink / raw)
To: gdb-patches, unladen-swallow
+ unladen-swallow
On Wed, Jul 22, 2009 at 4:15 PM, Reid Kleckner<rnk@mit.edu> wrote:
> Hi,
>
> I'm working on unladen swallow, and we're trying to speed up Python by
> using the LLVM JIT. However, when we merge back to mainline, we want
> developers to be able to debug CPython with GDB. So that means we
> need LLVM to generate dwarf debug info, and we need to register it
> with GDB. After talking with some GDB developers here at Google, we
> decided the best way to do this was to mirror the dlopen/dlclose
> interface.
>
> On the LLVM side, for each JITed function we create a small ELF in
> memory with the debug info and symbol. LLVM then writes a little code
> entry struct describing the ELF, adds it to a linked list, and calls
> __jit_debug_register_code.
>
> I've added a breakpoint at __jit_debug_register_code and a
> corresponding event handler on the GDB side, which then reads the code
> entry out of another special global symbol (__jit_debug_descriptor).
> GDB then copies over the ELF and creates a BFD with it in memory, as
> is done in symbol-file-from-memory. Then it can call
> add_symbol_file_from_bfd with the BFD, and life is good.
>
> If GDB attaches while the program is running, it reads the linked list
> of code entries from the descriptor and registers each ELF as above.
>
> If LLVM frees machine code, then it sets the action enum in the
> descriptor to JIT_UNREGISTER, points the descriptor at the relevant
> code entry, and calls __jit_debug_register_code again. This way, GDB
> can turn around and free the corresponding object file. It's a nicer
> interface than the shared library interface because it actually passes
> the relevant entry, so you don't have to iterate over the linked list
> in the inferior.
>
> Finally, if the inferior exits, GDB goes through and tosses out all
> the JITed object files.
>
> One nice thing about this interface is that we don't have to reinvent
> another "file" format to encode the debug information, but it is
> annoying that it requires the JIT to link in an object file writer.
> Right now LLVM only has call frame information support (which they use
> for dwarf exception handling), but they have plans to add more after
> this summer. With this interface, we don't have to change anything on
> the GDB side when that happens.
>
> Here is a demo of what this does on x86_64, which relies on call frame
> information to produce a backtrace.
>
> Without the interface, the backtrace is totally garbled (it has way
> too many frames) in addition to not having symbols:
>
> [rnk@knuckles llvm-gdb-64]$ gdb Debug/bin/lli
> GNU gdb 6.8-gg16
> ...
> (gdb) run t.bc
> ...
> Program received signal SIGSEGV, Segmentation fault.
> [Switching to Thread 0x7ffff7fd86f0 (LWP 15017)]
> (gdb) bt
> #0 0x00007ffff61441a4 in ?? ()
> #1 0x0000000000000003 in ?? ()
> #2 0x0000000000000004 in ?? ()
> #3 0x00037ffff5f43fd0 in ?? ()
> #4 0x00007ffff614411c in ?? ()
> #5 0x00027fff00000003 in ?? ()
> #6 0x00007ffff61440aa in ?? ()
> #7 0x01000002f5f43ff0 in ?? ()
> #8 0x00007ffff614402c in ?? ()
> #9 0x0100000000000001 in ?? ()
> #10 0x0000000001438a40 in ?? ()
> #11 0x00007fff00000001 in ?? ()
> #12 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
> ArgValues=@0x7fffffffdfd0)
> at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
> #13 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
> this=0x1405900, Fn=0x1402e10, argv=@0x13efab8, envp=0x7fffffffe3a0)
> at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
> #14 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe388,
> envp=0x7fffffffe3a0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
> (gdb)
>
> With the interface, it has symbols and no extra frames:
>
> [rnk@knuckles llvm-gdb-64]$ ../gdb-jit-64/gdb/gdb Debug/bin/lli
> ...
> (gdb) run t.bc
> ...
> Program received signal SIGSEGV, Segmentation fault.
> 0x00007ffff61441a4 in baz ()
> (gdb) bt
> #0 0x00007ffff61441a4 in baz ()
> #1 0x00007ffff614411c in bar ()
> #2 0x00007ffff61440aa in foo ()
> #3 0x00007ffff614402c in main ()
> #4 0x0000000000b84d63 in llvm::JIT::runFunction (this=0x1405900, F=0x1402e10,
> ArgValues=...) at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:411
> #5 0x0000000000ba8985 in llvm::ExecutionEngine::runFunctionAsMain (
> this=0x1405900, Fn=0x1402e10, argv=..., envp=0x7fffffffe390)
> at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:378
> #6 0x00000000007e8635 in main (argc=2, argv=0x7fffffffe378,
> envp=0x7fffffffe390) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:220
> (gdb)
>
> I've tested this on x86_64 debugging both 64-bit and 32-bit inferiors.
>
> Please review!
>
> Thanks,
> Reid
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-23 1:58 [RFA] Add interface for registering JITed code Reid Kleckner
2009-07-23 12:08 ` Reid Kleckner
@ 2009-07-23 23:21 ` Tom Tromey
2009-07-24 13:25 ` Ulrich Weigand
2009-07-24 16:55 ` Reid Kleckner
1 sibling, 2 replies; 32+ messages in thread
From: Tom Tromey @ 2009-07-23 23:21 UTC (permalink / raw)
To: Reid Kleckner; +Cc: gdb-patches, unladen-swallow
>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
Reid> So that means we need LLVM to generate dwarf debug info, and we
Reid> need to register it with GDB.
Nice.
Your overall approach seems good to me.
[...]
Reid> If LLVM frees machine code, then it sets the action enum in the
Reid> descriptor to JIT_UNREGISTER, points the descriptor at the relevant
Reid> code entry, and calls __jit_debug_register_code again.
Given that this will be a supported way for GDB to connect to JITs, I
think that the official interface (symbol names, types, enum values,
etc) should be documented in the GDB manual somewhere.
I wonder what happens if the ELF data in the inferior is corrupted.
Perhaps there should be a way to disable the feature. What do you
think?
This patch also deserves an entry in NEWS.
I have a few comments on the details of the patch itself.
Reid> +struct breakpoint *
Reid> +create_jit_event_breakpoint (CORE_ADDR address)
Reid> +{
Reid> + struct breakpoint *b;
Reid> +
Reid> + b = create_internal_breakpoint (address, bp_jit_event);
Reid> + update_global_location_list_nothrow (1);
Reid> + return b;
Reid> +}
I am not sure but I suspect this should be called from
breakpoint_re_set -- that is, follow the longjmp model a bit more.
Does the current code work ok if LLVM is a .so linked into the
application? I suspect, but do not know for sure, that in this
situation gdb will not see the JIT symbols when the inferior-created
hook is run.q
There is also the somewhat more pathological case of a program with
multiple JITs linked in. I'm on the fence about whether this is really
needed.
Reid> +/* This is a linked list mapping code entry addresses to objfiles. */
Reid> +static struct jit_obj_link *jit_objfiles = NULL;
You can store arbitrary module-specific data in an objfile using the
objfile_data API. This would be better because it is an already
supported way of dealing with the lifetime of the associated data.
This would mean getting rid of the linked list here and just looping
over objfiles when needed.
Reid> + error (_("Unable to read JIT descriptor from remote memory!\n"));
Don't use \n at the end of error text. (There is code in gdb that does
this but I think the rule is not to.)
Reid> + descriptor->version = extract_unsigned_integer (&desc_buf[0], 4);
You'll need to update to head ... a few of these functions have an extra
argument now.
I am not sure if using target_gdbarch is ok -- maybe passing the arch to
jit_read_descriptor as an argument would be better. Maybe Ulrich could
weigh in here.
Reid> + descriptor->first_entry = extract_typed_address
Reid> + (&desc_buf[8 + ptr_size], ptr_type);
A formatting nit ... I think it is more typical to split before the '='
than before the '('.
Reid> +static void
Reid> +jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
[...]
Reid> + free (buffer);
xfree, but...
Reid> + objfile = symbol_file_add_from_local_memory (templ, buffer, size);
Reid> + if (objfile == NULL)
Reid> + free (buffer);
symbol_file_add_from_local_memory can call error, so you need a cleanup
here rather than explicit calls to free.
Reid> + if (reg_addr == (CORE_ADDR)NULL)
Reid> + return;
Just write `0' instead of `(CORE_ADDR)NULL'. There are 2 instances of
this.
Reid> + if (err) return;
Newline before the return. There are a few of these.
Reid> + /* Check that the version number agrees with that we support. */
Reid> + if (descriptor.version != 1)
Reid> + {
Reid> + error (_("Unsupported JIT protocol version in descriptor!\n"));
Reid> + return;
error calls longjmp, so the return is redundant. There are a couple of
these in the patch.
Reid> +/* When the JIT breakpoint fires, the inferior wants us to take one of these
Reid> + actions. */
Reid> +
Reid> +typedef enum
Reid> +{
Reid> + JIT_NOACTION = 0,
Reid> + JIT_REGISTER,
Reid> + JIT_UNREGISTER
Reid> +} jit_actions_t;
Please update the comment here to explain that these values are exported
and used by the inferior, so they cannot be changed.
Reid> + /* Hack to work around the fact that BFD does not take ownership of the
Reid> + memory for files allocated in memory. */
Is it possible to fix this directly in BFD? Since...
Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
... this is definitely fishy :-)
Reid> +struct objfile *
Reid> +symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
Reid> + bfd_size_type size)
Reid> +{
[...]
Reid> + filename = xstrdup ("<in-memory>");
Because this function can call error, you need a cleanup here.
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-23 23:21 ` Tom Tromey
@ 2009-07-24 13:25 ` Ulrich Weigand
2009-07-24 16:52 ` Doug Evans
2009-07-25 0:40 ` [unladen-swallow] " Reid Kleckner
2009-07-24 16:55 ` Reid Kleckner
1 sibling, 2 replies; 32+ messages in thread
From: Ulrich Weigand @ 2009-07-24 13:25 UTC (permalink / raw)
To: tromey; +Cc: Reid Kleckner, gdb-patches, unladen-swallow
Tom Tromey wrote:
> >>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>
> Reid> So that means we need LLVM to generate dwarf debug info, and we
> Reid> need to register it with GDB.
>
> Nice.
>
> Your overall approach seems good to me.
I agree, this looks good to me as well.
> Reid> + /* Hack to work around the fact that BFD does not take ownership of the
> Reid> + memory for files allocated in memory. */
>
> Is it possible to fix this directly in BFD? Since...
>
> Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
>
> ... this is definitely fishy :-)
I'd suggest that Reid instead use bfd_openr_iovec to access an ELF image
directly in inferior memory, as is currently done e.g. by
remote.c:remote_bfd_open
spu-linux-nat.c:spu_bfd_open
solib-spu.c:spu_bfd_open (is about to be introduced by the patch
http://sourceware.org/ml/gdb-patches/2009-07/msg00546.html)
This works without BFD changes or directly accessing BFD internals ...
Bye,
Ulrich
--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 13:25 ` Ulrich Weigand
@ 2009-07-24 16:52 ` Doug Evans
2009-07-25 0:40 ` [unladen-swallow] " Reid Kleckner
1 sibling, 0 replies; 32+ messages in thread
From: Doug Evans @ 2009-07-24 16:52 UTC (permalink / raw)
To: Ulrich Weigand; +Cc: tromey, Reid Kleckner, gdb-patches, unladen-swallow
On Fri, Jul 24, 2009 at 4:30 AM, Ulrich Weigand<uweigand@de.ibm.com> wrote:
> I'd suggest that Reid instead use bfd_openr_iovec to access an ELF image
> directly in inferior memory, as is currently done e.g. by
> remote.c:remote_bfd_open
> spu-linux-nat.c:spu_bfd_open
> solib-spu.c:spu_bfd_open (is about to be introduced by the patch
> http://sourceware.org/ml/gdb-patches/2009-07/msg00546.html)
> This works without BFD changes or directly accessing BFD internals ...
And it could work if a more scatter/gather kind of access was desired.
Heh, learn something new every day. :-)
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-23 23:21 ` Tom Tromey
2009-07-24 13:25 ` Ulrich Weigand
@ 2009-07-24 16:55 ` Reid Kleckner
2009-07-24 20:42 ` Eli Zaretskii
2009-07-24 21:06 ` Tom Tromey
1 sibling, 2 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-07-24 16:55 UTC (permalink / raw)
To: tromey; +Cc: gdb-patches, unladen-swallow
[-- Attachment #1: Type: text/plain, Size: 7371 bytes --]
Here's an updated patch. I still need to write the manual
documentation and switch to the iovec, though. I wrote this message
and the patch yesterday before Ulrich's message arrived.
On Thu, Jul 23, 2009 at 10:42 AM, Tom Tromey<tromey@redhat.com> wrote:
>>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>
> Reid> So that means we need LLVM to generate dwarf debug info, and we
> Reid> need to register it with GDB.
>
> Nice.
>
> Your overall approach seems good to me.
Glad to hear I'm on the right track.
> [...]
>
> Reid> If LLVM frees machine code, then it sets the action enum in the
> Reid> descriptor to JIT_UNREGISTER, points the descriptor at the relevant
> Reid> code entry, and calls __jit_debug_register_code again.
>
> Given that this will be a supported way for GDB to connect to JITs, I
> think that the official interface (symbol names, types, enum values,
> etc) should be documented in the GDB manual somewhere.
Where should this go? It doesn't really fit under any of the
top-level topics, so far as I can tell. I only have about a page to
write about it.
> I wonder what happens if the ELF data in the inferior is corrupted.
> Perhaps there should be a way to disable the feature. What do you
> think?
>
> This patch also deserves an entry in NEWS.
Done.
> I have a few comments on the details of the patch itself.
>
> Reid> +struct breakpoint *
> Reid> +create_jit_event_breakpoint (CORE_ADDR address)
> Reid> +{
> Reid> + struct breakpoint *b;
> Reid> +
> Reid> + b = create_internal_breakpoint (address, bp_jit_event);
> Reid> + update_global_location_list_nothrow (1);
> Reid> + return b;
> Reid> +}
>
> I am not sure but I suspect this should be called from
> breakpoint_re_set -- that is, follow the longjmp model a bit more.
>
> Does the current code work ok if LLVM is a .so linked into the
> application? I suspect, but do not know for sure, that in this
> situation gdb will not see the JIT symbols when the inferior-created
> hook is run.q
I don't know, since we statically link to LLVM. I'm not an expert in
how dynamic loaders work, so I don't know how to fix this.
> There is also the somewhat more pathological case of a program with
> multiple JITs linked in. I'm on the fence about whether this is really
> needed.
How would this work? Would they both have separate functions
__jit_debug_register at different addresses? One way to solve both of
these problems might be to create a single libgdbjitagent library
similar to oprofile's agent library. That way we have some control
over both the inferior side and the GDB side.
> Reid> +/* This is a linked list mapping code entry addresses to objfiles. */
> Reid> +static struct jit_obj_link *jit_objfiles = NULL;
>
> You can store arbitrary module-specific data in an objfile using the
> objfile_data API. This would be better because it is an already
> supported way of dealing with the lifetime of the associated data.
>
> This would mean getting rid of the linked list here and just looping
> over objfiles when needed.
Ah, that's much better. Unfortunately because the data for other
objfiles is uninitialized (it's returned from calloc), there's no way
for me to check which objfiles contain JITed code. Do I actually need
to have this inferior_exit hook? I just noticed that when I kill and
restart the inferior it doesn't seem to free these objfiles, so I
added the hook.
> Reid> + error (_("Unable to read JIT descriptor from remote memory!\n"));
>
> Don't use \n at the end of error text. (There is code in gdb that does
> this but I think the rule is not to.)
Done.
> Reid> + descriptor->version = extract_unsigned_integer (&desc_buf[0], 4);
>
> You'll need to update to head ... a few of these functions have an extra
> argument now.
>
> I am not sure if using target_gdbarch is ok -- maybe passing the arch to
> jit_read_descriptor as an argument would be better. Maybe Ulrich could
> weigh in here.
I'll use target_gdbarch until anyone else says otherwise.
> Reid> + descriptor->first_entry = extract_typed_address
> Reid> + (&desc_buf[8 + ptr_size], ptr_type);
>
> A formatting nit ... I think it is more typical to split before the '='
> than before the '('.
Done.
> Reid> +static void
> Reid> +jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
> [...]
> Reid> + free (buffer);
>
> xfree, but...
Switched to xfree.
> Reid> + objfile = symbol_file_add_from_local_memory (templ, buffer, size);
> Reid> + if (objfile == NULL)
> Reid> + free (buffer);
>
> symbol_file_add_from_local_memory can call error, so you need a cleanup
> here rather than explicit calls to free.
I took a shot at putting cleanups in, but I'm not sure I did it
correctly. In particular, I'm not sure about the discard_cleanups
logic.
> Reid> + if (reg_addr == (CORE_ADDR)NULL)
> Reid> + return;
>
> Just write `0' instead of `(CORE_ADDR)NULL'. There are 2 instances of
> this.
Done.
> Reid> + if (err) return;
>
> Newline before the return. There are a few of these.
Done.
> Reid> + /* Check that the version number agrees with that we support. */
> Reid> + if (descriptor.version != 1)
> Reid> + {
> Reid> + error (_("Unsupported JIT protocol version in descriptor!\n"));
> Reid> + return;
>
> error calls longjmp, so the return is redundant. There are a couple of
> these in the patch.
Done. I made some functions just return void instead of error codes
and also call error.
> Reid> +/* When the JIT breakpoint fires, the inferior wants us to take one of these
> Reid> + actions. */
> Reid> +
> Reid> +typedef enum
> Reid> +{
> Reid> + JIT_NOACTION = 0,
> Reid> + JIT_REGISTER,
> Reid> + JIT_UNREGISTER
> Reid> +} jit_actions_t;
>
> Please update the comment here to explain that these values are exported
> and used by the inferior, so they cannot be changed.
Done. The same is true about the rest of the structs, the ordering of
the fields cannot be changed without updating the protocol version.
> Reid> + /* Hack to work around the fact that BFD does not take ownership of the
> Reid> + memory for files allocated in memory. */
>
> Is it possible to fix this directly in BFD? Since...
>
> Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
>
> ... this is definitely fishy :-)
I talked to Doug Evans about it, and he said he'd look into getting a
patch into BFD. The basic problem is that BFD doesn't take ownership
of any memory buffers that you give it. If you look in bfd_close, it
does a check for flags & BFD_IN_MEMORY and calls iovec->bclose for
BFD's that aren't in memory. It doesn't do anything with iostream
(bim) or bim->buffer. Other BFD clients may rely on this or also have
workarounds, so that will need to be sorted out before we can just go
ahead and free it on the BFD side.
> Reid> +struct objfile *
> Reid> +symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
> Reid> + bfd_size_type size)
> Reid> +{
> [...]
> Reid> + filename = xstrdup ("<in-memory>");
>
> Because this function can call error, you need a cleanup here.
Done.
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 32603 bytes --]
2009-07-24 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
* symfile.h:
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Added declaration.
(symbol_file_guess_template): New.
* symfile-mem.c:
(bfd_open_from_memory): New.
(symbol_file_add_from_local_memory): New.
(symbol_file_add_from_memory): Modified to use new common code.
(symbol_file_add_from_memory_common): New.
(symbol_file_guess_template): New.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -p -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 24 Jul 2009 16:45:06 -0000
@@ -677,7 +677,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -746,7 +747,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -828,7 +829,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -p -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 24 Jul 2009 16:45:06 -0000
@@ -3,6 +3,11 @@
*** Changes since GDB 6.8
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create object files with debug info in
+memory and register them with GDB. The interface also supports
+attaching to a running process.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -p -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 24 Jul 2009 16:45:07 -0000
@@ -2573,6 +2573,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3299,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3317,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3338,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3356,28 +3362,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3390,6 +3386,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3460,6 +3457,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3593,7 @@ print_one_breakpoint_location (struct br
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3721,6 +3722,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4364,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4647,17 @@ struct lang_and_radix
int radix;
};
+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5293,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7600,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7620,7 @@ delete_command (char *arg, int from_tty)
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7943,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -p -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 24 Jul 2009 16:45:07 -0000
@@ -120,6 +120,9 @@ enum bptype
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void);
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -p -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 24 Jul 2009 16:45:07 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -3529,6 +3530,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 24 Jul 2009 16:45:08 -0000
@@ -0,0 +1,277 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+
+static const struct objfile_data *jit_objfile_data;
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr;
+
+/* Helper method for reading the global JIT descriptor from remote memory. */
+
+static void
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ error (_("Unable to read JIT descriptor from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order);
+ descriptor->action_flag =
+ extract_unsigned_integer (&desc_buf[4], 4, byte_order);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry =
+ extract_typed_address (&desc_buf[8 + ptr_size], ptr_type);
+}
+
+/* Helper method for reading a JITed code entry from remote memory. */
+
+static void
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ error (_("Unable to read JIT code entry from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry =
+ extract_typed_address (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr =
+ extract_typed_address (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size =
+ extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *templ;
+ bfd *nbfd;
+ bfd_byte *buffer;
+ bfd_size_type size;
+ int err;
+ struct section_addr_info *sai;
+ struct objfile *objfile;
+ struct cleanup *old_cleanups;
+
+ /* We need some representative bfd to know the target we are looking at. */
+ templ = symbol_file_guess_template ();
+ if (templ == NULL)
+ error (_("\
+Cannot add symbol file for JITed code without an object file template."));
+
+ /* Read the symfile from remote memory. */
+ size = code_entry->symfile_size;
+ buffer = xmalloc (size);
+ old_cleanups = make_cleanup (xfree, buffer);
+ err = target_read_memory (code_entry->symfile_addr, buffer, size);
+ if (err)
+ error (_("Unable to read JITed symfile from remote memory!"));
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ objfile = symbol_file_add_from_local_memory (templ, buffer, size);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
+
+ discard_cleanups (old_cleanups);
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct objfile *objfile)
+{
+ free_objfile (objfile);
+}
+
+/* This function reads any existing code entries in the linked list and inserts
+ the breakpoint so we can listen for any more. */
+
+static void
+jit_inferior_created_hook (struct target_ops *objfile, int from_tty)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+ int err;
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == 0)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == 0)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ jit_read_descriptor (&descriptor);
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ error (_("Unsupported JIT protocol version in descriptor!"));
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (target_gdbarch, reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ jit_read_code_entry (cur_entry_addr, &cur_entry);
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ if (objfile_data (objf, jit_objfile_data) != NULL)
+ jit_unregister_code (objf);
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ int err;
+ CORE_ADDR entry_addr;
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ jit_read_code_entry (entry_addr, &code_entry);
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the objfile with this code entry address. */
+ ALL_OBJFILES_SAFE (objf, temp)
+ {
+ if (objfile_data (objf, jit_objfile_data) == (void*) entry_addr)
+ {
+ jit_unregister_code (objf);
+ return;
+ }
+ }
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void*) entry_addr);
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+ jit_objfile_data = register_objfile_data ();
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 24 Jul 2009 16:45:08 -0000
@@ -0,0 +1,75 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. These values are used by the inferior, so the values of these enums
+ cannot be changed. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ For the first element prev_entry should be NULL, and for the last element
+ next_entry should be NULL. The ordering of this struct must be consistent
+ with the ordering in the client, so it cannot be changed without updating the
+ protocol version. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. The ordering of this struct must be
+ consistent with the ordering in the client, so it cannot be changed without
+ updating the protocol version. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -p -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 24 Jul 2009 16:45:08 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
Index: gdb/symfile-mem.c
===================================================================
RCS file: /cvs/src/src/gdb/symfile-mem.c,v
retrieving revision 1.19
diff -u -p -u -r1.19 symfile-mem.c
--- gdb/symfile-mem.c 2 Jul 2009 17:21:06 -0000 1.19
+++ gdb/symfile-mem.c 24 Jul 2009 16:45:08 -0000
@@ -54,36 +54,23 @@
#include "observer.h"
#include "auxv.h"
#include "elf/common.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include <time.h>
-/* Read inferior memory at ADDR to find the header of a loaded object file
- and read its in-core symbols out of inferior memory. TEMPL is a bfd
- representing the target's format. NAME is the name to use for this
- symbol file in messages; it can be NULL or a malloc-allocated string
- which will be attached to the BFD. */
+/* Common code for symbol_file_add_from_memory and
+ symbol_file_add_from_local_memory. */
+
static struct objfile *
-symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
- int from_tty)
+symbol_file_add_from_memory_common (struct bfd *nbfd, bfd_vma loadbase,
+ int from_tty)
{
struct objfile *objf;
- struct bfd *nbfd;
struct bfd_section *sec;
- bfd_vma loadbase;
struct section_addr_info *sai;
unsigned int i;
-
- if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
- error (_("add-symbol-file-from-memory not supported for this target"));
-
- nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
- target_read_memory);
- if (nbfd == NULL)
- error (_("Failed to read a valid object file image from memory."));
-
- if (name == NULL)
- nbfd->filename = xstrdup ("shared object read from target memory");
- else
- nbfd->filename = name;
+ struct cleanup *old_cleanups;
if (!bfd_check_format (nbfd, bfd_object))
{
@@ -96,7 +83,7 @@ symbol_file_add_from_memory (struct bfd
}
sai = alloc_section_addr_info (bfd_count_sections (nbfd));
- make_cleanup (xfree, sai);
+ old_cleanups = make_cleanup (xfree, sai);
i = 0;
for (sec = nbfd->sections; sec != NULL; sec = sec->next)
if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
@@ -113,9 +100,127 @@ symbol_file_add_from_memory (struct bfd
/* This might change our ideas about frames already looked at. */
reinit_frame_cache ();
+ discard_cleanups (old_cleanups);
return objf;
}
+/* Read inferior memory at ADDR to find the header of a loaded object file
+ and read its in-core symbols out of inferior memory. TEMPL is a bfd
+ representing the target's format. NAME is the name to use for this
+ symbol file in messages; it can be NULL or a malloc-allocated string
+ which will be attached to the BFD. */
+
+struct objfile *
+symbol_file_add_from_memory (struct bfd *templ, CORE_ADDR addr, char *name,
+ int from_tty)
+{
+ struct bfd *nbfd;
+ bfd_vma loadbase;
+
+ if (bfd_get_flavour (templ) != bfd_target_elf_flavour)
+ error (_("add-symbol-file-from-memory not supported for this target"));
+
+ nbfd = bfd_elf_bfd_from_remote_memory (templ, addr, &loadbase,
+ target_read_memory);
+ if (nbfd == NULL)
+ error (_("Failed to read a valid object file image from memory."));
+
+ if (name == NULL)
+ nbfd->filename = xstrdup ("shared object read from target memory");
+ else
+ nbfd->filename = name;
+
+ return symbol_file_add_from_memory_common (nbfd, loadbase, from_tty);
+}
+
+/* Create a new BFD for a file that is already loaded into memory. This does
+ not take ownership of buffer or filename, and the caller must free them.
+ This code is based on bfd_from_remote_memory in bfd/elfcode.h, and should
+ probably live inside of the bfd library. */
+
+static bfd *
+bfd_open_from_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size, const char *filename)
+{
+ bfd *nbfd;
+ struct bfd_in_memory *bim;
+
+ bim = bfd_malloc (sizeof (struct bfd_in_memory));
+ if (bim == NULL)
+ return NULL;
+ bim->buffer = buffer;
+ bim->size = size;
+
+ nbfd = _bfd_new_bfd ();
+ if (nbfd == NULL)
+ {
+ free (bim);
+ return NULL;
+ }
+ nbfd->filename = filename;
+ nbfd->xvec = templ->xvec;
+ nbfd->iostream = bim;
+ nbfd->flags = BFD_IN_MEMORY;
+ nbfd->direction = read_direction;
+ nbfd->mtime = time (NULL);
+ nbfd->mtime_set = TRUE;
+
+ return nbfd;
+}
+
+/* Add a symbol file from local memory. This differs from
+ symbol_file_add_from_memory in that it assumes that we have the entire
+ symbol file laid out in memory and we don't need to use
+ bfd_elf_bfd_from_remote_memory to reconstruct the ELF from the program
+ headers. */
+
+struct objfile *
+symbol_file_add_from_local_memory (struct bfd *templ, bfd_byte *buffer,
+ bfd_size_type size)
+{
+ bfd *nbfd;
+ int err;
+ struct section_addr_info *sai;
+ bfd_vma loadbase;
+ const char *filename;
+ struct cleanup *old_cleanups;
+ struct objfile *objfile;
+
+ /* The filename must be strdup'd or GDB will crash when freeing the object
+ file. */
+ filename = xstrdup ("<in-memory>");
+ old_cleanups = make_cleanup (xfree, (void*)filename);
+
+ /* Create a new BFD from the buffer in memory and add it as a symbol file. */
+ nbfd = bfd_open_from_memory (templ, buffer, size, filename);
+ if (nbfd == NULL)
+ error (_("Unable to create BFD from local memory: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ /* We set loadbase to 0 and assume that the symbol file we're loading has the
+ absolute addresses set in the ELF. */
+ loadbase = 0;
+ objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0);
+ discard_cleanups (old_cleanups);
+ return objfile;
+}
+
+/* Use the BFD of the main executable to guess what kind of object file we want
+ to add. This is used for adding symbol files from memory, where we don't
+ know the format of the file. */
+
+struct bfd *
+symbol_file_guess_template (void)
+{
+ struct bfd *templ;
+
+ if (symfile_objfile != NULL)
+ templ = symfile_objfile->obfd;
+ else
+ templ = exec_bfd;
+
+ return templ;
+}
static void
add_symbol_file_from_memory_command (char *args, int from_tty)
@@ -129,10 +234,7 @@ add_symbol_file_from_memory_command (cha
addr = parse_and_eval_address (args);
/* We need some representative bfd to know the target we are looking at. */
- if (symfile_objfile != NULL)
- templ = symfile_objfile->obfd;
- else
- templ = exec_bfd;
+ templ = symbol_file_guess_template ();
if (templ == NULL)
error (_("\
Must use symbol-file or exec-file before add-symbol-file-from-memory."));
Index: gdb/symfile.h
===================================================================
RCS file: /cvs/src/src/gdb/symfile.h,v
retrieving revision 1.51
diff -u -p -u -r1.51 symfile.h
--- gdb/symfile.h 16 Jun 2009 18:49:25 -0000 1.51
+++ gdb/symfile.h 24 Jul 2009 16:45:08 -0000
@@ -408,4 +408,17 @@ extern void elfmdebug_build_psymtabs (st
const struct ecoff_debug_swap *,
asection *);
+/* From symfile-mem.c */
+
+extern struct bfd *symbol_file_guess_template (void);
+
+extern struct objfile *symbol_file_add_from_memory (struct bfd *templ,
+ CORE_ADDR addr,
+ char *name,
+ int from_tty);
+
+extern struct objfile *symbol_file_add_from_local_memory (struct bfd *templ,
+ bfd_byte *buffer,
+ bfd_size_type size);
+
#endif /* !defined(SYMFILE_H) */
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 16:55 ` Reid Kleckner
@ 2009-07-24 20:42 ` Eli Zaretskii
2009-07-24 20:55 ` Tom Tromey
2009-07-24 21:06 ` Tom Tromey
1 sibling, 1 reply; 32+ messages in thread
From: Eli Zaretskii @ 2009-07-24 20:42 UTC (permalink / raw)
To: Reid Kleckner; +Cc: tromey, gdb-patches, unladen-swallow
> From: Reid Kleckner <rnk@mit.edu>
> Date: Fri, 24 Jul 2009 09:46:52 -0700
> Cc: gdb-patches@sourceware.org, unladen-swallow@googlegroups.com
>
> > Given that this will be a supported way for GDB to connect to JITs, I
> > think that the official interface (symbol names, types, enum values,
> > etc) should be documented in the GDB manual somewhere.
>
> Where should this go? It doesn't really fit under any of the
> top-level topics, so far as I can tell.
Sorry to chime in this late (patches posted as binary attachments
usually discourage me from looking at them), but I cannot answer the
question at this time, given what I know about this feature. Maybe if
you post what you think should be described in the manual, I can make
up my mind then.
> > This patch also deserves an entry in NEWS.
>
> Done.
Unfortunately, the NEWS entry is not instrumental enough. It doesn't
tell the user how to use the feature. Is it possible to come up with
a more useful entry?
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 20:42 ` Eli Zaretskii
@ 2009-07-24 20:55 ` Tom Tromey
2009-07-25 15:29 ` Eli Zaretskii
0 siblings, 1 reply; 32+ messages in thread
From: Tom Tromey @ 2009-07-24 20:55 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Reid Kleckner, gdb-patches, unladen-swallow
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
Eli> Unfortunately, the NEWS entry is not instrumental enough. It doesn't
Eli> tell the user how to use the feature. Is it possible to come up with
Eli> a more useful entry?
I think the NEWS entry is fine. The feature works automatically. Users
need do nothing.
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 16:55 ` Reid Kleckner
2009-07-24 20:42 ` Eli Zaretskii
@ 2009-07-24 21:06 ` Tom Tromey
2009-07-25 0:23 ` Reid Kleckner
1 sibling, 1 reply; 32+ messages in thread
From: Tom Tromey @ 2009-07-24 21:06 UTC (permalink / raw)
To: Reid Kleckner; +Cc: gdb-patches, unladen-swallow
>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
Reid> Here's an updated patch.
Very quick!
Reid> Where should this go? It doesn't really fit under any of the
Reid> top-level topics, so far as I can tell. I only have about a page to
Reid> write about it.
Yeah, I don't really have a suggestion for this.
Today I've also been wondering whether this works with core files.
Have you tried that? (I can't imagine why it wouldn't work. And I
don't think it is really a requirement; I'm just curious.)q
Also I was wondering about a multi-threaded JIT. I suppose it is
sufficient to mention in the documentation that the JIT is responsible
for making sure only a single thread can call __jit_debug_register_code
at a time.
Tom> Does the current code work ok if LLVM is a .so linked into the
Tom> application? I suspect, but do not know for sure, that in this
Tom> situation gdb will not see the JIT symbols when the inferior-created
Tom> hook is run.
Reid> I don't know, since we statically link to LLVM. I'm not an expert in
Reid> how dynamic loaders work, so I don't know how to fix this.
Ok. One way would be to stick some code in breakpoint_re_set and
update_breakpoints_after_exec. (I am not sure if this is the best way or
not.) Anyway the idea is that after a .so is noticed, re-do the searching,
in case the new .so is a JIT. So, the work would be done per-objfile,
and not once per inferior.
Maybe this can be done via observers as well; I don't know.
Tom> There is also the somewhat more pathological case of a program with
Tom> multiple JITs linked in. I'm on the fence about whether this is really
Tom> needed.
Reid> How would this work? Would they both have separate functions
Reid> __jit_debug_register at different addresses?
Yeah. This would work if you had two JITs in a process, say loaded
dynamically, and the various __jit symbols had hidden visibility.
Tom> You can store arbitrary module-specific data in an objfile using the
Tom> objfile_data API. This would be better because it is an already
Tom> supported way of dealing with the lifetime of the associated data.
Reid> Ah, that's much better. Unfortunately because the data for other
Reid> objfiles is uninitialized (it's returned from calloc), there's no way
Reid> for me to check which objfiles contain JITed code.
I don't think I understand. I thought you meant that you couldn't
detect whether or not the JIT datum was set on an objfile, but I see
that jit_inferior_exit_hook already does this the way that I would
expect.
Reid> Do I actually need to have this inferior_exit hook? I just
Reid> noticed that when I kill and restart the inferior it doesn't seem
Reid> to free these objfiles, so I added the hook.
Yeah, I think you need it.
I was thinking maybe this should just be a flag on the objfile, plus a
change to delete such objfiles in reread_symbols. But I am not sure
which is better, really; and your way is less invasive.
Reid> I took a shot at putting cleanups in, but I'm not sure I did it
Reid> correctly. In particular, I'm not sure about the discard_cleanups
Reid> logic.
It looks ok to me.
Just FYI, there's a section in the internals manual about cleanups that
is reasonably clear.
Tom> Please update the comment here to explain that these values are exported
Tom> and used by the inferior, so they cannot be changed.
Reid> Done. The same is true about the rest of the structs, the ordering of
Reid> the fields cannot be changed without updating the protocol version.
My reading of the code is that it unpacks target memory field-by-field
into the host-side structure. So, it is ok for this to change. What
cannot change is the definition of these structs on the target.
Am I misreading this?
These structs, in their target form, should probably all go in the
manual as well.
Reid> + /* Remember a mapping from entry_addr to objfile. */
Reid> + set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
I don't think you need the cast here. There are a few of these.
Reid> + printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
Reid> + (void*) entry_addr);
A style nit, in GNU style the cast is written "(void *)".
Reid> + nbfd = bfd_open_from_memory (templ, buffer, size, filename);
Reid> + if (nbfd == NULL)
Reid> + error (_("Unable to create BFD from local memory: %s"),
Reid> + bfd_errmsg (bfd_get_error ()));
Reid> +
Reid> + /* We set loadbase to 0 and assume that the symbol file we're loading has the
Reid> + absolute addresses set in the ELF. */
Reid> + loadbase = 0;
Reid> + objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0);
I suspect this needs a cleanup to free the BFD in case
symbol_file_add_from_memory_common errors. But I couldn't tell
immediately if that is possible (in gdb I tend to assume that anything
can throw...).
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 21:06 ` Tom Tromey
@ 2009-07-25 0:23 ` Reid Kleckner
2009-07-30 16:30 ` Tom Tromey
0 siblings, 1 reply; 32+ messages in thread
From: Reid Kleckner @ 2009-07-25 0:23 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches, unladen-swallow
[-- Attachment #1: Type: text/plain, Size: 5858 bytes --]
> Reid> Where should this go? It doesn't really fit under any of the
> Reid> top-level topics, so far as I can tell. I only have about a page to
> Reid> write about it.
>
> Yeah, I don't really have a suggestion for this.
I'll figure it out on Monday, but for now, here's some code changes.
> Today I've also been wondering whether this works with core files.
> Have you tried that? (I can't imagine why it wouldn't work. And I
> don't think it is really a requirement; I'm just curious.)q
I have absolutely no idea. :)
> Also I was wondering about a multi-threaded JIT. I suppose it is
> sufficient to mention in the documentation that the JIT is responsible
> for making sure only a single thread can call __jit_debug_register_code
> at a time.
Yes, on the LLVM side I actually put a big fat global lock around the
whole thing. I don't know if it's possible for there to be multiple
symbols when there are dynamic libraries or who knows what, but only
one thread should be touching those globals at a time. If you had
multiple JITs that were unaware of each other in the binary trying to
JIT, then it would break down, but that seems like an uncommon use
case. If it comes to that, someone could write a library that they
both link in which handles the synchronization.
> Tom> Does the current code work ok if LLVM is a .so linked into the
> Tom> application? I suspect, but do not know for sure, that in this
> Tom> situation gdb will not see the JIT symbols when the inferior-created
> Tom> hook is run.
>
> Reid> I don't know, since we statically link to LLVM. I'm not an expert in
> Reid> how dynamic loaders work, so I don't know how to fix this.
>
> Ok. One way would be to stick some code in breakpoint_re_set and
> update_breakpoints_after_exec. (I am not sure if this is the best way or
> not.) Anyway the idea is that after a .so is noticed, re-do the searching,
> in case the new .so is a JIT. So, the work would be done per-objfile,
> and not once per inferior.
>
> Maybe this can be done via observers as well; I don't know.
I put it in breakpoint_re_set and follow_exec after the solib stuff,
so that should probably work, but I have no way of testing it.
> Tom> There is also the somewhat more pathological case of a program with
> Tom> multiple JITs linked in. I'm on the fence about whether this is really
> Tom> needed.
>
> Reid> How would this work? Would they both have separate functions
> Reid> __jit_debug_register at different addresses?
>
> Yeah. This would work if you had two JITs in a process, say loaded
> dynamically, and the various __jit symbols had hidden visibility.
I think if someone has that use case, then they can write a client
side library that wraps the __jit symbols and make the two JITs call
that.
> Tom> You can store arbitrary module-specific data in an objfile using the
> Tom> objfile_data API. This would be better because it is an already
> Tom> supported way of dealing with the lifetime of the associated data.
>
> Reid> Ah, that's much better. Unfortunately because the data for other
> Reid> objfiles is uninitialized (it's returned from calloc), there's no way
> Reid> for me to check which objfiles contain JITed code.
>
> I don't think I understand. I thought you meant that you couldn't
> detect whether or not the JIT datum was set on an objfile, but I see
> that jit_inferior_exit_hook already does this the way that I would
> expect.
Oh right, I wrote that code, and then was worried that the
objfile_data wouldn't be initialized to NULL. It's returned from
calloc, which according to Jeff is initialized to 0's, so the code I
wrote works.
> Tom> Please update the comment here to explain that these values are exported
> Tom> and used by the inferior, so they cannot be changed.
>
> Reid> Done. The same is true about the rest of the structs, the ordering of
> Reid> the fields cannot be changed without updating the protocol version.
>
> My reading of the code is that it unpacks target memory field-by-field
> into the host-side structure. So, it is ok for this to change. What
> cannot change is the definition of these structs on the target.
>
> Am I misreading this?
>
> These structs, in their target form, should probably all go in the
> manual as well.
That's true, but they should probably stay closely coupled. I started
off just copying over the whole struct with one target_read_mem call,
and having them magically line up. However, that doesn't work if
you're debugging a 32-bit process with a 64-bit debugger.
> Reid> + /* Remember a mapping from entry_addr to objfile. */
> Reid> + set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
>
> I don't think you need the cast here. There are a few of these.
Actually, it is, because entry_addr is a CORE_ADDR. CORE_ADDR fits
into a void* right? I'm just doing it to avoid mallocing a single
CORE_ADDR.
> Reid> + printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
> Reid> + (void*) entry_addr);
>
> A style nit, in GNU style the cast is written "(void *)".
Done.
> Reid> + nbfd = bfd_open_from_memory (templ, buffer, size, filename);
> Reid> + if (nbfd == NULL)
> Reid> + error (_("Unable to create BFD from local memory: %s"),
> Reid> + bfd_errmsg (bfd_get_error ()));
> Reid> +
> Reid> + /* We set loadbase to 0 and assume that the symbol file we're loading has the
> Reid> + absolute addresses set in the ELF. */
> Reid> + loadbase = 0;
> Reid> + objfile = symbol_file_add_from_memory_common (nbfd, loadbase, 0);
>
> I suspect this needs a cleanup to free the BFD in case
> symbol_file_add_from_memory_common errors. But I couldn't tell
> immediately if that is possible (in gdb I tend to assume that anything
> can throw...).
This code has been reverted, and I use bfd_openr_iovec now.
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 29288 bytes --]
2009-07-24 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(allocate_bp_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -p -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 24 Jul 2009 23:34:27 -0000
@@ -677,7 +677,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -746,7 +747,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -828,7 +829,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -p -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 24 Jul 2009 23:34:27 -0000
@@ -3,6 +3,11 @@
*** Changes since GDB 6.8
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create object files with debug info in
+memory and register them with GDB. The interface also supports
+attaching to a running process.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -p -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 24 Jul 2009 23:34:33 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"
/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@ update_breakpoints_after_exec (void)
continue;
}
+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3307,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3325,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3346,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3356,28 +3370,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3390,6 +3394,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3460,6 +3465,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3601,7 @@ print_one_breakpoint_location (struct br
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3721,6 +3730,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4372,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4655,17 @@ struct lang_and_radix
int radix;
};
+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5301,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7608,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7628,7 @@ delete_command (char *arg, int from_tty)
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7951,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
@@ -7954,6 +7980,8 @@ breakpoint_re_set (void)
set_language (save_language);
input_radix = save_input_radix;
+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -p -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 24 Jul 2009 23:34:36 -0000
@@ -120,6 +120,9 @@ enum bptype
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void);
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -p -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 24 Jul 2009 23:34:41 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pat
solib_create_inferior_hook ();
#endif
+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 24 Jul 2009 23:34:41 -0000
@@ -0,0 +1,393 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+#include "gdb_stat.h"
+
+//#include "assert.h"
+//#include "string.h"
+
+static const struct objfile_data *jit_objfile_data;
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr = 0;
+
+struct target_buffer
+{
+ CORE_ADDR base;
+ size_t size;
+};
+
+/* Openning the file is a no-op. */
+
+static void *
+mem_bfd_iovec_open (struct bfd *abfd, void *open_closure)
+{
+ return open_closure;
+}
+
+/* Closing the file is just freeing the base/size pair on our side. */
+
+static int
+mem_bfd_iovec_close (struct bfd *abfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+/* For reading the file, we just need to pass through to target_read_memory and
+ fix up the arguments and return values. */
+
+static file_ptr
+mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ int err;
+ struct target_buffer *buffer = (struct target_buffer *) stream;
+
+ /* If this read will read all of the file, limit it to just the rest. */
+ if (offset + nbytes > buffer->size)
+ nbytes = buffer->size - offset;
+
+ /* If there are no more bytes left, we've reached EOF. */
+ if (nbytes == 0)
+ return 0;
+
+ err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes);
+ if (err)
+ return -1;
+
+ return nbytes;
+}
+
+/* For statting the file, we only support the st_size attribute. */
+
+static int
+mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
+{
+ struct target_buffer *buffer = (struct target_buffer*) stream;
+
+ sb->st_size = buffer->size;
+ return 0;
+}
+
+/* Open a BFD from the target's memory. */
+
+static struct bfd *
+bfd_open_from_target_memory (CORE_ADDR addr, size_t size, char *target)
+{
+ const char *filename = xstrdup ("<in-memory>");
+ struct target_buffer *buffer = xmalloc (sizeof (struct target_buffer));
+
+ buffer->base = addr;
+ buffer->size = size;
+ return bfd_openr_iovec (filename, target,
+ mem_bfd_iovec_open,
+ buffer,
+ mem_bfd_iovec_pread,
+ mem_bfd_iovec_close,
+ mem_bfd_iovec_stat);
+}
+
+/* Helper function for reading the global JIT descriptor from remote memory. */
+
+static void
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ error (_("Unable to read JIT descriptor from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order);
+ descriptor->action_flag =
+ extract_unsigned_integer (&desc_buf[4], 4, byte_order);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry =
+ extract_typed_address (&desc_buf[8 + ptr_size], ptr_type);
+}
+
+/* Helper function for reading a JITed code entry from remote memory. */
+
+static void
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ error (_("Unable to read JIT code entry from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry =
+ extract_typed_address (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr =
+ extract_typed_address (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size =
+ extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *nbfd;
+ struct section_addr_info *sai;
+ struct bfd_section *sec;
+ struct objfile *objfile;
+ struct cleanup *old_cleanups;
+ int i;
+ const struct bfd_arch_info *b;
+
+ nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+ code_entry->symfile_size, gnutarget);
+ old_cleanups = make_cleanup_bfd_close (nbfd);
+
+ /* Check the format. NOTE: This initializes important data that GDB uses!
+ We would segfault later without this line. */
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ printf_unfiltered (_("\
+JITed symbol file is not an object file, ignoring it.\n"));
+ do_cleanups (old_cleanups);
+ return;
+ }
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (target_gdbarch);
+ if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+ warning (_("JITed object file architecture %s is not compatible "
+ "with target architecture %s."), bfd_get_arch_info
+ (nbfd)->printable_name, b->printable_name);
+
+ /* Read the section address information out of the symbol file. Since the
+ file is generated by the JIT at runtime, it should all of the absolute
+ addresses that we care about. */
+ sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+ make_cleanup_free_section_addr_info (sai);
+ i = 0;
+ for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+ if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+ {
+ /* We assume that these virtual addresses are absolute, and do not
+ treat them as offsets. */
+ sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+ sai->other[i].name = (char *) bfd_get_section_name (nbfd, sec);
+ sai->other[i].sectindex = sec->index;
+ ++i;
+ }
+
+ /* This call takes ownership of sai. */
+ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ set_objfile_data (objfile, jit_objfile_data, (void *) entry_addr);
+
+ discard_cleanups (old_cleanups);
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct objfile *objfile)
+{
+ free_objfile (objfile);
+}
+
+void
+jit_inferior_created_hook (void)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+
+ /* If we already found the symbols and successfully set the breakpoint, don't
+ do it again. */
+ if (jit_descriptor_addr != 0)
+ return;
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == 0)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == 0)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ jit_read_descriptor (&descriptor);
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ error (_("Unsupported JIT protocol version in descriptor!"));
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (target_gdbarch, reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ jit_read_code_entry (cur_entry_addr, &cur_entry);
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+}
+
+/* Wrapper to match the observer function pointer prototype. */
+
+static void
+jit_inferior_created_hook1 (struct target_ops *objfile, int from_tty)
+{
+ jit_inferior_created_hook ();
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* We need to reset the descriptor addr so that next time we load up the
+ inferior we look for it again. */
+ jit_descriptor_addr = 0;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ if (objfile_data (objf, jit_objfile_data) != NULL)
+ jit_unregister_code (objf);
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ CORE_ADDR entry_addr;
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ jit_read_code_entry (entry_addr, &code_entry);
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the objfile with this code entry address. */
+ ALL_OBJFILES_SAFE (objf, temp)
+ {
+ if (objfile_data (objf, jit_objfile_data) == (void *) entry_addr)
+ {
+ jit_unregister_code (objf);
+ return;
+ }
+ }
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void *) entry_addr);
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook1);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+ jit_objfile_data = register_objfile_data ();
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 24 Jul 2009 23:34:41 -0000
@@ -0,0 +1,77 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. These values are used by the inferior, so the values of these enums
+ cannot be changed. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ For the first element prev_entry should be NULL, and for the last element
+ next_entry should be NULL. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* Looks for the descriptor and registration symbols and breakpoints the
+ registration function. If it finds both, it registers all the already JITed
+ code. If it has already found the symbols, then it doesn't try again. */
+
+extern void jit_inferior_created_hook (void);
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -p -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 24 Jul 2009 23:34:41 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-07-24 13:25 ` Ulrich Weigand
2009-07-24 16:52 ` Doug Evans
@ 2009-07-25 0:40 ` Reid Kleckner
1 sibling, 0 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-07-25 0:40 UTC (permalink / raw)
To: uweigand; +Cc: tromey, Reid Kleckner, gdb-patches, unladen-swallow
On Fri, Jul 24, 2009 at 4:30 AM, Ulrich Weigand<uweigand@de.ibm.com> wrote:
>> Reid> + /* Hack to work around the fact that BFD does not take ownership of the
>> Reid> + memory for files allocated in memory. */
>>
>> Is it possible to fix this directly in BFD? Since...
>>
>> Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
>>
>> ... this is definitely fishy :-)
>
> I'd suggest that Reid instead use bfd_openr_iovec to access an ELF image
> directly in inferior memory, as is currently done e.g. by
> remote.c:remote_bfd_open
> spu-linux-nat.c:spu_bfd_open
> solib-spu.c:spu_bfd_open (is about to be introduced by the patch
> http://sourceware.org/ml/gdb-patches/2009-07/msg00546.html)
> This works without BFD changes or directly accessing BFD internals ...
I implemented this in the last patch I sent out. It was a lot of fun.
It turns out you need to call bfd_check_format before adding the
object file, or GDB segfaults. To make it even more awesome, the data
that needs to be initialized is hidden behind layers of struct unions
and macros and all kinds of indirection... But I agree that the iovec
is the right way to do it. The BFD_IN_MEMORY flag seems like a hack.
Should I leave that memory leak fix in? It's still an open bug for
GDB/BFD, but it's less critical now that it only happens when someone
does add-symbol-file-from-memory.
Reid
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-24 20:55 ` Tom Tromey
@ 2009-07-25 15:29 ` Eli Zaretskii
2009-07-27 23:20 ` Reid Kleckner
0 siblings, 1 reply; 32+ messages in thread
From: Eli Zaretskii @ 2009-07-25 15:29 UTC (permalink / raw)
To: Tom Tromey; +Cc: rnk, gdb-patches, unladen-swallow
> Cc: Reid Kleckner <rnk@mit.edu>, gdb-patches@sourceware.org,
> unladen-swallow@googlegroups.com
> From: Tom Tromey <tromey@redhat.com>
> Date: Fri, 24 Jul 2009 13:18:30 -0600
>
> >>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
>
> Eli> Unfortunately, the NEWS entry is not instrumental enough. It doesn't
> Eli> tell the user how to use the feature. Is it possible to come up with
> Eli> a more useful entry?
>
> I think the NEWS entry is fine. The feature works automatically. Users
> need do nothing.
Maybe that is true, but the text as written left me wondering whether
I do need to do something to get that working.
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create object files with debug info in
+memory and register them with GDB. The interface also supports
+attaching to a running process.
The second sentence begs 2 questions: (1) how does one write or
compile or otherwise get hold of such applications, and (2) how does
one register object files with GDB.
Granted, a NEWS entry is not a place for long explanations (if they
are long), but without any pointers to the manual or at least some Web
site, I couldn't figure out how to go about adding this feature to my
workflow.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-25 15:29 ` Eli Zaretskii
@ 2009-07-27 23:20 ` Reid Kleckner
2009-07-28 20:20 ` Eli Zaretskii
0 siblings, 1 reply; 32+ messages in thread
From: Reid Kleckner @ 2009-07-27 23:20 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Tom Tromey, gdb-patches, unladen-swallow
[-- Attachment #1: Type: text/plain, Size: 1630 bytes --]
On Sat, Jul 25, 2009 at 12:26 AM, Eli Zaretskii<eliz@gnu.org> wrote:
>> >>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
>>
>> Eli> Unfortunately, the NEWS entry is not instrumental enough. It doesn't
>> Eli> tell the user how to use the feature. Is it possible to come up with
>> Eli> a more useful entry?
>>
>> I think the NEWS entry is fine. The feature works automatically. Users
>> need do nothing.
>
> Maybe that is true, but the text as written left me wondering whether
> I do need to do something to get that working.
>
> +* GDB now has an interface for JIT compilation. Applications that
> +dynamically generate code can create object files with debug info in
> +memory and register them with GDB. The interface also supports
> +attaching to a running process.
>
> The second sentence begs 2 questions: (1) how does one write or
> compile or otherwise get hold of such applications, and (2) how does
> one register object files with GDB.
>
> Granted, a NEWS entry is not a place for long explanations (if they
> are long), but without any pointers to the manual or at least some Web
> site, I couldn't figure out how to go about adding this feature to my
> workflow.
Here is an updated patch with a new manual chapter. It has the
necessary struct definitions that the client should include, and step
by step instructions for what to do. I'd appreciate if someone could
review the writing and my texinfo, though. I mentioned the manual
chapter in the NEWS entry, since the explanation of how it works is
long.
What else needs to be addressed?
Thanks,
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 34039 bytes --]
2009-07-24 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* doc/gdb.texinfo: Add chapter on JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(allocate_bp_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -p -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 27 Jul 2009 20:34:06 -0000
@@ -677,7 +677,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -746,7 +747,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -828,7 +829,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -p -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 27 Jul 2009 20:34:06 -0000
@@ -3,6 +3,11 @@
*** Changes since GDB 6.8
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create symbol files in memory and register
+them with GDB. For users, the feature should work transparently, and
+for JIT developers, the interface is documented in the GDB manual.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -p -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 27 Jul 2009 20:34:06 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"
/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@ update_breakpoints_after_exec (void)
continue;
}
+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3307,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3325,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3346,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3356,28 +3370,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3390,6 +3394,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3460,6 +3465,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3601,7 @@ print_one_breakpoint_location (struct br
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3721,6 +3730,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4372,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4655,17 @@ struct lang_and_radix
int radix;
};
+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5301,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7608,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7628,7 @@ delete_command (char *arg, int from_tty)
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7951,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
@@ -7954,6 +7980,8 @@ breakpoint_re_set (void)
set_language (save_language);
input_radix = save_input_radix;
+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -p -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 27 Jul 2009 20:34:06 -0000
@@ -120,6 +120,9 @@ enum bptype
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void);
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -p -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 27 Jul 2009 20:34:07 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pat
solib_create_inferior_hook ();
#endif
+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 27 Jul 2009 20:34:07 -0000
@@ -0,0 +1,393 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+#include "gdb_stat.h"
+
+//#include "assert.h"
+//#include "string.h"
+
+static const struct objfile_data *jit_objfile_data;
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr = 0;
+
+struct target_buffer
+{
+ CORE_ADDR base;
+ size_t size;
+};
+
+/* Openning the file is a no-op. */
+
+static void *
+mem_bfd_iovec_open (struct bfd *abfd, void *open_closure)
+{
+ return open_closure;
+}
+
+/* Closing the file is just freeing the base/size pair on our side. */
+
+static int
+mem_bfd_iovec_close (struct bfd *abfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+/* For reading the file, we just need to pass through to target_read_memory and
+ fix up the arguments and return values. */
+
+static file_ptr
+mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ int err;
+ struct target_buffer *buffer = (struct target_buffer *) stream;
+
+ /* If this read will read all of the file, limit it to just the rest. */
+ if (offset + nbytes > buffer->size)
+ nbytes = buffer->size - offset;
+
+ /* If there are no more bytes left, we've reached EOF. */
+ if (nbytes == 0)
+ return 0;
+
+ err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes);
+ if (err)
+ return -1;
+
+ return nbytes;
+}
+
+/* For statting the file, we only support the st_size attribute. */
+
+static int
+mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
+{
+ struct target_buffer *buffer = (struct target_buffer*) stream;
+
+ sb->st_size = buffer->size;
+ return 0;
+}
+
+/* Open a BFD from the target's memory. */
+
+static struct bfd *
+bfd_open_from_target_memory (CORE_ADDR addr, size_t size, char *target)
+{
+ const char *filename = xstrdup ("<in-memory>");
+ struct target_buffer *buffer = xmalloc (sizeof (struct target_buffer));
+
+ buffer->base = addr;
+ buffer->size = size;
+ return bfd_openr_iovec (filename, target,
+ mem_bfd_iovec_open,
+ buffer,
+ mem_bfd_iovec_pread,
+ mem_bfd_iovec_close,
+ mem_bfd_iovec_stat);
+}
+
+/* Helper function for reading the global JIT descriptor from remote memory. */
+
+static void
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ error (_("Unable to read JIT descriptor from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order);
+ descriptor->action_flag =
+ extract_unsigned_integer (&desc_buf[4], 4, byte_order);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry =
+ extract_typed_address (&desc_buf[8 + ptr_size], ptr_type);
+}
+
+/* Helper function for reading a JITed code entry from remote memory. */
+
+static void
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ error (_("Unable to read JIT code entry from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry =
+ extract_typed_address (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr =
+ extract_typed_address (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size =
+ extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *nbfd;
+ struct section_addr_info *sai;
+ struct bfd_section *sec;
+ struct objfile *objfile;
+ struct cleanup *old_cleanups;
+ int i;
+ const struct bfd_arch_info *b;
+
+ nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+ code_entry->symfile_size, gnutarget);
+ old_cleanups = make_cleanup_bfd_close (nbfd);
+
+ /* Check the format. NOTE: This initializes important data that GDB uses!
+ We would segfault later without this line. */
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ printf_unfiltered (_("\
+JITed symbol file is not an object file, ignoring it.\n"));
+ do_cleanups (old_cleanups);
+ return;
+ }
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (target_gdbarch);
+ if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+ warning (_("JITed object file architecture %s is not compatible "
+ "with target architecture %s."), bfd_get_arch_info
+ (nbfd)->printable_name, b->printable_name);
+
+ /* Read the section address information out of the symbol file. Since the
+ file is generated by the JIT at runtime, it should all of the absolute
+ addresses that we care about. */
+ sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+ make_cleanup_free_section_addr_info (sai);
+ i = 0;
+ for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+ if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+ {
+ /* We assume that these virtual addresses are absolute, and do not
+ treat them as offsets. */
+ sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+ sai->other[i].name = (char *) bfd_get_section_name (nbfd, sec);
+ sai->other[i].sectindex = sec->index;
+ ++i;
+ }
+
+ /* This call takes ownership of sai. */
+ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ set_objfile_data (objfile, jit_objfile_data, (void *) entry_addr);
+
+ discard_cleanups (old_cleanups);
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct objfile *objfile)
+{
+ free_objfile (objfile);
+}
+
+void
+jit_inferior_created_hook (void)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+
+ /* If we already found the symbols and successfully set the breakpoint, don't
+ do it again. */
+ if (jit_descriptor_addr != 0)
+ return;
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == 0)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == 0)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ jit_read_descriptor (&descriptor);
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ error (_("Unsupported JIT protocol version in descriptor!"));
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (target_gdbarch, reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ jit_read_code_entry (cur_entry_addr, &cur_entry);
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+}
+
+/* Wrapper to match the observer function pointer prototype. */
+
+static void
+jit_inferior_created_hook1 (struct target_ops *objfile, int from_tty)
+{
+ jit_inferior_created_hook ();
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* We need to reset the descriptor addr so that next time we load up the
+ inferior we look for it again. */
+ jit_descriptor_addr = 0;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ if (objfile_data (objf, jit_objfile_data) != NULL)
+ jit_unregister_code (objf);
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ CORE_ADDR entry_addr;
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ jit_read_code_entry (entry_addr, &code_entry);
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ /* Look up the objfile with this code entry address. */
+ ALL_OBJFILES_SAFE (objf, temp)
+ {
+ if (objfile_data (objf, jit_objfile_data) == (void *) entry_addr)
+ {
+ jit_unregister_code (objf);
+ return;
+ }
+ }
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void *) entry_addr);
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook1);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+ jit_objfile_data = register_objfile_data ();
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 27 Jul 2009 20:34:07 -0000
@@ -0,0 +1,77 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. These values are used by the inferior, so the values of these enums
+ cannot be changed. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ For the first element prev_entry should be NULL, and for the last element
+ next_entry should be NULL. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* Looks for the descriptor and registration symbols and breakpoints the
+ registration function. If it finds both, it registers all the already JITed
+ code. If it has already found the symbols, then it doesn't try again. */
+
+extern void jit_inferior_created_hook (void);
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -p -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 27 Jul 2009 20:34:07 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.608
diff -u -p -u -r1.608 gdb.texinfo
--- gdb/doc/gdb.texinfo 20 Jul 2009 18:51:41 -0000 1.608
+++ gdb/doc/gdb.texinfo 27 Jul 2009 20:34:08 -0000
@@ -159,6 +159,7 @@ software in general. We will miss him.
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
* GDB/MI:: @value{GDBN}'s Machine Interface.
* Annotations:: @value{GDBN}'s annotation interface.
+* JIT Interface:: Using the JIT debugging interface.
* GDB Bugs:: Reporting bugs in @value{GDBN}
@@ -25822,6 +25823,115 @@ source which is being displayed. @var{a
followed by one or more lowercase hex digits (note that this does not
depend on the language).
+@node JIT Interface
+@chapter @value{GDBN}'s JIT Interface
+
+This chapter documents @value{GDBN}'s just-in-time compiler (JIT) interface.
+Just-in-time compilers (JITs) are normally difficult to debug because they
+generate code at runtime, and GDB normally gets all of its symbols from object
+files. In order to debug executables that use JITs, GDB has an interface that
+allows the JIT to register in-memory symbol files with GDB at runtime. If you
+are using GDB to debug a program using this interface, then it should just work
+so long as you have not stripped the binary. If you are developing a JIT, then
+the interface is documented in the next section. At this time, the only known
+client of this interface is the LLVM JIT.
+
+@section Using the JIT Interface
+
+Broadly speaking, the JIT interface mirrors the dynamic loader interface, which
+means that the JIT communicates with GDB by writing some global data and calling
+a fuction at a well-known symbol. When GDB attaches, it puts a breakpoint in
+that function so that it can read the debug information.
+
+These are the relevant struct declarations that a C program should include to
+implement the interface:
+
+@smallexample
+typedef enum
+@{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+@} jit_actions_t;
+
+struct jit_code_entry
+@{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+@};
+
+struct jit_descriptor
+@{
+ uint32_t version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+@};
+
+/* GDB puts a breakpoint in this function. */
+void __attribute__((noinline)) __jit_debug_register_code() @{ @};
+
+/* Make sure to specify the version statically, because the
+ debugger may check the version before we can set it. */
+struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @};
+@end smallexample
+
+If the JIT is multi-threaded, then it is important that the JIT synchronize any
+modifications to this global data properly, mostly likely by putting a global
+mutex around these routines.
+
+@section Registering Code
+
+To register code with GDB, the JIT should follow this protocol:
+
+@itemize @bullet
+@item
+Generate an object file in memory with symbols and other desired debug
+information. The file must include the virtual addresses of the sections.
+
+@item
+Create a code entry for the file, which gives the start and size of the symbol
+file.
+
+@item
+Add it to the linked list in the JIT descriptor.
+
+@item
+Point the relevant_entry field of the descriptor at the entry.
+
+@item
+Set @code{action_flag} to JIT_REGISTER and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+When GDB is attached, it uses the relevant_entry pointer so it doesn't have to
+walk the list looking for new code. However, the linked list must still be
+maintained in order to allow GDB to attach to a running process and properly
+find all of the debugging information.
+
+@section Unregistering Code
+
+If code is freed, then the JIT should use the following protocol:
+
+@itemize @bullet
+@item
+Remove the code entry corresponding to the code from the linked list.
+
+@item
+Point the @code{relevant_entry} field of the descriptor at the code entry.
+
+@item
+Set @code{action_flag} to JIT_UNREGISTER and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+If the JIT recompiles code throughout its lifetime without unregistering it,
+then GDB and the JIT will continue to use extra memory for these symbol files.
+
@node GDB Bugs
@chapter Reporting Bugs in @value{GDBN}
@cindex bugs in @value{GDBN}
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-27 23:20 ` Reid Kleckner
@ 2009-07-28 20:20 ` Eli Zaretskii
2009-07-28 22:23 ` Reid Kleckner
0 siblings, 1 reply; 32+ messages in thread
From: Eli Zaretskii @ 2009-07-28 20:20 UTC (permalink / raw)
To: Reid Kleckner; +Cc: tromey, gdb-patches, unladen-swallow
> From: Reid Kleckner <rnk@mit.edu>
> Date: Mon, 27 Jul 2009 13:40:02 -0700
> Cc: Tom Tromey <tromey@redhat.com>, gdb-patches@sourceware.org,
> unladen-swallow@googlegroups.com
>
> I mentioned the manual chapter in the NEWS entry, since the
> explanation of how it works is long.
Thanks. I have a few comments.
> +* GDB now has an interface for JIT compilation. Applications that
> +dynamically generate code can create symbol files in memory and register
> +them with GDB. For users, the feature should work transparently, and
> +for JIT developers, the interface is documented in the GDB manual.
Please state the name of the chapter in the manual where this is
documented.
> +@node JIT Interface
> +@chapter @value{GDBN}'s JIT Interface
I think a better name is "JIT Compilation Interface".
Please add pertinent index entries here. I suggest these:
@cindex just-in-time compilation
@cindex JIT compilation interface
> +This chapter documents @value{GDBN}'s just-in-time compiler (JIT) interface.
Whenever you introduce new terminology, first usage should be in @dfn,
to make the term stand out:
This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT)
compilation interface.
Also, how about saying a few words here about JIT compiling, for those
who maybe hear about that for the first time? Just a sentence or two
would be good enough.
> +Just-in-time compilers (JITs) are normally difficult to debug because they
Are we talking about debugging a JIT compiler or about debugging a
program that uses a JIT compiler? I thought the latter, but it sounds
like you are talking about the former here.
> +generate code at runtime, and GDB normally gets all of its symbols from object
^^^
"@value{GDBN}" (here and elsewhere).
> +files.
Does this rewording of the above sentence catch what you meant to say
(I'm not sure I understood)?
Programs compiled with JIT compilers are normally difficult to debug
because portions of their code are generated at runtime, and not
written to object files, where @value{GDBN} normally finds the debug
information it needs.
The rest of the text needs similar rewording, to make it more clear.
In particular, you use "JIT" meaning "a JIT compiler" and "executables
that use JIT" when you really mean "programs that use JIT
compilation".
If you can rephrase the text along the above guidelines, that'd be
great. If not, go ahead and commit it with the few technical changes
I mention here, and I will do the rest.
> + In order to debug executables that use JITs, GDB has an interface that
> +allows the JIT to register in-memory symbol files with GDB at runtime. If you
> +are using GDB to debug a program using this interface, then it should just work
> +so long as you have not stripped the binary. If you are developing a JIT, then
> +the interface is documented in the next section. At this time, the only known
> +client of this interface is the LLVM JIT.
A question out of ignorance: are the special symbols you use to
interface with the JIT generated code specific to LLVM, or are they
part of some broader standard? If the former, the instructions below
are actually instructions to develop other JIT compilers that follow
the LLVM conventions, and we are in effect committing GDB to force
these conventions on developers of JIT compilers, don't we?
> +@section Using the JIT Interface
Please make each @section also a @node. (This will need a @menu be
added to the parent chapter.) I would like to avoid sections that are
not nodes, because nodes make it easier to navigate the manual in a
structural way.
> +If the JIT recompiles code throughout its lifetime without unregistering it,
> +then GDB and the JIT will continue to use extra memory for these symbol files.
Does this simply say "don't recompile without unregistering, or you
will leak memory", or does it say something else?
Thanks.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-28 20:20 ` Eli Zaretskii
@ 2009-07-28 22:23 ` Reid Kleckner
2009-07-29 15:20 ` Eli Zaretskii
0 siblings, 1 reply; 32+ messages in thread
From: Reid Kleckner @ 2009-07-28 22:23 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: tromey, gdb-patches, unladen-swallow
[-- Attachment #1: Type: text/plain, Size: 6860 bytes --]
On Tue, Jul 28, 2009 at 12:00 PM, Eli Zaretskii<eliz@gnu.org> wrote:
>> From: Reid Kleckner <rnk@mit.edu>
>> Date: Mon, 27 Jul 2009 13:40:02 -0700
>> Cc: Tom Tromey <tromey@redhat.com>, gdb-patches@sourceware.org,
>> unladen-swallow@googlegroups.com
>>
>> I mentioned the manual chapter in the NEWS entry, since the
>> explanation of how it works is long.
>
> Thanks. I have a few comments.
>
>> +* GDB now has an interface for JIT compilation. Applications that
>> +dynamically generate code can create symbol files in memory and register
>> +them with GDB. For users, the feature should work transparently, and
>> +for JIT developers, the interface is documented in the GDB manual.
>
> Please state the name of the chapter in the manual where this is
> documented.
Done.
>> +@node JIT Interface
>> +@chapter @value{GDBN}'s JIT Interface
>
> I think a better name is "JIT Compilation Interface".
>
> Please add pertinent index entries here. I suggest these:
>
> @cindex just-in-time compilation
> @cindex JIT compilation interface
Done.
>> +This chapter documents @value{GDBN}'s just-in-time compiler (JIT) interface.
>
> Whenever you introduce new terminology, first usage should be in @dfn,
> to make the term stand out:
>
> This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT)
> compilation interface.
>
> Also, how about saying a few words here about JIT compiling, for those
> who maybe hear about that for the first time? Just a sentence or two
> would be good enough.
Sure.
>> +Just-in-time compilers (JITs) are normally difficult to debug because they
>
> Are we talking about debugging a JIT compiler or about debugging a
> program that uses a JIT compiler? I thought the latter, but it sounds
> like you are talking about the former here.
The line is somewhat blurry, since for our purposes we are trying to
debug Unladen Swallow, which is an interpreter for Python that uses
the LLVM JIT. It's somewhat appropriate to refer to it as a JIT for
Python, but for the purposes of the documentation it's better to be
clear.
>> +generate code at runtime, and GDB normally gets all of its symbols from object
> ^^^
> "@value{GDBN}" (here and elsewhere).
Done.
>> +files.
>
> Does this rewording of the above sentence catch what you meant to say
> (I'm not sure I understood)?
>
> Programs compiled with JIT compilers are normally difficult to debug
> because portions of their code are generated at runtime, and not
> written to object files, where @value{GDBN} normally finds the debug
> information it needs.
>
> The rest of the text needs similar rewording, to make it more clear.
> In particular, you use "JIT" meaning "a JIT compiler" and "executables
> that use JIT" when you really mean "programs that use JIT
> compilation".
I've left the use of JIT as a noun in the later technical part of the
chapter where "JIT" means "the JIT compiler" and not the program using
the JIT. The audience for those sections should be JIT authors, not
JIT users. In other cases I've tried to use "program that uses JIT
compilation" or just "program".
> If you can rephrase the text along the above guidelines, that'd be
> great. If not, go ahead and commit it with the few technical changes
> I mention here, and I will do the rest.
>
>> + In order to debug executables that use JITs, GDB has an interface that
>> +allows the JIT to register in-memory symbol files with GDB at runtime. If you
>> +are using GDB to debug a program using this interface, then it should just work
>> +so long as you have not stripped the binary. If you are developing a JIT, then
>> +the interface is documented in the next section. At this time, the only known
>> +client of this interface is the LLVM JIT.
>
> A question out of ignorance: are the special symbols you use to
> interface with the JIT generated code specific to LLVM, or are they
> part of some broader standard? If the former, the instructions below
> are actually instructions to develop other JIT compilers that follow
> the LLVM conventions, and we are in effect committing GDB to force
> these conventions on developers of JIT compilers, don't we?
If only there were a standard. :) I talked with some GDB developers
here at Google a month and a half ago, and we decided that this would
be a decent interface for GDB, LLVM, and potentially other JIT
compilers out there. There's nothing intended to be LLVM specific
about it. Most of the constraints on the interface come from the GDB
side, since GDB thinks about symbols and debug info mostly in terms of
object files. The symbol names don't actually refer to LLVM or GDB
explicitly, and they are just __jit_debug_register_code and
__jit_debug_descriptor.
The only thing that that the interface forces on JIT authors is that
they need to link in some kind of object file generation like libelf.
LLVM happens to have code for writing ELFs, so it doesn't introduce an
external dependency, it only makes the binary using the JIT a bit
bigger.
If this restriction is a problem for somebody using this interface in
the future, then I think it could be extended to come up with some
kind of symbol "file" format, where the user specifies all of the
section headers, symbols, etc in some standard in memory structure,
and gives pointer/size pairs to the sections. If you've used libelf,
then you can imagine something similar to the in-memory structures
that you use to describe the ELF before writing it. That way they
only need one copy of the code and symbols. This would probably
require implementing a more interesting BFD iovector to read the
structures and follow the pointers, and then maybe even a custom BFD
filetype, which would be a bit of work.
So while I think that would be the ideal because it requires less
effort on the client side, I consider it future work.
>> +@section Using the JIT Interface
>
> Please make each @section also a @node. (This will need a @menu be
> added to the parent chapter.) I would like to avoid sections that are
> not nodes, because nodes make it easier to navigate the manual in a
> structural way.
Sure.
>> +If the JIT recompiles code throughout its lifetime without unregistering it,
>> +then GDB and the JIT will continue to use extra memory for these symbol files.
>
> Does this simply say "don't recompile without unregistering, or you
> will leak memory", or does it say something else?
Yeah, I didn't use the phrase "leak memory" because to some people it
means specifically that the pointers are lost and cannot be freed,
which is not the case. But on second thought, saying "leak memory" is
less confusing and less verbose.
Thanks for the review,
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 18244 bytes --]
2009-07-28 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* doc/gdb.texinfo: Add chapter on JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(allocate_bp_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 28 Jul 2009 21:32:02 -0000
@@ -677,7 +677,8 @@
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -746,7 +747,7 @@
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -828,7 +829,8 @@
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 28 Jul 2009 21:32:02 -0000
@@ -3,6 +3,12 @@
*** Changes since GDB 6.8
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create symbol files in memory and register
+them with GDB. For users, the feature should work transparently, and
+for JIT developers, the interface is documented in the GDB manual in the
+"JIT Compilation Interface" chapter.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 28 Jul 2009 21:32:02 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"
/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@
continue;
}
+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,7 @@
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3307,9 @@
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3325,7 @@
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3346,13 @@
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3356,28 +3370,18 @@
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3390,6 +3394,7 @@
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3460,6 +3465,9 @@
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3601,7 @@
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3721,6 +3730,7 @@
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4372,7 @@
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4655,17 @@
int radix;
};
+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5301,7 @@
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7608,7 @@
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7628,7 @@
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7951,7 @@
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
@@ -7954,6 +7980,8 @@
set_language (save_language);
input_radix = save_input_radix;
+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 28 Jul 2009 21:32:02 -0000
@@ -120,6 +120,9 @@
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 28 Jul 2009 21:32:03 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -544,6 +545,8 @@
solib_create_inferior_hook ();
#endif
+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,22 @@
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 28 Jul 2009 21:32:03 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.608
diff -u -r1.608 gdb.texinfo
--- gdb/doc/gdb.texinfo 20 Jul 2009 18:51:41 -0000 1.608
+++ gdb/doc/gdb.texinfo 28 Jul 2009 21:32:05 -0000
@@ -159,6 +159,7 @@
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
* GDB/MI:: @value{GDBN}'s Machine Interface.
* Annotations:: @value{GDBN}'s annotation interface.
+* JIT Interface:: Using the JIT debugging interface.
* GDB Bugs:: Reporting bugs in @value{GDBN}
@@ -25822,6 +25823,136 @@
followed by one or more lowercase hex digits (note that this does not
depend on the language).
+@node JIT Interface
+@chapter JIT Compilation Interface
+@cindex just-in-time compilation
+@cindex JIT compilation interface
+
+This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT) compilation
+interface. A JIT compiler is a program or library that generates native
+executable code at runtime and executes it, usually in order to achieve good
+performance while maintaining platform independence.
+
+Programs that use JIT compilation are normally difficult to debug because
+portions of their code are generated at runtime, instead of being loaded from
+object files, which is where @value{GDBN} normally finds the program's symbols
+and debug information. In order to debug programs that use JIT compilation,
+@value{GDBN} has an interface that allows the program to register in-memory
+symbol files with @value{GDBN} at runtime.
+
+If you are using @value{GDBN} to debug a program that uses this interface, then
+it should work transparently so long as you have not stripped the binary. If
+you are developing a JIT compiler, then the interface is documented in the rest
+of this chapter. At this time, the only known client of this interface is the
+LLVM JIT.
+
+Broadly speaking, the JIT interface mirrors the dynamic loader interface. The
+JIT compiler communicates with @value{GDBN} by writing data into a global
+variable and calling a fuction at a well-known symbol. When @value{GDBN}
+attaches, it reads a linked list of symbol files from the global variable to
+find existing code, and puts a breakpoint in the function so that it can find
+out about additional code.
+
+@menu
+* Declarations:: Relevant C struct declarations
+* Registering Code:: Steps to register code
+* Unregistering Code:: Steps to unregister code
+@end menu
+
+@node Declarations
+@section JIT Declarations
+
+These are the relevant struct declarations that a C program should include to
+implement the interface:
+
+@smallexample
+typedef enum
+@{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+@} jit_actions_t;
+
+struct jit_code_entry
+@{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+@};
+
+struct jit_descriptor
+@{
+ uint32_t version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+@};
+
+/* GDB puts a breakpoint in this function. */
+void __attribute__((noinline)) __jit_debug_register_code() @{ @};
+
+/* Make sure to specify the version statically, because the
+ debugger may check the version before we can set it. */
+struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @};
+@end smallexample
+
+If the JIT is multi-threaded, then it is important that the JIT synchronize any
+modifications to this global data properly, which can easily be done by putting
+a global mutex around modifications to these structures.
+
+@node Registering Code
+@section Registering Code
+
+To register code with @value{GDBN}, the JIT should follow this protocol:
+
+@itemize @bullet
+@item
+Generate an object file in memory with symbols and other desired debug
+information. The file must include the virtual addresses of the sections.
+
+@item
+Create a code entry for the file, which gives the start and size of the symbol
+file.
+
+@item
+Add it to the linked list in the JIT descriptor.
+
+@item
+Point the relevant_entry field of the descriptor at the entry.
+
+@item
+Set @code{action_flag} to @code{JIT_REGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+When @value{GDBN} is attached and the breakpoint fires, @value{GDBN} uses the
+@code{relevant_entry} pointer so it doesn't have to walk the list looking for
+new code. However, the linked list must still be maintained in order to allow
+@value{GDBN} to attach to a running process and still find the symbol files.
+
+@node Unregistering Code
+@section Unregistering Code
+
+If code is freed, then the JIT should use the following protocol:
+
+@itemize @bullet
+@item
+Remove the code entry corresponding to the code from the linked list.
+
+@item
+Point the @code{relevant_entry} field of the descriptor at the code entry.
+
+@item
+Set @code{action_flag} to @code{JIT_UNREGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+If the JIT frees or recompiles code without unregistering it, then @value{GDBN}
+and the JIT will leak the memory used for the associated symbol files.
+
@node GDB Bugs
@chapter Reporting Bugs in @value{GDBN}
@cindex bugs in @value{GDBN}
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-28 22:23 ` Reid Kleckner
@ 2009-07-29 15:20 ` Eli Zaretskii
0 siblings, 0 replies; 32+ messages in thread
From: Eli Zaretskii @ 2009-07-29 15:20 UTC (permalink / raw)
To: Reid Kleckner; +Cc: tromey, gdb-patches, unladen-swallow
> From: Reid Kleckner <rnk@mit.edu>
> Date: Tue, 28 Jul 2009 14:34:54 -0700
> Cc: tromey@redhat.com, gdb-patches@sourceware.org,
> unladen-swallow@googlegroups.com
>
> Thanks for the review,
Thanks for working on this. The documentation parts of this patch are
good to go.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-25 0:23 ` Reid Kleckner
@ 2009-07-30 16:30 ` Tom Tromey
2009-07-30 16:54 ` Tom Tromey
2009-07-30 21:10 ` Thiago Jung Bauermann
0 siblings, 2 replies; 32+ messages in thread
From: Tom Tromey @ 2009-07-30 16:30 UTC (permalink / raw)
To: Reid Kleckner; +Cc: gdb-patches, unladen-swallow
>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
Tom> Yeah. This would work if you had two JITs in a process, say loaded
Tom> dynamically, and the various __jit symbols had hidden visibility.
Reid> I think if someone has that use case, then they can write a client
Reid> side library that wraps the __jit symbols and make the two JITs call
Reid> that.
Yeah, I changed my mind, let's not worry about it. If it ever comes up,
we can deal with it then without breaking compatibility.
Reid> + /* Remember a mapping from entry_addr to objfile. */
Reid> + set_objfile_data (objfile, jit_objfile_data, (void*) entry_addr);
Tom> I don't think you need the cast here. There are a few of these.
Reid> Actually, it is, because entry_addr is a CORE_ADDR. CORE_ADDR fits
Reid> into a void* right? I'm just doing it to avoid mallocing a single
Reid> CORE_ADDR.
Oh, I see. It is better to just do the allocation; I think we try to
avoid casting between CORE_ADDR and void*.
Reid> + case BPSTAT_WHAT_CHECK_JIT:
Reid> + if (debug_infrun)
Reid> + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
Reid> +
Reid> + /* Switch terminal for any messages produced by breakpoint_re_set. */
Reid> + target_terminal_ours_for_output ();
Is the comment here really valid?
I think it should probably mention jit_event_handler, not
breakpoint_re_set.
Reid> +//#include "assert.h"
Reid> +//#include "string.h"
Just delete these.
(And just FYI, gdb doesn't allow "//" comments.)
Reid> +/* Openning the file is a no-op. */
Typo, should be "opening".
Reid> + /* If we already found the symbols and successfully set the breakpoint, don't
Reid> + do it again. */
Reid> + if (jit_descriptor_addr != 0)
Reid> + return;
I'm sorry I didn't notice this before.
This approach won't work if you have address space randomization enabled
and you re-run the inferior.
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-30 16:30 ` Tom Tromey
@ 2009-07-30 16:54 ` Tom Tromey
2009-08-05 21:05 ` [unladen-swallow] " Reid Kleckner
2009-07-30 21:10 ` Thiago Jung Bauermann
1 sibling, 1 reply; 32+ messages in thread
From: Tom Tromey @ 2009-07-30 16:54 UTC (permalink / raw)
To: Reid Kleckner; +Cc: gdb-patches, unladen-swallow
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
Reid> + if (jit_descriptor_addr != 0)
Reid> + return;
Tom> This approach won't work if you have address space randomization enabled
Tom> and you re-run the inferior.
Oh, never mind. I missed the assignment that resets this when the
inferior exits.
I'll take another look at the patch soon.
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-30 16:30 ` Tom Tromey
2009-07-30 16:54 ` Tom Tromey
@ 2009-07-30 21:10 ` Thiago Jung Bauermann
2009-07-31 18:18 ` Thiago Jung Bauermann
1 sibling, 1 reply; 32+ messages in thread
From: Thiago Jung Bauermann @ 2009-07-30 21:10 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Reid Kleckner, unladen-swallow
Em Quinta-feira 30 Julho 2009 12:12:50 Tom Tromey escreveu:
> >>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>
> Tom> Yeah. This would work if you had two JITs in a process, say loaded
> Tom> dynamically, and the various __jit symbols had hidden visibility.
>
> Reid> I think if someone has that use case, then they can write a client
> Reid> side library that wraps the __jit symbols and make the two JITs call
> Reid> that.
>
> Yeah, I changed my mind, let's not worry about it. If it ever comes up,
> we can deal with it then without breaking compatibility.
If/when someone wants to add another scripting language to GDB, we will be
bitten by this issue. I can see that happening already. :-)
You're saying that the current patch is extensible to deal with that?
Also, changing subjects: it is important that this feature works on core files,
or at least is forward-compatible with such a feature. I think this needs to
be thought through before the patch goes in.
Apologies if any of this has already been answered on the thread or is obvious
to people who have been involved in the thread since the beginning.
--
[]'s
Thiago Jung Bauermann
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-30 21:10 ` Thiago Jung Bauermann
@ 2009-07-31 18:18 ` Thiago Jung Bauermann
2009-07-31 20:31 ` [unladen-swallow] " Reid Kleckner
2009-07-31 20:55 ` Paul Pluzhnikov
0 siblings, 2 replies; 32+ messages in thread
From: Thiago Jung Bauermann @ 2009-07-31 18:18 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey, Reid Kleckner, unladen-swallow
Ok, I had one night's sleep over this, and just re-read the thread. :-)
Em Quinta-feira 30 Julho 2009 15:01:17 Thiago Jung Bauermann escreveu:
> Em Quinta-feira 30 Julho 2009 12:12:50 Tom Tromey escreveu:
> > >>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
> >
> > Tom> Yeah. This would work if you had two JITs in a process, say loaded
> > Tom> dynamically, and the various __jit symbols had hidden visibility.
> >
> > Reid> I think if someone has that use case, then they can write a client
> > Reid> side library that wraps the __jit symbols and make the two JITs
> > call Reid> that.
> >
> > Yeah, I changed my mind, let's not worry about it. If it ever comes up,
> > we can deal with it then without breaking compatibility.
>
> If/when someone wants to add another scripting language to GDB, we will be
> bitten by this issue. I can see that happening already. :-)
I still think this will happen soon enough ...
> You're saying that the current patch is extensible to deal with that?
... but I agree with Reid in that the inferior (or client) can deal with that
case internally (also it seems fair, since it is the client who wanted to have
two JITs in the first place. :-) ). It is nice that the GDB-facing interface is
as simple as possible.
Also the protocol is versioned. That is awesome. I don't have worries
regarding this issue.
> Also, changing subjects: it is important that this feature works on core
> files, or at least is forward-compatible with such a feature. I think this
> needs to be thought through before the patch goes in.
I still think that the patch needs to be tested with a core file scenario
before it is checked in. Since said scenario is similar to attaching to a
running process, it will likely magically work, though.
--
[]'s
Thiago Jung Bauermann
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-07-31 18:18 ` Thiago Jung Bauermann
@ 2009-07-31 20:31 ` Reid Kleckner
2009-08-01 14:43 ` Thiago Jung Bauermann
2009-08-14 19:29 ` Tom Tromey
2009-07-31 20:55 ` Paul Pluzhnikov
1 sibling, 2 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-07-31 20:31 UTC (permalink / raw)
To: unladen-swallow; +Cc: gdb-patches, Tom Tromey
[-- Attachment #1: Type: text/plain, Size: 2517 bytes --]
On Fri, Jul 31, 2009 at 8:01 AM, Thiago Jung
Bauermann<thiago.bauermann@gmail.com> wrote:
>
> Ok, I had one night's sleep over this, and just re-read the thread. :-)
>
> Em Quinta-feira 30 Julho 2009 15:01:17 Thiago Jung Bauermann escreveu:
>> Em Quinta-feira 30 Julho 2009 12:12:50 Tom Tromey escreveu:
>> > >>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>> >
>> > Tom> Yeah. This would work if you had two JITs in a process, say loaded
>> > Tom> dynamically, and the various __jit symbols had hidden visibility.
>> >
>> > Reid> I think if someone has that use case, then they can write a client
>> > Reid> side library that wraps the __jit symbols and make the two JITs
>> > call Reid> that.
>> >
>> > Yeah, I changed my mind, let's not worry about it. If it ever comes up,
>> > we can deal with it then without breaking compatibility.
>>
>> If/when someone wants to add another scripting language to GDB, we will be
>> bitten by this issue. I can see that happening already. :-)
>
> I still think this will happen soon enough ...
>
>> You're saying that the current patch is extensible to deal with that?
>
> ... but I agree with Reid in that the inferior (or client) can deal with that
> case internally (also it seems fair, since it is the client who wanted to have
> two JITs in the first place. :-) ). It is nice that the GDB-facing interface is
> as simple as possible.
>
> Also the protocol is versioned. That is awesome. I don't have worries
> regarding this issue.
Great, I really didn't want to tackle that issue from the GDB side. :)
>> Also, changing subjects: it is important that this feature works on core
>> files, or at least is forward-compatible with such a feature. I think this
>> needs to be thought through before the patch goes in.
>
> I still think that the patch needs to be tested with a core file scenario
> before it is checked in. Since said scenario is similar to attaching to a
> running process, it will likely magically work, though.
Well, it didn't magically work because the jit_inferior_create_hook
was getting called twice for core files instead of once. What would
happen is that the binary would get loaded, the hook called, there
would be no debug info, the memory would get loaded, it would get
called again, and it would think that it already read the descriptor.
So I made it look for new code every time just in case, and now it
works.
Here's an updated patch. What else needs to be dealt with?
Reid
[-- Attachment #2: jit-patch.txt --]
[-- Type: text/plain, Size: 36129 bytes --]
2009-07-28 Reid Kleckner <reid@kleckner.net>
Add interface for JIT code generation.
* NEWS: Announce JIT interface.
* doc/gdb.texinfo: Add chapter on JIT interface.
* Makefile.in (SFILES): Add jit.c.
(HFILES_NO_SRCDIR): Add jit.h.
(COMMON_OBS): Add jit.o.
* jit.c: New file.
* jit.h: New file.
* breakpoint.h:
(enum bptype): Add bp_jit_event to enum.
* breakpoint.c:
(update_breakpoints_after_exec): Delete jit breakpoints after exec.
(bpstat_what): Update event table for bp_jit_event.
(print_it_typical): Added case for bp_jit_event.
(print_one_breakpoint_location): Added case for bp_jit_event.
(allocate_bp_location): Added case for bp_jit_event.
(mention): Added case for bp_jit_event.
(delete_command): Added case for bp_jit_event.
(breakpoint_re_set_one): Added case for bp_jit_event.
(breakpoint_re_set): Added call to jit_inferior_created_hook.
(create_jit_event_breakpoint): New.
* infrun.c (handle_inferior_event): Add handler for jit event.
(follow_exec): Add call to jit_inferior_created_hook.
* objfiles.c (free_objfile): Fixed a memory leak.
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1091
diff -u -p -u -r1.1091 Makefile.in
--- gdb/Makefile.in 3 Jul 2009 12:06:35 -0000 1.1091
+++ gdb/Makefile.in 31 Jul 2009 18:03:35 -0000
@@ -677,7 +677,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
wrapper.c \
xml-tdesc.c xml-support.c \
inferior.c gdb_usleep.c \
- record.c
+ record.c \
+ jit.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -746,7 +747,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \
sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \
-gdb_usleep.h
+gdb_usleep.h jit.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -828,7 +829,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o
+ inferior.o osdata.o gdb_usleep.o record.o \
+ jit.o
TSOBS = inflow.o
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.319
diff -u -p -u -r1.319 NEWS
--- gdb/NEWS 20 Jul 2009 18:51:41 -0000 1.319
+++ gdb/NEWS 31 Jul 2009 18:03:35 -0000
@@ -3,6 +3,12 @@
*** Changes since GDB 6.8
+* GDB now has an interface for JIT compilation. Applications that
+dynamically generate code can create symbol files in memory and register
+them with GDB. For users, the feature should work transparently, and
+for JIT developers, the interface is documented in the GDB manual in the
+"JIT Compilation Interface" chapter.
+
* Tracepoints may now be conditional. The syntax is as for
breakpoints; either an "if" clause appended to the "trace" command,
or the "condition" command is available. GDB sends the condition to
Index: gdb/breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.413
diff -u -p -u -r1.413 breakpoint.c
--- gdb/breakpoint.c 7 Jul 2009 22:21:09 -0000 1.413
+++ gdb/breakpoint.c 31 Jul 2009 18:03:35 -0000
@@ -59,6 +59,7 @@
#include "top.h"
#include "wrapper.h"
#include "valprint.h"
+#include "jit.h"
/* readline include files */
#include "readline/readline.h"
@@ -1575,6 +1576,13 @@ update_breakpoints_after_exec (void)
continue;
}
+ /* JIT breakpoints must be explicitly reset after an exec(). */
+ if (b->type == bp_jit_event)
+ {
+ delete_breakpoint (b);
+ continue;
+ }
+
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
@@ -2573,6 +2581,7 @@ print_it_typical (bpstat bs)
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_tracepoint:
+ case bp_jit_event:
default:
result = PRINT_UNKNOWN;
break;
@@ -3298,6 +3307,9 @@ bpstat_what (bpstat bs)
/* We hit the shared library event breakpoint. */
shlib_event,
+ /* We hit the jit event breakpoint. */
+ jit_event,
+
/* This is just used to count how many enums there are. */
class_last
};
@@ -3313,6 +3325,7 @@ bpstat_what (bpstat bs)
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
#define sr BPSTAT_WHAT_STEP_RESUME
#define shl BPSTAT_WHAT_CHECK_SHLIBS
+#define jit BPSTAT_WHAT_CHECK_JIT
/* "Can't happen." Might want to print an error message.
abort() is not out of the question, but chances are GDB is just
@@ -3333,12 +3346,13 @@ bpstat_what (bpstat bs)
back and decide something of a lower priority is better. The
ordering is:
- kc < clr sgl shl slr sn sr ss
- sgl < shl slr sn sr ss
- slr < err shl sn sr ss
- clr < err shl sn sr ss
- ss < shl sn sr
- sn < shl sr
+ kc < jit clr sgl shl slr sn sr ss
+ sgl < jit shl slr sn sr ss
+ slr < jit err shl sn sr ss
+ clr < jit err shl sn sr ss
+ ss < jit shl sn sr
+ sn < jit shl sr
+ jit < shl sr
shl < sr
sr <
@@ -3356,28 +3370,18 @@ bpstat_what (bpstat bs)
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
{
/* old action */
- /* kc ss sn sgl slr clr sr shl
- */
-/*no_effect */
- {kc, ss, sn, sgl, slr, clr, sr, shl},
-/*wp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*wp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*bp_nostop */
- {sgl, ss, sn, sgl, slr, slr, sr, shl},
-/*bp_silent */
- {ss, ss, sn, ss, ss, ss, sr, shl},
-/*bp_noisy */
- {sn, sn, sn, sn, sn, sn, sr, shl},
-/*long_jump */
- {slr, ss, sn, slr, slr, err, sr, shl},
-/*long_resume */
- {clr, ss, sn, err, err, err, sr, shl},
-/*step_resume */
- {sr, sr, sr, sr, sr, sr, sr, sr},
-/*shlib */
- {shl, shl, shl, shl, shl, shl, sr, shl}
+ /* kc ss sn sgl slr clr sr shl jit */
+/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
+/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
+/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
+/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
+/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
+/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
+/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
+/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
+/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
};
#undef kc
@@ -3390,6 +3394,7 @@ bpstat_what (bpstat bs)
#undef sr
#undef ts
#undef shl
+#undef jit
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
struct bpstat_what retval;
@@ -3460,6 +3465,9 @@ bpstat_what (bpstat bs)
case bp_shlib_event:
bs_class = shlib_event;
break;
+ case bp_jit_event:
+ bs_class = jit_event;
+ break;
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
@@ -3593,6 +3601,7 @@ print_one_breakpoint_location (struct br
{bp_longjmp_master, "longjmp master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
+ {bp_jit_event, "jit events"},
};
static char bpenables[] = "nynny";
@@ -3721,6 +3730,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_tracepoint:
+ case bp_jit_event:
if (opts.addressprint)
{
annotate_field (4);
@@ -4362,6 +4372,7 @@ allocate_bp_location (struct breakpoint
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
@@ -4644,6 +4655,17 @@ struct lang_and_radix
int radix;
};
+/* Create a breakpoint for JIT code registration and unregistration. */
+
+struct breakpoint *
+create_jit_event_breakpoint (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (gdbarch, address, bp_jit_event);
+ update_global_location_list_nothrow (1);
+ return b;
+}
void
remove_solib_event_breakpoints (void)
@@ -5279,6 +5301,7 @@ mention (struct breakpoint *b)
case bp_shlib_event:
case bp_thread_event:
case bp_overlay_event:
+ case bp_jit_event:
case bp_longjmp_master:
break;
}
@@ -7585,6 +7608,7 @@ delete_command (char *arg, int from_tty)
{
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
+ && b->type != bp_jit_event
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
@@ -7604,6 +7628,7 @@ delete_command (char *arg, int from_tty)
if (b->type != bp_call_dummy
&& b->type != bp_shlib_event
&& b->type != bp_thread_event
+ && b->type != bp_jit_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->number >= 0)
@@ -7926,6 +7951,7 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_jit_event:
break;
}
@@ -7954,6 +7980,8 @@ breakpoint_re_set (void)
set_language (save_language);
input_radix = save_input_radix;
+ jit_inferior_created_hook ();
+
create_overlay_event_breakpoint ("_ovly_debug_event");
create_longjmp_master_breakpoint ("longjmp");
create_longjmp_master_breakpoint ("_longjmp");
Index: gdb/breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.95
diff -u -p -u -r1.95 breakpoint.h
--- gdb/breakpoint.h 2 Jul 2009 17:12:24 -0000 1.95
+++ gdb/breakpoint.h 31 Jul 2009 18:03:35 -0000
@@ -120,6 +120,9 @@ enum bptype
bp_catchpoint,
bp_tracepoint,
+
+ /* Event for JIT compiled code generation or deletion. */
+ bp_jit_event,
};
/* States of enablement of breakpoint. */
@@ -548,6 +551,9 @@ enum bpstat_what_main_action
keep checking. */
BPSTAT_WHAT_CHECK_SHLIBS,
+ /* Check for new JITed code. */
+ BPSTAT_WHAT_CHECK_JIT,
+
/* This is just used to keep track of how many enums there are. */
BPSTAT_WHAT_LAST
};
@@ -841,6 +847,9 @@ extern void mark_breakpoints_out (void);
extern void make_breakpoint_permanent (struct breakpoint *);
+extern struct breakpoint *create_jit_event_breakpoint (struct gdbarch *,
+ CORE_ADDR);
+
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
CORE_ADDR);
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.402
diff -u -p -u -r1.402 infrun.c
--- gdb/infrun.c 20 Jul 2009 15:05:12 -0000 1.402
+++ gdb/infrun.c 31 Jul 2009 18:03:36 -0000
@@ -50,6 +50,7 @@
#include "event-top.h"
#include "record.h"
#include "inline-frame.h"
+#include "jit.h"
/* Prototypes for local functions */
@@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pat
solib_create_inferior_hook ();
#endif
+ jit_inferior_created_hook ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
@@ -3529,6 +3532,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
}
break;
+ case BPSTAT_WHAT_CHECK_JIT:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
+
+ /* Switch terminal for any messages produced by breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+
+ jit_event_handler ();
+
+ target_terminal_inferior ();
+
+ /* We want to step over this breakpoint, then keep going. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
Index: gdb/jit.c
===================================================================
RCS file: gdb/jit.c
diff -N gdb/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.c 31 Jul 2009 18:03:36 -0000
@@ -0,0 +1,430 @@
+/* Handle JIT code generation in the inferior for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "jit.h"
+#include "breakpoint.h"
+#include "gdbcore.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "target.h"
+#include "gdb_stat.h"
+
+static const struct objfile_data *jit_objfile_data;
+
+static const char *const jit_break_name = "__jit_debug_register_code";
+
+static const char *const jit_descriptor_name = "__jit_debug_descriptor";
+
+/* This is the address of the JIT descriptor in the inferior. */
+
+static CORE_ADDR jit_descriptor_addr = 0;
+
+struct target_buffer
+{
+ CORE_ADDR base;
+ size_t size;
+};
+
+/* Openning the file is a no-op. */
+
+static void *
+mem_bfd_iovec_open (struct bfd *abfd, void *open_closure)
+{
+ return open_closure;
+}
+
+/* Closing the file is just freeing the base/size pair on our side. */
+
+static int
+mem_bfd_iovec_close (struct bfd *abfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+/* For reading the file, we just need to pass through to target_read_memory and
+ fix up the arguments and return values. */
+
+static file_ptr
+mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ int err;
+ struct target_buffer *buffer = (struct target_buffer *) stream;
+
+ /* If this read will read all of the file, limit it to just the rest. */
+ if (offset + nbytes > buffer->size)
+ nbytes = buffer->size - offset;
+
+ /* If there are no more bytes left, we've reached EOF. */
+ if (nbytes == 0)
+ return 0;
+
+ err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes);
+ if (err)
+ return -1;
+
+ return nbytes;
+}
+
+/* For statting the file, we only support the st_size attribute. */
+
+static int
+mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
+{
+ struct target_buffer *buffer = (struct target_buffer*) stream;
+
+ sb->st_size = buffer->size;
+ return 0;
+}
+
+/* Open a BFD from the target's memory. */
+
+static struct bfd *
+bfd_open_from_target_memory (CORE_ADDR addr, size_t size, char *target)
+{
+ const char *filename = xstrdup ("<in-memory>");
+ struct target_buffer *buffer = xmalloc (sizeof (struct target_buffer));
+
+ buffer->base = addr;
+ buffer->size = size;
+ return bfd_openr_iovec (filename, target,
+ mem_bfd_iovec_open,
+ buffer,
+ mem_bfd_iovec_pread,
+ mem_bfd_iovec_close,
+ mem_bfd_iovec_stat);
+}
+
+/* Helper function for reading the global JIT descriptor from remote memory. */
+
+static void
+jit_read_descriptor (struct jit_descriptor *descriptor)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int desc_size;
+ gdb_byte *desc_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the descriptor is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ desc_size = 8 + 2 * ptr_size; /* Two 32-bit ints and two pointers. */
+ desc_buf = alloca (desc_size);
+
+ /* Read the descriptor. */
+ err = target_read_memory (jit_descriptor_addr, desc_buf, desc_size);
+ if (err)
+ error (_("Unable to read JIT descriptor from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ descriptor->version = extract_unsigned_integer (&desc_buf[0], 4, byte_order);
+ descriptor->action_flag =
+ extract_unsigned_integer (&desc_buf[4], 4, byte_order);
+ descriptor->relevant_entry = extract_typed_address (&desc_buf[8], ptr_type);
+ descriptor->first_entry =
+ extract_typed_address (&desc_buf[8 + ptr_size], ptr_type);
+}
+
+/* Helper function for reading a JITed code entry from remote memory. */
+
+static void
+jit_read_code_entry (CORE_ADDR code_addr, struct jit_code_entry *code_entry)
+{
+ int err;
+ struct type *ptr_type;
+ int ptr_size;
+ int entry_size;
+ gdb_byte *entry_buf;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ /* Figure out how big the entry is on the remote and how to read it. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ ptr_size = TYPE_LENGTH (ptr_type);
+ entry_size = 3 * ptr_size + 8; /* Three pointers and one 64-bit int. */
+ entry_buf = alloca (entry_size);
+
+ /* Read the entry. */
+ err = target_read_memory (code_addr, entry_buf, entry_size);
+ if (err)
+ error (_("Unable to read JIT code entry from remote memory!"));
+
+ /* Fix the endianness to match the host. */
+ ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr;
+ code_entry->next_entry = extract_typed_address (&entry_buf[0], ptr_type);
+ code_entry->prev_entry =
+ extract_typed_address (&entry_buf[ptr_size], ptr_type);
+ code_entry->symfile_addr =
+ extract_typed_address (&entry_buf[2 * ptr_size], ptr_type);
+ code_entry->symfile_size =
+ extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
+}
+
+/* This function registers code associated with a JIT code entry. It uses the
+ pointer and size pair in the entry to read the symbol file from the remote
+ and then calls symbol_file_add_from_local_memory to add it as though it were
+ a symbol file added by the user. */
+
+static void
+jit_register_code (CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
+{
+ bfd *nbfd;
+ struct section_addr_info *sai;
+ struct bfd_section *sec;
+ struct objfile *objfile;
+ struct cleanup *old_cleanups;
+ int i;
+ const struct bfd_arch_info *b;
+ CORE_ADDR *entry_addr_ptr;
+
+ nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+ code_entry->symfile_size, gnutarget);
+ old_cleanups = make_cleanup_bfd_close (nbfd);
+
+ /* Check the format. NOTE: This initializes important data that GDB uses!
+ We would segfault later without this line. */
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ printf_unfiltered (_("\
+JITed symbol file is not an object file, ignoring it.\n"));
+ do_cleanups (old_cleanups);
+ return;
+ }
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (target_gdbarch);
+ if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+ warning (_("JITed object file architecture %s is not compatible "
+ "with target architecture %s."), bfd_get_arch_info
+ (nbfd)->printable_name, b->printable_name);
+
+ /* Read the section address information out of the symbol file. Since the
+ file is generated by the JIT at runtime, it should all of the absolute
+ addresses that we care about. */
+ sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+ make_cleanup_free_section_addr_info (sai);
+ i = 0;
+ for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+ if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+ {
+ /* We assume that these virtual addresses are absolute, and do not
+ treat them as offsets. */
+ sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+ sai->other[i].name = (char *) bfd_get_section_name (nbfd, sec);
+ sai->other[i].sectindex = sec->index;
+ ++i;
+ }
+
+ /* This call takes ownership of sai. */
+ objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
+
+ /* Remember a mapping from entry_addr to objfile. */
+ entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
+ *entry_addr_ptr = entry_addr;
+ set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
+
+ discard_cleanups (old_cleanups);
+}
+
+/* This function unregisters JITed code and frees the corresponding objfile. */
+
+static void
+jit_unregister_code (struct objfile *objfile)
+{
+ free_objfile (objfile);
+}
+
+/* Helper cleanup function to clear an integer flag. */
+
+void
+clear_int (void *int_addr)
+{
+ *((int *) int_addr) = 0;
+}
+
+/* Look up the objfile with this code entry address. */
+
+static struct objfile *
+jit_find_objf_with_entry_addr (CORE_ADDR entry_addr)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+ CORE_ADDR *objf_entry_addr;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ {
+ objf_entry_addr = (CORE_ADDR *) objfile_data (objf, jit_objfile_data);
+ if (objf_entry_addr != NULL && *objf_entry_addr == entry_addr)
+ return objf;
+ }
+ return NULL;
+}
+
+void
+jit_inferior_created_hook (void)
+{
+ struct minimal_symbol *reg_symbol;
+ struct minimal_symbol *desc_symbol;
+ CORE_ADDR reg_addr;
+ struct jit_descriptor descriptor;
+ struct jit_code_entry cur_entry;
+ CORE_ADDR cur_entry_addr;
+ static int already_entered;
+ struct cleanup *old_cleanups;
+
+ /* When we register code down there, GDB resets its breakpoints in case
+ symbols have changed. That in turn calls this handler, which makes us look
+ for new code again. There's nothing wrong with that because we won't find
+ any new code, but it makes an O(n^4) loop. Therefore we do nothing if
+ we're re-entered. */
+ if (already_entered)
+ return;
+ already_entered = 1;
+ old_cleanups = make_cleanup (clear_int, &already_entered);
+
+ /* Lookup the registration symbol. If it is missing, then we assume we are
+ not attached to a JIT. */
+ reg_symbol = lookup_minimal_symbol (jit_break_name, NULL, NULL);
+ if (reg_symbol == NULL)
+ return;
+ reg_addr = SYMBOL_VALUE_ADDRESS (reg_symbol);
+ if (reg_addr == 0)
+ return;
+
+ /* Lookup the descriptor symbol and cache the addr. If it is missing, we
+ assume we are not attached to a JIT and return early. */
+ desc_symbol = lookup_minimal_symbol (jit_descriptor_name, NULL, NULL);
+ if (desc_symbol == NULL)
+ return;
+ jit_descriptor_addr = SYMBOL_VALUE_ADDRESS (desc_symbol);
+ if (jit_descriptor_addr == 0)
+ return;
+
+ /* Read the descriptor so we can check the version number and load any already
+ JITed functions. */
+ jit_read_descriptor (&descriptor);
+
+ /* Check that the version number agrees with that we support. */
+ if (descriptor.version != 1)
+ error (_("Unsupported JIT protocol version in descriptor!"));
+
+ /* Put a breakpoint in the registration symbol. */
+ create_jit_event_breakpoint (target_gdbarch, reg_addr);
+
+ /* If we've attached to a running program, we need to check the descriptor to
+ register any functions that were already generated. */
+ for (cur_entry_addr = descriptor.first_entry;
+ cur_entry_addr != 0;
+ cur_entry_addr = cur_entry.next_entry)
+ {
+ jit_read_code_entry (cur_entry_addr, &cur_entry);
+
+ /* This hook may be called many times during setup, so make sure we don't
+ add the same symbol file twice. */
+ if (jit_find_objf_with_entry_addr (cur_entry_addr) != NULL)
+ continue;
+
+ jit_register_code (cur_entry_addr, &cur_entry);
+ }
+
+ /* Clear the already_entered flag. */
+ do_cleanups (old_cleanups);
+}
+
+/* Wrapper to match the observer function pointer prototype. */
+
+static void
+jit_inferior_created_hook1 (struct target_ops *objfile, int from_tty)
+{
+ jit_inferior_created_hook ();
+}
+
+/* This function cleans up any code entries left over when the inferior exits.
+ We get left over code when the inferior exits without unregistering its code,
+ for example when it crashes. */
+
+static void
+jit_inferior_exit_hook (int pid)
+{
+ struct objfile *objf;
+ struct objfile *temp;
+
+ /* We need to reset the descriptor addr so that next time we load up the
+ inferior we look for it again. */
+ jit_descriptor_addr = 0;
+
+ ALL_OBJFILES_SAFE (objf, temp)
+ if (objfile_data (objf, jit_objfile_data) != NULL)
+ jit_unregister_code (objf);
+}
+
+void
+jit_event_handler (void)
+{
+ struct jit_descriptor descriptor;
+ struct jit_code_entry code_entry;
+ CORE_ADDR entry_addr;
+ struct objfile *objf;
+
+ /* Read the descriptor from remote memory. */
+ jit_read_descriptor (&descriptor);
+ entry_addr = descriptor.relevant_entry;
+
+ /* Do the corresponding action. */
+ switch (descriptor.action_flag)
+ {
+ case JIT_NOACTION:
+ break;
+ case JIT_REGISTER:
+ jit_read_code_entry (entry_addr, &code_entry);
+ jit_register_code (entry_addr, &code_entry);
+ break;
+ case JIT_UNREGISTER:
+ objf = jit_find_objf_with_entry_addr (entry_addr);
+ if (objf == NULL)
+ printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
+ (void *) entry_addr);
+ else
+ jit_unregister_code (objf);
+
+ break;
+ default:
+ error (_("Unknown action_flag value in JIT descriptor!"));
+ break;
+ }
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+
+extern void _initialize_jit (void);
+
+void
+_initialize_jit (void)
+{
+ observer_attach_inferior_created (jit_inferior_created_hook1);
+ observer_attach_inferior_exit (jit_inferior_exit_hook);
+ jit_objfile_data = register_objfile_data ();
+}
Index: gdb/jit.h
===================================================================
RCS file: gdb/jit.h
diff -N gdb/jit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/jit.h 31 Jul 2009 18:03:36 -0000
@@ -0,0 +1,77 @@
+/* JIT declarations for GDB, the GNU Debugger.
+
+ Copyright (C) 2009
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef JIT_H
+#define JIT_H
+
+/* When the JIT breakpoint fires, the inferior wants us to take one of these
+ actions. These values are used by the inferior, so the values of these enums
+ cannot be changed. */
+
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER,
+ JIT_UNREGISTER
+} jit_actions_t;
+
+/* This struct describes a single symbol file in a linked list of symbol files
+ describing generated code. As the inferior generates code, it adds these
+ entries to the list, and when we attach to the inferior, we read them all.
+ For the first element prev_entry should be NULL, and for the last element
+ next_entry should be NULL. */
+
+struct jit_code_entry
+{
+ CORE_ADDR next_entry;
+ CORE_ADDR prev_entry;
+ CORE_ADDR symfile_addr;
+ uint64_t symfile_size;
+};
+
+/* This is the global descriptor that the inferior uses to communicate
+ information to the debugger. To alert the debugger to take an action, the
+ inferior sets the action_flag to the appropriate enum value, updates
+ relevant_entry to point to the relevant code entry, and calls the function at
+ the well-known symbol with our breakpoint. We then read this descriptor from
+ another global well-known symbol. */
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This should be jit_actions_t, but we want to be specific about the
+ bit-width. */
+ uint32_t action_flag;
+ CORE_ADDR relevant_entry;
+ CORE_ADDR first_entry;
+};
+
+/* Looks for the descriptor and registration symbols and breakpoints the
+ registration function. If it finds both, it registers all the already JITed
+ code. If it has already found the symbols, then it doesn't try again. */
+
+extern void jit_inferior_created_hook (void);
+
+/* This function is called by handle_inferior_event when it decides that the JIT
+ event breakpoint has fired. */
+
+extern void jit_event_handler (void);
+
+#endif /* JIT_H */
Index: gdb/objfiles.c
===================================================================
RCS file: /cvs/src/src/gdb/objfiles.c,v
retrieving revision 1.87
diff -u -p -u -r1.87 objfiles.c
--- gdb/objfiles.c 22 Jul 2009 19:21:31 -0000 1.87
+++ gdb/objfiles.c 31 Jul 2009 18:03:36 -0000
@@ -25,6 +25,7 @@
#include "defs.h"
#include "bfd.h" /* Binary File Description */
+#include "libbfd.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
@@ -438,9 +439,19 @@ free_objfile (struct objfile *objfile)
if (objfile->obfd != NULL && !(objfile->flags & OBJF_KEEPBFD))
{
char *name = bfd_get_filename (objfile->obfd);
+ struct bfd_in_memory *bim = NULL;
+ /* Hack to work around the fact that BFD does not take ownership of the
+ memory for files allocated in memory. */
+ if (objfile->obfd->flags & BFD_IN_MEMORY)
+ bim = (struct bfd_in_memory *) objfile->obfd->iostream;
if (!bfd_close (objfile->obfd))
warning (_("cannot close \"%s\": %s"),
name, bfd_errmsg (bfd_get_error ()));
+ if (bim != NULL)
+ {
+ xfree (bim->buffer);
+ xfree (bim);
+ }
xfree (name);
}
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.608
diff -u -p -u -r1.608 gdb.texinfo
--- gdb/doc/gdb.texinfo 20 Jul 2009 18:51:41 -0000 1.608
+++ gdb/doc/gdb.texinfo 31 Jul 2009 18:03:38 -0000
@@ -159,6 +159,7 @@ software in general. We will miss him.
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
* GDB/MI:: @value{GDBN}'s Machine Interface.
* Annotations:: @value{GDBN}'s annotation interface.
+* JIT Interface:: Using the JIT debugging interface.
* GDB Bugs:: Reporting bugs in @value{GDBN}
@@ -25822,6 +25823,136 @@ source which is being displayed. @var{a
followed by one or more lowercase hex digits (note that this does not
depend on the language).
+@node JIT Interface
+@chapter JIT Compilation Interface
+@cindex just-in-time compilation
+@cindex JIT compilation interface
+
+This chapter documents @value{GDBN}'s @dfn{just-in-time} (JIT) compilation
+interface. A JIT compiler is a program or library that generates native
+executable code at runtime and executes it, usually in order to achieve good
+performance while maintaining platform independence.
+
+Programs that use JIT compilation are normally difficult to debug because
+portions of their code are generated at runtime, instead of being loaded from
+object files, which is where @value{GDBN} normally finds the program's symbols
+and debug information. In order to debug programs that use JIT compilation,
+@value{GDBN} has an interface that allows the program to register in-memory
+symbol files with @value{GDBN} at runtime.
+
+If you are using @value{GDBN} to debug a program that uses this interface, then
+it should work transparently so long as you have not stripped the binary. If
+you are developing a JIT compiler, then the interface is documented in the rest
+of this chapter. At this time, the only known client of this interface is the
+LLVM JIT.
+
+Broadly speaking, the JIT interface mirrors the dynamic loader interface. The
+JIT compiler communicates with @value{GDBN} by writing data into a global
+variable and calling a fuction at a well-known symbol. When @value{GDBN}
+attaches, it reads a linked list of symbol files from the global variable to
+find existing code, and puts a breakpoint in the function so that it can find
+out about additional code.
+
+@menu
+* Declarations:: Relevant C struct declarations
+* Registering Code:: Steps to register code
+* Unregistering Code:: Steps to unregister code
+@end menu
+
+@node Declarations
+@section JIT Declarations
+
+These are the relevant struct declarations that a C program should include to
+implement the interface:
+
+@smallexample
+typedef enum
+@{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+@} jit_actions_t;
+
+struct jit_code_entry
+@{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+@};
+
+struct jit_descriptor
+@{
+ uint32_t version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+@};
+
+/* GDB puts a breakpoint in this function. */
+void __attribute__((noinline)) __jit_debug_register_code() @{ @};
+
+/* Make sure to specify the version statically, because the
+ debugger may check the version before we can set it. */
+struct jit_descriptor __jit_debug_descriptor = @{ 1, 0, 0, 0 @};
+@end smallexample
+
+If the JIT is multi-threaded, then it is important that the JIT synchronize any
+modifications to this global data properly, which can easily be done by putting
+a global mutex around modifications to these structures.
+
+@node Registering Code
+@section Registering Code
+
+To register code with @value{GDBN}, the JIT should follow this protocol:
+
+@itemize @bullet
+@item
+Generate an object file in memory with symbols and other desired debug
+information. The file must include the virtual addresses of the sections.
+
+@item
+Create a code entry for the file, which gives the start and size of the symbol
+file.
+
+@item
+Add it to the linked list in the JIT descriptor.
+
+@item
+Point the relevant_entry field of the descriptor at the entry.
+
+@item
+Set @code{action_flag} to @code{JIT_REGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+When @value{GDBN} is attached and the breakpoint fires, @value{GDBN} uses the
+@code{relevant_entry} pointer so it doesn't have to walk the list looking for
+new code. However, the linked list must still be maintained in order to allow
+@value{GDBN} to attach to a running process and still find the symbol files.
+
+@node Unregistering Code
+@section Unregistering Code
+
+If code is freed, then the JIT should use the following protocol:
+
+@itemize @bullet
+@item
+Remove the code entry corresponding to the code from the linked list.
+
+@item
+Point the @code{relevant_entry} field of the descriptor at the code entry.
+
+@item
+Set @code{action_flag} to @code{JIT_UNREGISTER} and call
+@code{__jit_debug_register_code}.
+@end itemize
+
+If the JIT frees or recompiles code without unregistering it, then @value{GDBN}
+and the JIT will leak the memory used for the associated symbol files.
+
@node GDB Bugs
@chapter Reporting Bugs in @value{GDBN}
@cindex bugs in @value{GDBN}
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [RFA] Add interface for registering JITed code
2009-07-31 18:18 ` Thiago Jung Bauermann
2009-07-31 20:31 ` [unladen-swallow] " Reid Kleckner
@ 2009-07-31 20:55 ` Paul Pluzhnikov
1 sibling, 0 replies; 32+ messages in thread
From: Paul Pluzhnikov @ 2009-07-31 20:55 UTC (permalink / raw)
To: Thiago Jung Bauermann
Cc: gdb-patches, Tom Tromey, Reid Kleckner, unladen-swallow
On Fri, Jul 31, 2009 at 8:01 AM, Thiago Jung
Bauermann<thiago.bauermann@gmail.com> wrote:
>> If/when someone wants to add another scripting language to GDB, we will be
>> bitten by this issue. I can see that happening already. :-)
>
> I still think this will happen soon enough ...
If I understand correctly what you mean, you aren't worried about adding a
new scripting language to GDB (as in, embed 'Tcl' in addition to 'Python'
into GDB), but rather about debugging inferior with two (or more) separate
JITters in it.
If I understand the patch, proposed GDB/JIT interface allows this: there
could be several (local) "__jit_debug_register_code" symbols in a
statically-linked executable, and several (possibly global)
"__jit_debug_register_code" symbols if JITters are in separate shared libs.
When this situation arises, GDB could be fixed to set a breakpoint on all
such symbols, and read data from all "__jit_debug_descriptor" symbols.
Once that's done, debugging inferior with multiple JITters will "Just
Work(TM)".
What did I miss?
Thanks,
--
Paul Pluzhnikov
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-07-31 20:31 ` [unladen-swallow] " Reid Kleckner
@ 2009-08-01 14:43 ` Thiago Jung Bauermann
2009-08-14 19:29 ` Tom Tromey
1 sibling, 0 replies; 32+ messages in thread
From: Thiago Jung Bauermann @ 2009-08-01 14:43 UTC (permalink / raw)
To: gdb-patches; +Cc: Reid Kleckner, unladen-swallow, Tom Tromey
Em Sexta-feira 31 Julho 2009 15:17:24 Reid Kleckner escreveu:
> On Fri, Jul 31, 2009 at 8:01 AM, Thiago Jung
> > I still think that the patch needs to be tested with a core file scenario
> > before it is checked in. Since said scenario is similar to attaching to a
> > running process, it will likely magically work, though.
>
> Well, it didn't magically work because the jit_inferior_create_hook
> was getting called twice for core files instead of once. What would
> happen is that the binary would get loaded, the hook called, there
> would be no debug info, the memory would get loaded, it would get
> called again, and it would think that it already read the descriptor.
> So I made it look for new code every time just in case, and now it
> works.
That's great. Thanks for fixing this issue.
> Here's an updated patch. What else needs to be dealt with?
Looks like it just needs the blessing from the GDB powers that be, now.
--
[]'s
Thiago Jung Bauermann
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-07-30 16:54 ` Tom Tromey
@ 2009-08-05 21:05 ` Reid Kleckner
0 siblings, 0 replies; 32+ messages in thread
From: Reid Kleckner @ 2009-08-05 21:05 UTC (permalink / raw)
To: tromey; +Cc: Reid Kleckner, gdb-patches, unladen-swallow
Ping? Please take a look!
Thanks,
Reid
On Thu, Jul 30, 2009 at 8:15 AM, Tom Tromey<tromey@redhat.com> wrote:
>
>
>>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
>
> Reid> + if (jit_descriptor_addr != 0)
> Reid> + return;
>
> Tom> This approach won't work if you have address space randomization enabled
> Tom> and you re-run the inferior.
>
> Oh, never mind. I missed the assignment that resets this when the
> inferior exits.
>
> I'll take another look at the patch soon.
>
> Tom
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-07-31 20:31 ` [unladen-swallow] " Reid Kleckner
2009-08-01 14:43 ` Thiago Jung Bauermann
@ 2009-08-14 19:29 ` Tom Tromey
2009-08-14 23:37 ` Reid Kleckner
1 sibling, 1 reply; 32+ messages in thread
From: Tom Tromey @ 2009-08-14 19:29 UTC (permalink / raw)
To: Reid Kleckner; +Cc: unladen-swallow, gdb-patches
>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
Reid> Here's an updated patch. What else needs to be dealt with?
Nothing. I think this is ok.
I read it a few more times and nothing popped out at me.
Reid> + /* Hack to work around the fact that BFD does not take ownership of the
Reid> + memory for files allocated in memory. */
Reid> + if (objfile->obfd->flags & BFD_IN_MEMORY)
Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
What is the status of the BFD patch to fix this?
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-14 19:29 ` Tom Tromey
@ 2009-08-14 23:37 ` Reid Kleckner
2009-08-17 15:31 ` Tom Tromey
0 siblings, 1 reply; 32+ messages in thread
From: Reid Kleckner @ 2009-08-14 23:37 UTC (permalink / raw)
To: tromey; +Cc: Reid Kleckner, unladen-swallow, gdb-patches
On Fri, Aug 14, 2009 at 12:27 PM, Tom Tromey<tromey@redhat.com> wrote:
>
>
>>>>>> "Reid" == Reid Kleckner <rnk@mit.edu> writes:
>
> Reid> Here's an updated patch. What else needs to be dealt with?
>
> Nothing. I think this is ok.
>
> I read it a few more times and nothing popped out at me.
Is it OK if Doug commits it then?
> Reid> + /* Hack to work around the fact that BFD does not take ownership of the
> Reid> + memory for files allocated in memory. */
> Reid> + if (objfile->obfd->flags & BFD_IN_MEMORY)
> Reid> + bim = (struct bfd_in_memory *) objfile->obfd->iostream;
>
> What is the status of the BFD patch to fix this?
Doug has permission to remove the leak from BFD, so I think I'll
remove this hack from my patch to reduce churn.
Thanks,
Reid
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-14 23:37 ` Reid Kleckner
@ 2009-08-17 15:31 ` Tom Tromey
2009-08-20 18:22 ` Doug Evans
0 siblings, 1 reply; 32+ messages in thread
From: Tom Tromey @ 2009-08-17 15:31 UTC (permalink / raw)
To: Reid Kleckner; +Cc: Reid Kleckner, unladen-swallow, gdb-patches
>>>>> "Reid" == Reid Kleckner <rnk@google.com> writes:
Tom> I read it a few more times and nothing popped out at me.
Reid> Is it OK if Doug commits it then?
Yes.
Tom
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-17 15:31 ` Tom Tromey
@ 2009-08-20 18:22 ` Doug Evans
2009-08-21 15:17 ` Ken Werner
0 siblings, 1 reply; 32+ messages in thread
From: Doug Evans @ 2009-08-20 18:22 UTC (permalink / raw)
To: Tom Tromey; +Cc: Reid Kleckner, Reid Kleckner, unladen-swallow, gdb-patches
On Mon, Aug 17, 2009 at 8:29 AM, Tom Tromey<tromey@redhat.com> wrote:
>>>>>> "Reid" == Reid Kleckner <rnk@google.com> writes:
>
> Tom> I read it a few more times and nothing popped out at me.
>
> Reid> Is it OK if Doug commits it then?
>
> Yes.
>
> Tom
>
Committed. Thanks.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-20 18:22 ` Doug Evans
@ 2009-08-21 15:17 ` Ken Werner
2009-08-21 16:31 ` Doug Evans
0 siblings, 1 reply; 32+ messages in thread
From: Ken Werner @ 2009-08-21 15:17 UTC (permalink / raw)
To: Doug Evans, gdb-patches
[-- Attachment #1: Type: text/plain, Size: 559 bytes --]
Doug Evans wrote:
> On Mon, Aug 17, 2009 at 8:29 AM, Tom Tromey<tromey@redhat.com> wrote:
>>>>>>> "Reid" == Reid Kleckner <rnk@google.com> writes:
>>
>> Tom> I read it a few more times and nothing popped out at me.
>>
>> Reid> Is it OK if Doug commits it then?
>>
>> Yes.
>>
>> Tom
>>
>
> Committed. Thanks.
Hi,
I noticed the gdb build fails on ppc64 if -Werror is used:
gdb/jit.c: In function 'jit_event_handler':
gdb/jit.c:417: warning: cast to pointer from integer of different size
The attached patch uses paddress for printing the address.
-ken
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gdb-jit.patch --]
[-- Type: text/x-patch; name="gdb-jit.patch", Size: 709 bytes --]
Index: gdb/jit.c
===================================================================
RCS file: /cvs/src/src/gdb/jit.c,v
retrieving revision 1.1
diff -u -r1.1 jit.c
--- gdb/jit.c 20 Aug 2009 18:02:47 -0000 1.1
+++ gdb/jit.c 21 Aug 2009 12:27:13 -0000
@@ -413,8 +413,8 @@
case JIT_UNREGISTER:
objf = jit_find_objf_with_entry_addr (entry_addr);
if (objf == NULL)
- printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
- (void *) entry_addr);
+ printf_unfiltered ("Unable to find JITed code entry at address: %s\n",
+ paddress (target_gdbarch, entry_addr));
else
jit_unregister_code (objf);
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-21 15:17 ` Ken Werner
@ 2009-08-21 16:31 ` Doug Evans
2009-08-21 18:59 ` Ken Werner
0 siblings, 1 reply; 32+ messages in thread
From: Doug Evans @ 2009-08-21 16:31 UTC (permalink / raw)
To: Ken Werner; +Cc: gdb-patches
On Fri, Aug 21, 2009 at 5:35 AM, Ken Werner<ken@linux.vnet.ibm.com> wrote:
> Doug Evans wrote:
>
>> On Mon, Aug 17, 2009 at 8:29 AM, Tom Tromey<tromey@redhat.com> wrote:
>>>>>>>> "Reid" == Reid Kleckner <rnk@google.com> writes:
>>>
>>> Tom> I read it a few more times and nothing popped out at me.
>>>
>>> Reid> Is it OK if Doug commits it then?
>>>
>>> Yes.
>>>
>>> Tom
>>>
>>
>> Committed. Thanks.
>
> Hi,
> I noticed the gdb build fails on ppc64 if -Werror is used:
> gdb/jit.c: In function 'jit_event_handler':
> gdb/jit.c:417: warning: cast to pointer from integer of different size
> The attached patch uses paddress for printing the address.
>
> -ken
Heh. Thanks.
Can you check it in? If not I can for you.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-21 16:31 ` Doug Evans
@ 2009-08-21 18:59 ` Ken Werner
2009-08-21 19:53 ` Doug Evans
0 siblings, 1 reply; 32+ messages in thread
From: Ken Werner @ 2009-08-21 18:59 UTC (permalink / raw)
To: Doug Evans, gdb-patches
On Friday 21 August 2009 05:17:30 pm you wrote:
> On Fri, Aug 21, 2009 at 5:35 AM, Ken Werner<ken@linux.vnet.ibm.com> wrote:
> > Doug Evans wrote:
> >> On Mon, Aug 17, 2009 at 8:29 AM, Tom Tromey<tromey@redhat.com> wrote:
> >>>>>>>> "Reid" == Reid Kleckner <rnk@google.com> writes:
> >>>
> >>> Tom> I read it a few more times and nothing popped out at me.
> >>>
> >>> Reid> Is it OK if Doug commits it then?
> >>>
> >>> Yes.
> >>>
> >>> Tom
> >>
> >> Committed. Thanks.
> >
> > Hi,
> > I noticed the gdb build fails on ppc64 if -Werror is used:
> > gdb/jit.c: In function 'jit_event_handler':
> > gdb/jit.c:417: warning: cast to pointer from integer of different size
> > The attached patch uses paddress for printing the address.
> >
> > -ken
>
> Heh. Thanks.
> Can you check it in? If not I can for you.
Please go ahead since I do not have commit privileges.
Thanks.
-ken
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [unladen-swallow] Re: [RFA] Add interface for registering JITed code
2009-08-21 18:59 ` Ken Werner
@ 2009-08-21 19:53 ` Doug Evans
0 siblings, 0 replies; 32+ messages in thread
From: Doug Evans @ 2009-08-21 19:53 UTC (permalink / raw)
To: Ken Werner; +Cc: gdb-patches
On Fri, Aug 21, 2009 at 9:59 AM, Ken Werner<ken@linux.vnet.ibm.com> wrote:
>> > Hi,
>> > I noticed the gdb build fails on ppc64 if -Werror is used:
>> > gdb/jit.c: In function 'jit_event_handler':
>> > gdb/jit.c:417: warning: cast to pointer from integer of different size
>> > The attached patch uses paddress for printing the address.
>> >
>> > -ken
>>
>> Heh. Thanks.
>> Can you check it in? If not I can for you.
>
> Please go ahead since I do not have commit privileges.
> Thanks.
> -ken
>
I committed this change.
2009-08-21 Ken Werner <ken@linux.vnet.ibm.com>
Doug Evans <dje@google.com>
* jit.c (jit_event_handler): Use paddress to print target addresses.
Wrap printf string in _().
Index: jit.c
===================================================================
RCS file: /cvs/src/src/gdb/jit.c,v
retrieving revision 1.2
diff -u -p -r1.2 jit.c
--- jit.c 21 Aug 2009 18:54:44 -0000 1.2
+++ jit.c 21 Aug 2009 19:03:58 -0000
@@ -436,8 +436,8 @@ jit_event_handler (struct gdbarch *gdbar
case JIT_UNREGISTER:
objf = jit_find_objf_with_entry_addr (entry_addr);
if (objf == NULL)
- printf_unfiltered ("Unable to find JITed code entry at address: %p\n",
- (void *) entry_addr);
+ printf_unfiltered (_("Unable to find JITed code entry at
address: %s\n"),
+ paddress (gdbarch, entry_addr));
else
jit_unregister_code (objf);
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2009-08-21 19:15 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-07-23 1:58 [RFA] Add interface for registering JITed code Reid Kleckner
2009-07-23 12:08 ` Reid Kleckner
2009-07-23 23:21 ` Tom Tromey
2009-07-24 13:25 ` Ulrich Weigand
2009-07-24 16:52 ` Doug Evans
2009-07-25 0:40 ` [unladen-swallow] " Reid Kleckner
2009-07-24 16:55 ` Reid Kleckner
2009-07-24 20:42 ` Eli Zaretskii
2009-07-24 20:55 ` Tom Tromey
2009-07-25 15:29 ` Eli Zaretskii
2009-07-27 23:20 ` Reid Kleckner
2009-07-28 20:20 ` Eli Zaretskii
2009-07-28 22:23 ` Reid Kleckner
2009-07-29 15:20 ` Eli Zaretskii
2009-07-24 21:06 ` Tom Tromey
2009-07-25 0:23 ` Reid Kleckner
2009-07-30 16:30 ` Tom Tromey
2009-07-30 16:54 ` Tom Tromey
2009-08-05 21:05 ` [unladen-swallow] " Reid Kleckner
2009-07-30 21:10 ` Thiago Jung Bauermann
2009-07-31 18:18 ` Thiago Jung Bauermann
2009-07-31 20:31 ` [unladen-swallow] " Reid Kleckner
2009-08-01 14:43 ` Thiago Jung Bauermann
2009-08-14 19:29 ` Tom Tromey
2009-08-14 23:37 ` Reid Kleckner
2009-08-17 15:31 ` Tom Tromey
2009-08-20 18:22 ` Doug Evans
2009-08-21 15:17 ` Ken Werner
2009-08-21 16:31 ` Doug Evans
2009-08-21 18:59 ` Ken Werner
2009-08-21 19:53 ` Doug Evans
2009-07-31 20:55 ` Paul Pluzhnikov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox