gdbserver/Linux: Use TRAP_BRKPT/TRAP_HWBPT
authorPedro Alves <palves@redhat.com>
Wed, 4 Mar 2015 20:41:17 +0000 (20:41 +0000)
committerPedro Alves <palves@redhat.com>
Wed, 4 Mar 2015 20:41:17 +0000 (20:41 +0000)
This patch adjusts gdbserver's Linux backend to tell gdbserver core
(and ultimately GDB) whether a trap was caused by a breakpoint.

It teaches the backend to get that information out of the si_code of
the SIGTRAP siginfo.

gdb/gdbserver/ChangeLog:
2015-03-04  Pedro Alves  <palves@redhat.com>

        * linux-low.c (check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]:
Decide whether a breakpoint triggered based on the SIGTRAP's
siginfo.si_code.
        (thread_still_has_status_pending_p) [USE_SIGTRAP_SIGINFO]: Don't check whether a
        breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
(linux_low_filter_event): Check for breakpoints before checking
watchpoints.
(linux_wait_1): Don't re-increment the PC if relying on SIGTRAP's
siginfo.si_code.
        (linux_stopped_by_sw_breakpoint)
        (linux_supports_stopped_by_sw_breakpoint)
        (linux_stopped_by_hw_breakpoint)
        (linux_supports_stopped_by_hw_breakpoint): New functions.
        (linux_target_ops): Install new target methods.

gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c

index 53538a4f0101d8ebe3642e248f06c42f79345511..2f1c9937189ee7d8c84097cd0b46505269508188 100644 (file)
@@ -1,3 +1,20 @@
+2015-03-04  Pedro Alves  <palves@redhat.com>
+
+        * linux-low.c (check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]:
+       Decide whether a breakpoint triggered based on the SIGTRAP's
+       siginfo.si_code.
+        (thread_still_has_status_pending_p) [USE_SIGTRAP_SIGINFO]: Don't check whether a
+        breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
+       (linux_low_filter_event): Check for breakpoints before checking
+       watchpoints.
+       (linux_wait_1): Don't re-increment the PC if relying on SIGTRAP's
+       siginfo.si_code.
+        (linux_stopped_by_sw_breakpoint)
+        (linux_supports_stopped_by_sw_breakpoint)
+        (linux_stopped_by_hw_breakpoint)
+        (linux_supports_stopped_by_hw_breakpoint): New functions.
+        (linux_target_ops): Install new target methods.
+
 2015-03-04  Pedro Alves  <palves@redhat.com>
 
        * remote-utils.c (prepare_resume_reply): Report swbreak/hbreak.
index 48e4fa3698bb357048892380d6f6aab8e732a4aa..4da11c6b083c2c3fb1d04ee962761e19904dad1d 100644 (file)
@@ -493,6 +493,9 @@ check_stopped_by_breakpoint (struct lwp_info *lwp)
   CORE_ADDR pc;
   CORE_ADDR sw_breakpoint_pc;
   struct thread_info *saved_thread;
+#if USE_SIGTRAP_SIGINFO
+  siginfo_t siginfo;
+#endif
 
   if (the_low_target.get_pc == NULL)
     return 0;
@@ -504,6 +507,54 @@ check_stopped_by_breakpoint (struct lwp_info *lwp)
   saved_thread = current_thread;
   current_thread = get_lwp_thread (lwp);
 
+#if USE_SIGTRAP_SIGINFO
+  if (ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread),
+             (PTRACE_TYPE_ARG3) 0, &siginfo) == 0)
+    {
+      if (siginfo.si_signo == SIGTRAP)
+       {
+         if (siginfo.si_code == GDB_ARCH_TRAP_BRKPT)
+           {
+             if (debug_threads)
+               {
+                 struct thread_info *thr = get_lwp_thread (lwp);
+
+                 debug_printf ("CSBB: Push back software breakpoint for %s\n",
+                               target_pid_to_str (ptid_of (thr)));
+               }
+
+             /* Back up the PC if necessary.  */
+             if (pc != sw_breakpoint_pc)
+               {
+                 struct regcache *regcache
+                   = get_thread_regcache (current_thread, 1);
+                 (*the_low_target.set_pc) (regcache, sw_breakpoint_pc);
+               }
+
+             lwp->stop_pc = sw_breakpoint_pc;
+             lwp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+             current_thread = saved_thread;
+             return 1;
+           }
+         else if (siginfo.si_code == TRAP_HWBKPT)
+           {
+             if (debug_threads)
+               {
+                 struct thread_info *thr = get_lwp_thread (lwp);
+
+                 debug_printf ("CSBB: Push back hardware "
+                               "breakpoint/watchpoint for %s\n",
+                               target_pid_to_str (ptid_of (thr)));
+               }
+
+             lwp->stop_pc = pc;
+             lwp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+             current_thread = saved_thread;
+             return 1;
+           }
+       }
+    }
+#else
   /* We may have just stepped a breakpoint instruction.  E.g., in
      non-stop mode, GDB first tells the thread A to step a range, and
      then the user inserts a breakpoint inside the range.  In that
@@ -548,6 +599,7 @@ check_stopped_by_breakpoint (struct lwp_info *lwp)
       current_thread = saved_thread;
       return 1;
     }
+#endif
 
   current_thread = saved_thread;
   return 0;
@@ -1242,6 +1294,8 @@ thread_still_has_status_pending_p (struct thread_info *thread)
                          lwpid_of (thread));
          discard = 1;
        }
+
+#if !USE_SIGTRAP_SIGINFO
       else if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
               && !(*the_low_target.breakpoint_at) (pc))
        {
@@ -1258,6 +1312,7 @@ thread_still_has_status_pending_p (struct thread_info *thread)
                          lwpid_of (thread));
          discard = 1;
        }
+#endif
 
       current_thread = saved_thread;
 
@@ -1875,15 +1930,24 @@ linux_low_filter_event (int lwpid, int wstat)
       return NULL;
     }
 
-  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
-      && check_stopped_by_watchpoint (child))
-    ;
-  else if (WIFSTOPPED (wstat) && linux_wstatus_maybe_breakpoint (wstat))
+  /* Check first whether this was a SW/HW breakpoint before checking
+     watchpoints, because at least s390 can't tell the data address of
+     hardware watchpoint hits, and returns stopped-by-watchpoint as
+     long as there's a watchpoint set.  */
+  if (WIFSTOPPED (wstat) && linux_wstatus_maybe_breakpoint (wstat))
     {
       if (check_stopped_by_breakpoint (child))
        have_stop_pc = 1;
     }
 
+  /* Note that TRAP_HWBKPT can indicate either a hardware breakpoint
+     or hardware watchpoint.  Check which is which if we got
+     TARGET_STOPPED_BY_HW_BREAKPOINT.  */
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+      && (child->stop_reason == TARGET_STOPPED_BY_NO_REASON
+         || child->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT))
+    check_stopped_by_watchpoint (child);
+
   if (!have_stop_pc)
     child->stop_pc = get_pc (child);
 
@@ -2938,8 +3002,10 @@ linux_wait_1 (ptid_t ptid,
   ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   /* Now that we've selected our final event LWP, un-adjust its PC if
-     it was a software breakpoint.  */
-  if (event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+     it was a software breakpoint, and the client doesn't know we can
+     adjust the breakpoint ourselves.  */
+  if (event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+      && !swbreak_feature)
     {
       int decr_pc = the_low_target.decr_pc_after_break;
 
@@ -4901,6 +4967,46 @@ linux_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
     return 1;
 }
 
+/* Implement the to_stopped_by_sw_breakpoint target_ops
+   method.  */
+
+static int
+linux_stopped_by_sw_breakpoint (void)
+{
+  struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+  return (lwp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_sw_breakpoint target_ops
+   method.  */
+
+static int
+linux_supports_stopped_by_sw_breakpoint (void)
+{
+  return USE_SIGTRAP_SIGINFO;
+}
+
+/* Implement the to_stopped_by_hw_breakpoint target_ops
+   method.  */
+
+static int
+linux_stopped_by_hw_breakpoint (void)
+{
+  struct lwp_info *lwp = get_thread_lwp (current_thread);
+
+  return (lwp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT);
+}
+
+/* Implement the to_supports_stopped_by_hw_breakpoint target_ops
+   method.  */
+
+static int
+linux_supports_stopped_by_hw_breakpoint (void)
+{
+  return USE_SIGTRAP_SIGINFO;
+}
+
 static int
 linux_stopped_by_watchpoint (void)
 {
@@ -6087,6 +6193,10 @@ static struct target_ops linux_target_ops = {
   linux_supports_z_point_type,
   linux_insert_point,
   linux_remove_point,
+  linux_stopped_by_sw_breakpoint,
+  linux_supports_stopped_by_sw_breakpoint,
+  linux_stopped_by_hw_breakpoint,
+  linux_supports_stopped_by_hw_breakpoint,
   linux_stopped_by_watchpoint,
   linux_stopped_data_address,
 #if defined(__UCLIBC__) && defined(HAS_NOMMU)        \