range stepping: gdbserver (x86 GNU/Linux)
authorPedro Alves <palves@redhat.com>
Thu, 23 May 2013 17:17:50 +0000 (17:17 +0000)
committerPedro Alves <palves@redhat.com>
Thu, 23 May 2013 17:17:50 +0000 (17:17 +0000)
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.

gdb/ChangeLog
gdb/NEWS
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/linux-x86-low.c
gdb/gdbserver/server.c
gdb/gdbserver/target.h

index ac1cbee0e3109d530449a334c77fff973dbf82c5..5baaf272f76154c37ef1f1facdcda121d2aabd33 100644 (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>
 
index 1a4140d439a52d81001e38cc7b1d57da10bc38f6..a23e8e3d060167282781a56462ed9ef098a87f41 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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.
index bc2ba38d7a97e727969baf3012f3c8d7857e4971..0af0bb82dff680955535d66f4fd78966510bc814 100644 (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
index b01b37cb74c9e40a7adca0558455b6b35b2e4695..7e189422289d74b4105ede8fae890c291023a7d4 100644 (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
index 205d8039fa08e359c97598712163139e4203291b..4dd3c9c2c2b1be982f5b44f82d44df3257c512d2 100644 (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;
index 31657d3f07cca9df2395eac3c062afa6e4a344a5..1d1df95713729dc432250536011736be00fea4d4 100644 (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,
 };
index 6bb36d87c3761c6f045bbb5ff5dacae27652e5af..1083aa9e56e08a0c2895f1005531a6622cce3523 100644 (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;
        }
     }
index f257459b143555122454ad9c1cd5907869e54956..c57cb40deb250f899473241e7e8067340333d67d 100644 (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);