mkolar / rpms / gdb

Forked from rpms/gdb 3 years ago
Clone
Blob Blame History Raw
http://sourceware.org/ml/gdb-patches/2008-01/msg00042.html
[ Backported for GDB-6.6 (only removed the new file inclusion). ]

+

2007-09-16  Daniel Jacobowitz  <dan@codesourcery.com>
	    Jeff Johnston  <jjohnstn@redhat.com>

	* gdb.texinfo (Setting Watchpoints): Adjust warning text about
	multi-threaded watchpoints.

2007-12-15  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.texinfo (Setting Watchpoints): New paragraph on the software
	watchpoints safety wrt `set scheduler-locking'.

diff -u -ruNp gdb-6.7.1-orig/gdb/amd64-linux-nat.c gdb-6.7.1/gdb/amd64-linux-nat.c
--- gdb-6.7.1-orig/gdb/amd64-linux-nat.c	2008-01-11 20:59:17.000000000 +0100
+++ gdb-6.7.1/gdb/amd64-linux-nat.c	2008-01-11 20:49:42.000000000 +0100
@@ -501,6 +501,12 @@ amd64_linux_insert_watchpoint (CORE_ADDR
   return rc;
 }
 
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+   process of traced FORK.  We must clear such watchpoints only once during
+   DETACH_BREAKPOINTS.  */
+
+static int amd64_linux_detach_breakpoints_pid;
+
 /* Remove a watchpoint that watched the memory region which starts at
    address ADDR, whose length is LEN bytes, and for accesses of the
    type TYPE.  Return 0 on success, -1 on failure.  */
@@ -508,12 +514,33 @@ int
 amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
 {
   int rc;
+
+  if (ptid_get_pid (inferior_ptid) == amd64_linux_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  amd64_linux_detach_breakpoints_pid = 0;
+
   rc = i386_remove_watchpoint (addr, len, type);
   if (!rc)
     amd64_linux_sync_debug_registers_across_threads ();
   return rc;
 }
 
+static void
+amd64_linux_detach_breakpoints (int detached_pid)
+{
+  struct cleanup *old_chain = save_inferior_ptid ();
+  int i;
+
+  amd64_linux_detach_breakpoints_pid = detached_pid;
+  /* Depend on `!is_lwp (inferior_ptid)' for the I386_* macros.  */
+  inferior_ptid = pid_to_ptid (detached_pid);
+
+  i386_detach_breakpoints (detached_pid);
+
+  do_cleanups (old_chain);
+}
+
 /* Insert a hardware-assisted breakpoint at address ADDR.  SHADOW is
    unused.  Return 0 on success, EBUSY on failure.  */
 int
@@ -532,6 +559,12 @@ int
 amd64_linux_remove_hw_breakpoint (struct bp_target_info *bp_tgt)
 {
   int rc;
+
+  if (ptid_get_pid (inferior_ptid) == amd64_linux_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  amd64_linux_detach_breakpoints_pid = 0;
+
   rc = i386_remove_hw_breakpoint (bp_tgt);
   if (!rc)
     amd64_linux_sync_debug_registers_across_threads ();
@@ -620,6 +653,41 @@ amd64_linux_child_post_startup_inferior 
   i386_cleanup_dregs ();
   super_post_startup_inferior (ptid);
 }
+
+static int (*amd64_linux_super_follow_fork) (struct target_ops *ops,
+					     int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+   called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror.  */
+
+static int
+amd64_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+  ptid_t last_ptid;
+  struct target_waitstatus last_status;
+  int has_vforked;
+  int parent_pid, child_pid;
+
+  get_last_target_status (&last_ptid, &last_status);
+  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (last_ptid);
+  if (parent_pid == 0)
+    parent_pid = ptid_get_pid (last_ptid);
+  child_pid = last_status.value.related_pid;
+
+  if (! follow_child)
+    {
+      amd64_linux_detach_breakpoints (child_pid);
+    }
+  else
+    {
+      if (! has_vforked)
+	amd64_linux_detach_breakpoints (child_pid);
+    }
+
+  return (*amd64_linux_super_follow_fork) (ops, follow_child);
+}
+
 
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -656,6 +724,9 @@ _initialize_amd64_linux_nat (void)
   linux_elfcore_write_prstatus = amd64_linux_elfcore_write_prstatus;
   linux_elfcore_write_prfpreg = amd64_linux_elfcore_write_prfpreg;
 
+  amd64_linux_super_follow_fork = t->to_follow_fork;
+  t->to_follow_fork = amd64_linux_follow_fork;
+
   /* Register the target.  */
   linux_nat_add_target (t);
 
diff -u -ruNp gdb-6.7.1-orig/gdb/config/i386/nm-i386.h gdb-6.7.1/gdb/config/i386/nm-i386.h
--- gdb-6.7.1-orig/gdb/config/i386/nm-i386.h	2007-08-23 20:08:48.000000000 +0200
+++ gdb-6.7.1/gdb/config/i386/nm-i386.h	2008-01-11 20:47:42.000000000 +0100
@@ -114,6 +114,8 @@ extern int i386_stopped_by_watchpoint (v
    reset all debug registers by calling i386_cleanup_dregs ().  */ 
 #define CHILD_POST_STARTUP_INFERIOR
 
+extern void i386_detach_breakpoints (int detached_pid);
+
 #endif /* I386_USE_GENERIC_WATCHPOINTS */
 
 #endif /* NM_I386_H */
diff -u -ruNp gdb-6.7.1-orig/gdb/i386-linux-nat.c gdb-6.7.1/gdb/i386-linux-nat.c
--- gdb-6.7.1-orig/gdb/i386-linux-nat.c	2008-01-11 20:59:17.000000000 +0100
+++ gdb-6.7.1/gdb/i386-linux-nat.c	2008-01-11 20:49:52.000000000 +0100
@@ -745,6 +745,12 @@ i386_linux_insert_watchpoint (CORE_ADDR 
   return rc;
 }
 
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+   process of traced FORK.  We must clear such watchpoints only once during
+   DETACH_BREAKPOINTS.  */
+
+static int i386_linux_detach_breakpoints_pid;
+
 /* Remove a watchpoint that watched the memory region which starts at
    address ADDR, whose length is LEN bytes, and for accesses of the
    type TYPE.  Return 0 on success, -1 on failure.  */
@@ -752,12 +758,33 @@ int
 i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
 {
   int rc;
+
+  if (ptid_get_pid (inferior_ptid) == i386_linux_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  i386_linux_detach_breakpoints_pid = 0;
+
   rc = i386_remove_watchpoint (addr, len, type);
   if (!rc)
     i386_linux_sync_debug_registers_across_threads ();
   return rc;
 }
 
+static void
+i386_linux_detach_breakpoints (int detached_pid)
+{
+  struct cleanup *old_chain = save_inferior_ptid ();
+  int i;
+
+  i386_linux_detach_breakpoints_pid = detached_pid;
+  /* Depend on `!is_lwp (inferior_ptid)' for the I386_* macros.  */
+  inferior_ptid = pid_to_ptid (detached_pid);
+
+  i386_detach_breakpoints (detached_pid);
+
+  do_cleanups (old_chain);
+}
+
 /* Insert a hardware-assisted breakpoint at address ADDR.  SHADOW is
    unused.  Return 0 on success, EBUSY on failure.  */
 int
@@ -940,6 +967,40 @@ i386_linux_child_post_startup_inferior (
   super_post_startup_inferior (ptid);
 }
 
+static int (*i386_linux_super_follow_fork) (struct target_ops *ops,
+					    int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+   called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror.  */
+
+static int
+i386_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+  ptid_t last_ptid;
+  struct target_waitstatus last_status;
+  int has_vforked;
+  int parent_pid, child_pid;
+
+  get_last_target_status (&last_ptid, &last_status);
+  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (last_ptid);
+  if (parent_pid == 0)
+    parent_pid = ptid_get_pid (last_ptid);
+  child_pid = last_status.value.related_pid;
+
+  if (! follow_child)
+    {
+      i386_linux_detach_breakpoints (child_pid);
+    }
+  else
+    {
+      if (! has_vforked)
+	i386_linux_detach_breakpoints (child_pid);
+    }
+
+  return (*i386_linux_super_follow_fork) (ops, follow_child);
+}
+
 void
 _initialize_i386_linux_nat (void)
 {
@@ -959,6 +1020,9 @@ _initialize_i386_linux_nat (void)
   t->to_fetch_registers = i386_linux_fetch_inferior_registers;
   t->to_store_registers = i386_linux_store_inferior_registers;
 
+  i386_linux_super_follow_fork = t->to_follow_fork;
+  t->to_follow_fork = i386_linux_follow_fork;
+
   /* Register the target.  */
   linux_nat_add_target (t);
 
diff -u -ruNp gdb-6.7.1-orig/gdb/i386-nat.c gdb-6.7.1/gdb/i386-nat.c
--- gdb-6.7.1-orig/gdb/i386-nat.c	2008-01-11 20:59:17.000000000 +0100
+++ gdb-6.7.1/gdb/i386-nat.c	2008-01-11 20:47:57.000000000 +0100
@@ -545,6 +546,17 @@ i386_remove_watchpoint (CORE_ADDR addr, 
   return retval;
 }
 
+void
+i386_detach_breakpoints (int detached_pid)
+{
+  int i;
+
+  /* Do not touch any DR_MIRROR or DR_CONTROL_MIRROR mirrors here.  */
+  I386_DR_LOW_SET_CONTROL (0);
+  ALL_DEBUG_REGISTERS(i)
+    I386_DR_LOW_RESET_ADDR (i);
+}
+
 /* Return non-zero if we can watch a memory region that starts at
    address ADDR and whose length is LEN bytes.  */
 
--- gdb-6.5/gdb/ia64-linux-nat.c.orig	2008-01-12 15:47:40.000000000 +0100
+++ gdb-6.5/gdb/ia64-linux-nat.c	2008-01-12 15:57:58.000000000 +0100
@@ -675,12 +675,23 @@ ia64_linux_remove_watchpoint_callback (s
 		 		     	   args->len);
 }
 
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+   process of traced FORK.  We must clear such watchpoints only once during
+   DETACH_BREAKPOINTS.  */
+
+static int ia64_linux_detach_breakpoints_pid;
+
 /* Remove a watchpoint for all threads.  */
 int
 ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
 {
   struct linux_watchpoint args;
 
+  if (ptid_get_pid (inferior_ptid) == ia64_linux_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  ia64_linux_detach_breakpoints_pid = 0;
+
   args.addr = addr;
   args.len = len;
 
@@ -771,6 +782,22 @@ ia64_linux_xfer_partial (struct target_o
 /* Observer function for a new thread attach.  We need to insert
    existing watchpoints on the new thread.  */
 static void
+ia64_linux_detach_breakpoints (int detached_pid)
+{
+  int idx, i;
+  long dbr_addr, dbr_mask;
+  int max_watchpoints = 4;
+
+  ia64_linux_detach_breakpoints_pid = detached_pid;
+
+  /* Do not touch any DEBUG_REGISTERS mirrors here.  */
+  dbr_addr = 0;
+  dbr_mask = 0;
+  for (idx = 0; idx < max_watchpoints; idx++)
+    store_debug_register_pair (ptid_build (detached_pid, 0, 0), idx, &dbr_addr, &dbr_mask);
+}
+
+static void
 ia64_linux_new_thread (ptid_t ptid)
 {
   insert_watchpoints_for_new_thread (ptid, 
@@ -793,6 +820,40 @@ ia64_linux_save_sigtrap_info (void *queu
           lp->saved_trap_data);
 }
 
+static int (*ia64_linux_super_follow_fork) (struct target_ops *ops,
+					    int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+   called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror.  */
+
+int
+ia64_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+  ptid_t last_ptid;
+  struct target_waitstatus last_status;
+  int has_vforked;
+  int parent_pid, child_pid;
+
+  get_last_target_status (&last_ptid, &last_status);
+  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (last_ptid);
+  if (parent_pid == 0)
+    parent_pid = ptid_get_pid (last_ptid);
+  child_pid = last_status.value.related_pid;
+
+  if (! follow_child)
+    {
+      ia64_linux_detach_breakpoints (child_pid);
+    }
+  else
+    {
+      if (! has_vforked)
+	ia64_linux_detach_breakpoints (child_pid);
+    }
+
+  return (*ia64_linux_super_follow_fork) (ops, follow_child);
+}
+
 void _initialize_ia64_linux_nat (void);
 
 /*
@@ -865,6 +926,9 @@ _initialize_ia64_linux_nat (void)
   super_xfer_partial = t->to_xfer_partial;
   t->to_xfer_partial = ia64_linux_xfer_partial;
 
+  ia64_linux_super_follow_fork = t->to_follow_fork;
+  t->to_follow_fork = ia64_linux_follow_fork;
+
   /* Register the target.  */
   linux_nat_add_target (t);
 
diff -u -ruNp gdb-6.7.1-orig/gdb/ppc-linux-nat.c gdb-6.7.1/gdb/ppc-linux-nat.c
--- gdb-6.7.1-orig/gdb/ppc-linux-nat.c	2007-08-30 15:13:59.000000000 +0200
+++ gdb-6.7.1/gdb/ppc-linux-nat.c	2008-01-11 20:43:12.000000000 +0100
@@ -837,12 +837,23 @@ ppc_linux_insert_watchpoint (CORE_ADDR a
   return ptrace (PTRACE_SET_DEBUGREG, tid, 0, dabr_value);
 }
 
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+   process of traced FORK.  We must clear such watchpoints only once during
+   DETACH_BREAKPOINTS.  */
+
+static int ppc_linux_detach_breakpoints_pid;
+
 static int
 ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
 {
   int tid;
   ptid_t ptid = inferior_ptid;
 
+  if (ptid_get_pid (inferior_ptid) == ppc_linux_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  ppc_linux_detach_breakpoints_pid = 0;
+
   tid = TIDGET (ptid);
   if (tid == 0)
     tid = PIDGET (ptid);
@@ -850,6 +861,15 @@ ppc_linux_remove_watchpoint (CORE_ADDR a
   return ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0);
 }
 
+static void
+ppc_linux_detach_breakpoints (int detached_pid)
+{
+  ppc_linux_detach_breakpoints_pid = detached_pid;
+
+  /* Do not touch the SAVED_DABR_VALUE mirror here.  */
+  ptrace (PTRACE_SET_DEBUGREG, detached_pid, 0, 0);
+}
+
 static int
 ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
 {
@@ -945,6 +965,40 @@ fill_fpregset (const struct regcache *re
 			fpregsetp, sizeof (*fpregsetp));
 }
 
+static int (*ppc_linux_super_follow_fork) (struct target_ops *ops,
+					   int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+   called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror.  */
+
+int
+ppc_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+  ptid_t last_ptid;
+  struct target_waitstatus last_status;
+  int has_vforked;
+  int parent_pid, child_pid;
+
+  get_last_target_status (&last_ptid, &last_status);
+  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (last_ptid);
+  if (parent_pid == 0)
+    parent_pid = ptid_get_pid (last_ptid);
+  child_pid = last_status.value.related_pid;
+
+  if (! follow_child)
+    {
+      ppc_linux_detach_breakpoints (child_pid);
+    }
+  else
+    {
+      if (! has_vforked)
+	ppc_linux_detach_breakpoints (child_pid);
+    }
+
+  return (*ppc_linux_super_follow_fork) (ops, follow_child);
+}
+
 void _initialize_ppc_linux_nat (void);
 
 void
@@ -967,6 +1021,9 @@ _initialize_ppc_linux_nat (void)
   t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint;
   t->to_stopped_data_address = ppc_linux_stopped_data_address;
 
+  ppc_linux_super_follow_fork = t->to_follow_fork;
+  t->to_follow_fork = ppc_linux_follow_fork;
+
   /* Register the target.  */
   linux_nat_add_target (t);
 }
--- gdb-6.7.1-unpatched/gdb/s390-nat.c	2008-01-11 15:33:48.000000000 -0500
+++ gdb-6.7.1/gdb/s390-nat.c	2008-01-11 15:35:50.000000000 -0500
@@ -269,17 +269,15 @@ s390_stopped_by_watchpoint (void)
 }
 
 static void
-s390_fix_watch_points (ptid_t ptid)
+s390_fix_watch_points_list (int tid, struct watch_area *area_list)
 {
-  int tid = s390_tid (ptid);
-
   per_struct per_info;
   ptrace_area parea;
 
   CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
   struct watch_area *area;
 
-  for (area = watch_base; area; area = area->next)
+  for (area = area_list; area; area = area->next)
     {
       watch_lo_addr = min (watch_lo_addr, area->lo_addr);
       watch_hi_addr = max (watch_hi_addr, area->hi_addr);
@@ -291,7 +289,7 @@ s390_fix_watch_points (ptid_t ptid)
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0)
     perror_with_name (_("Couldn't retrieve watchpoint status"));
 
-  if (watch_base)
+  if (area_list)
     {
       per_info.control_regs.bits.em_storage_alteration = 1;
       per_info.control_regs.bits.storage_alt_space_ctl = 1;
@@ -308,6 +306,12 @@ s390_fix_watch_points (ptid_t ptid)
     perror_with_name (_("Couldn't modify watchpoint status"));
 }
 
+static void
+s390_fix_watch_points (ptid_t ptid)
+{
+  s390_fix_watch_points_list (s390_tid (ptid), watch_base);
+}
+
 /* Callback routine to use with iterate_over_lwps to insert a specified
    watchpoint on all threads.  */
 static int
@@ -348,12 +352,23 @@ s390_remove_watchpoint_callback (struct 
   return 0;
 }
 
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+   process of traced FORK.  We must clear such watchpoints only once during
+   DETACH_BREAKPOINTS.  */
+
+static int s390_detach_breakpoints_pid;
+
 /* Remove a specified watchpoint from all threads.  */
 static int
 s390_remove_watchpoint (CORE_ADDR addr, int len, int type)
 {
   struct watch_area *area, **parea;
 
+  if (ptid_get_pid (inferior_ptid) == s390_detach_breakpoints_pid)
+    return 0;
+  /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions.  */
+  s390_detach_breakpoints_pid = 0;
+
   for (parea = &watch_base; *parea; parea = &(*parea)->next)
     if ((*parea)->lo_addr == addr
 	&& (*parea)->hi_addr == addr + len - 1)
@@ -378,6 +393,15 @@ s390_remove_watchpoint (CORE_ADDR addr, 
   return 0;
 }
 
+static void
+s390_detach_breakpoints (int detached_pid)
+{
+  s390_detach_breakpoints_pid = detached_pid;
+
+  /* Do not touch the WATCH_BASE here.  */
+  s390_fix_watch_points_list (detached_pid, NULL);
+}
+
 static int
 s390_can_use_hw_breakpoint (int type, int cnt, int othertype)
 {
@@ -399,6 +423,39 @@ s390_linux_new_thread (ptid_t ptid)
   s390_fix_watch_points (ptid);
 }
 
+static int (*s390_super_follow_fork) (struct target_ops *ops, int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+   called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror.  */
+
+int
+s390_follow_fork (struct target_ops *ops, int follow_child)
+{
+  ptid_t last_ptid;
+  struct target_waitstatus last_status;
+  int has_vforked;
+  int parent_pid, child_pid;
+
+  get_last_target_status (&last_ptid, &last_status);
+  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (last_ptid);
+  if (parent_pid == 0)
+    parent_pid = ptid_get_pid (last_ptid);
+  child_pid = last_status.value.related_pid;
+
+  if (! follow_child)
+    {
+      s390_detach_breakpoints (child_pid);
+    }
+  else
+    {
+      if (! has_vforked)
+	s390_detach_breakpoints (child_pid);
+    }
+
+  return (*s390_super_follow_fork) (ops, follow_child);
+}
+
 
 void _initialize_s390_nat (void);
 
@@ -422,6 +479,9 @@ _initialize_s390_nat (void)
   t->to_insert_watchpoint = s390_insert_watchpoint;
   t->to_remove_watchpoint = s390_remove_watchpoint;
 
+  s390_super_follow_fork = t->to_follow_fork;
+  t->to_follow_fork = s390_follow_fork;
+
   /* Register the target.  */
   linux_nat_add_target (t);
 
diff -u -ruNp gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c
--- gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c	2008-01-11 20:28:48.000000000 +0100
@@ -0,0 +1,160 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+   Copyright 2008
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <string.h>
+#include <errno.h>
+
+static void delay (void)
+{
+  int i = usleep (1000000 / 100);
+  assert (i == 0 || errno == EINTR);
+}
+
+#if defined FOLLOW_PARENT
+
+static void forkoff (int nr)
+{
+  pid_t child, pid_got;
+  int exit_code = 42 + nr;
+  int status;
+
+  child = fork ();
+  switch (child)
+    {
+    case -1:
+      assert (0);
+    case 0:
+      printf ("child%d: %d\n", nr, (int) getpid ());
+
+      /* We must not get caught here (against a forgotten breakpoint).  */
+      var++;
+      breakpoint ();
+
+      _exit (exit_code);
+    default:
+      printf ("parent%d: %d\n", nr, (int) child);
+      pid_got = wait (&status);
+      assert (pid_got == child);
+      assert (WIFEXITED (status));
+      assert (WEXITSTATUS (status) == exit_code);
+
+      /* We must get caught here (against a false watchpoint removal).  */
+      breakpoint ();
+    }
+}
+
+#elif defined FOLLOW_CHILD
+
+static volatile int usr1_got;
+
+static void handler_usr1 (int signo)
+{
+  usr1_got++;
+}
+
+static void forkoff (int nr)
+{
+  pid_t child;
+  int i, loop;
+  struct sigaction act, oldact;
+#ifdef THREAD
+  void *thread_result;
+#endif
+
+  memset (&act, 0, sizeof act);
+  act.sa_flags = SA_RESTART;
+  act.sa_handler = handler_usr1;
+  sigemptyset (&act.sa_mask);
+  i = sigaction (SIGUSR1, &act, &oldact);
+  assert (i == 0);
+
+  child = fork ();
+  switch (child)
+    {
+    case -1:
+      assert (0);
+    default:
+      printf ("parent%d: %d\n", nr, (int) child);
+
+      /* Sleep for a while to possibly get incorrectly ATTACH_THREADed by GDB
+         tracing the child fork with no longer valid thread/lwp entries of the
+         parent.  */
+
+      i = sleep (2);
+      assert (i == 0);
+
+      /* We must not get caught here (against a forgotten breakpoint).  */
+
+      var++;
+      breakpoint ();
+
+#ifdef THREAD
+      /* And neither got caught our thread.  */
+
+      step = 99;
+      i = pthread_join (thread, &thread_result);
+      assert (i == 0);
+      assert (thread_result == (void *) 99UL);
+#endif
+
+      /* Be sure our child knows we did not get caught above.  */
+
+      i = kill (child, SIGUSR1);
+      assert (i == 0);
+
+      /* Sleep for a while to check GDB's `info threads' no longer tracks us in
+         the child fork.  */
+
+      i = sleep (2);
+      assert (i == 0);
+
+      _exit (0);
+    case 0:
+      printf ("child%d: %d\n", nr, (int) getpid ());
+
+      /* Let the parent signal us about its success.  Be careful of races.  */
+
+      for (loop = 0; loop < 1000; loop++)
+        {
+	  /* Parent either died (and USR1_GOT is zero) or it succeeded.  */
+	  if (kill (getppid (), 0) != 0)
+	    break;
+	  /* Parent succeeded?  */
+	  if (usr1_got)
+	    break;
+
+	  delay ();
+	}
+      assert (usr1_got);
+
+      /* We must get caught here (against a false watchpoint removal).  */
+
+      breakpoint ();
+    }
+
+  i = sigaction (SIGUSR1, &oldact, NULL);
+  assert (i == 0);
+}
+
+#else
+# error "!FOLLOW_PARENT && !FOLLOW_CHILD"
+#endif
diff -u -ruNp gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c
--- gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c	2008-01-11 20:28:48.000000000 +0100
@@ -0,0 +1,154 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+   Copyright 2008
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define gettid() syscall (__NR_gettid)
+
+/* Non-atomic `var++' should not hurt as we synchronize the threads by the STEP
+   variable.  Hit-comments need to be duplicite there to catch both at-stops
+   and behind-stops, depending on the target.  */
+
+static volatile int var;
+
+static void dummy (void)
+{
+}
+
+static void breakpoint (void)
+{
+}
+
+/* Include here the functions:
+   static void forkoff (int nr);
+   static void delay (void);  */
+
+static pthread_t thread;
+static volatile int step;
+#define THREAD
+
+#include "watchpoint-fork-forkoff.c"
+
+static void *start (void *arg)
+{
+  if (step >= 3)
+    goto step_3;
+
+  while (step != 1)
+    delay ();
+
+  var++;	/* validity-thread-B */
+  dummy ();	/* validity-thread-B */
+  step = 2;
+  while (step != 3)
+    {
+      if (step == 99)
+        goto step_99;
+      delay ();
+    }
+
+step_3:
+  if (step >= 5)
+    goto step_5;
+
+  var++;	/* after-fork1-B */
+  dummy ();	/* after-fork1-B */
+  step = 4;
+  while (step != 5)
+    {
+      if (step == 99)
+        goto step_99;
+      delay ();
+    }
+
+step_5:
+  var++;	/* after-fork2-B */
+  dummy ();	/* after-fork2-B */
+  return (void *) 5UL;
+
+step_99:
+  /* We must not get caught here (against a forgotten breakpoint).  */
+  var++;
+  breakpoint ();
+  return (void *) 99UL;
+}
+
+int main (void)
+{ 
+  int i;
+  void *thread_result;
+
+  setbuf (stdout, NULL);
+  printf ("main: %d\n", (int) gettid ());
+
+  /* General watchpoints validity.  */
+  var++;	/* validity-first */
+  dummy ();	/* validity-first */
+
+  i = pthread_create (&thread, NULL, start, NULL);
+  assert (i == 0);
+
+  var++;	/* validity-thread-A */
+  dummy ();	/* validity-thread-A */
+  step = 1;
+  while (step != 2)
+    delay ();
+
+  /* Hardware watchpoints got disarmed here.  */
+  forkoff (1);
+
+  var++;	/* after-fork1-A */
+  dummy ();	/* after-fork1-A */
+  step = 3;
+#ifdef FOLLOW_CHILD
+  /* Spawn new thread as it was deleted in the child of FORK.  */
+  i = pthread_create (&thread, NULL, start, NULL);
+  assert (i == 0);
+#endif
+  while (step != 4)
+    delay ();
+
+  /* A sanity check for double hardware watchpoints removal.  */
+  forkoff (2);
+
+  var++;	/* after-fork2-A */
+  dummy ();	/* after-fork2-A */
+  step = 5;
+#ifdef FOLLOW_CHILD
+  /* Spawn new thread as it was deleted in the child of FORK.  */
+  i = pthread_create (&thread, NULL, start, NULL);
+  assert (i == 0);
+#endif
+
+  i = pthread_join (thread, &thread_result);
+  assert (i == 0);
+  assert (thread_result == (void *) 5UL);
+
+  return 0;
+}
diff -u -ruNp gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork.c gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork.c
--- gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork.c	2008-01-11 20:28:48.000000000 +0100
@@ -0,0 +1,56 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+   Copyright 2008
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static volatile int var;
+
+static void breakpoint (void)
+{
+}
+
+/* Include here the function:
+   static void forkoff (int nr);  */
+
+#include "watchpoint-fork-forkoff.c"
+
+int main (void)
+{
+  setbuf (stdout, NULL);
+  printf ("main: %d\n", (int) getpid ());
+
+  /* General watchpoints validity.  */
+  var++;
+  /* Hardware watchpoints got disarmed here.  */
+  forkoff (1);
+  /* This watchpoint got lost before.  */
+  var++;
+  /* A sanity check for double hardware watchpoints removal.  */
+  forkoff (2);
+  var++;
+
+  return 0;
+}
diff -u -ruNp gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork.exp gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork.exp
--- gdb-6.7.1-orig/gdb/testsuite/gdb.threads/watchpoint-fork.exp	1970-01-01 01:00:00.000000000 +0100
+++ gdb-6.7.1/gdb/testsuite/gdb.threads/watchpoint-fork.exp	2008-01-11 20:28:48.000000000 +0100
@@ -0,0 +1,140 @@
+# Copyright 2008 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/>.
+
+# Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+proc test {type symbol} {
+    global objdir subdir srcdir
+
+    global pf_prefix
+    set prefix_test $pf_prefix
+    lappend pf_prefix "$type:"
+    set prefix_mt $pf_prefix
+
+    # no threads
+
+    set pf_prefix $prefix_mt
+    lappend pf_prefix "singlethreaded:"
+
+    set testfile watchpoint-fork
+    set srcfile ${testfile}.c
+    set binfile ${objdir}/${subdir}/${testfile}
+
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug additional_flags=-D$symbol"] != "" } {
+	untested "Couldn't compile test program"
+	return -1
+    }
+
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    gdb_test "set follow-fork-mode $type"
+    # Testcase uses it for the `follow-fork-mode child' type.
+    gdb_test "handle SIGUSR1 nostop noprint pass"
+
+    if { ![runto_main] } then {
+	gdb_suppress_tests
+	return
+    }
+
+    # Install the watchpoint only after getting into MAIN - workaround some PPC
+    # problem.
+    gdb_test "watch var" "atchpoint 2: var" "Set the watchpoint"
+
+    # It is never hit but it should not be left over in the fork()ed-off child.
+    gdb_breakpoint "breakpoint"
+
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 0.*New value = 1.*forkoff *\\(1\\).*" "watchpoints work"
+    gdb_test "continue" \
+	     "reakpoint 3, breakpoint.*" "breakpoint after the first fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 1.*New value = 2.*forkoff *\\(2\\).*" "watchpoint after the first fork"
+    gdb_test "continue" \
+	     "reakpoint 3, breakpoint.*" "breakpoint after the second fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 2.*New value = 3.*return *0;" "watchpoint after the second fork"
+    gdb_test "continue" "Continuing..*Program exited normally." "finish"
+
+
+    # threads
+
+    set pf_prefix $prefix_mt
+    lappend pf_prefix "multithreaded:"
+
+    set testfile watchpoint-fork-mt
+    set srcfile ${testfile}.c
+    set binfile ${objdir}/${subdir}/${testfile}
+
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug additional_flags=-D$symbol"] != "" } {
+	untested "Couldn't compile test program"
+	return -1
+    }
+
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    gdb_test "set follow-fork-mode $type"
+    # Testcase uses it for the `follow-fork-mode child' type.
+    gdb_test "handle SIGUSR1 nostop noprint pass"
+
+    if { ![runto_main] } then {
+	gdb_suppress_tests
+	return
+    }
+
+    # Install the watchpoint only after getting into MAIN - workaround some PPC
+    # problem.
+    gdb_test "watch var" "atchpoint 2: var" "Set the watchpoint"
+
+    # It is never hit but it should not be left over in the fork()ed-off child.
+    gdb_breakpoint "breakpoint"
+
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 0.*New value = 1.*validity-first.*" "singlethread watchpoints work"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 1.*New value = 2.*validity-thread-A.*" "multithreaded watchpoints work at A"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 2.*New value = 3.*validity-thread-B.*" "multithreaded watchpoints work at B"
+    gdb_test "continue" \
+	     "reakpoint 3, breakpoint.*" "breakpoint (A) after the first fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 3.*New value = 4.*after-fork1-A.*" "watchpoint A after the first fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 4.*New value = 5.*after-fork1-B.*" "watchpoint B after the first fork"
+    gdb_test "continue" \
+	     "reakpoint 3, breakpoint.*" "breakpoint (A) after the second fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 5.*New value = 6.*after-fork2-A.*" "watchpoint A after the second fork"
+    gdb_test "continue" \
+	     "atchpoint 2: var.*Old value = 6.*New value = 7.*after-fork2-B.*" "watchpoint B after the second fork"
+    gdb_test "continue" "Continuing..*Program exited normally." "finish"
+
+
+    # cleanup
+
+    set pf_prefix $prefix_test
+}
+
+test parent FOLLOW_PARENT
+
+# Only GNU/Linux is known to support `set follow-fork-mode child'.
+if {[istarget "*-*-linux*"]} {
+    test child FOLLOW_CHILD
+}
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.434
retrieving revision 1.435
diff -u -r1.434 -r1.435
--- src/gdb/doc/gdb.texinfo	2007/09/28 11:09:55	1.434
+++ src/gdb/doc/gdb.texinfo	2007/10/01 00:17:58	1.435
@@ -3346,20 +3346,13 @@
 way of doing that would be to set a code breakpoint at the entry to the
 @code{main} function and when it breaks, set all the watchpoints.
 
-@quotation
 @cindex watchpoints and threads
 @cindex threads and watchpoints
-@emph{Warning:} In multi-thread programs, watchpoints have only limited
-usefulness.  With the current watchpoint implementation, @value{GDBN}
-can only watch the value of an expression @emph{in a single thread}.  If
-you are confident that the expression can only change due to the current
-thread's activity (and if you are also confident that no other thread
-can become current), then you can use watchpoints as usual.  However,
-@value{GDBN} may not notice when a non-current thread's activity changes
-the expression.
+In multi-threaded programs, watchpoints will detect changes to the
+watched expression from every thread.
 
-@c FIXME: this is almost identical to the previous paragraph.
-@emph{HP-UX Warning:} In multi-thread programs, software watchpoints
+@quotation
+@emph{Warning:} In multi-threaded programs, software watchpoints
 have only limited usefulness.  If @value{GDBN} creates a software
 watchpoint, it can only watch the value of an expression @emph{in a
 single thread}.  If you are confident that the expression can only

--- gdb-6.5/gdb/doc/gdb.texinfo-orig	2007-12-15 13:25:14.000000000 +0100
+++ gdb-6.5/gdb/doc/gdb.texinfo	2007-12-15 13:45:25.000000000 +0100
@@ -3261,6 +3261,14 @@
 software watchpoints as usual.  However, @value{GDBN} may not notice
 when a non-current thread's activity changes the expression.  (Hardware
 watchpoints, in contrast, watch an expression in all threads.)
+
+Software watchpoints single-step the current thread to track the changes.  
+Other threads are left freely running on @code{continue}; therefore, their
+changes cannot be caught.  To get more reliable software watchpoints, please
+use @code{set scheduler-locking on}.  The default for Red Hat/Fedora
+@value{GDBN} is @code{set scheduler-locking step}, which makes the software
+watchpoints safe for the @code{step} command, but not for the @code{continue}
+command.  @xref{Thread Stops}.
 @end quotation
 
 @xref{set remote hardware-watchpoint-limit}.