[6/7] Support conditional breakpoints on targets that can software single step in GDBServer.

Message ID 1441973603-15247-7-git-send-email-antoine.tremblay@ericsson.com
State New, archived
Headers

Commit Message

Antoine Tremblay Sept. 11, 2015, 12:13 p.m. UTC
  First a bit of background :

Since this patch https://sourceware.org/ml/gdb-patches/2015-04/msg01110.html
conditional breakpoints have been disabled on targets that do not support
software single step properly.

This was due to the get_next_pcs beeing to simple to be used or broken entirely.

This in effect made the get_next_pcs callback implemented on those targets dead
code since there is no situation in which a step over will occur as there is also
no software breakpoint support on those targets.

In fact the only purpose of the code after this change is to populate the
function pointer so that can_hardware_single_step can return false since the
previous way to know if a target supported hardware single step was to check if
a software single step function was implemented.

This patch cleans this up a bit so that targets that do support software single
step properly can enable conditional breakpoints and that we do not keep dead
code. It also makes it clear that if a target can't hardware or software single
step memory breakpoints are not supported.

To do this, on linux a new low target callback supports_hardware_single_step is
implemented and is to return true if the target does support such a
feature. This way can_hardware_single_step will use that callback rather than
check if the get_next_pcs implementation is present.

This enables the bad get_next_pcs functions to be removed and to be able to
trust that if get_next_pcs is implemented it means that the target really
supports software single step and that we can use it for conditional breakpoints
for example.

Note that the hardware single step support was enabled as per the current
behavior, I did not check if tile for example really has ptrace singlestep
support but since the current implementation assumed it had I kept it that way.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.

Also compilation was tested on aarch64, bfin, cris, crisv32,
m32r, mips, nios2, ppc, s390, sparc, tic6x, tile,  xtensa

gdb/gdbserver/ChangeLog:
	* linux-aarch64-low.c
	(aarch64_supports_hardware_single_step): New function.
	(struct linux_target_ops): New field supports_hardware_single_step.
	* linux-arm-low.c (arm_supports_hardware_single_step): New function.
	* linux-bfin-low.c (bfin_supports_hardware_single_step): Likewise.
	* linux-cris-low.c (cris_get_next_pcs): Remove.
	* linux-crisv32-low.c
	(cris_supports_hardware_single_step): New function.
	* linux-low.c (can_hardware_single_step): Use supports_hardware_single_step.
	(can_software_single_step): New function.
	(supports_breakpoints): Add single step support requirement.
	(linux_resume_one_lwp_throw): Add error if target can't single step.
	(start_step_over): Likewise.
	(linux_supports_software_single_step): New function.
	* linux-m32r-low.c (m32r_supports_hardware_single_step): New function.
	* linux-mips-low.c (mips_get_next_pcs): Remove.
	* linux-nios2-low.c (nios2_get_next_pcs): Remove.
	* linux-ppc-low.c (ppc_supports_hardware_single_step): New function.
	* linux-s390-low.c (s390_supports_hardware_single_step): Likewise.
	* linux-sh-low.c (sh_supports_hardware_single_step): Likewise.
	* linux-sparc-low.c (sparc_get_next_pcs): Remove.
	* linux-tic6x-low.c (tic6x_supports_hardware_single_step): New function.
	* linux-tile-low.c (tile_supports_hardware_single_step): Likewise.
	* linux-x86-low.c (x86_supports_hardware_single_step): Likewise.
	* linux-xtensa-low.c (xtensa_supports_hardware_single_step): Likewise.
	* server.c (handle_query): Support conditional breakpoints for software
        single step.
	* target.c (target_can_do_software_single_step): New function.
	* target.h (struct target_ops): New field target_can_do_software_single_step.
---
 gdb/gdbserver/linux-aarch64-low.c |  9 +++++++
 gdb/gdbserver/linux-arm-low.c     |  9 +++++++
 gdb/gdbserver/linux-bfin-low.c    | 28 +++++++++++++++++++++
 gdb/gdbserver/linux-cris-low.c    | 18 +------------
 gdb/gdbserver/linux-crisv32-low.c | 41 +++++++++++++++++-------------
 gdb/gdbserver/linux-low.c         | 53 +++++++++++++++++++++++++++++++--------
 gdb/gdbserver/linux-low.h         |  3 +++
 gdb/gdbserver/linux-m32r-low.c    | 28 +++++++++++++++++++++
 gdb/gdbserver/linux-mips-low.c    | 17 +------------
 gdb/gdbserver/linux-nios2-low.c   | 14 +----------
 gdb/gdbserver/linux-ppc-low.c     | 21 ++++++++++++++++
 gdb/gdbserver/linux-s390-low.c    | 21 ++++++++++++++++
 gdb/gdbserver/linux-sh-low.c      | 28 +++++++++++++++++++++
 gdb/gdbserver/linux-sparc-low.c   | 15 +----------
 gdb/gdbserver/linux-tic6x-low.c   | 28 +++++++++++++++++++++
 gdb/gdbserver/linux-tile-low.c    | 30 +++++++++++++++++++++-
 gdb/gdbserver/linux-x86-low.c     |  9 +++++++
 gdb/gdbserver/linux-xtensa-low.c  | 28 +++++++++++++++++++++
 gdb/gdbserver/server.c            |  8 ++----
 gdb/gdbserver/target.c            |  7 ++++++
 gdb/gdbserver/target.h            |  9 +++++++
 21 files changed, 329 insertions(+), 95 deletions(-)
  

Patch

diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 2956211..567ab98 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -571,6 +571,14 @@  aarch64_supports_range_stepping (void)
   return 1;
 }
 
+/* Support for hardware single step.  */
+
+static int
+aarch64_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 struct linux_target_ops the_low_target =
 {
   aarch64_arch_setup,
@@ -603,6 +611,7 @@  struct linux_target_ops the_low_target =
   NULL, /* emit_ops */
   NULL, /* get_min_fast_tracepoint_insn_len */
   aarch64_supports_range_stepping,
+  aarch64_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index eb98a36..a9ed91d 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -1112,6 +1112,14 @@  arm_arch_setup (void)
   arm_set_byte_order ();
 }
 
+/* Support for hardware single step.  */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+  return 0;
+}
+
 /* Register sets without using PTRACE_GETREGSET.  */
 
 static struct regset_info arm_regsets[] = {
@@ -1190,6 +1198,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* emit_ops */
   NULL, /* get_min_fast_tracepoint_insn_len */
   NULL, /* supports_range_stepping */
+  arm_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 3032a34..18b8e43 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -103,6 +103,14 @@  bfin_arch_setup (void)
   current_process ()->tdesc = tdesc_bfin;
 }
 
+/* Support for hardware single step.  */
+
+static int
+bfin_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct usrregs_info bfin_usrregs_info =
   {
     bfin_num_regs,
@@ -133,6 +141,26 @@  struct linux_target_ops the_low_target = {
   NULL, /* get_next_pcs */
   2,
   bfin_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  bfin_supports_hardware_single_step,
 };
 
 
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index cd1ae4d..b56827e 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -104,18 +104,6 @@  cris_breakpoint_at (CORE_ADDR where)
   return 0;
 }
 
-/* We only place breakpoints in empty marker functions, and thread locking
-   is outside of the function.  So rather than importing software single-step,
-   we can just run until exit.  */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-  CORE_ADDR pc;
-  collect_register_by_name (regcache, "srp", &pc);
-  VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
 static void
 cris_arch_setup (void)
 {
@@ -149,13 +137,9 @@  struct linux_target_ops the_low_target = {
   cris_get_pc,
   cris_set_pc,
   cris_breakpoint_from_pc,
-  cris_get_next_pcs,
+  NULL,
   0,
   cris_breakpoint_at,
-  0,
-  0,
-  0,
-  0,
 };
 
 void
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 05ad85a..4e9ebc1 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -100,22 +100,6 @@  cris_breakpoint_at (CORE_ADDR where)
   return 0;
 }
 
-/* We only place breakpoints in empty marker functions, and thread locking
-   is outside of the function.  So rather than importing software single-step,
-   we can just run until exit.  */
-
-/* FIXME: This function should not be needed, since we have PTRACE_SINGLESTEP
-   for CRISv32.  Without it, td_ta_event_getmsg in thread_db_create_event
-   will fail when debugging multi-threaded applications.  */
-static void
-cris_get_next_pcs (struct get_next_pcs *next_pcs)
-{
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-  CORE_ADDR pc;
-  collect_register_by_name (regcache, "srp", &pc);
-  VEC_safe_push (CORE_ADDR, next_pcs->result, pc);
-}
-
 static void
 cris_write_data_breakpoint (struct regcache *regcache,
 			    int bp, unsigned long start, unsigned long end)
@@ -386,6 +370,14 @@  cris_arch_setup (void)
   current_process ()->tdesc = tdesc_crisv32;
 }
 
+/* Support for hardware single step.  */
+
+static int
+cris_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct regset_info cris_regsets[] = {
   { PTRACE_GETREGS, PTRACE_SETREGS, 0, cris_num_regs * 4,
     GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
@@ -428,7 +420,7 @@  struct linux_target_ops the_low_target = {
   cris_get_pc,
   cris_set_pc,
   cris_breakpoint_from_pc,
-  cris_get_next_pcs,
+  NULL,
   0,
   cris_breakpoint_at,
   cris_supports_z_point_type,
@@ -436,6 +428,21 @@  struct linux_target_ops the_low_target = {
   cris_remove_point,
   cris_stopped_by_watchpoint,
   cris_stopped_data_address,
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  cris_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 229df46..f19769d 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -272,22 +272,35 @@  static void complete_ongoing_step_over (void);
    being stepped.  */
 ptid_t step_over_bkpt;
 
-/* True if the low target can hardware single-step.  Such targets
-   don't need a GET_NEXT_PCS callback.  */
+/* True if the low target can hardware single-step.  */
 
 static int
 can_hardware_single_step (void)
 {
-  return (the_low_target.get_next_pcs == NULL);
+  if (the_low_target.supports_hardware_single_step != NULL)
+    return the_low_target.supports_hardware_single_step ();
+  else
+    return 0;
+}
+
+/* True if the low target can software single-step.  Such targets
+   implement the GET_NEXT_PCS callback.  */
+
+static int
+can_software_single_step (void)
+{
+  return (the_low_target.get_next_pcs != NULL);
 }
 
-/* True if the low target supports memory breakpoints.  If so, we'll
-   have a GET_PC implementation.  */
+/* True if the low target supports memory breakpoints.  If so, we'll have a
+   GET_PC implementation. We will also need to support hardware or software
+   single stepping. */
 
 static int
 supports_breakpoints (void)
 {
-  return (the_low_target.get_pc != NULL);
+  return (the_low_target.get_pc != NULL &&
+	  (can_hardware_single_step () || can_software_single_step ()));
 }
 
 /* Returns true if this target can support fast tracepoints.  This
@@ -4026,10 +4039,16 @@  linux_resume_one_lwp_throw (struct lwp_info *lwp,
       {
 	step = 1;
       }
-    else {
-      install_software_single_step_breakpoints (lwp);
-      step = 0;
-    }
+    else if (can_software_single_step ())
+      {
+	install_software_single_step_breakpoints (lwp);
+	step = 0;
+      }
+    else
+      {
+	internal_error (__FILE__, __LINE__,
+			"stepping is not implemented on this target");
+      }
   }
 
   if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
@@ -4439,11 +4458,16 @@  start_step_over (struct lwp_info *lwp)
     {
       step = 1;
     }
-  else
+  else if (can_software_single_step ())
     {
       install_software_single_step_breakpoints (lwp);
       step = 0;
     }
+  else
+    {
+      internal_error (__FILE__, __LINE__,
+		      "stepping is not implemented on this target");
+    }
 
   current_thread = saved_thread;
 
@@ -5638,6 +5662,12 @@  linux_supports_hardware_single_step (void)
 }
 
 static int
+linux_supports_software_single_step (void)
+{
+  return can_software_single_step ();
+}
+
+static int
 linux_stopped_by_watchpoint (void)
 {
   struct lwp_info *lwp = get_thread_lwp (current_thread);
@@ -7064,6 +7094,7 @@  static struct target_ops linux_target_ops = {
   linux_mntns_unlink,
   linux_mntns_readlink,
   linux_breakpoint_from_pc,
+  linux_supports_software_single_step,
 };
 
 static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 597c36d..b6f8175 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -240,6 +240,9 @@  struct linux_target_ops
 
   /* Returns true if the low target supports range stepping.  */
   int (*supports_range_stepping) (void);
+
+  /* Returns true if the low target supports hardware single step.  */
+  int (*supports_hardware_single_step) (void);
 };
 
 extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 4712a32..3767f35 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -101,6 +101,14 @@  m32r_arch_setup (void)
   current_process ()->tdesc = tdesc_m32r;
 }
 
+/* Support for hardware single step.  */
+
+static int
+m32r_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct usrregs_info m32r_usrregs_info =
   {
     m32r_num_regs,
@@ -131,6 +139,26 @@  struct linux_target_ops the_low_target = {
   NULL,
   0,
   m32r_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  m32r_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d462f2c..e752b7e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -274,21 +274,6 @@  mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
   return (const unsigned char *) &mips_breakpoint;
 }
 
-/* We only place breakpoints in empty marker functions, and thread locking
-   is outside of the function.  So rather than importing software single-step,
-   we can just run until exit.  */
-static void
-mips_get_next_pcs (struct get_next_pcs *next_pcs)
-{
-  CORE_ADDR result;
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-  union mips_register ra;
-  collect_register_by_name (regcache, "r31", ra.buf);
-  result = register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64;
-
-  VEC_safe_push (CORE_ADDR, next_pcs->result, result);
-}
-
 static int
 mips_breakpoint_at (CORE_ADDR where)
 {
@@ -893,7 +878,7 @@  struct linux_target_ops the_low_target = {
   mips_get_pc,
   mips_set_pc,
   mips_breakpoint_from_pc,
-  mips_get_next_pcs,
+  NULL, /* get_next_pcs */
   0,
   mips_breakpoint_at,
   mips_supports_z_point_type,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index a24eb23..cb3addd 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -146,18 +146,6 @@  nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
   return (const unsigned char *) &nios2_breakpoint;
 }
 
-/* Implement the get_next_pcs linux_target_ops method.  */
-
-static void
-nios2_get_next_pcs (struct get_next_pcs *next_pcs)
-{
-  union nios2_register ra;
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-
-  collect_register_by_name (regcache, "ra", ra.buf);
-  VEC_safe_push (CORE_ADDR, next_pcs->result, ra.reg32);
-}
-
 /* Implement the breakpoint_at linux_target_ops method.  */
 
 static int
@@ -280,7 +268,7 @@  struct linux_target_ops the_low_target =
   nios2_get_pc,
   nios2_set_pc,
   nios2_breakpoint_from_pc,
-  nios2_get_next_pcs,
+  NULL, /* get_next_pcs */
   0,
   nios2_breakpoint_at,
 };
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index fa87e19..4939cfa 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -641,6 +641,14 @@  ppc_store_evrregset (struct regcache *regcache, const void *buf)
   supply_register_by_name (regcache, "spefscr", &regset->spefscr);
 }
 
+/* Support for hardware single step.  */
+
+static int
+ppc_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct regset_info ppc_regsets[] = {
   /* List the extra register sets before GENERAL_REGS.  That way we will
      fetch them every time, but still fall back to PTRACE_PEEKUSER for the
@@ -701,6 +709,19 @@  struct linux_target_ops the_low_target = {
   NULL,
   ppc_collect_ptrace_register,
   ppc_supply_ptrace_register,
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  ppc_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 44d3f26..794a621 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -611,6 +611,14 @@  s390_breakpoint_at (CORE_ADDR pc)
   return memcmp (c, s390_breakpoint, s390_breakpoint_len) == 0;
 }
 
+/* Support for hardware single step.  */
+
+static int
+s390_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct usrregs_info s390_usrregs_info =
   {
     s390_num_regs,
@@ -687,6 +695,19 @@  struct linux_target_ops the_low_target = {
   NULL,
   s390_collect_ptrace_register,
   s390_supply_ptrace_register,
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  s390_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 5c11fd7..6a210bf 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -98,6 +98,14 @@  sh_breakpoint_at (CORE_ADDR where)
   return 0;
 }
 
+/* Support for hardware single step.  */
+
+static int
+sh_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 /* Provide only a fill function for the general register set.  ps_lgetregs
    will use this for NPTL support.  */
 
@@ -159,6 +167,26 @@  struct linux_target_ops the_low_target = {
   NULL,
   0,
   sh_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  sh_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index e804281..25546e3 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -264,19 +264,6 @@  sparc_breakpoint_at (CORE_ADDR where)
   return 0;
 }
 
-/* We only place breakpoints in empty marker functions, and thread locking
-   is outside of the function.  So rather than importing software single-step,
-   we can just run until exit.  */
-static void
-sparc_get_next_pcs (struct get_next_pcs *next_pcs)
-{
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-  CORE_ADDR lr;
-  /* O7 is the equivalent to the 'lr' of other archs.  */
-  collect_register_by_name (regcache, "o7", &lr);
-  VEC_safe_push (CORE_ADDR, next_pcs->result, lr);
-}
-
 static void
 sparc_arch_setup (void)
 {
@@ -331,7 +318,7 @@  struct linux_target_ops the_low_target = {
   /* No sparc_set_pc is needed.  */
   NULL,
   sparc_breakpoint_from_pc,
-  sparc_get_next_pcs,
+  NULL,
   0,
   sparc_breakpoint_at,
   NULL,  /* supports_z_point_type */
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index 86b433c..22bffe2 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -339,6 +339,14 @@  tic6x_arch_setup (void)
   current_process ()->tdesc = tic6x_read_description ();
 }
 
+/* Support for hardware single step.  */
+
+static int
+tic6x_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static struct regsets_info tic6x_regsets_info =
   {
     tic6x_regsets, /* regsets */
@@ -377,6 +385,26 @@  struct linux_target_ops the_low_target = {
   NULL,
   0,
   tic6x_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  tic6x_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 802812b..b712b81 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -179,6 +179,14 @@  tile_arch_setup (void)
     current_process ()->tdesc = tdesc_tilegx;
 }
 
+/* Support for hardware single step.  */
+
+static int
+tile_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 
 struct linux_target_ops the_low_target =
 {
@@ -190,9 +198,29 @@  struct linux_target_ops the_low_target =
   tile_get_pc,
   tile_set_pc,
   tile_breakpoint_from_pc,
-  NULL,
+  NULL, /* get_next_pcs */
   0,
   tile_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  tile_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 0731141..0c5b6a2 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3256,6 +3256,14 @@  x86_supports_range_stepping (void)
   return 1;
 }
 
+/* Support for hardware single step.  */
+
+static int
+x86_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 /* This is initialized assuming an amd64 target.
    x86_arch_setup will correct it for i386 or amd64 targets.  */
 
@@ -3295,6 +3303,7 @@  struct linux_target_ops the_low_target =
   x86_emit_ops,
   x86_get_min_fast_tracepoint_insn_len,
   x86_supports_range_stepping,
+  x86_supports_hardware_single_step,
 };
 
 void
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index 1e2cf94..432b1b7 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -227,6 +227,14 @@  xtensa_arch_setup (void)
   current_process ()->tdesc = tdesc_xtensa;
 }
 
+/* Support for hardware single step.  */
+
+static int
+xtensa_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
 static const struct regs_info *
 xtensa_regs_info (void)
 {
@@ -245,6 +253,26 @@  struct linux_target_ops the_low_target = {
   NULL,
   0,
   xtensa_breakpoint_at,
+  NULL, /* supports_z_point_type */
+  NULL, /* insert_point */
+  NULL, /* remove_point */
+  NULL, /* stopped_by_watchpoint */
+  NULL, /* stopped_data_address */
+  NULL, /* collect_ptrace_register */
+  NULL, /* supply_ptrace_register */
+  NULL, /* siginfo_fixup */
+  NULL, /* new_process */
+  NULL, /* new_thread */
+  NULL, /* new_fork */
+  NULL, /* prepare_to_resume */
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  xtensa_supports_hardware_single_step,
 };
 
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 5c0d83d..fb0e843 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2192,13 +2192,9 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	  strcat (own_buf, ";tracenz+");
 	}
 
-      if (target_supports_hardware_single_step ())
+      if (target_supports_hardware_single_step () ||
+	  target_supports_software_single_step () )
 	{
-	  /* Support target-side breakpoint conditions and commands.
-	     GDBserver needs to step over the breakpoint if the condition
-	     is false.  GDBserver software single step is too simple, so
-	     disable conditional breakpoints if the target doesn't have
-	     hardware single step.  */
 	  strcat (own_buf, ";ConditionalBreakpoints+");
 	}
       strcat (own_buf, ";BreakpointCommands+");
diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c
index 17ff7a6..1ae42cc 100644
--- a/gdb/gdbserver/target.c
+++ b/gdb/gdbserver/target.c
@@ -224,3 +224,10 @@  target_can_do_hardware_single_step (void)
 {
   return 1;
 }
+
+/* Target can do software single step.  */
+int
+target_can_do_software_single_step (void)
+{
+  return 0;
+}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index fc2dbd7..aa40c4a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -443,6 +443,9 @@  struct target_ops
      can be NULL, the default breakpoint for the target should be returned in
      this case.  */
   const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+  /* Returns true if the target can do software single step.  */
+  int (*supports_software_single_step) (void);
 };
 
 extern struct target_ops *the_target;
@@ -621,6 +624,10 @@  int kill_inferior (int);
   (the_target->stopped_by_hw_breakpoint ? \
    (*the_target->stopped_by_hw_breakpoint) () : 0)
 
+#define target_supports_software_single_step() \
+  (the_target->supports_software_single_step ? \
+   (*the_target->supports_software_single_step) () : 0)
+
 /* Start non-stop mode, returns 0 on success, -1 on failure.   */
 
 int start_non_stop (int nonstop);
@@ -655,4 +662,6 @@  const char *target_pid_to_str (ptid_t);
 
 int target_can_do_hardware_single_step (void);
 
+int target_can_do_software_single_step (void);
+
 #endif /* TARGET_H */