[PATCH 3/7] range stepping: gdbserver on x86/linux

Yao Qi
2013-04-11T02:43:56Z

This patch adds the support of range stepping in GDBserver for
x86/linux.

gdb/gdbserver:

2013-03-11 Yao Qi <yao@codesourcery.com>

* gdbthread.h (struct thread_info) <start, end>: New fields.
(thread_in_range_stepping_p): Declare.
(thread_clear_range_stepping): Declare.
* inferiors.c (thread_clear_range_stepping): New.
(add_thread): Call thread_clear_range_stepping.
(thread_in_range_stepping_p): New.
* linux-low.c (linux_wait_1): Call thread_in_range_stepping_p
to check if report stop to GDB.
Call thread_clear_range_stepping.
(linux_supports_range_stepping): New.
(linux_target_ops): Initialize 'supports_range_stepping' to
linux_supports_range_stepping.
* linux-low.h (struct linux_target_ops)
<supports_range_stepping>: New field.
* linux-x86-low.c (x86_supports_range_stepping): New.
(the_low_target): Initialize 'supports_range_stepping' to
* server.c (handle_v_cont): Handle action 'r'.
(handle_v_requests): Append ";r" if target supports range
stepping.
* target.h (struct target_ops) <supports_range_stepping>>:
New field.
(target_supports_range_stepping): New macro.
---
gdb/gdbserver/gdbthread.h | 6 ++++++
gdb/gdbserver/inferiors.c | 19 +++++++++++++++++++
gdb/gdbserver/linux-low.c | 38 +++++++++++++++++++++++++++++++++++---
gdb/gdbserver/linux-low.h | 2 ++
gdb/gdbserver/linux-x86-low.c | 7 +++++++
gdb/gdbserver/server.c | 40 ++++++++++++++++++++++++++++++++++++++++
gdb/gdbserver/target.h | 6 ++++++
7 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
index 5d4955b..fbd768b 100644
--- a/gdb/gdbserver/gdbthread.h
+++ b/gdb/gdbserver/gdbthread.h
@@ -62,12 +62,18 @@ struct thread_info

/* Branch trace target information for this thread. */
struct btrace_target_info *btrace;
+
+ /* The start and end address of range stepping. */
+ CORE_ADDR start, end;
};

extern struct inferior_list all_threads;

void remove_thread (struct thread_info *thread);
void add_thread (ptid_t ptid, void *target_data);
+int thread_in_range_stepping_p (struct thread_info *thread,
+ CORE_ADDR pc);
+void thread_clear_range_stepping (struct thread_info *thread);

struct thread_info *find_thread_ptid (ptid_t ptid);
struct thread_info *gdb_id_to_thread (unsigned int);
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index 6953d0e..2184a7b 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -85,6 +85,15 @@ remove_inferior (struct inferior_list *list,
list->tail = *cur;
}

+/* Clear the state of range stepping of THREAD. */
+
+void
+thread_clear_range_stepping (struct thread_info *thread)
+{
+ thread->start = 0;
+ thread->end = 0;
+}
+
void
add_thread (ptid_t thread_id, void *target_data)
{
@@ -103,6 +112,16 @@ add_thread (ptid_t thread_id, void *target_data)

new_thread->target_data = target_data;
set_inferior_regcache_data (new_thread, new_register_cache ());
+ thread_clear_range_stepping (new_thread);
+}
+
+/* Return true if THREAD is in range stepping. PC is the value of pc
+ of THREAD. */
+
+int
+thread_in_range_stepping_p (struct thread_info *thread, CORE_ADDR pc)
+{
+ return (pc >= thread->start && pc < thread->end);
}

ptid_t
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 72c51e0..e9879cc 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2313,6 +2313,7 @@ linux_wait_1 (ptid_t ptid,
int maybe_internal_trap;
int report_to_gdb;
int trace_event;
+ int in_range_stepping;

/* Translate generic target options into linux options. */
options = __WALL;
@@ -2322,6 +2323,7 @@ linux_wait_1 (ptid_t ptid,
retry:
bp_explains_trap = 0;
trace_event = 0;
+ in_range_stepping = 0;
ourstatus->kind = TARGET_WAITKIND_IGNORE;

/* If we were only supposed to resume one thread, only wait for
@@ -2615,6 +2617,9 @@ Check if we're already there.\n",
goto retry;
}

+ in_range_stepping
+ = thread_in_range_stepping_p (current_inferior,
+ event_child->stop_pc);
/* 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
@@ -2624,9 +2629,12 @@ Check if we're already there.\n",
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
+ /* Report the event back to GDB if the inferior is
+ stepping and not in range stepping. */
+ || (current_inferior->last_resume_kind == resume_step
+ && !in_range_stepping)
|| event_child->stopped_by_watchpoint
- || (!step_over_finished
+ || (!step_over_finished && !in_range_stepping
&& !bp_explains_trap && !trace_event)
|| (gdb_breakpoint_here (event_child->stop_pc)
&& gdb_condition_true_at_breakpoint (event_child->stop_pc)
@@ -2647,6 +2655,12 @@ Check if we're already there.\n",
fprintf (stderr, "Step-over finished.\n");
if (trace_event)
fprintf (stderr, "Tracepoint event.\n");
+ if (thread_in_range_stepping_p (current_inferior,
+ event_child->stop_pc))
+ fprintf (stderr, "Range stepping pc 0x%s [0x%s, 0x%s).\n",
+ paddress (event_child->stop_pc),
+ paddress (current_inferior->start),
+ paddress (current_inferior->end));
}

/* We're not reporting this breakpoint to GDB, so apply the
@@ -2678,7 +2692,13 @@ 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 (!thread_in_range_stepping_p (current_inferior,
+ event_child->stop_pc))
+ fprintf (stderr, "Range stepping is done, reporting event.\n");
+ else
+ fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
+ }
if (event_child->stopped_by_watchpoint)
fprintf (stderr, "Stopped by watchpoint.\n");
if (gdb_breakpoint_here (event_child->stop_pc))
@@ -2687,6 +2707,8 @@ Check if we're already there.\n",
fprintf (stderr, "Hit a non-gdbserver trap event.\n");
}

+ thread_clear_range_stepping (current_inferior);
+
/* Alright, we're going to report a stop. */

if (!non_stop && !stabilizing_threads)
@@ -5083,6 +5105,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)
@@ -5939,6 +5970,7 @@ static struct target_ops linux_target_ops = {
NULL,
NULL,
#endif
+ linux_supports_range_stepping,
};

static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 27dd3b5..5d3e8cf 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -168,6 +168,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;
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 31657d3..1d1df95 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -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,
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 6bb36d8..aa4ddf6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2040,10 +2040,15 @@ handle_v_cont (char *own_buf)
p = &own_buf[5];
while (*p)
{
+ CORE_ADDR start = 0;
+ CORE_ADDR end = 0;
+
p++;

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,6 +2068,21 @@ 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 (&start, p, p1 - p);
+
+ p = p1 + 1;
+ p1 = strchr (p, ':');
+ decode_address (&end, p, p1 - p);
+
+ resume_info[i].sig = 0;
+ p = p1;
+ }
else
{
resume_info[i].sig = 0;
@@ -2088,6 +2108,21 @@ handle_v_cont (char *own_buf)
goto err;

resume_info[i].thread = ptid;
+ if (end > 0)
+ {
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ /* GDB should not send range stepping for all threads of
+ a process, like 'vCont;rSTART,END:pPID.-1', TP can't
+ be NULL. */
+ gdb_assert (tp != NULL);
+
+ tp->start = start;
+ tp->end = end;
+
+ start = 0;
+ end = 0;
+ }

i++;
}
@@ -2311,6 +2346,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;
}
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index f257459..8c0790e 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -414,6 +414,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 +551,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);
--
1.7.7.6