This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] PR threads/10729: x86 hw watchpoints and non-stop mode


On Tuesday 13 December 2011 18:49:23, Jan Kratochvil wrote:
> On Tue, 13 Dec 2011 17:26:03 +0100, Pedro Alves wrote:
> > Anything else I should address?  
> 
> I am fine with this patch, thanks.

That's great.

> > Since we just branched, I'm a bit less worried about
> > breaking things on other platforms.  :-)
> +
> >  /* 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 *))
> 
> In such case you should change also all the callers on exotic arches:
> 
> ./arm-linux-nat.c:1274:  linux_nat_set_new_thread (t, arm_linux_new_thread);
> ./ia64-linux-nat.c:861:  linux_nat_set_new_thread (t, ia64_linux_new_thread);
> ./mips-linux-nat.c:1088:  linux_nat_set_new_thread (t, mips_linux_new_thread);
> ./ppc-linux-nat.c:2530:  linux_nat_set_new_thread (t, ppc_linux_new_thread);
> ./s390-nat.c:698:  linux_nat_set_new_thread (t, s390_fix_watch_points);
> 

Indeed!  Completely forgot.  Done now.

> > +     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
> 
> IMO s/re-resume/their resume/ but I do not master the language.

Thanks, but it sounds more natural to me how it is.  I really want to
convey the idea of resuming again, and, there's already "they" and
"their" spelled out enforcing the subject.

> On Tue, 13 Dec 2011 17:26:03 +0100, Pedro Alves wrote:
> > +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]);
> 
> FYI this way it will leave non-zero DR_FIRSTADDR...DR_LASTADDR after detaching
> from the inferior.  This is not a bug, just that it may look as a sort of
> regression.  I do not think a fix - clearing them - is required.

Agreed.  But there's actually a related bug here, that I had thought
of fixing, then forgot.  GDBserver has this bug too; I'll fix it in a bit.
We need to call prepare_to_resume just before detaching, to clear
the previously active debug registers in DR_CONTROL, otherwise, the
inferior dies with SIGTRAP just after detaching.

That is:

(gdb) watch j
or
(gdb) hbreak foo
(gdb) si
*watchpoints inserted*
(gdb) delete
*deletes the watchpoints from global mirror, but doesn't update the threads' copies.*
(gdb) detach
*inferior trips on old watchpoints -> SIGTRAP death *


Retested on x86_64-linux and applied.


gdb/
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.
---
 gdb/amd64-linux-nat.c                     |  130 ++++++++++++++++++--------
 gdb/arm-linux-nat.c                       |    4 -
 gdb/go32-nat.c                            |   26 +++++
 gdb/i386-darwin-nat.c                     |   31 ++----
 gdb/i386-linux-nat.c                      |  143 ++++++++++++++++++-----------
 gdb/i386-nat.c                            |  111 ++++++++++++-----------
 gdb/i386-nat.h                            |   42 +++++++--
 gdb/i386bsd-nat.c                         |   32 ++++--
 gdb/i386bsd-nat.h                         |    4 +
 gdb/i386fbsd-nat.c                        |    3 -
 gdb/ia64-linux-nat.c                      |    6 +
 gdb/linux-nat.c                           |   67 ++++++++++++--
 gdb/linux-nat.h                           |   14 +++
 gdb/mips-linux-nat.c                      |    4 -
 gdb/ppc-linux-nat.c                       |    4 -
 gdb/s390-nat.c                            |    6 +
 gdb/testsuite/gdb.mi/mi-watch-nonstop.exp |   77 ++++++++++++++++
 gdb/testsuite/gdb.mi/watch-nonstop.c      |   24 +++++
 gdb/windows-nat.c                         |   21 ++++
 19 files changed, 537 insertions(+), 212 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-watch-nonstop.exp
 create mode 100644 gdb/testsuite/gdb.mi/watch-nonstop.c

diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index c673965..288160b 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -64,6 +64,14 @@
 #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,
 
 /* 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;
 }
 
 
@@ -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);
 }
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index 6424dc3..c614f96 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -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)
diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c
index 8295adf..79618ec 100644
--- a/gdb/go32-nat.c
+++ b/gdb/go32-nat.c
@@ -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;
diff --git a/gdb/i386-darwin-nat.c b/gdb/i386-darwin-nat.c
index 61e2e15..23f6a6d 100644
--- a/gdb/i386-darwin-nat.c
+++ b/gdb/i386-darwin-nat.c
@@ -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)
 {
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 7eb49ae..190979b 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -47,22 +47,6 @@
 #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"
 
@@ -83,6 +67,14 @@
 #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;
 
@@ -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;
 }
 
 
@@ -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);
 }
diff --git a/gdb/i386-nat.c b/gdb/i386-nat.c
index 568b79b..cd479b3 100644
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -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;
+  /* 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;
-  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;
+  /* 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);
diff --git a/gdb/i386-nat.h b/gdb/i386-nat.h
index 819c6b8..1a75daa 100644
--- a/gdb/i386-nat.h
+++ b/gdb/i386-nat.h
@@ -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 */
diff --git a/gdb/i386bsd-nat.c b/gdb/i386bsd-nat.c
index fcd772f..22c79e2 100644
--- a/gdb/i386bsd-nat.c
+++ b/gdb/i386bsd-nat.c
@@ -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 */
diff --git a/gdb/i386bsd-nat.h b/gdb/i386bsd-nat.h
index 1c27ed5..df0b0f3 100644
--- a/gdb/i386bsd-nat.h
+++ b/gdb/i386bsd-nat.h
@@ -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 */
diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
index ecc797e..52ae031 100644
--- a/gdb/i386fbsd-nat.c
+++ b/gdb/i386fbsd-nat.c
@@ -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 */
diff --git a/gdb/ia64-linux-nat.c b/gdb/ia64-linux-nat.c
index 65e077b..abe532a 100644
--- a/gdb/ia64-linux-nat.c
+++ b/gdb/ia64-linux-nat.c
@@ -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
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 19b4b57..1cbfc44 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -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)
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 1fa94ce..33727d6 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -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);
diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c
index 2602e2d..2cfd156 100644
--- a/gdb/mips-linux-nat.c
+++ b/gdb/mips-linux-nat.c
@@ -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"));
 }
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 94cfbf8..dc7f152 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -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 ())
     {
diff --git a/gdb/s390-nat.c b/gdb/s390-nat.c
index ae3c868..b1c3f11 100644
--- a/gdb/s390-nat.c
+++ b/gdb/s390-nat.c
@@ -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)
     {
diff --git a/gdb/testsuite/gdb.mi/mi-watch-nonstop.exp b/gdb/testsuite/gdb.mi/mi-watch-nonstop.exp
new file mode 100644
index 0000000..b8aa903
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-watch-nonstop.exp
@@ -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
index 0000000..7222cb6
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/watch-nonstop.c
@@ -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;
+}
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 20e3c67..97ed237 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -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.  */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]