gdb/gdb-6.6-multifork-debugreg....

1170 lines
36 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
@@ -664,12 +664,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. */
static int
ia64_linux_remove_watchpoint (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}.