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>
Pedro Alves <palves@redhat.com>

View File

@ -95,6 +95,11 @@ vCont;r
stub to step through an address range itself, without GDB
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
* 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>
* 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;
}
/* 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
{
int signal;
@ -2337,6 +2347,7 @@ linux_wait_1 (ptid_t ptid,
int maybe_internal_trap;
int report_to_gdb;
int trace_event;
int in_step_range;
/* Translate generic target options into linux options. */
options = __WALL;
@ -2346,6 +2357,7 @@ linux_wait_1 (ptid_t ptid,
retry:
bp_explains_trap = 0;
trace_event = 0;
in_step_range = 0;
ourstatus->kind = TARGET_WAITKIND_IGNORE;
/* 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;
}
/* If GDB wanted this thread to single step, we always want to
report the SIGTRAP, and let GDB handle it. Watchpoints should
always be reported. So should 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. */
/* Note that all addresses are always "out of the step range" when
there's no range to begin with. */
in_step_range = lwp_in_step_range (event_child);
/* If GDB wanted this thread to single step, and the thread is out
of the step range, we always want to report the SIGTRAP, and let
GDB handle it. Watchpoints should always be reported. So should
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
|| current_inferior->last_resume_kind == resume_step
|| (current_inferior->last_resume_kind == resume_step
&& !in_step_range)
|| event_child->stopped_by_watchpoint
|| (!step_over_finished
|| (!step_over_finished && !in_step_range
&& !bp_explains_trap && !trace_event)
|| (gdb_breakpoint_here (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");
if (trace_event)
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
@ -2702,7 +2725,12 @@ Check if we're already there.\n",
if (debug_threads)
{
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)
fprintf (stderr, "Stopped by watchpoint.\n");
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];
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.
This can happen if LWP gets more than one signal while
trying to get out of a jump pad. */
@ -5094,6 +5125,15 @@ linux_supports_agent (void)
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. */
static int
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,
#endif
linux_supports_range_stepping,
};
static void

View File

@ -166,6 +166,8 @@ struct linux_target_ops
for use as a fast tracepoint. */
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;
@ -235,6 +237,12 @@ struct lwp_info
level on this process was a single-step. */
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
next time we see this LWP stop. */
int must_set_ptrace_flags;

View File

@ -3175,6 +3175,12 @@ x86_emit_ops (void)
return &i386_emit_ops;
}
static int
x86_supports_range_stepping (void)
{
return 1;
}
/* This is initialized assuming an amd64 target.
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_emit_ops,
x86_get_min_fast_tracepoint_insn_len,
x86_supports_range_stepping,
};

View File

@ -2042,8 +2042,12 @@ handle_v_cont (char *own_buf)
{
p++;
memset (&resume_info[i], 0, sizeof resume_info[i]);
if (p[0] == 's' || p[0] == 'S')
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')
resume_info[i].kind = resume_continue;
else if (p[0] == 't')
@ -2063,9 +2067,22 @@ handle_v_cont (char *own_buf)
goto err;
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
{
resume_info[i].sig = 0;
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)
{
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;
}
}

View File

@ -57,6 +57,15 @@ struct thread_resume
linux; SuspendThread on win32). This is a host signal value (not
enum gdb_signal). */
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? */
@ -414,6 +423,8 @@ struct target_ops
to break a cyclic dependency. */
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;
@ -549,6 +560,10 @@ int kill_inferior (int);
#define 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. */
int start_non_stop (int nonstop);