mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-28 23:39:35 +08:00
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. * doc/gdb.texinfo: Add chapter on JIT interface.
This commit is contained in:
@ -1,3 +1,27 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
2009-08-19 Ulrich Weigand <uweigand@de.ibm.com>
|
2009-08-19 Ulrich Weigand <uweigand@de.ibm.com>
|
||||||
|
|
||||||
* value.c (enum internalvar_kind): Replace INTERNALVAR_SCALAR by
|
* value.c (enum internalvar_kind): Replace INTERNALVAR_SCALAR by
|
||||||
|
@ -676,7 +676,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
|||||||
wrapper.c \
|
wrapper.c \
|
||||||
xml-tdesc.c xml-support.c \
|
xml-tdesc.c xml-support.c \
|
||||||
inferior.c gdb_usleep.c \
|
inferior.c gdb_usleep.c \
|
||||||
record.c
|
record.c \
|
||||||
|
jit.c
|
||||||
|
|
||||||
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
|
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
|
||||||
|
|
||||||
@ -745,7 +746,7 @@ config/rs6000/nm-rs6000.h top.h bsd-kvm.h gdb-stabs.h reggroups.h \
|
|||||||
annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \
|
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 \
|
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 \
|
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.
|
# Header files that already have srcdir in them, or which are in objdir.
|
||||||
|
|
||||||
@ -827,7 +828,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
|||||||
solib.o solib-null.o \
|
solib.o solib-null.o \
|
||||||
prologue-value.o memory-map.o xml-support.o \
|
prologue-value.o memory-map.o xml-support.o \
|
||||||
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.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
|
TSOBS = inflow.o
|
||||||
|
|
||||||
|
6
gdb/NEWS
6
gdb/NEWS
@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
*** Changes since GDB 6.8
|
*** 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
|
* Tracepoints may now be conditional. The syntax is as for
|
||||||
breakpoints; either an "if" clause appended to the "trace" command,
|
breakpoints; either an "if" clause appended to the "trace" command,
|
||||||
or the "condition" command is available. GDB sends the condition to
|
or the "condition" command is available. GDB sends the condition to
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
#include "top.h"
|
#include "top.h"
|
||||||
#include "wrapper.h"
|
#include "wrapper.h"
|
||||||
#include "valprint.h"
|
#include "valprint.h"
|
||||||
|
#include "jit.h"
|
||||||
|
|
||||||
/* readline include files */
|
/* readline include files */
|
||||||
#include "readline/readline.h"
|
#include "readline/readline.h"
|
||||||
@ -1592,6 +1593,13 @@ update_breakpoints_after_exec (void)
|
|||||||
continue;
|
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(),
|
/* Thread event breakpoints must be set anew after an exec(),
|
||||||
as must overlay event and longjmp master breakpoints. */
|
as must overlay event and longjmp master breakpoints. */
|
||||||
if (b->type == bp_thread_event || b->type == bp_overlay_event
|
if (b->type == bp_thread_event || b->type == bp_overlay_event
|
||||||
@ -2583,6 +2591,7 @@ print_it_typical (bpstat bs)
|
|||||||
case bp_watchpoint_scope:
|
case bp_watchpoint_scope:
|
||||||
case bp_call_dummy:
|
case bp_call_dummy:
|
||||||
case bp_tracepoint:
|
case bp_tracepoint:
|
||||||
|
case bp_jit_event:
|
||||||
default:
|
default:
|
||||||
result = PRINT_UNKNOWN;
|
result = PRINT_UNKNOWN;
|
||||||
break;
|
break;
|
||||||
@ -3308,6 +3317,9 @@ bpstat_what (bpstat bs)
|
|||||||
/* We hit the shared library event breakpoint. */
|
/* We hit the shared library event breakpoint. */
|
||||||
shlib_event,
|
shlib_event,
|
||||||
|
|
||||||
|
/* We hit the jit event breakpoint. */
|
||||||
|
jit_event,
|
||||||
|
|
||||||
/* This is just used to count how many enums there are. */
|
/* This is just used to count how many enums there are. */
|
||||||
class_last
|
class_last
|
||||||
};
|
};
|
||||||
@ -3323,6 +3335,7 @@ bpstat_what (bpstat bs)
|
|||||||
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
|
#define clr BPSTAT_WHAT_CLEAR_LONGJMP_RESUME
|
||||||
#define sr BPSTAT_WHAT_STEP_RESUME
|
#define sr BPSTAT_WHAT_STEP_RESUME
|
||||||
#define shl BPSTAT_WHAT_CHECK_SHLIBS
|
#define shl BPSTAT_WHAT_CHECK_SHLIBS
|
||||||
|
#define jit BPSTAT_WHAT_CHECK_JIT
|
||||||
|
|
||||||
/* "Can't happen." Might want to print an error message.
|
/* "Can't happen." Might want to print an error message.
|
||||||
abort() is not out of the question, but chances are GDB is just
|
abort() is not out of the question, but chances are GDB is just
|
||||||
@ -3343,12 +3356,13 @@ bpstat_what (bpstat bs)
|
|||||||
back and decide something of a lower priority is better. The
|
back and decide something of a lower priority is better. The
|
||||||
ordering is:
|
ordering is:
|
||||||
|
|
||||||
kc < clr sgl shl slr sn sr ss
|
kc < jit clr sgl shl slr sn sr ss
|
||||||
sgl < shl slr sn sr ss
|
sgl < jit shl slr sn sr ss
|
||||||
slr < err shl sn sr ss
|
slr < jit err shl sn sr ss
|
||||||
clr < err shl sn sr ss
|
clr < jit err shl sn sr ss
|
||||||
ss < shl sn sr
|
ss < jit shl sn sr
|
||||||
sn < shl sr
|
sn < jit shl sr
|
||||||
|
jit < shl sr
|
||||||
shl < sr
|
shl < sr
|
||||||
sr <
|
sr <
|
||||||
|
|
||||||
@ -3366,28 +3380,18 @@ bpstat_what (bpstat bs)
|
|||||||
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
|
table[(int) class_last][(int) BPSTAT_WHAT_LAST] =
|
||||||
{
|
{
|
||||||
/* old action */
|
/* old action */
|
||||||
/* kc ss sn sgl slr clr sr shl
|
/* kc ss sn sgl slr clr sr shl jit */
|
||||||
*/
|
/* no_effect */ {kc, ss, sn, sgl, slr, clr, sr, shl, jit},
|
||||||
/*no_effect */
|
/* wp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
|
||||||
{kc, ss, sn, sgl, slr, clr, sr, shl},
|
/* wp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
|
||||||
/*wp_silent */
|
/* bp_nostop */ {sgl, ss, sn, sgl, slr, slr, sr, shl, jit},
|
||||||
{ss, ss, sn, ss, ss, ss, sr, shl},
|
/* bp_silent */ {ss, ss, sn, ss, ss, ss, sr, shl, jit},
|
||||||
/*wp_noisy */
|
/* bp_noisy */ {sn, sn, sn, sn, sn, sn, sr, shl, jit},
|
||||||
{sn, sn, sn, sn, sn, sn, sr, shl},
|
/* long_jump */ {slr, ss, sn, slr, slr, err, sr, shl, jit},
|
||||||
/*bp_nostop */
|
/* long_resume */ {clr, ss, sn, err, err, err, sr, shl, jit},
|
||||||
{sgl, ss, sn, sgl, slr, slr, sr, shl},
|
/* step_resume */ {sr, sr, sr, sr, sr, sr, sr, sr, sr },
|
||||||
/*bp_silent */
|
/* shlib */ {shl, shl, shl, shl, shl, shl, sr, shl, shl},
|
||||||
{ss, ss, sn, ss, ss, ss, sr, shl},
|
/* jit_event */ {jit, jit, jit, jit, jit, jit, sr, jit, jit}
|
||||||
/*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}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef kc
|
#undef kc
|
||||||
@ -3400,6 +3404,7 @@ bpstat_what (bpstat bs)
|
|||||||
#undef sr
|
#undef sr
|
||||||
#undef ts
|
#undef ts
|
||||||
#undef shl
|
#undef shl
|
||||||
|
#undef jit
|
||||||
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
|
enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING;
|
||||||
struct bpstat_what retval;
|
struct bpstat_what retval;
|
||||||
|
|
||||||
@ -3470,6 +3475,9 @@ bpstat_what (bpstat bs)
|
|||||||
case bp_shlib_event:
|
case bp_shlib_event:
|
||||||
bs_class = shlib_event;
|
bs_class = shlib_event;
|
||||||
break;
|
break;
|
||||||
|
case bp_jit_event:
|
||||||
|
bs_class = jit_event;
|
||||||
|
break;
|
||||||
case bp_thread_event:
|
case bp_thread_event:
|
||||||
case bp_overlay_event:
|
case bp_overlay_event:
|
||||||
case bp_longjmp_master:
|
case bp_longjmp_master:
|
||||||
@ -3603,6 +3611,7 @@ print_one_breakpoint_location (struct breakpoint *b,
|
|||||||
{bp_longjmp_master, "longjmp master"},
|
{bp_longjmp_master, "longjmp master"},
|
||||||
{bp_catchpoint, "catchpoint"},
|
{bp_catchpoint, "catchpoint"},
|
||||||
{bp_tracepoint, "tracepoint"},
|
{bp_tracepoint, "tracepoint"},
|
||||||
|
{bp_jit_event, "jit events"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static char bpenables[] = "nynny";
|
static char bpenables[] = "nynny";
|
||||||
@ -3731,6 +3740,7 @@ print_one_breakpoint_location (struct breakpoint *b,
|
|||||||
case bp_overlay_event:
|
case bp_overlay_event:
|
||||||
case bp_longjmp_master:
|
case bp_longjmp_master:
|
||||||
case bp_tracepoint:
|
case bp_tracepoint:
|
||||||
|
case bp_jit_event:
|
||||||
if (opts.addressprint)
|
if (opts.addressprint)
|
||||||
{
|
{
|
||||||
annotate_field (4);
|
annotate_field (4);
|
||||||
@ -4375,6 +4385,7 @@ allocate_bp_location (struct breakpoint *bpt)
|
|||||||
case bp_shlib_event:
|
case bp_shlib_event:
|
||||||
case bp_thread_event:
|
case bp_thread_event:
|
||||||
case bp_overlay_event:
|
case bp_overlay_event:
|
||||||
|
case bp_jit_event:
|
||||||
case bp_longjmp_master:
|
case bp_longjmp_master:
|
||||||
loc->loc_type = bp_loc_software_breakpoint;
|
loc->loc_type = bp_loc_software_breakpoint;
|
||||||
break;
|
break;
|
||||||
@ -4657,6 +4668,17 @@ struct lang_and_radix
|
|||||||
int 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
|
void
|
||||||
remove_solib_event_breakpoints (void)
|
remove_solib_event_breakpoints (void)
|
||||||
@ -5338,6 +5360,7 @@ mention (struct breakpoint *b)
|
|||||||
case bp_shlib_event:
|
case bp_shlib_event:
|
||||||
case bp_thread_event:
|
case bp_thread_event:
|
||||||
case bp_overlay_event:
|
case bp_overlay_event:
|
||||||
|
case bp_jit_event:
|
||||||
case bp_longjmp_master:
|
case bp_longjmp_master:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -7654,6 +7677,7 @@ delete_command (char *arg, int from_tty)
|
|||||||
{
|
{
|
||||||
if (b->type != bp_call_dummy
|
if (b->type != bp_call_dummy
|
||||||
&& b->type != bp_shlib_event
|
&& b->type != bp_shlib_event
|
||||||
|
&& b->type != bp_jit_event
|
||||||
&& b->type != bp_thread_event
|
&& b->type != bp_thread_event
|
||||||
&& b->type != bp_overlay_event
|
&& b->type != bp_overlay_event
|
||||||
&& b->type != bp_longjmp_master
|
&& b->type != bp_longjmp_master
|
||||||
@ -7673,6 +7697,7 @@ delete_command (char *arg, int from_tty)
|
|||||||
if (b->type != bp_call_dummy
|
if (b->type != bp_call_dummy
|
||||||
&& b->type != bp_shlib_event
|
&& b->type != bp_shlib_event
|
||||||
&& b->type != bp_thread_event
|
&& b->type != bp_thread_event
|
||||||
|
&& b->type != bp_jit_event
|
||||||
&& b->type != bp_overlay_event
|
&& b->type != bp_overlay_event
|
||||||
&& b->type != bp_longjmp_master
|
&& b->type != bp_longjmp_master
|
||||||
&& b->number >= 0)
|
&& b->number >= 0)
|
||||||
@ -7999,6 +8024,7 @@ breakpoint_re_set_one (void *bint)
|
|||||||
case bp_step_resume:
|
case bp_step_resume:
|
||||||
case bp_longjmp:
|
case bp_longjmp:
|
||||||
case bp_longjmp_resume:
|
case bp_longjmp_resume:
|
||||||
|
case bp_jit_event:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8027,6 +8053,8 @@ breakpoint_re_set (void)
|
|||||||
set_language (save_language);
|
set_language (save_language);
|
||||||
input_radix = save_input_radix;
|
input_radix = save_input_radix;
|
||||||
|
|
||||||
|
jit_inferior_created_hook ();
|
||||||
|
|
||||||
create_overlay_event_breakpoint ("_ovly_debug_event");
|
create_overlay_event_breakpoint ("_ovly_debug_event");
|
||||||
create_longjmp_master_breakpoint ("longjmp");
|
create_longjmp_master_breakpoint ("longjmp");
|
||||||
create_longjmp_master_breakpoint ("_longjmp");
|
create_longjmp_master_breakpoint ("_longjmp");
|
||||||
|
@ -120,6 +120,9 @@ enum bptype
|
|||||||
bp_catchpoint,
|
bp_catchpoint,
|
||||||
|
|
||||||
bp_tracepoint,
|
bp_tracepoint,
|
||||||
|
|
||||||
|
/* Event for JIT compiled code generation or deletion. */
|
||||||
|
bp_jit_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* States of enablement of breakpoint. */
|
/* States of enablement of breakpoint. */
|
||||||
@ -554,6 +557,9 @@ enum bpstat_what_main_action
|
|||||||
keep checking. */
|
keep checking. */
|
||||||
BPSTAT_WHAT_CHECK_SHLIBS,
|
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. */
|
/* This is just used to keep track of how many enums there are. */
|
||||||
BPSTAT_WHAT_LAST
|
BPSTAT_WHAT_LAST
|
||||||
};
|
};
|
||||||
@ -865,6 +871,9 @@ extern void mark_breakpoints_out (void);
|
|||||||
|
|
||||||
extern void make_breakpoint_permanent (struct breakpoint *);
|
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 *,
|
extern struct breakpoint *create_solib_event_breakpoint (struct gdbarch *,
|
||||||
CORE_ADDR);
|
CORE_ADDR);
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2009-08-20 Reid Kleckner <reid@kleckner.net>
|
||||||
|
|
||||||
|
* gdb.texinfo: Add chapter on JIT interface.
|
||||||
|
|
||||||
2009-08-07 Nick Roberts <nickrob@snap.net.nz>
|
2009-08-07 Nick Roberts <nickrob@snap.net.nz>
|
||||||
|
|
||||||
* gdb.texinfo (Server Prefix): Explain that server prefix suppresses
|
* gdb.texinfo (Server Prefix): Explain that server prefix suppresses
|
||||||
|
@ -159,6 +159,7 @@ software in general. We will miss him.
|
|||||||
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
|
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
|
||||||
* GDB/MI:: @value{GDBN}'s Machine Interface.
|
* GDB/MI:: @value{GDBN}'s Machine Interface.
|
||||||
* Annotations:: @value{GDBN}'s annotation interface.
|
* Annotations:: @value{GDBN}'s annotation interface.
|
||||||
|
* JIT Interface:: Using the JIT debugging interface.
|
||||||
|
|
||||||
* GDB Bugs:: Reporting bugs in @value{GDBN}
|
* GDB Bugs:: Reporting bugs in @value{GDBN}
|
||||||
|
|
||||||
@ -25921,6 +25922,136 @@ source which is being displayed. @var{addr} is in the form @samp{0x}
|
|||||||
followed by one or more lowercase hex digits (note that this does not
|
followed by one or more lowercase hex digits (note that this does not
|
||||||
depend on the language).
|
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
|
@node GDB Bugs
|
||||||
@chapter Reporting Bugs in @value{GDBN}
|
@chapter Reporting Bugs in @value{GDBN}
|
||||||
@cindex bugs in @value{GDBN}
|
@cindex bugs in @value{GDBN}
|
||||||
|
19
gdb/infrun.c
19
gdb/infrun.c
@ -50,6 +50,7 @@
|
|||||||
#include "event-top.h"
|
#include "event-top.h"
|
||||||
#include "record.h"
|
#include "record.h"
|
||||||
#include "inline-frame.h"
|
#include "inline-frame.h"
|
||||||
|
#include "jit.h"
|
||||||
|
|
||||||
/* Prototypes for local functions */
|
/* Prototypes for local functions */
|
||||||
|
|
||||||
@ -544,6 +545,8 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
|||||||
solib_create_inferior_hook ();
|
solib_create_inferior_hook ();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
jit_inferior_created_hook ();
|
||||||
|
|
||||||
/* Reinsert all breakpoints. (Those which were symbolic have
|
/* Reinsert all breakpoints. (Those which were symbolic have
|
||||||
been reset to the proper address in the new a.out, thanks
|
been reset to the proper address in the new a.out, thanks
|
||||||
to symbol_file_command...) */
|
to symbol_file_command...) */
|
||||||
@ -3540,6 +3543,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case BPSTAT_WHAT_LAST:
|
||||||
/* Not a real code, but listed here to shut up gcc -Wall. */
|
/* Not a real code, but listed here to shut up gcc -Wall. */
|
||||||
|
|
||||||
|
438
gdb/jit.c
Normal file
438
gdb/jit.c
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* This is a boolean indicating whether we're currently registering code. This
|
||||||
|
is used to avoid re-entering the registration code. We want to check for
|
||||||
|
new JITed every time a new object file is loaded, but we want to avoid
|
||||||
|
checking for new code while we're registering object files for JITed code.
|
||||||
|
Therefore, we flip this variable to 1 before registering new object files,
|
||||||
|
and set it to 0 before returning. */
|
||||||
|
|
||||||
|
static int registering_code = 0;
|
||||||
|
|
||||||
|
/* Helper cleanup function to clear an integer flag like the one above. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_int (void *int_addr)
|
||||||
|
{
|
||||||
|
*((int *) int_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, *my_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Raise this flag while we register code so we won't trigger any
|
||||||
|
re-registration. */
|
||||||
|
registering_code = 1;
|
||||||
|
my_cleanups = make_cleanup (clear_int, ®istering_code);
|
||||||
|
|
||||||
|
/* This call takes ownership of sai. */
|
||||||
|
objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED);
|
||||||
|
|
||||||
|
/* Clear the registering_code flag. */
|
||||||
|
do_cleanups (my_cleanups);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
CORE_ADDR *objf_entry_addr;
|
||||||
|
|
||||||
|
ALL_OBJFILES (objf)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
struct cleanup *old_cleanups;
|
||||||
|
|
||||||
|
/* When we register code, GDB resets its breakpoints in case symbols have
|
||||||
|
changed. That in turn calls this handler, which makes us look for new
|
||||||
|
code again. To avoid being re-entered, we check this flag. */
|
||||||
|
if (registering_code)
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 ();
|
||||||
|
}
|
77
gdb/jit.h
Normal file
77
gdb/jit.h
Normal file
@ -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 */
|
Reference in New Issue
Block a user