diff options
author | Pedro Alves <palves@redhat.com> | 2013-05-23 17:17:50 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2013-05-23 17:17:50 +0000 |
commit | c2d6af84da44465f30e3bb487721128dfe03d0e4 (patch) | |
tree | 6d1037aa6d79b5fcebf8e353c5f013128e1ca8a4 | |
parent | c1e36e3e919994da4fa0da232b173939f3e44bb8 (diff) | |
download | gdb-c2d6af84da44465f30e3bb487721128dfe03d0e4.zip gdb-c2d6af84da44465f30e3bb487721128dfe03d0e4.tar.gz gdb-c2d6af84da44465f30e3bb487721128dfe03d0e4.tar.bz2 |
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.
-rw-r--r-- | gdb/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/NEWS | 5 | ||||
-rw-r--r-- | gdb/gdbserver/ChangeLog | 27 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 63 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 8 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-low.c | 7 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 24 | ||||
-rw-r--r-- | gdb/gdbserver/target.h | 15 |
8 files changed, 141 insertions, 12 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ac1cbee..5baaf27 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -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> @@ -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. diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index bc2ba38..0af0bb8 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -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 diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index b01b37c..7e18942 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -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 diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 205d803..4dd3c9c 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -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; 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..1083aa9 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -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; } } diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index f257459..c57cb40 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -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); |