range stepping: gdb
authorPedro Alves <palves@redhat.com>
Thu, 23 May 2013 17:15:35 +0000 (17:15 +0000)
committerPedro Alves <palves@redhat.com>
Thu, 23 May 2013 17:15:35 +0000 (17:15 +0000)
This patch teaches GDB to take advantage of target-assisted range
stepping.  It adds a new 'r ADDR1,ADDR2' action to vCont (vCont;r),
meaning, "step once, and keep stepping as long as the thread is in the
[ADDR1,ADDR2) range".

Rationale:

When user issues the "step" command on the following line of source,

   a = b + c + d * e - a;

GDB single-steps every single instruction until the program reaches a
new different line.  E.g., on x86_64, that line compiles to:

   0x08048434 <+65>:    mov    0x1c(%esp),%eax
   0x08048438 <+69>:    mov    0x30(%esp),%edx
   0x0804843c <+73>:    add    %eax,%edx
   0x0804843e <+75>:    mov    0x18(%esp),%eax
   0x08048442 <+79>:    imul   0x2c(%esp),%eax
   0x08048447 <+84>:    add    %edx,%eax
   0x08048449 <+86>:    sub    0x34(%esp),%eax
   0x0804844d <+90>:    mov    %eax,0x34(%esp)
   0x08048451 <+94>:    mov    0x1c(%esp),%eax

and the following is the RSP traffic between GDB and GDBserver:

 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:3c840408;thread:p2e13.2e13;core:1;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:3e840408;thread:p2e13.2e13;core:2;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:42840408;thread:p2e13.2e13;core:2;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:47840408;thread:p2e13.2e13;core:0;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:49840408;thread:p2e13.2e13;core:0;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:4d840408;thread:p2e13.2e13;core:0;
 --> vCont;s:p2e13.2e13;c
 <-- T0505:68efffbf;04:30efffbf;08:51840408;thread:p2e13.2e13;core:0;

IOW, a lot of roundtrips between GDB and GDBserver.

If we add a new command to the RSP, meaning "keep stepping and don't
report a stop until the program goes out of the [0x08048434,
0x08048451) address range", then the RSP traffic can be reduced down
to:

 --> vCont;r8048434,8048451:p2db0.2db0;c
 <-- T0505:68efffbf;04:30efffbf;08:51840408;thread:p2db0.2db0;core:1;

As number of packets is reduced dramatically, the performance of
stepping source lines is much improved.

In case something is wrong with range stepping on the stub side, the
debug info or even gdb, this adds a "set/show range-stepping" command
to be able to turn range stepping off.

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

* gdbthread.h (struct thread_control_state) <may_range_step>: New
field.
* infcmd.c (step_once, until_next_command): Enable range stepping.
* infrun.c (displaced_step_prepare): Disable range stepping.
(resume): Disable range stepping if stepping over a breakpoint or
we have software watchpoints.  If range stepping is enabled,
assert the thread is in the stepping range.
(clear_proceed_status_thread): Clear may_range_step.
(handle_inferior_event): Disable range stepping as soon as we know
the thread that hit the event.  Re-enable it whenever we're going
to step with a step range.
* remote.c (struct vCont_action_support) <r>: New field.
(use_range_stepping): New global.
(remote_vcont_probe): Handle 'r' action.
(append_resumption): Append an 'r' action if the thread may range
step.
(show_range_stepping): New function.
(set_range_stepping): New function.
(_initialize_remote): Call add_setshow_boolean_cmd to register the
'set range-stepping' and 'show range-stepping' commands.
* NEWS: Mention range stepping, the new vCont;r action, and the
new "set/show range-stepping" commands.

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

* gdb.texinfo (Packets): Document 'vCont;r'.
(Continuing and Stepping): Document target-assisted range
stepping, and the 'set range-stepping' and 'show range-stepping'
commands.

gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbthread.h
gdb/infcmd.c
gdb/infrun.c
gdb/remote.c

index c352fd29668a8495858ddeb91d09466c20bd5c89..ac1cbee0e3109d530449a334c77fff973dbf82c5 100644 (file)
@@ -1,3 +1,29 @@
+2013-05-23  Yao Qi  <yao@codesourcery.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdbthread.h (struct thread_control_state) <may_range_step>: New
+       field.
+       * infcmd.c (step_once, until_next_command): Enable range stepping.
+       * infrun.c (displaced_step_prepare): Disable range stepping.
+       (resume): Disable range stepping if stepping over a breakpoint or
+       we have software watchpoints.  If range stepping is enabled,
+       assert the thread is in the stepping range.
+       (clear_proceed_status_thread): Clear may_range_step.
+       (handle_inferior_event): Disable range stepping as soon as we know
+       the thread that hit the event.  Re-enable it whenever we're going
+       to step with a step range.
+       * remote.c (struct vCont_action_support) <r>: New field.
+       (use_range_stepping): New global.
+       (remote_vcont_probe): Handle 'r' action.
+       (append_resumption): Append an 'r' action if the thread may range
+       step.
+       (show_range_stepping): New function.
+       (set_range_stepping): New function.
+       (_initialize_remote): Call add_setshow_boolean_cmd to register the
+       'set range-stepping' and 'show range-stepping' commands.
+       * NEWS: Mention range stepping, the new vCont;r action, and the
+       new "set/show range-stepping" commands.
+
 2013-05-23  Yao Qi  <yao@codesourcery.com>
            Pedro Alves  <palves@redhat.com>
 
index c6a5e5dcb9b5d811c86ad0dc45377c81c19c3415..1a4140d439a52d81001e38cc7b1d57da10bc38f6 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -41,6 +41,10 @@ set debug nios2
 show debug nios2
   Control display of debugging messages related to Nios II targets.
 
+set range-stepping
+show range-stepping
+  Control whether target-assisted range stepping is enabled.
+
 * You can now use a literal value 'unlimited' for options that
   interpret 0 or -1 as meaning "unlimited".  E.g., "set
   trace-buffer-size unlimited" is now an alias for "set
@@ -78,6 +82,19 @@ show debug nios2
     ** ElinOS
     ** Wind River Linux
 
+* GDB now supports target-assigned range stepping with remote targets.
+  This improves the performance of stepping source lines by reducing
+  the number of control packets from/to GDB.  See "New remote packets"
+  below.
+
+* New remote packets
+
+vCont;r
+
+  The vCont packet supports a new 'r' action, that tells the remote
+  stub to step through an address range itself, without GDB
+  involvemement at each single-step.
+
 *** Changes in GDB 7.6
 
 * Target record has been renamed to record-full.
index ddc766adc15be2e6b4e9fbe06a284bd818a0ff68..7330f295c67cdd064c09c623988d90b047d4d93b 100644 (file)
@@ -1,3 +1,11 @@
+2013-05-23  Yao Qi  <yao@codesourcery.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdb.texinfo (Packets): Document 'vCont;r'.
+       (Continuing and Stepping): Document target-assisted range
+       stepping, and the 'set range-stepping' and 'show range-stepping'
+       commands.
+
 2013-05-23  Joel Brobecker  <brobecker@adacore.com>
 
        * gdb.texinfo (Installed System-wide Configuration Scripts):
index b68d2f8b4ebf977fa66987108acab43058c8cf34..721e96a1bf71d4dac7ece32d6d0c97782942957a 100644 (file)
@@ -5219,6 +5219,38 @@ Execute one machine instruction, but if it is a function call,
 proceed until the function returns.
 
 An argument is a repeat count, as in @code{next}.
+
+@end table
+
+@anchor{range stepping}
+@cindex range stepping
+@cindex target-assisted range stepping
+By default, and if available, @value{GDBN} makes use of
+target-assisted @dfn{range stepping}.  In other words, whenever you
+use a stepping command (e.g., @code{step}, @code{next}), @value{GDBN}
+tells the target to step the corresponding range of instruction
+addresses instead of issuing multiple single-steps.  This speeds up
+line stepping, particularly for remote targets.  Ideally, there should
+be no reason you would want to turn range stepping off.  However, it's
+possible that a bug in the debug info, a bug in the remote stub (for
+remote targets), or even a bug in @value{GDBN} could make line
+stepping behave incorrectly when target-assisted range stepping is
+enabled.  You can use the following command to turn off range stepping
+if necessary:
+
+@table @code
+@kindex set range-stepping
+@kindex show range-stepping
+@item set range-stepping
+@itemx show range-stepping
+Control whether range stepping is enabled.
+
+If @code{on}, and the target supports it, @value{GDBN} tells the
+target to step a range of addresses itself, instead of issuing
+multiple single-steps.  If @code{off}, @value{GDBN} always issues
+single-steps, even if range stepping is supported by the target.  The
+default is @code{on}.
+
 @end table
 
 @node Skipping Over Functions and Files
@@ -37511,6 +37543,22 @@ Step.
 Step with signal @var{sig}.  The signal @var{sig} should be two hex digits.
 @item t
 Stop.
+@item r @var{start},@var{end}
+Step once, and then keep stepping as long as the thread stops at
+addresses between @var{start} (inclusive) and @var{end} (exclusive).
+The remote stub reports a stop reply when either the thread goes out
+of the range or is stopped due to an unrelated reason, such as hitting
+a breakpoint.  @xref{range stepping}.
+
+If the range is empty (@var{start} == @var{end}), then the action
+becomes equivalent to the @samp{s} action.  In other words,
+single-step once, and report the stop (even if the stepped instruction
+jumps to @var{start}).
+
+(A stop reply may be sent at any point even if the PC is still within
+the stepping range; for example, it is valid to implement this packet
+in a degenerate way as a single instruction step operation.)
+
 @end table
 
 The optional argument @var{addr} normally associated with the 
index a9f8a941df17e02564ebd3b708aa43ec475dd53a..c3b85dc7058d44a299de3adeb5dac7fe0fd39934 100644 (file)
@@ -65,6 +65,14 @@ struct thread_control_state
   CORE_ADDR step_range_start;  /* Inclusive */
   CORE_ADDR step_range_end;    /* Exclusive */
 
+  /* If GDB issues a target step request, and this is nonzero, the
+     target should single-step this thread once, and then continue
+     single-stepping it without GDB core involvement as long as the
+     thread stops in the step range above.  If this is zero, the
+     target should ignore the step range, and only issue one single
+     step.  */
+  int may_range_step;
+
   /* Stack frame address as of when stepping command was issued.
      This is how we know when we step into a subroutine call, and how
      to set the frame for the breakpoint used to step out.  */
index aeb24ff3fccdb7a1112c32c72d43b80a649593d0..30621e4ba50fbef2dba90424631bd51ea5851d27 100644 (file)
@@ -1046,9 +1046,14 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
                                 &tp->control.step_range_start,
                                 &tp->control.step_range_end);
 
+         tp->control.may_range_step = 1;
+
          /* If we have no line info, switch to stepi mode.  */
          if (tp->control.step_range_end == 0 && step_stop_if_no_debug)
-           tp->control.step_range_start = tp->control.step_range_end = 1;
+           {
+             tp->control.step_range_start = tp->control.step_range_end = 1;
+             tp->control.may_range_step = 0;
+           }
          else if (tp->control.step_range_end == 0)
            {
              const char *name;
@@ -1337,6 +1342,7 @@ until_next_command (int from_tty)
       tp->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
       tp->control.step_range_end = sal.end;
     }
+  tp->control.may_range_step = 1;
 
   tp->control.step_over_calls = STEP_OVER_ALL;
 
index 57c427da6d429ac68d67bdfb36c471aa269b89ed..376a440d46a0c5b346f10b1d567cc6099dd22520 100644 (file)
@@ -1311,6 +1311,7 @@ static int
 displaced_step_prepare (ptid_t ptid)
 {
   struct cleanup *old_cleanups, *ignore_cleanups;
+  struct thread_info *tp = find_thread_ptid (ptid);
   struct regcache *regcache = get_thread_regcache (ptid);
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   CORE_ADDR original, copy;
@@ -1323,6 +1324,12 @@ displaced_step_prepare (ptid_t ptid)
      support displaced stepping.  */
   gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
 
+  /* Disable range stepping while executing in the scratch pad.  We
+     want a single-step even if executing the displaced instruction in
+     the scratch buffer lands within the stepping range (e.g., a
+     jump/branch).  */
+  tp->control.may_range_step = 0;
+
   /* We have to displaced step one thread at a time, as we only have
      access to a single scratch space per inferior.  */
 
@@ -1778,6 +1785,11 @@ how to step past a permanent breakpoint on this architecture.  Try using\n\
 a command like `return' or `jump' to continue execution."));
     }
 
+  /* If we have a breakpoint to step over, make sure to do a single
+     step only.  Same if we have software watchpoints.  */
+  if (tp->control.trap_expected || bpstat_should_step ())
+    tp->control.may_range_step = 0;
+
   /* If enabled, step over breakpoints by executing a copy of the
      instruction at a different address.
 
@@ -1939,6 +1951,16 @@ a command like `return' or `jump' to continue execution."));
           displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
         }
 
+      if (tp->control.may_range_step)
+       {
+         /* If we're resuming a thread with the PC out of the step
+            range, then we're doing some nested/finer run control
+            operation, like stepping the thread out of the dynamic
+            linker or the displaced stepping scratch pad.  We
+            shouldn't have allowed a range step then.  */
+         gdb_assert (pc_in_thread_step_range (pc, tp));
+       }
+
       /* Install inferior's terminal modes.  */
       target_terminal_inferior ();
 
@@ -1980,6 +2002,7 @@ clear_proceed_status_thread (struct thread_info *tp)
   tp->control.trap_expected = 0;
   tp->control.step_range_start = 0;
   tp->control.step_range_end = 0;
+  tp->control.may_range_step = 0;
   tp->control.step_frame_id = null_frame_id;
   tp->control.step_stack_frame_id = null_frame_id;
   tp->control.step_over_calls = STEP_OVER_UNDEBUGGABLE;
@@ -3223,6 +3246,10 @@ handle_inferior_event (struct execution_control_state *ecs)
       /* If it's a new thread, add it to the thread database.  */
       if (ecs->event_thread == NULL)
        ecs->event_thread = add_thread (ecs->ptid);
+
+      /* Disable range stepping.  If the next step request could use a
+        range, this will be end up re-enabled then.  */
+      ecs->event_thread->control.may_range_step = 0;
     }
 
   /* Dependent on valid ECS->EVENT_THREAD.  */
@@ -4717,6 +4744,11 @@ process_event_stop_test:
           paddress (gdbarch, ecs->event_thread->control.step_range_start),
           paddress (gdbarch, ecs->event_thread->control.step_range_end));
 
+      /* Tentatively re-enable range stepping; `resume' disables it if
+        necessary (e.g., if we're stepping over a breakpoint or we
+        have software watchpoints).  */
+      ecs->event_thread->control.may_range_step = 1;
+
       /* When stepping backward, stop at beginning of line range
         (unless it's the function entry point, in which case
         keep going back to the call point).  */
@@ -5233,6 +5265,7 @@ process_event_stop_test:
 
   ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
   ecs->event_thread->control.step_range_end = stop_pc_sal.end;
+  ecs->event_thread->control.may_range_step = 1;
   set_step_info (frame, stop_pc_sal);
 
   if (debug_infrun)
index eb58b9454e23cac23f13199e63e0dd61d71e8044..658fae22074538c9911f2914fe1affeabd2d443f 100644 (file)
@@ -260,8 +260,15 @@ struct vCont_action_support
 {
   /* vCont;t */
   int t;
+
+  /* vCont;r */
+  int r;
 };
 
+/* Controls whether GDB is willing to use range stepping.  */
+
+static int use_range_stepping = 1;
+
 /* Description of the remote protocol state for the currently
    connected target.  This is per-target state, and independent of the
    selected architecture.  */
@@ -4653,6 +4660,7 @@ remote_vcont_probe (struct remote_state *rs)
       support_c = 0;
       support_C = 0;
       rs->supports_vCont.t = 0;
+      rs->supports_vCont.r = 0;
       while (p && *p == ';')
        {
          p++;
@@ -4666,6 +4674,8 @@ remote_vcont_probe (struct remote_state *rs)
            support_C = 1;
          else if (*p == 't' && (*(p + 1) == ';' || *(p + 1) == 0))
            rs->supports_vCont.t = 1;
+         else if (*p == 'r' && (*(p + 1) == ';' || *(p + 1) == 0))
+           rs->supports_vCont.r = 1;
 
          p = strchr (p, ';');
        }
@@ -4697,6 +4707,42 @@ append_resumption (char *p, char *endp,
 
   if (step && siggnal != GDB_SIGNAL_0)
     p += xsnprintf (p, endp - p, ";S%02x", siggnal);
+  else if (step
+          /* GDB is willing to range step.  */
+          && use_range_stepping
+          /* Target supports range stepping.  */
+          && rs->supports_vCont.r
+          /* We don't currently support range stepping multiple
+             threads with a wildcard (though the protocol allows it,
+             so stubs shouldn't make an active effort to forbid
+             it).  */
+          && !(remote_multi_process_p (rs) && ptid_is_pid (ptid)))
+    {
+      struct thread_info *tp;
+
+      if (ptid_equal (ptid, minus_one_ptid))
+       {
+         /* If we don't know about the target thread's tid, then
+            we're resuming magic_null_ptid (see caller).  */
+         tp = find_thread_ptid (magic_null_ptid);
+       }
+      else
+       tp = find_thread_ptid (ptid);
+      gdb_assert (tp != NULL);
+
+      if (tp->control.may_range_step)
+       {
+         int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+         p += xsnprintf (p, endp - p, ";r%s,%s",
+                         phex_nz (tp->control.step_range_start,
+                                  addr_size),
+                         phex_nz (tp->control.step_range_end,
+                                  addr_size));
+       }
+      else
+       p += xsnprintf (p, endp - p, ";s");
+    }
   else if (step)
     p += xsnprintf (p, endp - p, ";s");
   else if (siggnal != GDB_SIGNAL_0)
@@ -11659,6 +11705,44 @@ remote_upload_trace_state_variables (struct uploaded_tsv **utsvp)
   return 0;
 }
 
+/* The "set/show range-stepping" show hook.  */
+
+static void
+show_range_stepping (struct ui_file *file, int from_tty,
+                    struct cmd_list_element *c,
+                    const char *value)
+{
+  fprintf_filtered (file,
+                   _("Debugger's willingness to use range stepping "
+                     "is %s.\n"), value);
+}
+
+/* The "set/show range-stepping" set hook.  */
+
+static void
+set_range_stepping (char *ignore_args, int from_tty,
+                   struct cmd_list_element *c)
+{
+  /* Whene enabling, check whether range stepping is actually
+     supported by the target, and warn if not.  */
+  if (use_range_stepping)
+    {
+      if (remote_desc != NULL)
+       {
+         struct remote_state *rs = get_remote_state ();
+
+         if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN)
+           remote_vcont_probe (rs);
+
+         if (remote_protocol_packets[PACKET_vCont].support == PACKET_ENABLE
+             && rs->supports_vCont.r)
+           return;
+       }
+
+      warning (_("Range stepping is not supported by the current target"));
+    }
+}
+
 void
 _initialize_remote (void)
 {
@@ -12056,6 +12140,20 @@ Set the remote pathname for \"run\""), _("\
 Show the remote pathname for \"run\""), NULL, NULL, NULL,
                                   &remote_set_cmdlist, &remote_show_cmdlist);
 
+  add_setshow_boolean_cmd ("range-stepping", class_run,
+                          &use_range_stepping, _("\
+Enable or disable range stepping."), _("\
+Show whether target-assisted range stepping is enabled."), _("\
+If on, and the target supports it, when stepping a source line, GDB\n\
+tells the target to step the corresponding range of addresses itself instead\n\
+of issuing multiple single-steps.  This speeds up source level\n\
+stepping.  If off, GDB always issues single-steps, even if range\n\
+stepping is supported by the target.  The default is on."),
+                          set_range_stepping,
+                          show_range_stepping,
+                          &setlist,
+                          &showlist);
+
   /* Eventually initialize fileio.  See fileio.c */
   initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
 
This page took 0.065206 seconds and 4 git commands to generate.