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'. 2008-03-01 Jan Kratochvil Port to GDB-6.8pre. 2008-03-31 Jan Kratochvil * gdb.threads/watchpoint-fork-forkoff.c (forkoff): New delay after the parent/child messages to fix a race. Index: gdb-6.7.50.20080227/gdb/amd64-linux-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/amd64-linux-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/amd64-linux-nat.c 2008-03-01 16:33:40.000000000 +0100 @@ -408,25 +408,43 @@ amd64_linux_dr_set (ptid_t ptid, int reg void amd64_linux_dr_set_control (unsigned long control) { - struct lwp_info *lp; - ptid_t ptid; - amd64_linux_dr[DR_CONTROL] = control; - ALL_LWPS (lp, ptid) - amd64_linux_dr_set (ptid, DR_CONTROL, control); + + /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process + not listed for ALL_LWPS. */ + + if (ptid_get_lwp (inferior_ptid) == 0) + amd64_linux_dr_set (inferior_ptid, DR_CONTROL, control); + else + { + struct lwp_info *lp; + ptid_t ptid; + + ALL_LWPS (lp, ptid) + amd64_linux_dr_set (ptid, DR_CONTROL, control); + } } void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) { - struct lwp_info *lp; - ptid_t ptid; - gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); amd64_linux_dr[DR_FIRSTADDR + regnum] = addr; - ALL_LWPS (lp, ptid) - amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); + + /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process + not listed for ALL_LWPS. */ + + if (ptid_get_lwp (inferior_ptid) == 0) + amd64_linux_dr_set (inferior_ptid, DR_FIRSTADDR + regnum, addr); + else + { + struct lwp_info *lp; + ptid_t ptid; + + ALL_LWPS (lp, ptid) + amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); + } } void @@ -451,6 +469,41 @@ amd64_linux_new_thread (ptid_t ptid) amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]); } + +/* 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. */ +int +amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) +{ + 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; + + return i386_remove_watchpoint (addr, len, type); +} + +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); +} /* This function is called by libthread_db as part of its handling of @@ -520,6 +573,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. */ @@ -556,6 +644,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); linux_nat_set_new_thread (t, amd64_linux_new_thread); Index: gdb-6.7.50.20080227/gdb/config/i386/nm-i386.h =================================================================== --- gdb-6.7.50.20080227.orig/gdb/config/i386/nm-i386.h 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/config/i386/nm-i386.h 2008-03-01 10:50:39.000000000 +0100 @@ -110,6 +110,8 @@ extern int i386_stopped_by_watchpoint (v #define target_remove_hw_breakpoint(bp_tgt) \ i386_remove_hw_breakpoint (bp_tgt) +extern void i386_detach_breakpoints (int detached_pid); + #endif /* I386_USE_GENERIC_WATCHPOINTS */ #endif /* NM_I386_H */ Index: gdb-6.7.50.20080227/gdb/i386-linux-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/i386-linux-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/i386-linux-nat.c 2008-03-01 16:33:40.000000000 +0100 @@ -655,21 +655,42 @@ i386_linux_dr_set_control (unsigned long ptid_t ptid; i386_linux_dr[DR_CONTROL] = control; - ALL_LWPS (lp, ptid) - i386_linux_dr_set (ptid, DR_CONTROL, control); + + /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process + not listed for ALL_LWPS. */ + + if (ptid_get_lwp (inferior_ptid) == 0) + i386_linux_dr_set (inferior_ptid, DR_CONTROL, control); + else + { + struct lwp_info *lp; + ptid_t ptid; + + ALL_LWPS (lp, ptid) + i386_linux_dr_set (ptid, DR_CONTROL, control); + } } void i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) { - struct lwp_info *lp; - ptid_t ptid; - gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); i386_linux_dr[DR_FIRSTADDR + regnum] = addr; - ALL_LWPS (lp, ptid) - i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); + + /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process + not listed for ALL_LWPS. */ + + if (ptid_get_lwp (inferior_ptid) == 0) + i386_linux_dr_set (inferior_ptid, DR_FIRSTADDR + regnum, addr); + else + { + struct lwp_info *lp; + ptid_t ptid; + + ALL_LWPS (lp, ptid) + i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); + } } void @@ -694,6 +715,41 @@ i386_linux_new_thread (ptid_t ptid) i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]); } + +/* 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. */ +int +i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) +{ + 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; + + return i386_remove_watchpoint (addr, len, type); +} + +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); +} /* Called by libthread_db. Returns a pointer to the thread local @@ -833,6 +889,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) { @@ -852,6 +942,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); linux_nat_set_new_thread (t, i386_linux_new_thread); Index: gdb-6.7.50.20080227/gdb/i386-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/i386-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/i386-nat.c 2008-03-01 10:50:39.000000000 +0100 @@ -544,6 +544,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. */ Index: gdb-6.7.50.20080227/gdb/ia64-linux-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/ia64-linux-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/ia64-linux-nat.c 2008-03-01 10:50:39.000000000 +0100 @@ -583,6 +583,12 @@ ia64_linux_insert_watchpoint (CORE_ADDR 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 ia64_linux_detach_breakpoints_pid; + static int ia64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) { @@ -590,6 +596,11 @@ ia64_linux_remove_watchpoint (CORE_ADDR long dbr_addr, dbr_mask; int max_watchpoints = 4; + 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; + if (len <= 0 || !is_power_of_2 (len)) return -1; @@ -617,6 +628,22 @@ ia64_linux_remove_watchpoint (CORE_ADDR } 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) { int i, any; @@ -805,6 +832,40 @@ ia64_linux_xfer_partial (struct target_o offset, len); } +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); /* @@ -899,6 +960,9 @@ _initialize_ia64_linux_nat (void) t->to_insert_watchpoint = ia64_linux_insert_watchpoint; t->to_remove_watchpoint = ia64_linux_remove_watchpoint; + 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); linux_nat_set_new_thread (t, ia64_linux_new_thread); Index: gdb-6.7.50.20080227/gdb/ppc-linux-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/ppc-linux-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/ppc-linux-nat.c 2008-03-01 10:50:39.000000000 +0100 @@ -847,6 +847,12 @@ ppc_linux_insert_watchpoint (CORE_ADDR a 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 ppc_linux_detach_breakpoints_pid; + static int ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw) { @@ -854,6 +860,11 @@ ppc_linux_remove_watchpoint (CORE_ADDR a ptid_t ptid; long dabr_value = 0; + 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; + saved_dabr_value = 0; ALL_LWPS (lp, ptid) if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0) @@ -867,6 +878,15 @@ ppc_linux_new_thread (ptid_t ptid) ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value); } +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) { @@ -976,6 +996,40 @@ ppc_linux_read_description (struct targe return NULL; } +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 @@ -1000,6 +1054,9 @@ _initialize_ppc_linux_nat (void) t->to_read_description = ppc_linux_read_description; + 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); linux_nat_set_new_thread (t, ppc_linux_new_thread); Index: gdb-6.7.50.20080227/gdb/s390-nat.c =================================================================== --- gdb-6.7.50.20080227.orig/gdb/s390-nat.c 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/s390-nat.c 2008-03-01 10:50:39.000000000 +0100 @@ -283,21 +283,15 @@ s390_stopped_by_watchpoint (void) } static void -s390_fix_watch_points (ptid_t ptid) +s390_fix_watch_points (int tid, struct watch_area *area_list) { - int tid; - per_struct per_info; ptrace_area parea; CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0; struct watch_area *area; - tid = TIDGET (ptid); - if (tid == 0) - tid = PIDGET (ptid); - - 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); @@ -309,7 +303,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; @@ -326,6 +320,18 @@ s390_fix_watch_points (ptid_t ptid) perror_with_name (_("Couldn't modify watchpoint status")); } +static void +s390_fix_watch_points (ptid_t ptid) +{ + int tid; + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + s390_fix_watch_points_list (tid, watch_base); +} + static int s390_insert_watchpoint (CORE_ADDR addr, int len, int type) { @@ -347,6 +353,12 @@ s390_insert_watchpoint (CORE_ADDR addr, 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; + static int s390_remove_watchpoint (CORE_ADDR addr, int len, int type) { @@ -354,6 +366,11 @@ s390_remove_watchpoint (CORE_ADDR addr, ptid_t ptid; 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) @@ -375,6 +392,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) { @@ -387,6 +413,39 @@ s390_region_ok_for_hw_watchpoint (CORE_A return 1; } +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); @@ -410,6 +469,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); linux_nat_set_new_thread (t, s390_fix_watch_points); --- /dev/null 2008-03-30 17:41:11.547000906 -0400 +++ gdb-6.8-patched/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c 2008-03-30 18:09:25.000000000 -0400 @@ -0,0 +1,172 @@ +/* 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, i; + + child = fork (); + switch (child) + { + case -1: + assert (0); + case 0: + printf ("child%d: %d\n", nr, (int) getpid ()); + /* Delay to get both the "child%d" and "parent%d" message printed without + a race breaking expect by its endless wait on `$gdb_prompt$': + Breakpoint 3, breakpoint () at ../../../gdb/testsuite/gdb.threads/watchpoint-fork.c:33 + 33 } + (gdb) parent2: 14223 */ + i = sleep (1); + assert (i == 0); + + /* We must not get caught here (against a forgotten breakpoint). */ + var++; + breakpoint (); + + _exit (exit_code); + default: + printf ("parent%d: %d\n", nr, (int) child); + /* Delay to get both the "child%d" and "parent%d" message printed, see + above. */ + i = sleep (1); + assert (i == 0); + + 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 Index: gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c 2008-03-01 10:50:39.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; +} Index: gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork.c 2008-03-01 10:50:39.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; +} Index: gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.7.50.20080227/gdb/testsuite/gdb.threads/watchpoint-fork.exp 2008-03-01 10:50:39.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 +} Index: gdb-6.7.50.20080227/gdb/doc/gdb.texinfo =================================================================== --- gdb-6.7.50.20080227.orig/gdb/doc/gdb.texinfo 2008-03-01 10:50:39.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/doc/gdb.texinfo 2008-03-01 10:50:39.000000000 +0100 @@ -3386,6 +3386,14 @@ confident that no other thread can becom 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}. Index: gdb-6.7.50.20080227/gdb/config/i386/nm-linux.h =================================================================== --- gdb-6.7.50.20080227.orig/gdb/config/i386/nm-linux.h 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/config/i386/nm-linux.h 2008-03-01 10:50:39.000000000 +0100 @@ -44,6 +44,17 @@ extern void i386_linux_dr_reset_addr (in extern unsigned long i386_linux_dr_get_status (void); #define I386_DR_LOW_GET_STATUS() \ i386_linux_dr_get_status () + +/* 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. */ +extern int i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type); + +/* Override basic i386 macros for watchpoint and hardware breakpoint + insertion/removal to support threads. */ +#undef target_remove_watchpoint +#define target_remove_watchpoint(addr, len, type) \ + i386_linux_remove_watchpoint (addr, len, type) #ifdef HAVE_PTRACE_GETFPXREGS Index: gdb-6.7.50.20080227/gdb/config/i386/nm-linux64.h =================================================================== --- gdb-6.7.50.20080227.orig/gdb/config/i386/nm-linux64.h 2008-03-01 10:38:02.000000000 +0100 +++ gdb-6.7.50.20080227/gdb/config/i386/nm-linux64.h 2008-03-01 10:50:39.000000000 +0100 @@ -50,4 +50,15 @@ extern unsigned long amd64_linux_dr_get_ #define I386_DR_LOW_GET_STATUS() \ amd64_linux_dr_get_status () +/* 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. */ +extern int amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type); + +/* Override basic amd64 macros for watchpoint and hardware breakpoint + insertion/removal to support threads. */ +#undef target_remove_watchpoint +#define target_remove_watchpoint(addr, len, type) \ + amd64_linux_remove_watchpoint (addr, len, type) + #endif /* nm-linux64.h */