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 Jeff Johnston * gdb.texinfo (Setting Watchpoints): Adjust warning text about multi-threaded watchpoints. 2007-12-15 Jan Kratochvil * 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 +#include + +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 +#include +#include +#include +#include +#include + +#include +#include +#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 +#include +#include +#include +#include + +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 . + +# 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}.