range stepping: gdbserver (x86 GNU/Linux)

This patch adds support for range stepping to GDBserver, teaching it
about vCont;r.

It'd be easy to enable this for all hardware single-step targets
without needing the linux_target_ops hook, however, at least PPC needs
special care, due to the fact that PPC atomic sequences can't be
hardware single-stepped through, a thing which GDBserver doesn't know
about.  So this leaves the support limited to x86/x86_64.

gdb/
2013-05-23  Pedro Alves  <palves@redhat.com>

	* NEWS: Mention GDBserver range stepping support.

gdb/gdbserver/
2013-05-23  Yao Qi  <yao@codesourcery.com>
	    Pedro Alves  <palves@redhat.com>

	* linux-low.c (lwp_in_step_range): New function.
	(linux_wait_1): If the thread was range stepping and stopped
	outside the stepping range, report the stop to GDB.  Otherwise,
	continue stepping.  Add range stepping debug output.
	(linux_set_resume_request): Copy the step range from the resume
	request to the lwp.
	(linux_supports_range_stepping): New.
	(linux_target_ops) <supports_range_stepping>: Set to
	linux_supports_range_stepping.
	* linux-low.h (struct linux_target_ops)
	<supports_range_stepping>: New field.
	(struct lwp_info) <step_range_start, step_range_end>: New fields.
	* linux-x86-low.c (x86_supports_range_stepping): New.
	(the_low_target) <supports_range_stepping>: Set to
	x86_supports_range_stepping.
	* server.c (handle_v_cont): Handle 'r' action.
	(handle_v_requests): Append ";r" if the target supports range
	stepping.
	* target.h (struct thread_resume) <step_range_start,
	step_range_end>: New fields.
	(struct target_ops) <supports_range_stepping>:
	New field.
	(target_supports_range_stepping): New macro.
This commit is contained in:
Pedro Alves
2013-05-23 17:17:50 +00:00
parent c1e36e3e91
commit c2d6af84da
8 changed files with 141 additions and 12 deletions

View File

@ -1,3 +1,7 @@
2013-05-23 Pedro Alves <palves@redhat.com>
* NEWS: Mention GDBserver range stepping support.
2013-05-23 Yao Qi <yao@codesourcery.com> 2013-05-23 Yao Qi <yao@codesourcery.com>
Pedro Alves <palves@redhat.com> Pedro Alves <palves@redhat.com>

View File

@ -95,6 +95,11 @@ vCont;r
stub to step through an address range itself, without GDB stub to step through an address range itself, without GDB
involvemement at each single-step. involvemement at each single-step.
* New features in the GDB remote stub, GDBserver
** GDBserver now supports target-assisted range stepping. Currently
enabled on x86/x86_64 GNU/Linux targets.
*** Changes in GDB 7.6 *** Changes in GDB 7.6
* Target record has been renamed to record-full. * Target record has been renamed to record-full.

View File

@ -1,3 +1,30 @@
2013-05-23 Yao Qi <yao@codesourcery.com>
Pedro Alves <palves@redhat.com>
* linux-low.c (lwp_in_step_range): New function.
(linux_wait_1): If the thread was range stepping and stopped
outside the stepping range, report the stop to GDB. Otherwise,
continue stepping. Add range stepping debug output.
(linux_set_resume_request): Copy the step range from the resume
request to the lwp.
(linux_supports_range_stepping): New.
(linux_target_ops) <supports_range_stepping>: Set to
linux_supports_range_stepping.
* linux-low.h (struct linux_target_ops)
<supports_range_stepping>: New field.
(struct lwp_info) <step_range_start, step_range_end>: New fields.
* linux-x86-low.c (x86_supports_range_stepping): New.
(the_low_target) <supports_range_stepping>: Set to
x86_supports_range_stepping.
* server.c (handle_v_cont): Handle 'r' action.
(handle_v_requests): Append ";r" if the target supports range
stepping.
* target.h (struct thread_resume) <step_range_start,
step_range_end>: New fields.
(struct target_ops) <supports_range_stepping>:
New field.
(target_supports_range_stepping): New macro.
2013-05-17 Joel Brobecker <brobecker@adacore.com> 2013-05-17 Joel Brobecker <brobecker@adacore.com>
* lynx-low.c (lynx_resume): Fix null_ptid/minus_one_ptid * lynx-low.c (lynx_resume): Fix null_ptid/minus_one_ptid

View File

@ -276,6 +276,16 @@ supports_fast_tracepoints (void)
return the_low_target.install_fast_tracepoint_jump_pad != NULL; return the_low_target.install_fast_tracepoint_jump_pad != NULL;
} }
/* True if LWP is stopped in its stepping range. */
static int
lwp_in_step_range (struct lwp_info *lwp)
{
CORE_ADDR pc = lwp->stop_pc;
return (pc >= lwp->step_range_start && pc < lwp->step_range_end);
}
struct pending_signals struct pending_signals
{ {
int signal; int signal;
@ -2337,6 +2347,7 @@ linux_wait_1 (ptid_t ptid,
int maybe_internal_trap; int maybe_internal_trap;
int report_to_gdb; int report_to_gdb;
int trace_event; int trace_event;
int in_step_range;
/* Translate generic target options into linux options. */ /* Translate generic target options into linux options. */
options = __WALL; options = __WALL;
@ -2346,6 +2357,7 @@ linux_wait_1 (ptid_t ptid,
retry: retry:
bp_explains_trap = 0; bp_explains_trap = 0;
trace_event = 0; trace_event = 0;
in_step_range = 0;
ourstatus->kind = TARGET_WAITKIND_IGNORE; ourstatus->kind = TARGET_WAITKIND_IGNORE;
/* If we were only supposed to resume one thread, only wait for /* If we were only supposed to resume one thread, only wait for
@ -2639,18 +2651,24 @@ Check if we're already there.\n",
goto retry; goto retry;
} }
/* If GDB wanted this thread to single step, we always want to /* Note that all addresses are always "out of the step range" when
report the SIGTRAP, and let GDB handle it. Watchpoints should there's no range to begin with. */
always be reported. So should signals we can't explain. A in_step_range = lwp_in_step_range (event_child);
SIGTRAP we can't explain could be a GDB breakpoint --- we may or
not support Z0 breakpoints. If we do, we're be able to handle /* If GDB wanted this thread to single step, and the thread is out
GDB breakpoints on top of internal breakpoints, by handling the of the step range, we always want to report the SIGTRAP, and let
internal breakpoint and still reporting the event to GDB. If we GDB handle it. Watchpoints should always be reported. So should
don't, we're out of luck, GDB won't see the breakpoint hit. */ signals we can't explain. A SIGTRAP we can't explain could be a
GDB breakpoint --- we may or not support Z0 breakpoints. If we
do, we're be able to handle GDB breakpoints on top of internal
breakpoints, by handling the internal breakpoint and still
reporting the event to GDB. If we don't, we're out of luck, GDB
won't see the breakpoint hit. */
report_to_gdb = (!maybe_internal_trap report_to_gdb = (!maybe_internal_trap
|| current_inferior->last_resume_kind == resume_step || (current_inferior->last_resume_kind == resume_step
&& !in_step_range)
|| event_child->stopped_by_watchpoint || event_child->stopped_by_watchpoint
|| (!step_over_finished || (!step_over_finished && !in_step_range
&& !bp_explains_trap && !trace_event) && !bp_explains_trap && !trace_event)
|| (gdb_breakpoint_here (event_child->stop_pc) || (gdb_breakpoint_here (event_child->stop_pc)
&& gdb_condition_true_at_breakpoint (event_child->stop_pc) && gdb_condition_true_at_breakpoint (event_child->stop_pc)
@ -2671,6 +2689,11 @@ Check if we're already there.\n",
fprintf (stderr, "Step-over finished.\n"); fprintf (stderr, "Step-over finished.\n");
if (trace_event) if (trace_event)
fprintf (stderr, "Tracepoint event.\n"); fprintf (stderr, "Tracepoint event.\n");
if (lwp_in_step_range (event_child))
fprintf (stderr, "Range stepping pc 0x%s [0x%s, 0x%s).\n",
paddress (event_child->stop_pc),
paddress (event_child->step_range_start),
paddress (event_child->step_range_end));
} }
/* We're not reporting this breakpoint to GDB, so apply the /* We're not reporting this breakpoint to GDB, so apply the
@ -2702,7 +2725,12 @@ Check if we're already there.\n",
if (debug_threads) if (debug_threads)
{ {
if (current_inferior->last_resume_kind == resume_step) if (current_inferior->last_resume_kind == resume_step)
fprintf (stderr, "GDB wanted to single-step, reporting event.\n"); {
if (event_child->step_range_start == event_child->step_range_end)
fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
else if (!lwp_in_step_range (event_child))
fprintf (stderr, "Out of step range, reporting event.\n");
}
if (event_child->stopped_by_watchpoint) if (event_child->stopped_by_watchpoint)
fprintf (stderr, "Stopped by watchpoint.\n"); fprintf (stderr, "Stopped by watchpoint.\n");
if (gdb_breakpoint_here (event_child->stop_pc)) if (gdb_breakpoint_here (event_child->stop_pc))
@ -3401,6 +3429,9 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg)
lwp->resume = &r->resume[ndx]; lwp->resume = &r->resume[ndx];
thread->last_resume_kind = lwp->resume->kind; thread->last_resume_kind = lwp->resume->kind;
lwp->step_range_start = lwp->resume->step_range_start;
lwp->step_range_end = lwp->resume->step_range_end;
/* If we had a deferred signal to report, dequeue one now. /* If we had a deferred signal to report, dequeue one now.
This can happen if LWP gets more than one signal while This can happen if LWP gets more than one signal while
trying to get out of a jump pad. */ trying to get out of a jump pad. */
@ -5094,6 +5125,15 @@ linux_supports_agent (void)
return 1; return 1;
} }
static int
linux_supports_range_stepping (void)
{
if (*the_low_target.supports_range_stepping == NULL)
return 0;
return (*the_low_target.supports_range_stepping) ();
}
/* Enumerate spufs IDs for process PID. */ /* Enumerate spufs IDs for process PID. */
static int static int
spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len) spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
@ -5952,6 +5992,7 @@ static struct target_ops linux_target_ops = {
NULL, NULL,
NULL, NULL,
#endif #endif
linux_supports_range_stepping,
}; };
static void static void

View File

@ -166,6 +166,8 @@ struct linux_target_ops
for use as a fast tracepoint. */ for use as a fast tracepoint. */
int (*get_min_fast_tracepoint_insn_len) (void); int (*get_min_fast_tracepoint_insn_len) (void);
/* Returns true if the low target supports range stepping. */
int (*supports_range_stepping) (void);
}; };
extern struct linux_target_ops the_low_target; extern struct linux_target_ops the_low_target;
@ -235,6 +237,12 @@ struct lwp_info
level on this process was a single-step. */ level on this process was a single-step. */
int stepping; int stepping;
/* Range to single step within. This is a copy of the step range
passed along the last resume request. See 'struct
thread_resume'. */
CORE_ADDR step_range_start; /* Inclusive */
CORE_ADDR step_range_end; /* Exclusive */
/* If this flag is set, we need to set the event request flags the /* If this flag is set, we need to set the event request flags the
next time we see this LWP stop. */ next time we see this LWP stop. */
int must_set_ptrace_flags; int must_set_ptrace_flags;

View File

@ -3175,6 +3175,12 @@ x86_emit_ops (void)
return &i386_emit_ops; return &i386_emit_ops;
} }
static int
x86_supports_range_stepping (void)
{
return 1;
}
/* This is initialized assuming an amd64 target. /* This is initialized assuming an amd64 target.
x86_arch_setup will correct it for i386 or amd64 targets. */ x86_arch_setup will correct it for i386 or amd64 targets. */
@ -3214,4 +3220,5 @@ struct linux_target_ops the_low_target =
x86_install_fast_tracepoint_jump_pad, x86_install_fast_tracepoint_jump_pad,
x86_emit_ops, x86_emit_ops,
x86_get_min_fast_tracepoint_insn_len, x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
}; };

View File

@ -2042,8 +2042,12 @@ handle_v_cont (char *own_buf)
{ {
p++; p++;
memset (&resume_info[i], 0, sizeof resume_info[i]);
if (p[0] == 's' || p[0] == 'S') if (p[0] == 's' || p[0] == 'S')
resume_info[i].kind = resume_step; resume_info[i].kind = resume_step;
else if (p[0] == 'r')
resume_info[i].kind = resume_step;
else if (p[0] == 'c' || p[0] == 'C') else if (p[0] == 'c' || p[0] == 'C')
resume_info[i].kind = resume_continue; resume_info[i].kind = resume_continue;
else if (p[0] == 't') else if (p[0] == 't')
@ -2063,9 +2067,22 @@ handle_v_cont (char *own_buf)
goto err; goto err;
resume_info[i].sig = gdb_signal_to_host (sig); resume_info[i].sig = gdb_signal_to_host (sig);
} }
else if (p[0] == 'r')
{
char *p1;
p = p + 1;
p1 = strchr (p, ',');
decode_address (&resume_info[i].step_range_start, p, p1 - p);
p = p1 + 1;
p1 = strchr (p, ':');
decode_address (&resume_info[i].step_range_end, p, p1 - p);
p = p1;
}
else else
{ {
resume_info[i].sig = 0;
p = p + 1; p = p + 1;
} }
@ -2311,6 +2328,11 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len)
if (strncmp (own_buf, "vCont?", 6) == 0) if (strncmp (own_buf, "vCont?", 6) == 0)
{ {
strcpy (own_buf, "vCont;c;C;s;S;t"); strcpy (own_buf, "vCont;c;C;s;S;t");
if (target_supports_range_stepping ())
{
own_buf = own_buf + strlen (own_buf);
strcpy (own_buf, ";r");
}
return; return;
} }
} }

View File

@ -57,6 +57,15 @@ struct thread_resume
linux; SuspendThread on win32). This is a host signal value (not linux; SuspendThread on win32). This is a host signal value (not
enum gdb_signal). */ enum gdb_signal). */
int sig; int sig;
/* Range to single step within. Valid only iff KIND is resume_step.
Single-step once, and then continuing stepping as long as the
thread stops in this range. (If the range is empty
[STEP_RANGE_START == STEP_RANGE_END], then this is a single-step
request.) */
CORE_ADDR step_range_start; /* Inclusive */
CORE_ADDR step_range_end; /* Exclusive */
}; };
/* Generally, what has the program done? */ /* Generally, what has the program done? */
@ -414,6 +423,8 @@ struct target_ops
to break a cyclic dependency. */ to break a cyclic dependency. */
void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type); void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type);
/* Return true if target supports range stepping. */
int (*supports_range_stepping) (void);
}; };
extern struct target_ops *the_target; extern struct target_ops *the_target;
@ -549,6 +560,10 @@ int kill_inferior (int);
#define target_read_btrace(tinfo, buffer, type) \ #define target_read_btrace(tinfo, buffer, type) \
(*the_target->read_btrace) (tinfo, buffer, type) (*the_target->read_btrace) (tinfo, buffer, type)
#define target_supports_range_stepping() \
(the_target->supports_range_stepping ? \
(*the_target->supports_range_stepping) () : 0)
/* Start non-stop mode, returns 0 on success, -1 on failure. */ /* Start non-stop mode, returns 0 on success, -1 on failure. */
int start_non_stop (int nonstop); int start_non_stop (int nonstop);