gdb/
authorPedro Alves <palves@redhat.com>
Wed, 14 Dec 2011 17:20:32 +0000 (17:20 +0000)
committerPedro Alves <palves@redhat.com>
Wed, 14 Dec 2011 17:20:32 +0000 (17:20 +0000)
2011-12-14  Pedro Alves  <pedro@codesourcery.com>

PR threads/10729

* linux-nat.c (linux_nat_new_thread): Change parameter to an lwp
pointer.
(linux_nat_prepare_to_resume): New global.
(lwp_free): New.
(purge_lwp_list): Use it.
(add_lwp): Call linux_nat_new_thread even on the first LWP.
Adjust to interface change.
(delete_lwp): Call lwp_free instead of xfree.
(detach_callback, linux_nat_detach, resume_lwp, linux_nat_resume)
(linux_handle_syscall_trap, linux_handle_extended_wait)
(linux_nat_filter_event, resume_stopped_resumed_lwps): Call
linux_nat_prepare_to_resume before resuming.
(linux_stop_lwp): New.
(linux_nat_set_new_thread): Adjust.
(linux_nat_set_prepare_to_resume): New.
* linux-nat.h (struct arch_lwp_info): Forward declare.
(struct lwp_info) <arch_private>: New field.
(linux_stop_lwp): Declare.
(linux_nat_set_new_thread): Adjust.
(linux_nat_set_prepare_to_resume): New.

* i386-nat.c (DR_NADDR, DR_STATUS, DR_CONTROL)
(struct i386_debug_reg_state): Move to i386-nat.h.
(dr_mirror): Comment.
(i386_debug_reg_state): New.
(i386_update_inferior_debug_regs): Simplify.
(i386_stopped_data_address): Use the debug register state from the
inferior, not from the local cache.
* i386-nat.h (struct i386_dr_low_type): Delete reset_addr and
unset_status fields.  New get_addr and get_control fields.
(DR_FIRSTADDR, DR_LASTADDR, DR_CONTROL): Moved from i386-nat.c.
(DR_NADDR, DR_STATUS): New.
(struct i386_debug_reg_state): Moved from i386-nat.c.

* amd64-linux-nat.c (struct arch_lwp_info): New.
(amd64_linux_dr): Delete global.
(amd64_linux_dr_get_addr): New.
(amd64_linux_dr_get_control): New.
(amd64_linux_dr_unset_status): Delete.
(amd64_linux_dr_set_addr): Reimplement.
(amd64_linux_dr_reset_addr): Delete.
(update_debug_registers_callback): New.
(amd64_linux_dr_set_control): Reimplement.
(amd64_linux_dr_set_addr): Reimplement.
(amd64_linux_prepare_to_resume): New.
(amd64_linux_new_thread): Change parameter to an lwp pointer.
Reimplement.
(_initialize_amd64_linux_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
amd64_linux_dr_get_control as i386_dr_low.get_control.  Install
amd64_linux_dr_get_addr as i386_dr_low.get_addr.  Install
amd64_linux_prepare_to_resume.
* i386-linux-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
(DR_CONTROL): Delete.
(struct arch_lwp_info): New.
(i386_linux_dr): Delete global.
(i386_linux_dr_set_control): Reimplement.
(i386_linux_dr_get_addr): New.
(i386_linux_dr_set_addr): Reimplement.
(i386_linux_dr_get_control): New.
(update_debug_registers_callback): New.
(i386_linux_dr_unset_status): Delete.
(i386_linux_dr_set_addr): Reimplement.
(i386_linux_prepare_to_resume): New.
(i386_linux_new_thread): Change parameter to an lwp pointer.
Reimplement.
(_initialize_i386_linux_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
i386_linux_dr_get_control as i386_dr_low.get_control.  Install
i386_linux_dr_get_addr as i386_dr_low.get_addr.  Install
i386_linux_prepare_to_resume.

* arm-linux-nat.c (arm_linux_new_thread): Change parameter to an
lwp pointer.  Adjust.
* ia64-linux-nat.c (ia64_linux_new_thread): Likewise.
* mips-linux-nat.c (mips_linux_new_thread): Likewise.
* ppc-linux-nat.c (ppc_linux_new_thread): Likewise.
* s390-nat.c (s390_fix_watch_points): Likewise.

* i386-darwin-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
(DR_CONTROL): Delete.
(i386_darwin_dr_reset_addr): Delete.
(i386_darwin_dr_get_addr): New.
(i386_darwin_dr_get_control): New.
* go32-nat.c
(go32_get_dr7, go32_get_dr): New.
(init_go32_ops): No longer install i386_dr_low.reset_addr.
Install go32_get_dr7 as i386_dr_low.get_control.  Install
go32_get_dr as i386_dr_low.get_addr.
* i386bsd-nat.c (i386bsd_dr_get): New.
(i386bsd_dr_reset_addr): Delete.
(i386bsd_dr_get_addr): New.
(i386bsd_dr_get_status): Use i386bsd_dr_get.
(i386bsd_dr_get_control): New.
* i386bsd-nat.h (i386bsd_dr_reset_addr): Delete.
(i386bsd_dr_get_addr): New.
(i386bsd_dr_get_control): New.
* i386fbsd-nat.c (_initialize_i386fbsd_nat): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
i386bsd_dr_get_control as i386_dr_low.get_control.  Install
i386bsd_dr_get_addr as i386_dr_low.get_addr.
* windows-nat.c (init_windows_ops): No longer install
i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
cygwin_get_dr7 as i386_dr_low.get_control.  Install cygwin_get_dr
as i386_dr_low.get_addr.
(cygwin_get_dr): New.
(cygwin_get_dr7): New.

gdb/testsuite/
2011-12-14  Pedro Alves  <pedro@codesourcery.com>

PR threads/10729

* gdb.mi/watch-nonstop.c: New file.
  * gdb.mi/mi-watch-nonstop.exp: New file.

21 files changed:
gdb/ChangeLog
gdb/amd64-linux-nat.c
gdb/arm-linux-nat.c
gdb/go32-nat.c
gdb/i386-darwin-nat.c
gdb/i386-linux-nat.c
gdb/i386-nat.c
gdb/i386-nat.h
gdb/i386bsd-nat.c
gdb/i386bsd-nat.h
gdb/i386fbsd-nat.c
gdb/ia64-linux-nat.c
gdb/linux-nat.c
gdb/linux-nat.h
gdb/mips-linux-nat.c
gdb/ppc-linux-nat.c
gdb/s390-nat.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.mi/mi-watch-nonstop.exp [new file with mode: 0644]
gdb/testsuite/gdb.mi/watch-nonstop.c [new file with mode: 0644]
gdb/windows-nat.c

index 1f8cade34ec524396a7eaa64d73c412e2803142d..951e9fd64e4ece3baf192826d6519757946a641d 100644 (file)
@@ -1,3 +1,115 @@
+2011-12-14  Pedro Alves  <pedro@codesourcery.com>
+
+       PR threads/10729
+
+       * linux-nat.c (linux_nat_new_thread): Change parameter to an lwp
+       pointer.
+       (linux_nat_prepare_to_resume): New global.
+       (lwp_free): New.
+       (purge_lwp_list): Use it.
+       (add_lwp): Call linux_nat_new_thread even on the first LWP.
+       Adjust to interface change.
+       (delete_lwp): Call lwp_free instead of xfree.
+       (detach_callback, linux_nat_detach, resume_lwp, linux_nat_resume)
+       (linux_handle_syscall_trap, linux_handle_extended_wait)
+       (linux_nat_filter_event, resume_stopped_resumed_lwps): Call
+       linux_nat_prepare_to_resume before resuming.
+       (linux_stop_lwp): New.
+       (linux_nat_set_new_thread): Adjust.
+       (linux_nat_set_prepare_to_resume): New.
+       * linux-nat.h (struct arch_lwp_info): Forward declare.
+       (struct lwp_info) <arch_private>: New field.
+       (linux_stop_lwp): Declare.
+       (linux_nat_set_new_thread): Adjust.
+       (linux_nat_set_prepare_to_resume): New.
+
+       * i386-nat.c (DR_NADDR, DR_STATUS, DR_CONTROL)
+       (struct i386_debug_reg_state): Move to i386-nat.h.
+       (dr_mirror): Comment.
+       (i386_debug_reg_state): New.
+       (i386_update_inferior_debug_regs): Simplify.
+       (i386_stopped_data_address): Use the debug register state from the
+       inferior, not from the local cache.
+       * i386-nat.h (struct i386_dr_low_type): Delete reset_addr and
+       unset_status fields.  New get_addr and get_control fields.
+       (DR_FIRSTADDR, DR_LASTADDR, DR_CONTROL): Moved from i386-nat.c.
+       (DR_NADDR, DR_STATUS): New.
+       (struct i386_debug_reg_state): Moved from i386-nat.c.
+
+       * amd64-linux-nat.c (struct arch_lwp_info): New.
+       (amd64_linux_dr): Delete global.
+       (amd64_linux_dr_get_addr): New.
+       (amd64_linux_dr_get_control): New.
+       (amd64_linux_dr_unset_status): Delete.
+       (amd64_linux_dr_set_addr): Reimplement.
+       (amd64_linux_dr_reset_addr): Delete.
+       (update_debug_registers_callback): New.
+       (amd64_linux_dr_set_control): Reimplement.
+       (amd64_linux_dr_set_addr): Reimplement.
+       (amd64_linux_prepare_to_resume): New.
+       (amd64_linux_new_thread): Change parameter to an lwp pointer.
+       Reimplement.
+       (_initialize_amd64_linux_nat): No longer install
+       i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
+       amd64_linux_dr_get_control as i386_dr_low.get_control.  Install
+       amd64_linux_dr_get_addr as i386_dr_low.get_addr.  Install
+       amd64_linux_prepare_to_resume.
+       * i386-linux-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
+       (DR_CONTROL): Delete.
+       (struct arch_lwp_info): New.
+       (i386_linux_dr): Delete global.
+       (i386_linux_dr_set_control): Reimplement.
+       (i386_linux_dr_get_addr): New.
+       (i386_linux_dr_set_addr): Reimplement.
+       (i386_linux_dr_get_control): New.
+       (update_debug_registers_callback): New.
+       (i386_linux_dr_unset_status): Delete.
+       (i386_linux_dr_set_addr): Reimplement.
+       (i386_linux_prepare_to_resume): New.
+       (i386_linux_new_thread): Change parameter to an lwp pointer.
+       Reimplement.
+       (_initialize_i386_linux_nat): No longer install
+       i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
+       i386_linux_dr_get_control as i386_dr_low.get_control.  Install
+       i386_linux_dr_get_addr as i386_dr_low.get_addr.  Install
+       i386_linux_prepare_to_resume.
+
+       * arm-linux-nat.c (arm_linux_new_thread): Change parameter to an
+       lwp pointer.  Adjust.
+       * ia64-linux-nat.c (ia64_linux_new_thread): Likewise.
+       * mips-linux-nat.c (mips_linux_new_thread): Likewise.
+       * ppc-linux-nat.c (ppc_linux_new_thread): Likewise.
+       * s390-nat.c (s390_fix_watch_points): Likewise.
+
+       * i386-darwin-nat.c (DR_FIRSTADDR, DR_LASTADDR, DR_STATUS)
+       (DR_CONTROL): Delete.
+       (i386_darwin_dr_reset_addr): Delete.
+       (i386_darwin_dr_get_addr): New.
+       (i386_darwin_dr_get_control): New.
+       * go32-nat.c
+       (go32_get_dr7, go32_get_dr): New.
+       (init_go32_ops): No longer install i386_dr_low.reset_addr.
+       Install go32_get_dr7 as i386_dr_low.get_control.  Install
+       go32_get_dr as i386_dr_low.get_addr.
+       * i386bsd-nat.c (i386bsd_dr_get): New.
+       (i386bsd_dr_reset_addr): Delete.
+       (i386bsd_dr_get_addr): New.
+       (i386bsd_dr_get_status): Use i386bsd_dr_get.
+       (i386bsd_dr_get_control): New.
+       * i386bsd-nat.h (i386bsd_dr_reset_addr): Delete.
+       (i386bsd_dr_get_addr): New.
+       (i386bsd_dr_get_control): New.
+       * i386fbsd-nat.c (_initialize_i386fbsd_nat): No longer install
+       i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
+       i386bsd_dr_get_control as i386_dr_low.get_control.  Install
+       i386bsd_dr_get_addr as i386_dr_low.get_addr.
+       * windows-nat.c (init_windows_ops): No longer install
+       i386_dr_low.reset_addr and i386_dr_low.unset_status.  Install
+       cygwin_get_dr7 as i386_dr_low.get_control.  Install cygwin_get_dr
+       as i386_dr_low.get_addr.
+       (cygwin_get_dr): New.
+       (cygwin_get_dr7): New.
+
 2011-12-14  Pedro Alves  <pedro@codesourcery.com>
 
        * ia64-tdep.c (ia64_memory_remove_breakpoint): Use
index c67396562f535027a8c7dc68f28c96974367359d..288160b33896da4e37b6ff3f54925020a13224f6 100644 (file)
 #define PTRACE_SETREGSET       0x4205
 #endif
 
+/* Per-thread arch-specific data we want to keep.  */
+
+struct arch_lwp_info
+{
+  /* Non-zero if our copy differs from what's recorded in the thread.  */
+  int debug_registers_changed;
+};
+
 /* Does the current host support PTRACE_GETREGSET?  */
 static int have_ptrace_getregset = -1;
 
@@ -265,8 +273,6 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
 \f
 /* Support for debug registers.  */
 
-static unsigned long amd64_linux_dr[DR_CONTROL + 1];
-
 static unsigned long
 amd64_linux_dr_get (ptid_t ptid, int regnum)
 {
@@ -304,75 +310,116 @@ amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
     perror_with_name (_("Couldn't write debug register"));
 }
 
-/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST.  */
+/* Return the inferior's debug register REGNUM.  */
 
-static void
-amd64_linux_dr_set_control (unsigned long control)
+static CORE_ADDR
+amd64_linux_dr_get_addr (int regnum)
 {
-  struct lwp_info *lp;
+  /* DR6 and DR7 are retrieved with some other way.  */
+  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
 
-  amd64_linux_dr[DR_CONTROL] = control;
-  ALL_LWPS (lp)
-    amd64_linux_dr_set (lp->ptid, DR_CONTROL, control);
+  return amd64_linux_dr_get (inferior_ptid, regnum);
 }
 
-/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST.  */
+/* Return the inferior's DR7 debug control register.  */
 
-static void
-amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+static unsigned long
+amd64_linux_dr_get_control (void)
 {
-  struct lwp_info *lp;
+  return amd64_linux_dr_get (inferior_ptid, DR_CONTROL);
+}
 
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
+
+static unsigned long
+amd64_linux_dr_get_status (void)
+{
+  return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+}
+
+/* Callback for iterate_over_lwps.  Update the debug registers of
+   LWP.  */
+
+static int
+update_debug_registers_callback (struct lwp_info *lwp, void *arg)
+{
+  /* The actual update is done later just before resuming the lwp, we
+     just mark that the registers need updating.  */
+  lwp->arch_private->debug_registers_changed = 1;
+
+  /* If the lwp isn't stopped, force it to momentarily pause, so we
+     can update its debug registers.  */
+  if (!lwp->stopped)
+    linux_stop_lwp (lwp);
 
-  amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
-  ALL_LWPS (lp)
-    amd64_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr);
+  return 0;
 }
 
-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST.  */
+/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior.  */
 
 static void
-amd64_linux_dr_reset_addr (int regnum)
+amd64_linux_dr_set_control (unsigned long control)
 {
-  amd64_linux_dr_set_addr (regnum, 0);
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
 }
 
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+   inferior.  */
 
-static unsigned long
-amd64_linux_dr_get_status (void)
+static void
+amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
 {
-  return amd64_linux_dr_get (inferior_ptid, DR_STATUS);
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
 }
 
-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST.  */
+/* Called when resuming a thread.
+   If the debug regs have changed, update the thread's copies.  */
 
 static void
-amd64_linux_dr_unset_status (unsigned long mask)
+amd64_linux_prepare_to_resume (struct lwp_info *lwp)
 {
-  struct lwp_info *lp;
+  int clear_status = 0;
 
-  ALL_LWPS (lp)
+  if (lwp->arch_private->debug_registers_changed)
     {
-      unsigned long value;
-      
-      value = amd64_linux_dr_get (lp->ptid, DR_STATUS);
-      value &= ~mask;
-      amd64_linux_dr_set (lp->ptid, DR_STATUS, value);
+      struct i386_debug_reg_state *state = i386_debug_reg_state ();
+      int i;
+
+      for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+       if (state->dr_ref_count[i] > 0)
+         {
+           amd64_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
+
+           /* If we're setting a watchpoint, any change the inferior
+              had done itself to the debug registers needs to be
+              discarded, otherwise, i386_stopped_data_address can get
+              confused.  */
+           clear_status = 1;
+         }
+
+      amd64_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
+
+      lwp->arch_private->debug_registers_changed = 0;
     }
-}
 
+  if (clear_status || lwp->stopped_by_watchpoint)
+    amd64_linux_dr_set (lwp->ptid, DR_STATUS, 0);
+}
 
 static void
-amd64_linux_new_thread (ptid_t ptid)
+amd64_linux_new_thread (struct lwp_info *lp)
 {
-  int i;
+  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
 
-  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
-    amd64_linux_dr_set (ptid, i, amd64_linux_dr[i]);
+  info->debug_registers_changed = 1;
 
-  amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]);
+  lp->arch_private = info;
 }
 \f
 
@@ -785,9 +832,9 @@ _initialize_amd64_linux_nat (void)
 
   i386_dr_low.set_control = amd64_linux_dr_set_control;
   i386_dr_low.set_addr = amd64_linux_dr_set_addr;
-  i386_dr_low.reset_addr = amd64_linux_dr_reset_addr;
+  i386_dr_low.get_addr = amd64_linux_dr_get_addr;
   i386_dr_low.get_status = amd64_linux_dr_get_status;
-  i386_dr_low.unset_status = amd64_linux_dr_unset_status;
+  i386_dr_low.get_control = amd64_linux_dr_get_control;
   i386_set_debug_register_length (8);
 
   /* Override the GNU/Linux inferior startup hook.  */
@@ -804,4 +851,5 @@ _initialize_amd64_linux_nat (void)
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, amd64_linux_new_thread);
   linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
+  linux_nat_set_prepare_to_resume (t, amd64_linux_prepare_to_resume);
 }
index 6424dc3489ce8df46e5397969e08fd27feec8949..c614f968654bcc6bbf83e1fc635f07b7792769d3 100644 (file)
@@ -1178,9 +1178,9 @@ arm_linux_watchpoint_addr_within_range (struct target_ops *target,
 /* Handle thread creation.  We need to copy the breakpoints and watchpoints
    in the parent thread to the child thread.  */
 static void
-arm_linux_new_thread (ptid_t ptid)
+arm_linux_new_thread (struct lwp_info *lp)
 {
-  int tid = TIDGET (ptid);
+  int tid = TIDGET (lp->ptid);
   const struct arm_linux_hwbp_cap *info = arm_linux_get_hwbp_cap ();
 
   if (info != NULL)
index 8295adfc4acb64fe8dba658a5511b75520096c8d..79618ecbe8f4c56e55aa76a5a6cb34ebcc4e19d5 100644 (file)
@@ -801,6 +801,29 @@ go32_get_dr6 (void)
   return STATUS;
 }
 
+/* Get the value of the DR7 debug status register from the inferior.
+   Here we just return the value stored in D_REGS, as we've got it
+   from the last go32_wait call.  */
+
+static unsigned long
+go32_get_dr7 (void)
+{
+  return CONTROL;
+}
+
+/* Get the value of the DR debug register I from the inferior.  Here
+   we just return the value stored in D_REGS, as we've got it from the
+   last go32_wait call.  */
+
+static CORE_ADDR
+go32_get_dr (int i)
+{
+  if (i < 0 || i > 3)
+    internal_error (__FILE__, __LINE__,
+                   _("Invalid register %d in go32_get_dr.\n"), i);
+  return D_REGS[i];
+}
+
 /* Put the device open on handle FD into either raw or cooked
    mode, return 1 if it was in raw mode, zero otherwise.  */
 
@@ -984,8 +1007,9 @@ init_go32_ops (void)
 
   i386_dr_low.set_control = go32_set_dr7;
   i386_dr_low.set_addr = go32_set_dr;
-  i386_dr_low.reset_addr = NULL;
   i386_dr_low.get_status = go32_get_dr6;
+  i386_dr_low.get_control = go32_get_dr7;
+  i386_dr_low.get_addr = go32_get_dr;
   i386_set_debug_register_length (4);
 
   go32_ops.to_magic = OPS_MAGIC;
index 61e2e1598566d4bdcf0a23f1400553a3d874d55b..23f6a6dfe2172544c285ab4050809bb6251cdd6b 100644 (file)
@@ -263,23 +263,6 @@ i386_darwin_store_inferior_registers (struct target_ops *ops,
 
 /* Support for debug registers, boosted mostly from i386-linux-nat.c.  */
 
-#ifndef DR_FIRSTADDR
-#define DR_FIRSTADDR 0
-#endif
-
-#ifndef DR_LASTADDR
-#define DR_LASTADDR 3
-#endif
-
-#ifndef DR_STATUS
-#define DR_STATUS 6
-#endif
-
-#ifndef DR_CONTROL
-#define DR_CONTROL 7
-#endif
-
-
 static void
 i386_darwin_dr_set (int regnum, uint32_t value)
 {
@@ -410,12 +393,10 @@ i386_darwin_dr_set_addr (int regnum, CORE_ADDR addr)
   i386_darwin_dr_set (DR_FIRSTADDR + regnum, addr);
 }
 
-void
-i386_darwin_dr_reset_addr (int regnum)
+CORE_ADDR
+i386_darwin_dr_get_addr (int regnum)
 {
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
-  i386_darwin_dr_set (DR_FIRSTADDR + regnum, 0L);
+  return i386_darwin_dr_get (regnum);
 }
 
 unsigned long
@@ -424,6 +405,12 @@ i386_darwin_dr_get_status (void)
   return i386_darwin_dr_get (DR_STATUS);
 }
 
+unsigned long
+i386_darwin_dr_get_control (void)
+{
+  return i386_darwin_dr_get (DR_CONTROL);
+}
+
 void
 darwin_check_osabi (darwin_inferior *inf, thread_t thread)
 {
index 7eb49ae7f012b8babc62b4df8ae9582a17f1a91d..190979b8a6653587160c66b154ed8246cce63c86 100644 (file)
 #include <sys/debugreg.h>
 #endif
 
-#ifndef DR_FIRSTADDR
-#define DR_FIRSTADDR 0
-#endif
-
-#ifndef DR_LASTADDR
-#define DR_LASTADDR 3
-#endif
-
-#ifndef DR_STATUS
-#define DR_STATUS 6
-#endif
-
-#ifndef DR_CONTROL
-#define DR_CONTROL 7
-#endif
-
 /* Prototypes for supply_gregset etc.  */
 #include "gregset.h"
 
 #define PTRACE_SETREGSET       0x4205
 #endif
 
+/* Per-thread arch-specific data we want to keep.  */
+
+struct arch_lwp_info
+{
+  /* Non-zero if our copy differs from what's recorded in the thread.  */
+  int debug_registers_changed;
+};
+
 /* Does the current host support PTRACE_GETREGSET?  */
 static int have_ptrace_getregset = -1;
 \f
@@ -651,8 +643,6 @@ i386_linux_store_inferior_registers (struct target_ops *ops,
 
 /* Support for debug registers.  */
 
-static unsigned long i386_linux_dr[DR_CONTROL + 1];
-
 /* Get debug register REGNUM value from only the one LWP of PTID.  */
 
 static unsigned long
@@ -692,74 +682,116 @@ i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
     perror_with_name (_("Couldn't write debug register"));
 }
 
-/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST.  */
+/* Return the inferior's debug register REGNUM.  */
 
-static void
-i386_linux_dr_set_control (unsigned long control)
+static CORE_ADDR
+i386_linux_dr_get_addr (int regnum)
 {
-  struct lwp_info *lp;
+  /* DR6 and DR7 are retrieved with some other way.  */
+  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
 
-  i386_linux_dr[DR_CONTROL] = control;
-  ALL_LWPS (lp)
-    i386_linux_dr_set (lp->ptid, DR_CONTROL, control);
+  return i386_linux_dr_get (inferior_ptid, regnum);
 }
 
-/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST.  */
+/* Return the inferior's DR7 debug control register.  */
 
-static void
-i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+static unsigned long
+i386_linux_dr_get_control (void)
 {
-  struct lwp_info *lp;
+  return i386_linux_dr_get (inferior_ptid, DR_CONTROL);
+}
 
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
 
-  i386_linux_dr[DR_FIRSTADDR + regnum] = addr;
-  ALL_LWPS (lp)
-    i386_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr);
+static unsigned long
+i386_linux_dr_get_status (void)
+{
+  return i386_linux_dr_get (inferior_ptid, DR_STATUS);
 }
 
-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST.  */
+/* Callback for iterate_over_lwps.  Update the debug registers of
+   LWP.  */
+
+static int
+update_debug_registers_callback (struct lwp_info *lwp, void *arg)
+{
+  /* The actual update is done later just before resuming the lwp, we
+     just mark that the registers need updating.  */
+  lwp->arch_private->debug_registers_changed = 1;
+
+  /* If the lwp isn't stopped, force it to momentarily pause, so we
+     can update its debug registers.  */
+  if (!lwp->stopped)
+    linux_stop_lwp (lwp);
+
+  return 0;
+}
+
+/* Set DR_CONTROL to ADDR in all LWPs of the current inferior.  */
 
 static void
-i386_linux_dr_reset_addr (int regnum)
+i386_linux_dr_set_control (unsigned long control)
 {
-  i386_linux_dr_set_addr (regnum, 0);
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
 }
 
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+   inferior.  */
 
-static unsigned long
-i386_linux_dr_get_status (void)
+static void
+i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
 {
-  return i386_linux_dr_get (inferior_ptid, DR_STATUS);
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
 }
 
-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST.  */
+/* Called when resuming a thread.
+   If the debug regs have changed, update the thread's copies.  */
 
 static void
-i386_linux_dr_unset_status (unsigned long mask)
+i386_linux_prepare_to_resume (struct lwp_info *lwp)
 {
-  struct lwp_info *lp;
+  int clear_status = 0;
 
-  ALL_LWPS (lp)
+  if (lwp->arch_private->debug_registers_changed)
     {
-      unsigned long value;
+      struct i386_debug_reg_state *state = i386_debug_reg_state ();
+      int i;
+
+      for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+       if (state->dr_ref_count[i] > 0)
+         {
+           i386_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
+
+           /* If we're setting a watchpoint, any change the inferior
+              had done itself to the debug registers needs to be
+              discarded, otherwise, i386_stopped_data_address can get
+              confused.  */
+           clear_status = 1;
+         }
 
-      value = i386_linux_dr_get (lp->ptid, DR_STATUS);
-      value &= ~mask;
-      i386_linux_dr_set (lp->ptid, DR_STATUS, value);
+      i386_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
+
+      lwp->arch_private->debug_registers_changed = 0;
     }
+
+  if (clear_status || lwp->stopped_by_watchpoint)
+    i386_linux_dr_set (lwp->ptid, DR_STATUS, 0);
 }
 
 static void
-i386_linux_new_thread (ptid_t ptid)
+i386_linux_new_thread (struct lwp_info *lp)
 {
-  int i;
+  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
 
-  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
-    i386_linux_dr_set (ptid, i, i386_linux_dr[i]);
+  info->debug_registers_changed = 1;
 
-  i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]);
+  lp->arch_private = info;
 }
 \f
 
@@ -978,9 +1010,9 @@ _initialize_i386_linux_nat (void)
 
   i386_dr_low.set_control = i386_linux_dr_set_control;
   i386_dr_low.set_addr = i386_linux_dr_set_addr;
-  i386_dr_low.reset_addr = i386_linux_dr_reset_addr;
+  i386_dr_low.get_addr = i386_linux_dr_get_addr;
   i386_dr_low.get_status = i386_linux_dr_get_status;
-  i386_dr_low.unset_status = i386_linux_dr_unset_status;
+  i386_dr_low.get_control = i386_linux_dr_get_control;
   i386_set_debug_register_length (4);
 
   /* Override the default ptrace resume method.  */
@@ -999,4 +1031,5 @@ _initialize_i386_linux_nat (void)
   /* Register the target.  */
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, i386_linux_new_thread);
+  linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume);
 }
index 568b79bfced5d040f705d48de730539d41519f8e..593401b9983353ab3adaf19f61b61c1806304895 100644 (file)
@@ -43,11 +43,6 @@ struct i386_dr_low_type i386_dr_low;
 /* Support for 8-byte wide hw watchpoints.  */
 #define TARGET_HAS_DR_LEN_8 (i386_dr_low.debug_register_length == 8)
 
-/* Debug registers' indices.  */
-#define DR_NADDR       4       /* The number of debug address registers.  */
-#define DR_STATUS      6       /* Index of debug status register (DR6).  */
-#define DR_CONTROL     7       /* Index of debug control register (DR7).  */
-
 /* DR7 Debug Control register fields.  */
 
 /* How many bits to skip in DR7 to get to R/W and LEN fields.  */
@@ -158,23 +153,6 @@ struct i386_dr_low_type i386_dr_low;
 /* A macro to loop over all debug registers.  */
 #define ALL_DEBUG_REGISTERS(i) for (i = 0; i < DR_NADDR; i++)
 
-
-/* Global state needed to track h/w watchpoints.  */
-
-struct i386_debug_reg_state
-{
-  /* Mirror the inferior's DRi registers.  We keep the status and
-     control registers separated because they don't hold addresses.
-     Note that since we can change these mirrors while threads are
-     running, we never trust them to explain a cause of a trap.
-     For that, we need to peek directly in the inferior registers.  */
-  CORE_ADDR dr_mirror[DR_NADDR];
-  unsigned dr_status_mirror, dr_control_mirror;
-
-  /* Reference counts for each debug register.  */
-  int dr_ref_count[DR_NADDR];
-};
-
 /* Clear the reference counts and forget everything we knew about the
    debug registers.  */
 
@@ -192,8 +170,16 @@ i386_init_dregs (struct i386_debug_reg_state *state)
   state->dr_status_mirror  = 0;
 }
 
+/* The local mirror of the inferior's debug registers.  Currently this
+   is a global, but it should really be per-inferior.  */
 static struct i386_debug_reg_state dr_mirror;
 
+struct i386_debug_reg_state *
+i386_debug_reg_state (void)
+{
+  return &dr_mirror;
+}
+
 /* Whether or not to print the mirrored debug registers.  */
 static int maint_show_dr;
 
@@ -513,22 +499,7 @@ i386_update_inferior_debug_regs (struct i386_debug_reg_state *new_state)
   ALL_DEBUG_REGISTERS (i)
     {
       if (I386_DR_VACANT (new_state, i) != I386_DR_VACANT (&dr_mirror, i))
-       {
-         if (!I386_DR_VACANT (new_state, i))
-           {
-             i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
-
-             /* Only a sanity check for leftover bits (set possibly only
-                by inferior).  */
-             if (i386_dr_low.unset_status)
-               i386_dr_low.unset_status (I386_DR_WATCH_MASK (i));
-           }
-         else
-           {
-             if (i386_dr_low.reset_addr)
-               i386_dr_low.reset_addr (i);
-           }
-       }
+       i386_dr_low.set_addr (i, new_state->dr_mirror[i]);
       else
        gdb_assert (new_state->dr_mirror[i] == dr_mirror.dr_mirror[i]);
     }
@@ -634,28 +605,62 @@ i386_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
   CORE_ADDR addr = 0;
   int i;
   int rc = 0;
+  /* The current thread's DR_STATUS.  We always need to read this to
+     check whether some watchpoint caused the trap.  */
   unsigned status;
-  unsigned control;
-  struct i386_debug_reg_state *state = &dr_mirror;
-
-  dr_mirror.dr_status_mirror = i386_dr_low.get_status ();
-  status = dr_mirror.dr_status_mirror;
-  control = dr_mirror.dr_control_mirror;
+  /* We need DR_CONTROL as well, but only iff DR_STATUS indicates a
+     data breakpoint trap.  Only fetch it when necessary, to avoid an
+     unnecessary extra syscall when no watchpoint triggered.  */
+  int control_p = 0;
+  unsigned control = 0;
+
+  /* In non-stop/async, threads can be running while we change the
+     global dr_mirror (and friends).  Say, we set a watchpoint, and
+     let threads resume.  Now, say you delete the watchpoint, or
+     add/remove watchpoints such that dr_mirror changes while threads
+     are running.  On targets that support non-stop,
+     inserting/deleting watchpoints updates the global dr_mirror only.
+     It does not update the real thread's debug registers; that's only
+     done prior to resume.  Instead, if threads are running when the
+     mirror changes, a temporary and transparent stop on all threads
+     is forced so they can get their copy of the debug registers
+     updated on re-resume.  Now, say, a thread hit a watchpoint before
+     having been updated with the new dr_mirror contents, and we
+     haven't yet handled the corresponding SIGTRAP.  If we trusted
+     dr_mirror below, we'd mistake the real trapped address (from the
+     last time we had updated debug registers in the thread) with
+     whatever was currently in dr_mirror.  So to fix this, dr_mirror
+     always represents intention, what we _want_ threads to have in
+     debug registers.  To get at the address and cause of the trap, we
+     need to read the state the thread still has in its debug
+     registers.
+
+     In sum, always get the current debug register values the current
+     thread has, instead of trusting the global mirror.  If the thread
+     was running when we last changed watchpoints, the mirror no
+     longer represents what was set in this thread's debug
+     registers.  */
+  status = i386_dr_low.get_status ();
 
   ALL_DEBUG_REGISTERS(i)
     {
-      if (I386_DR_WATCH_HIT (status, i)
-         /* This second condition makes sure DRi is set up for a data
-            watchpoint, not a hardware breakpoint.  The reason is
-            that GDB doesn't call the target_stopped_data_address
-            method except for data watchpoints.  In other words, I'm
-            being paranoiac.  */
-         && I386_DR_GET_RW_LEN (control, i) != 0
-         /* This third condition makes sure DRi is not vacant, this
-            avoids false positives in windows-nat.c.  */
-         && !I386_DR_VACANT (state, i))
+      if (!I386_DR_WATCH_HIT (status, i))
+       continue;
+
+      if (!control_p)
+       {
+         control = i386_dr_low.get_control ();
+         control_p = 1;
+       }
+
+      /* This second condition makes sure DRi is set up for a data
+        watchpoint, not a hardware breakpoint.  The reason is that
+        GDB doesn't call the target_stopped_data_address method
+        except for data watchpoints.  In other words, I'm being
+        paranoiac.  */
+      if (I386_DR_GET_RW_LEN (control, i) != 0)
        {
-         addr = state->dr_mirror[i];
+         addr = i386_dr_low.get_addr (i);
          rc = 1;
          if (maint_show_dr)
            i386_show_dr (&dr_mirror, "watchpoint_hit", addr, -1, hw_write);
index 819c6b8e656630433c51a2e9e452f01e1a043a34..1a75daa80e2a7709c9507a7d4532069974282c56 100644 (file)
@@ -53,31 +53,54 @@ extern void i386_use_watchpoints (struct target_ops *);
       set_addr                 -- put an address into one debug
                                  register for all LWPs
 
-      reset_addr               -- reset the address stored in
-                                 one debug register for all LWPs
+      get_addr                 -- return the address in a given debug
+                                 register of the current LWP
 
       get_status               -- return the value of the debug
                                  status (DR6) register for current LWP
 
-      unset_status             -- unset the specified bits of the debug
-                                 status (DR6) register for all LWPs
+      get_control               -- return the value of the debug
+                                 control (DR7) register for current LWP
 
    Additionally, the native file should set the debug_register_length
    field to 4 or 8 depending on the number of bytes used for
    deubg registers.  */
 
-struct i386_dr_low_type 
+struct i386_dr_low_type
   {
     void (*set_control) (unsigned long);
     void (*set_addr) (int, CORE_ADDR);
-    void (*reset_addr) (int);
+    CORE_ADDR (*get_addr) (int);
     unsigned long (*get_status) (void);
-    void (*unset_status) (unsigned long);
+    unsigned long (*get_control) (void);
     int debug_register_length;
   };
 
 extern struct i386_dr_low_type i386_dr_low;
 
+/* Debug registers' indices.  */
+#define DR_FIRSTADDR 0
+#define DR_LASTADDR  3
+#define DR_NADDR     4 /* The number of debug address registers.  */
+#define DR_STATUS    6 /* Index of debug status register (DR6).  */
+#define DR_CONTROL   7 /* Index of debug control register (DR7).  */
+
+/* Global state needed to track h/w watchpoints.  */
+
+struct i386_debug_reg_state
+{
+  /* Mirror the inferior's DRi registers.  We keep the status and
+     control registers separated because they don't hold addresses.
+     Note that since we can change these mirrors while threads are
+     running, we never trust them to explain a cause of a trap.
+     For that, we need to peek directly in the inferior registers.  */
+  CORE_ADDR dr_mirror[DR_NADDR];
+  unsigned dr_status_mirror, dr_control_mirror;
+
+  /* Reference counts for each debug register.  */
+  int dr_ref_count[DR_NADDR];
+};
+
 /* Use this function to set i386_dr_low debug_register_length field
    rather than setting it directly to check that the length is only
    set once.  It also enables the 'maint set/show show-debug-regs' 
@@ -89,4 +112,9 @@ extern void i386_set_debug_register_length (int len);
 
 extern void i386_cleanup_dregs (void);
 
+/* Return a pointer to the the local mirror of the inferior's debug
+   registers.  */
+
+extern struct i386_debug_reg_state *i386_debug_reg_state (void);
+
 #endif /* I386_NAT_H */
index fcd772fb9397ce3573c8baa919fca8ceec4eaede..22c79e2cdd9e46193337bf24106b5491c50ec661 100644 (file)
@@ -264,6 +264,18 @@ i386bsd_target (void)
 #define DBREG_DRX(d, x) ((&d->dr0)[x])
 #endif
 
+static unsigned long
+i386bsd_dr_get (ptid_t ptid, int regnum)
+{
+  struct dbreg dbregs;
+
+  if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid),
+             (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
+    perror_with_name (_("Couldn't read debug registers"));
+
+  return DBREG_DRX ((&dbregs), regnum);
+}
+
 static void
 i386bsd_dr_set (int regnum, unsigned int value)
 {
@@ -299,24 +311,22 @@ i386bsd_dr_set_addr (int regnum, CORE_ADDR addr)
   i386bsd_dr_set (regnum, addr);
 }
 
-void
-i386bsd_dr_reset_addr (int regnum)
+CORE_ADDR
+i386bsd_dr_get_addr (int regnum)
 {
-  gdb_assert (regnum >= 0 && regnum <= 4);
-
-  i386bsd_dr_set (regnum, 0);
+  return i386bsd_dr_get (inferior_ptid, regnum);
 }
 
 unsigned long
 i386bsd_dr_get_status (void)
 {
-  struct dbreg dbregs;
-
-  if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid),
-             (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't read debug registers"));
+  return i386bsd_dr_get (inferior_ptid, 6);
+}
 
-  return DBREG_DRX ((&dbregs), 6);
+unsigned long
+i386bsd_dr_get_control (void)
+{
+  return i386bsd_dr_get (inferior_ptid, 7);
 }
 
 #endif /* PT_GETDBREGS */
index 1c27ed5bb2cbbb95dcfb50eda3f32544a3d262dc..df0b0f3d4d650236fc7608abed202054ad7d181b 100644 (file)
@@ -32,8 +32,10 @@ extern void i386bsd_dr_set_control (unsigned long control);
 
 extern void i386bsd_dr_set_addr (int regnum, CORE_ADDR addr);
 
-extern void i386bsd_dr_reset_addr (int regnum);
+extern CORE_ADDR i386bsd_dr_get_addr (int regnum);
 
 extern unsigned long i386bsd_dr_get_status (void);
 
+extern unsigned long i386bsd_dr_get_control (void);
+
 #endif /* i386bsd-nat.h */
index ecc797e570e4ce867a47ada0e88d0e152629827b..52ae031d096ac898a2e772dfc29049115370fa83 100644 (file)
@@ -134,8 +134,9 @@ _initialize_i386fbsd_nat (void)
 
   i386_dr_low.set_control = i386bsd_dr_set_control;
   i386_dr_low.set_addr = i386bsd_dr_set_addr;
-  i386_dr_low.reset_addr = i386bsd_dr_reset_addr;
+  i386_dr_low.get_addr = i386bsd_dr_get_addr;
   i386_dr_low.get_status = i386bsd_dr_get_status;
+  i386_dr_low.get_control = i386bsd_dr_get_control;
   i386_set_debug_register_length (4);
 
 #endif /* HAVE_PT_GETDBREGS */
index 65e077b165d4a546ab3486dfa4475a9986b8e044..abe532afb5b759c8ce5f6395f840c172a3f13298 100644 (file)
@@ -618,7 +618,7 @@ ia64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type,
 }
 
 static void
-ia64_linux_new_thread (ptid_t ptid)
+ia64_linux_new_thread (struct lwp_info *lp)
 {
   int i, any;
 
@@ -627,11 +627,11 @@ ia64_linux_new_thread (ptid_t ptid)
     {
       if (debug_registers[i] != 0)
        any = 1;
-      store_debug_register (ptid, i, debug_registers[i]);
+      store_debug_register (lp->ptid, i, debug_registers[i]);
     }
 
   if (any)
-    enable_watchpoints_in_psr (ptid);
+    enable_watchpoints_in_psr (lp->ptid);
 }
 
 static int
index 19b4b57030ac4b782ef589db1fb7db194e3ac83b..1cbfc44456c54b4a858a780fdd4a293ea4ddd0b3 100644 (file)
@@ -175,7 +175,10 @@ static struct target_ops *linux_ops;
 static struct target_ops linux_ops_saved;
 
 /* The method to call, if any, when a new thread is attached.  */
-static void (*linux_nat_new_thread) (ptid_t);
+static void (*linux_nat_new_thread) (struct lwp_info *);
+
+/* Hook to call prior to resuming a thread.  */
+static void (*linux_nat_prepare_to_resume) (struct lwp_info *);
 
 /* The method to call, if any, when the siginfo object needs to be
    converted between the layout returned by ptrace, and the layout in
@@ -1073,6 +1076,15 @@ status_to_str (int status)
   return buf;
 }
 
+/* Destroy and free LP.  */
+
+static void
+lwp_free (struct lwp_info *lp)
+{
+  xfree (lp->arch_private);
+  xfree (lp);
+}
+
 /* Remove all LWPs belong to PID from the lwp list.  */
 
 static void
@@ -1093,7 +1105,7 @@ purge_lwp_list (int pid)
          else
            lpprev->next = lp->next;
 
-         xfree (lp);
+         lwp_free (lp);
        }
       else
        lpprev = lp;
@@ -1139,8 +1151,8 @@ add_lwp (ptid_t ptid)
   lp->next = lwp_list;
   lwp_list = lp;
 
-  if (num_lwps (GET_PID (ptid)) > 1 && linux_nat_new_thread != NULL)
-    linux_nat_new_thread (ptid);
+  if (linux_nat_new_thread != NULL)
+    linux_nat_new_thread (lp);
 
   return lp;
 }
@@ -1166,7 +1178,7 @@ delete_lwp (ptid_t ptid)
   else
     lwp_list = lp->next;
 
-  xfree (lp);
+  lwp_free (lp);
 }
 
 /* Return a pointer to the structure describing the LWP corresponding
@@ -1724,6 +1736,8 @@ detach_callback (struct lwp_info *lp, void *data)
       /* Pass on any pending signal for this LWP.  */
       get_pending_status (lp, &status);
 
+      if (linux_nat_prepare_to_resume != NULL)
+       linux_nat_prepare_to_resume (lp);
       errno = 0;
       if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
                  WSTOPSIG (status)) < 0)
@@ -1784,6 +1798,8 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
                            target_pid_to_str (main_lwp->ptid));
     }
 
+  if (linux_nat_prepare_to_resume != NULL)
+    linux_nat_prepare_to_resume (main_lwp);
   delete_lwp (main_lwp->ptid);
 
   if (forks_exist_p ())
@@ -1825,6 +1841,8 @@ resume_lwp (struct lwp_info *lp, int step)
                                "RC:  PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
                                target_pid_to_str (lp->ptid));
 
+         if (linux_nat_prepare_to_resume != NULL)
+           linux_nat_prepare_to_resume (lp);
          linux_ops->to_resume (linux_ops,
                                pid_to_ptid (GET_LWP (lp->ptid)),
                                step, TARGET_SIGNAL_0);
@@ -1969,6 +1987,8 @@ linux_nat_resume (struct target_ops *ops,
   /* Convert to something the lower layer understands.  */
   ptid = pid_to_ptid (GET_LWP (lp->ptid));
 
+  if (linux_nat_prepare_to_resume != NULL)
+    linux_nat_prepare_to_resume (lp);
   linux_ops->to_resume (linux_ops, ptid, step, signo);
   memset (&lp->siginfo, 0, sizeof (lp->siginfo));
   lp->stopped_by_watchpoint = 0;
@@ -2138,6 +2158,8 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
   /* Note that gdbarch_get_syscall_number may access registers, hence
      fill a regcache.  */
   registers_changed ();
+  if (linux_nat_prepare_to_resume != NULL)
+    linux_nat_prepare_to_resume (lp);
   linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                        lp->step, TARGET_SIGNAL_0);
   return 1;
@@ -2325,6 +2347,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
                    fprintf_unfiltered (gdb_stdlog,
                                        "LHEW: resuming new LWP %ld\n",
                                        GET_LWP (new_lp->ptid));
+                 if (linux_nat_prepare_to_resume != NULL)
+                   linux_nat_prepare_to_resume (new_lp);
                  linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
                                        0, TARGET_SIGNAL_0);
                  new_lp->stopped = 0;
@@ -2334,6 +2358,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
          if (debug_linux_nat)
            fprintf_unfiltered (gdb_stdlog,
                                "LHEW: resuming parent LWP %d\n", pid);
+         if (linux_nat_prepare_to_resume != NULL)
+           linux_nat_prepare_to_resume (lp);
          linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                                0, TARGET_SIGNAL_0);
 
@@ -2597,6 +2623,14 @@ stop_callback (struct lwp_info *lp, void *data)
   return 0;
 }
 
+/* Request a stop on LWP.  */
+
+void
+linux_stop_lwp (struct lwp_info *lwp)
+{
+  stop_callback (lwp, NULL);
+}
+
 /* Return non-zero if LWP PID has a pending SIGINT.  */
 
 static int
@@ -3333,6 +3367,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
 
          registers_changed ();
 
+         if (linux_nat_prepare_to_resume != NULL)
+           linux_nat_prepare_to_resume (lp);
          linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                            lp->step, TARGET_SIGNAL_0);
          if (debug_linux_nat)
@@ -3364,6 +3400,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
       lp->ignore_sigint = 0;
 
       registers_changed ();
+      if (linux_nat_prepare_to_resume != NULL)
+       linux_nat_prepare_to_resume (lp);
       linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                            lp->step, TARGET_SIGNAL_0);
       if (debug_linux_nat)
@@ -3538,6 +3576,8 @@ retry:
       /* Resume the thread.  It should halt immediately returning the
          pending SIGSTOP.  */
       registers_changed ();
+      if (linux_nat_prepare_to_resume != NULL)
+       linux_nat_prepare_to_resume (lp);
       linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                            lp->step, TARGET_SIGNAL_0);
       if (debug_linux_nat)
@@ -3787,6 +3827,8 @@ retry:
             newly attached threads may cause an unwanted delay in
             getting them running.  */
          registers_changed ();
+         if (linux_nat_prepare_to_resume != NULL)
+           linux_nat_prepare_to_resume (lp);
          linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                                lp->step, signo);
          if (debug_linux_nat)
@@ -3943,6 +3985,8 @@ resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
                            lp->step);
 
       registers_changed ();
+      if (linux_nat_prepare_to_resume != NULL)
+       linux_nat_prepare_to_resume (lp);
       linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
                            lp->step, TARGET_SIGNAL_0);
       lp->stopped = 0;
@@ -5840,7 +5884,8 @@ linux_nat_add_target (struct target_ops *t)
 
 /* Register a method to call whenever a new thread is attached.  */
 void
-linux_nat_set_new_thread (struct target_ops *t, void (*new_thread) (ptid_t))
+linux_nat_set_new_thread (struct target_ops *t,
+                         void (*new_thread) (struct lwp_info *))
 {
   /* Save the pointer.  We only support a single registered instance
      of the GNU/Linux native target, so we do not need to map this to
@@ -5861,6 +5906,16 @@ linux_nat_set_siginfo_fixup (struct target_ops *t,
   linux_nat_siginfo_fixup = siginfo_fixup;
 }
 
+/* Register a method to call prior to resuming a thread.  */
+
+void
+linux_nat_set_prepare_to_resume (struct target_ops *t,
+                                void (*prepare_to_resume) (struct lwp_info *))
+{
+  /* Save the pointer.  */
+  linux_nat_prepare_to_resume = prepare_to_resume;
+}
+
 /* Return the saved siginfo associated with PTID.  */
 struct siginfo *
 linux_nat_get_siginfo (ptid_t ptid)
index 1fa94cec567a49fe9ced7bec66524e8ffc3ace25..33727d64fa4e2a4f551253048e95d8ff13280ffc 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <signal.h>
 
+struct arch_lwp_info;
+
 /* Ways to "resume" a thread.  */
 
 enum resume_kind
@@ -109,6 +111,9 @@ struct lwp_info
   /* The processor core this LWP was last seen on.  */
   int core;
 
+  /* Arch-specific additions.  */
+  struct arch_lwp_info *arch_private;
+
   /* Next LWP in list.  */
   struct lwp_info *next;
 };
@@ -146,6 +151,8 @@ extern void linux_enable_event_reporting (ptid_t ptid);
 
 extern int lin_lwp_attach_lwp (ptid_t ptid);
 
+extern void linux_stop_lwp (struct lwp_info *lwp);
+
 /* Iterator function for lin-lwp's lwp list.  */
 struct lwp_info *iterate_over_lwps (ptid_t filter,
                                    int (*callback) (struct lwp_info *,
@@ -166,7 +173,7 @@ linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int));
 void linux_nat_add_target (struct target_ops *);
 
 /* Register a method to call whenever a new thread is attached.  */
-void linux_nat_set_new_thread (struct target_ops *, void (*) (ptid_t));
+void linux_nat_set_new_thread (struct target_ops *, void (*) (struct lwp_info *));
 
 /* Register a method that converts a siginfo object between the layout
    that ptrace returns, and the layout in the architecture of the
@@ -176,6 +183,11 @@ void linux_nat_set_siginfo_fixup (struct target_ops *,
                                           gdb_byte *,
                                           int));
 
+/* Register a method to call prior to resuming a thread.  */
+
+void linux_nat_set_prepare_to_resume (struct target_ops *,
+                                     void (*) (struct lwp_info *));
+
 /* Update linux-nat internal state when changing from one fork
    to another.  */
 void linux_nat_switch_fork (ptid_t new_ptid);
index 2602e2d617ee827572a2022bf13bc4e886f745ea..2cfd156f252e6fdcfa461c76cea34a9bf37e08b5 100644 (file)
@@ -886,14 +886,14 @@ write_watchpoint_regs (void)
  register values for the new thread.  */
 
 static void
-mips_linux_new_thread (ptid_t ptid)
+mips_linux_new_thread (struct lwp_info *lp)
 {
   int tid;
 
   if (!mips_linux_read_watch_registers (0))
     return;
 
-  tid = ptid_get_lwp (ptid);
+  tid = ptid_get_lwp (lp->ptid);
   if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
     perror_with_name (_("Couldn't write debug register"));
 }
index 94cfbf8782e5f2404ac27100de580709be9f1aed..dc7f15254f203aa367ab6c346a057826f82d3d61 100644 (file)
@@ -2151,9 +2151,9 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw,
 }
 
 static void
-ppc_linux_new_thread (ptid_t ptid)
+ppc_linux_new_thread (struct lwp_info *lp)
 {
-  int tid = TIDGET (ptid);
+  int tid = TIDGET (lp->ptid);
 
   if (have_ptrace_booke_interface ())
     {
index ae3c868a47e2836d8bee95d32017cd4c2b691490..b1c3f11c75326bedb09f1458a54970e36e58bb75 100644 (file)
@@ -472,7 +472,7 @@ s390_stopped_by_watchpoint (void)
 }
 
 static void
-s390_fix_watch_points (ptid_t ptid)
+s390_fix_watch_points (struct lwp_info *lp)
 {
   int tid;
 
@@ -482,9 +482,9 @@ s390_fix_watch_points (ptid_t ptid)
   CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
   struct watch_area *area;
 
-  tid = TIDGET (ptid);
+  tid = TIDGET (lp->ptid);
   if (tid == 0)
-    tid = PIDGET (ptid);
+    tid = PIDGET (lp->ptid);
 
   for (area = watch_base; area; area = area->next)
     {
index ed3caec98783909bcc049bb8ad063629cc06d963..b2cc4bb72fa9cbab425a42056fc14692a20b3ea4 100644 (file)
@@ -1,3 +1,10 @@
+2011-12-14  Pedro Alves  <pedro@codesourcery.com>
+
+       PR threads/10729
+
+       * gdb.mi/watch-nonstop.c: New file.
+       * gdb.mi/mi-watch-nonstop.exp: New file.
+
 2011-12-13  Pedro Alves  <pedro@codesourcery.com>
            Doug Evans  <dje@google.com>
 
diff --git a/gdb/testsuite/gdb.mi/mi-watch-nonstop.exp b/gdb/testsuite/gdb.mi/mi-watch-nonstop.exp
new file mode 100644 (file)
index 0000000..b8aa903
--- /dev/null
@@ -0,0 +1,77 @@
+#   Copyright 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if [target_info exists gdb,no_hardware_watchpoints] {
+    return -1
+}
+
+if { ![support_displaced_stepping] } { 
+    unsupported "displaced stepping"
+    return -1
+}
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if {[mi_gdb_start]} {
+    continue
+}
+
+proc mi_nonstop_resume { command test } {
+    if { [mi_send_resuming_command $command $test] != 0 } {
+       # If a resume fails, assume non-stop is broken or unsupported
+       # for this target.  We have logged a FAIL or UNSUPPORTED; skip
+       # the remaining tests to limit timeouts.
+       return -code continue
+    }
+}
+
+#
+# Start here
+#
+set testfile "watch-nonstop"
+set srcfile "$testfile.c"
+set binfile "$objdir/$subdir/mi-$testfile"
+
+if {[gdb_compile "$srcdir/$subdir/$srcfile" $binfile executable {debug}] != "" } {
+    return -1
+}
+
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load $binfile
+
+mi_gdb_test "-gdb-set non-stop 1" ".*"
+mi_gdb_test "-gdb-set target-async 1" ".*"
+mi_detect_async
+
+if { [mi_run_to_main] < 0 } {
+    continue
+}
+
+# Set a watchpoint.
+mi_gdb_test "111-break-watch global" \
+    "111\\^done,wpt=\{number=\"2\",exp=\"global\"\}" \
+    "break-watch operation"
+
+# Set the target running.
+mi_nonstop_resume "exec-continue" "resume 1"
+
+# Now try deleting the watchpoint.  This would fail with "Couldn't
+# write debug register: No such process."  on GNU/Linux, because we'd
+# try to poke at the debug registers of a running thread.
+mi_gdb_test "777-break-delete 2" \
+    "777\\^done" \
+    "delete watchpoint"
diff --git a/gdb/testsuite/gdb.mi/watch-nonstop.c b/gdb/testsuite/gdb.mi/watch-nonstop.c
new file mode 100644 (file)
index 0000000..7222cb6
--- /dev/null
@@ -0,0 +1,24 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int global;
+
+int main ()
+{
+  sleep (60);
+  return 0;
+}
index 20e3c67b23d31a508122765b5f0c505e4db68e92..97ed237030492d8d635894b201efa497c7251cde 100644 (file)
@@ -2494,8 +2494,9 @@ init_windows_ops (void)
 
   i386_dr_low.set_control = cygwin_set_dr7;
   i386_dr_low.set_addr = cygwin_set_dr;
-  i386_dr_low.reset_addr = NULL;
+  i386_dr_low.get_addr = cygwin_get_dr;
   i386_dr_low.get_status = cygwin_get_dr6;
+  i386_dr_low.get_control = cygwin_get_dr7;
 
   /* i386_dr_low.debug_register_length field is set by
      calling i386_set_debug_register_length function
@@ -2627,6 +2628,14 @@ cygwin_set_dr7 (unsigned long val)
   debug_registers_used = 1;
 }
 
+/* Get the value of debug register I from the inferior.  */
+
+static CORE_ADDR
+cygwin_get_dr (int i)
+{
+  return dr[i];
+}
+
 /* Get the value of the DR6 debug status register from the inferior.
    Here we just return the value stored in dr[6]
    by the last call to thread_rec for current_event.dwThreadId id.  */
@@ -2636,6 +2645,16 @@ cygwin_get_dr6 (void)
   return (unsigned long) dr[6];
 }
 
+/* Get the value of the DR7 debug status register from the inferior.
+   Here we just return the value stored in dr[7] by the last call to
+   thread_rec for current_event.dwThreadId id.  */
+
+static unsigned long
+cygwin_get_dr7 (void)
+{
+  return (unsigned long) dr[7];
+}
+
 /* Determine if the thread referenced by "ptid" is alive
    by "polling" it.  If WaitForSingleObject returns WAIT_OBJECT_0
    it means that the thread has died.  Otherwise it is assumed to be alive.  */