From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15386 invoked by alias); 11 Dec 2010 05:15:56 -0000 Received: (qmail 15358 invoked by uid 22791); 11 Dec 2010 05:15:49 -0000 X-SWARE-Spam-Status: No, hits=-5.4 required=5.0 tests=AWL,BAYES_00,KAM_STOCKGEN,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_BJ,TW_CP,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 11 Dec 2010 05:15:40 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oBB5FcbW019176 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Sat, 11 Dec 2010 00:15:39 -0500 Received: from host0.dyn.jankratochvil.net (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id oBB5FYvJ017250 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Sat, 11 Dec 2010 00:15:38 -0500 Received: from host0.dyn.jankratochvil.net (host0.dyn.jankratochvil.net [127.0.0.1]) by host0.dyn.jankratochvil.net (8.14.4/8.14.4) with ESMTP id oBB5FYWn013751; Sat, 11 Dec 2010 06:15:34 +0100 Received: (from jkratoch@localhost) by host0.dyn.jankratochvil.net (8.14.4/8.14.4/Submit) id oBB5FYXP013750; Sat, 11 Dec 2010 06:15:34 +0100 Date: Sat, 11 Dec 2010 05:15:00 -0000 From: Jan Kratochvil To: Mark Kettenis Cc: gdb-patches@sourceware.org Subject: Re: [patch 2/4] hw watchpoints across fork() Message-ID: <20101211051533.GB10298@host0.dyn.jankratochvil.net> References: <20101206111300.GC27176@host0.dyn.jankratochvil.net> <201012061305.oB6D5NMi021251@glazunov.sibelius.xs4all.nl> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <201012061305.oB6D5NMi021251@glazunov.sibelius.xs4all.nl> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-12/txt/msg00155.txt.bz2 On Mon, 06 Dec 2010 14:05:23 +0100, Mark Kettenis wrote: > > +static struct i386_inferior_data * > > +i386_inferior_data_get (void) > > +{ > > + static struct i386_inferior_data inf_data_local; > > + struct inferior *inf = current_inferior (); > > + struct i386_inferior_data *inf_data = &inf_data_local; > > + static struct i386_inferior_data *detached_inf_data; > > + static int detached_inf_pid = -1; > > The whole dance with inf_data seems unecessarily complicated to me. > Why not... It gets clear by the multi-inferior patch. Forgot to mark inf_data_local as: /* Intermediate patch stub. */ Whether to make such complications instead of merging the patchset is questionable. In this case the patchset has to implement two different features (multi-fork and multi-inferior) so I considered its separation highly appropriate for review. > > + xfree (detached_inf_data); > > + detached_inf_pid = ptid_get_pid (inferior_ptid); > > + detached_inf_data = xmalloc (sizeof (*detached_inf_data)); > > Huh, free and immediately malloc? Good point, using just a static local variable now. > > + gdb_assert (detached_inf_data != NULL); > > + inf_data = detached_inf_data; > > Simply return detached_inf_data here. Done (I do not see a difference, though). > > > + } > > + > > + return inf_data; > > And return &inf_data_local here? But why are you returning pointers > to static storage in the first place? I'm abviously missing the point > of this function. Can you explain? See the multi-inferior patch part (the only one you have not reviewed so far). Thanks, Jan gdb/ 2010-12-06 Jan Kratochvil Fix watchpoints across fork. * amd64-linux-nat.c (amd64_linux_dr_set_control_callback): Use parameter tid. Remove the return value. (amd64_linux_dr_set_control): Use linux_nat_iterate_watchpoint_lwps. (amd64_linux_dr_set_addr_callback): Use parameter tid. Remove the return value. (amd64_linux_dr_set_addr): Use linux_nat_iterate_watchpoint_lwps. (amd64_linux_dr_unset_status_callback): Use parameter tid. Remove variable tid. Remove the return value. (amd64_linux_dr_unset_status): Use linux_nat_iterate_watchpoint_lwps. * i386-linux-nat.c (i386_linux_dr_set_control_callback): Use parameter tid. Remove the return value. (i386_linux_dr_set_control): Use linux_nat_iterate_watchpoint_lwps. (i386_linux_dr_set_addr_callback): Use parameter tid. Remove the return value. (i386_linux_dr_set_addr): Use linux_nat_iterate_watchpoint_lwps. (i386_linux_dr_unset_status_callback): Use parameter tid. Remove variable tid. Remove the return value. (i386_linux_dr_unset_status): Use linux_nat_iterate_watchpoint_lwps. * i386-nat.c: Include inferior.h. (i386_inferior_data, struct i386_inferior_data) (i386_inferior_data_get): New. (i386_dr_mirror_get): Remove variable dr_mirror, call i386_inferior_data_get. * linux-nat.c (struct iterate_watchpoint_lwps_data) (iterate_watchpoint_lwps_callback, linux_nat_iterate_watchpoint_lwps): New. * linux-nat.h (linux_nat_iterate_watchpoint_lwps_ftype) (linux_nat_iterate_watchpoint_lwps): New. * ppc-linux-nat.c (booke_insert_point_callback): Use parameter tid. Remove the return value. (ppc_linux_insert_hw_breakpoint): Use linux_nat_iterate_watchpoint_lwps. (booke_remove_point_callback): Use parameter tid. Remove the return value. (ppc_linux_remove_hw_breakpoint): Use linux_nat_iterate_watchpoint_lwps. (set_saved_dabr_value_callback): Use parameter tid. Remove the return value. (ppc_linux_insert_watchpoint, ppc_linux_remove_watchpoint): Use linux_nat_iterate_watchpoint_lwps. gdb/testsuite/ 2010-12-06 Jan Kratochvil Fix watchpoints across fork. * gdb.threads/watchpoint-fork-child.c: New file. * gdb.threads/watchpoint-fork-mt.c: New file. * gdb.threads/watchpoint-fork-parent.c: New file. * gdb.threads/watchpoint-fork-st.c: New file. * gdb.threads/watchpoint-fork.exp: New file. * gdb.threads/watchpoint-fork.h: New file. --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -304,15 +304,12 @@ amd64_linux_dr_set (int tid, int regnum, unsigned long value) /* Helper for amd64_linux_dr_set_control. */ -static int -amd64_linux_dr_set_control_callback (struct lwp_info *lp, void *control_voidp) +static void +amd64_linux_dr_set_control_callback (int tid, void *control_voidp) { unsigned long control = *(unsigned long *) control_voidp; - amd64_linux_dr_set (GET_LWP (lp->ptid), DR_CONTROL, control); - - /* Continue the traversal. */ - return 0; + amd64_linux_dr_set (tid, DR_CONTROL, control); } /* Set DR_CONTROL to ADDR in all LWPs of CURRENT_INFERIOR. */ @@ -322,8 +319,8 @@ amd64_linux_dr_set_control (unsigned long control) { amd64_linux_dr[DR_CONTROL] = control; - iterate_over_lwps (minus_one_ptid, amd64_linux_dr_set_control_callback, - &control); + linux_nat_iterate_watchpoint_lwps (amd64_linux_dr_set_control_callback, + &control); } /* Helper for amd64_linux_dr_set_addr. */ @@ -334,16 +331,12 @@ struct amd64_linux_dr_set_addr_data CORE_ADDR addr; }; -static int -amd64_linux_dr_set_addr_callback (struct lwp_info *lp, void *datap_voidp) +static void +amd64_linux_dr_set_addr_callback (int tid, void *datap_voidp) { const struct amd64_linux_dr_set_addr_data *datap = datap_voidp; - amd64_linux_dr_set (GET_LWP (lp->ptid), DR_FIRSTADDR + datap->regnum, - datap->addr); - - /* Continue the traversal. */ - return 0; + amd64_linux_dr_set (tid, DR_FIRSTADDR + datap->regnum, datap->addr); } /* Set address REGNUM (zero based) to ADDR in all LWPs of CURRENT_INFERIOR. @@ -360,7 +353,7 @@ amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) data.regnum = regnum; data.addr = addr; - iterate_over_lwps (minus_one_ptid, amd64_linux_dr_set_addr_callback, &data); + linux_nat_iterate_watchpoint_lwps (amd64_linux_dr_set_addr_callback, &data); } /* Set address REGNUM (zero based) to zero in all LWPs of CURRENT_INFERIOR. @@ -388,19 +381,15 @@ amd64_linux_dr_get_status (void) /* Helper for amd64_linux_dr_unset_status. */ -static int -amd64_linux_dr_unset_status_callback (struct lwp_info *lp, void *mask_voidp) +static void +amd64_linux_dr_unset_status_callback (int tid, void *mask_voidp) { unsigned long mask = *(unsigned long *) mask_voidp; unsigned long value; - int tid = GET_LWP (lp->ptid); value = amd64_linux_dr_get (tid, DR_STATUS); value &= ~mask; amd64_linux_dr_set (tid, DR_STATUS, value); - - /* Continue the traversal. */ - return 0; } /* Unset MASK bits in DR_STATUS in all LWPs of CURRENT_INFERIOR. */ @@ -408,7 +397,7 @@ amd64_linux_dr_unset_status_callback (struct lwp_info *lp, void *mask_voidp) static void amd64_linux_dr_unset_status (unsigned long mask) { - iterate_over_lwps (minus_one_ptid, amd64_linux_dr_unset_status_callback, + linux_nat_iterate_watchpoint_lwps (amd64_linux_dr_unset_status_callback, &mask); } --- a/gdb/i386-linux-nat.c +++ b/gdb/i386-linux-nat.c @@ -676,15 +676,12 @@ i386_linux_dr_set (int tid, int regnum, unsigned long value) /* Helper for i386_linux_dr_set_control. */ -static int -i386_linux_dr_set_control_callback (struct lwp_info *lp, void *control_voidp) +static void +i386_linux_dr_set_control_callback (int tid, void *control_voidp) { unsigned long control = *(unsigned long *) control_voidp; - i386_linux_dr_set (GET_LWP (lp->ptid), DR_CONTROL, control); - - /* Continue the traversal. */ - return 0; + i386_linux_dr_set (tid, DR_CONTROL, control); } /* Set DR_CONTROL to ADDR in all LWPs of CURRENT_INFERIOR. */ @@ -694,7 +691,7 @@ i386_linux_dr_set_control (unsigned long control) { i386_linux_dr[DR_CONTROL] = control; - iterate_over_lwps (minus_one_ptid, i386_linux_dr_set_control_callback, + linux_nat_iterate_watchpoint_lwps (i386_linux_dr_set_control_callback, &control); } @@ -706,16 +703,12 @@ struct i386_linux_dr_set_addr_data CORE_ADDR addr; }; -static int -i386_linux_dr_set_addr_callback (struct lwp_info *lp, void *datap_voidp) +static void +i386_linux_dr_set_addr_callback (int tid, void *datap_voidp) { const struct i386_linux_dr_set_addr_data *datap = datap_voidp; - i386_linux_dr_set (GET_LWP (lp->ptid), DR_FIRSTADDR + datap->regnum, - datap->addr); - - /* Continue the traversal. */ - return 0; + i386_linux_dr_set (tid, DR_FIRSTADDR + datap->regnum, datap->addr); } /* Set address REGNUM (zero based) to ADDR in all LWPs of CURRENT_INFERIOR. @@ -732,7 +725,7 @@ i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) data.regnum = regnum; data.addr = addr; - iterate_over_lwps (minus_one_ptid, i386_linux_dr_set_addr_callback, &data); + linux_nat_iterate_watchpoint_lwps (i386_linux_dr_set_addr_callback, &data); } /* Set address REGNUM (zero based) to zero in all LWPs of CURRENT_INFERIOR. @@ -760,19 +753,15 @@ i386_linux_dr_get_status (void) /* Helper for i386_linux_dr_unset_status. */ -static int -i386_linux_dr_unset_status_callback (struct lwp_info *lp, void *mask_voidp) +static void +i386_linux_dr_unset_status_callback (int tid, void *mask_voidp) { unsigned long mask = *(unsigned long *) mask_voidp; unsigned long value; - int tid = GET_LWP (lp->ptid); value = i386_linux_dr_get (tid, DR_STATUS); value &= ~mask; i386_linux_dr_set (tid, DR_STATUS, value); - - /* Continue the traversal. */ - return 0; } /* Unset MASK bits in DR_STATUS in all LWPs of CURRENT_INFERIOR. */ @@ -780,7 +769,7 @@ i386_linux_dr_unset_status_callback (struct lwp_info *lp, void *mask_voidp) static void i386_linux_dr_unset_status (unsigned long mask) { - iterate_over_lwps (minus_one_ptid, i386_linux_dr_unset_status_callback, + linux_nat_iterate_watchpoint_lwps (i386_linux_dr_unset_status_callback, &mask); } --- a/gdb/i386-nat.c +++ b/gdb/i386-nat.c @@ -25,6 +25,7 @@ #include "gdbcmd.h" #include "target.h" #include "gdb_assert.h" +#include "inferior.h" /* Support for hardware watchpoints and breakpoints using the i386 debug registers. @@ -219,16 +220,50 @@ static int i386_handle_nonaligned_watchpoint (i386_wp_op_t what, /* Implementation. */ +/* Per-inferior data key. */ +static const struct inferior_data *i386_inferior_data; + +struct i386_inferior_data + { + /* Copy of i386 hardware debug registers for performance reasons. */ + struct i386_dr_mirror dr_mirror; + }; + +static struct i386_inferior_data * +i386_inferior_data_get (void) +{ + /* Intermediate patch stub. */ + static struct i386_inferior_data inf_data_local; + struct inferior *inf = current_inferior (); + struct i386_inferior_data *inf_data = &inf_data_local; + + if (inf->pid != ptid_get_pid (inferior_ptid)) + { + static struct i386_inferior_data detached_inf_data_local; + static int detached_inf_pid = -1; + + if (detached_inf_pid != ptid_get_pid (inferior_ptid)) + { + detached_inf_pid = ptid_get_pid (inferior_ptid); + + /* Forked processes get a copy of the debug registers. */ + memcpy (&detached_inf_data_local, inf_data, + sizeof (detached_inf_data_local)); + } + + return &detached_inf_data_local; + } + + return inf_data; +} + /* Clear the reference counts and forget everything we knew about the debug registers. */ static struct i386_dr_mirror * i386_dr_mirror_get (void) { - /* Intermediate patch stub. */ - static struct i386_dr_mirror dr_mirror; - - return &dr_mirror; + return &i386_inferior_data_get ()->dr_mirror; } void --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1243,6 +1243,60 @@ iterate_over_lwps (ptid_t filter, return NULL; } +/* Helper for linux_nat_iterate_watchpoint_lwps. */ + +struct iterate_watchpoint_lwps_data + { + linux_nat_iterate_watchpoint_lwps_ftype callback; + void *callback_data; + }; + +static int +iterate_watchpoint_lwps_callback (struct lwp_info *lp, void *datap_voidp) +{ + struct iterate_watchpoint_lwps_data *datap = datap_voidp; + int tid; + + tid = TIDGET (lp->ptid); + if (tid == 0) + tid = PIDGET (lp->ptid); + + datap->callback (tid, datap->callback_data); + + /* Continue the traversal. */ + return 0; +} + +/* Iterate like iterate_over_lwps does except when forking-off a child call + CALLBACK with CALLBACK_DATA specifically only for that new child PID. + + During `set follow-fork-mode child' the call is also made for the new child + PID; parent watchpoints get detached elsewhere (during target_detach). */ + +void +linux_nat_iterate_watchpoint_lwps + (linux_nat_iterate_watchpoint_lwps_ftype callback, void *callback_data) +{ + struct iterate_watchpoint_lwps_data data; + int inferior_pid = ptid_get_pid (inferior_ptid); + struct inferior *inf = current_inferior (); + + data.callback = callback; + data.callback_data = callback_data; + + if (inf->pid == inferior_pid) + { + /* Standard mode. */ + iterate_over_lwps (minus_one_ptid, + iterate_watchpoint_lwps_callback, &data); + } + else + { + /* Detaching a new child PID temporarily present in INFERIOR_PID. */ + callback (inferior_pid, callback_data); + } +} + /* Update our internal state when changing from one checkpoint to another indicated by NEW_PTID. We can only switch single-threaded applications, so we only create one new LWP, and the previous list --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -139,6 +139,11 @@ struct lwp_info *iterate_over_lwps (ptid_t filter, void *), void *data); +typedef void (*linux_nat_iterate_watchpoint_lwps_ftype) (int tid, void *data); + +extern void linux_nat_iterate_watchpoint_lwps + (linux_nat_iterate_watchpoint_lwps_ftype callback, void *callback_data); + /* Create a prototype generic GNU/Linux target. The client can override it with local methods. */ struct target_ops * linux_target (void); --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -1620,15 +1620,12 @@ booke_remove_point (struct ppc_hw_breakpoint *b, int tid) hw_breaks[i].hw_break = NULL; } -static int -booke_insert_point_callback (struct lwp_info *lp, void *pp_voidp) +static void +booke_insert_point_callback (int tid, void *pp_voidp) { struct ppc_hw_breakpoint *pp = pp_voidp; - booke_insert_point (pp, GET_LWP (lp->ptid)); - - /* Continue the traversal. */ - return 0; + booke_insert_point (pp, tid); } static int @@ -1648,20 +1645,17 @@ ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch, p.addr2 = 0; p.condition_value = 0; - iterate_over_lwps (minus_one_ptid, booke_insert_point_callback, &p); + linux_nat_iterate_watchpoint_lwps (booke_insert_point_callback, &p); return 0; } -static int -booke_remove_point_callback (struct lwp_info *lp, void *pp_voidp) +static void +booke_remove_point_callback (int tid, void *pp_voidp) { struct ppc_hw_breakpoint *pp = pp_voidp; - booke_remove_point (pp, GET_LWP (lp->ptid)); - - /* Continue the traversal. */ - return 0; + booke_remove_point (pp, tid); } static int @@ -1681,7 +1675,7 @@ ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch, p.addr2 = 0; p.condition_value = 0; - iterate_over_lwps (minus_one_ptid, booke_remove_point_callback, &p); + linux_nat_iterate_watchpoint_lwps (booke_remove_point_callback, &p); return 0; } @@ -1894,17 +1888,13 @@ ppc_linux_can_accel_watchpoint_condition (CORE_ADDR addr, int len, int rw, && check_condition (addr, cond, &data_value)); } -static int -set_saved_dabr_value_callback (struct lwp_info *lp, void *retp_voidp) +static void +set_saved_dabr_value_callback (int tid, void *retp_voidp) { int *retp = retp_voidp; - if (ptrace (PTRACE_SET_DEBUGREG, GET_LWP (lp->ptid), 0, saved_dabr_value) - < 0) + if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value) < 0) *retp = -1; - - /* Continue the traversal. */ - return 0; } static int @@ -1934,7 +1924,7 @@ ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw, p.addr = (uint64_t) addr; p.addr2 = 0; - iterate_over_lwps (minus_one_ptid, booke_insert_point_callback, &p); + linux_nat_iterate_watchpoint_lwps (booke_insert_point_callback, &p); ret = 0; } @@ -1978,7 +1968,7 @@ ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw, saved_dabr_value = dabr_value; ret = 0; - iterate_over_lwps (minus_one_ptid, set_saved_dabr_value_callback, &ret); + linux_nat_iterate_watchpoint_lwps (set_saved_dabr_value_callback, &ret); } return ret; @@ -2011,7 +2001,7 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw, p.addr = (uint64_t) addr; p.addr2 = 0; - iterate_over_lwps (minus_one_ptid, booke_remove_point_callback, &p); + linux_nat_iterate_watchpoint_lwps (booke_remove_point_callback, &p); ret = 0; } @@ -2020,7 +2010,7 @@ ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw, saved_dabr_value = 0; ret = 0; - iterate_over_lwps (minus_one_ptid, set_saved_dabr_value_callback, &ret); + linux_nat_iterate_watchpoint_lwps (set_saved_dabr_value_callback, &ret); } return ret; --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork-child.c @@ -0,0 +1,127 @@ +/* Test case for forgotten hw-watchpoints after fork()-off of a process. + + Copyright 2008, 2009, 2010 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 "watchpoint-fork.h" + +static volatile int usr1_got; + +static void +handler_usr1 (int signo) +{ + usr1_got++; +} + +void +forkoff (int nr) +{ + pid_t child, save_parent = getpid (); + int i; + 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++; + marker (); + +#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 (;;) + { + /* Parent either died (and USR1_GOT is zero) or it succeeded. */ + if (getppid () != save_parent) + break; + if (kill (getppid (), 0) != 0) + break; + /* Parent succeeded? */ + if (usr1_got) + break; + +#ifdef THREAD + i = pthread_yield (); + assert (i == 0); +#endif + } + assert (usr1_got); + + /* We must get caught here (against a false watchpoint removal). */ + + marker (); + } + + i = sigaction (SIGUSR1, &oldact, NULL); + assert (i == 0); +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c @@ -0,0 +1,174 @@ +/* Test case for forgotten hw-watchpoints after fork()-off of a process. + + Copyright 2008, 2009, 2010 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) + +#include "watchpoint-fork.h" + +/* 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. */ + +volatile int var; + +void +marker (void) +{ +} + +static void +empty (void) +{ +} + +static void +mark_exit (void) +{ +} + +pthread_t thread; +volatile int step; + +static void * +start (void *arg) +{ + int i; + + if (step >= 3) + goto step_3; + + while (step != 1) + { + i = pthread_yield (); + assert (i == 0); + } + + var++; /* validity-thread-B */ + empty (); /* validity-thread-B */ + step = 2; + while (step != 3) + { + if (step == 99) + goto step_99; + + i = pthread_yield (); + assert (i == 0); + } + +step_3: + if (step >= 5) + goto step_5; + + var++; /* after-fork1-B */ + empty (); /* after-fork1-B */ + step = 4; + while (step != 5) + { + if (step == 99) + goto step_99; + + i = pthread_yield (); + assert (i == 0); + } + +step_5: + var++; /* after-fork2-B */ + empty (); /* after-fork2-B */ + return (void *) 5UL; + +step_99: + /* We must not get caught here (against a forgotten breakpoint). */ + var++; + marker (); + return (void *) 99UL; +} + +int +main (void) +{ + int i; + void *thread_result; + + setbuf (stdout, NULL); + printf ("main: %d\n", (int) gettid ()); + + /* General hardware breakpoints and watchpoints validity. */ + marker (); + var++; /* validity-first */ + empty (); /* validity-first */ + + i = pthread_create (&thread, NULL, start, NULL); + assert (i == 0); + + var++; /* validity-thread-A */ + empty (); /* validity-thread-A */ + step = 1; + while (step != 2) + { + i = pthread_yield (); + assert (i == 0); + } + + /* Hardware watchpoints got disarmed here. */ + forkoff (1); + + var++; /* after-fork1-A */ + empty (); /* 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) + { + i = pthread_yield (); + assert (i == 0); + } + + /* A sanity check for double hardware watchpoints removal. */ + forkoff (2); + + var++; /* after-fork2-A */ + empty (); /* 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); + + mark_exit (); + return 0; +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork-parent.c @@ -0,0 +1,74 @@ +/* Test case for forgotten hw-watchpoints after fork()-off of a process. + + Copyright 2008, 2009, 2010 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 "watchpoint-fork.h" + +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, marker () 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++; + marker (); + + _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). */ + marker (); + } +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork-st.c @@ -0,0 +1,61 @@ +/* Test case for forgotten hw-watchpoints after fork()-off of a process. + + Copyright 2008, 2009, 2010 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 "watchpoint-fork.h" + +volatile int var; + +void +marker (void) +{ +} + +static void +mark_exit (void) +{ +} + +int +main (void) +{ + setbuf (stdout, NULL); + printf ("main: %d\n", (int) getpid ()); + + /* General hardware breakpoints and watchpoints validity. */ + marker (); + 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++; + + mark_exit (); + return 0; +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork.exp @@ -0,0 +1,149 @@ +# Copyright 2008, 2009, 2010 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 gdb_prompt + + set testfile watchpoint-fork + + global pf_prefix + set prefix_test $pf_prefix + lappend pf_prefix "$type:" + set prefix_mt $pf_prefix + + set srcfile_type ${srcdir}/${subdir}/${testfile}-${type}.c + + + # no threads + + set pf_prefix $prefix_mt + lappend pf_prefix "singlethreaded:" + + set executable ${testfile}-${type}-st + set srcfile_main ${srcdir}/${subdir}/${testfile}-st.c + if { [gdb_compile "${srcfile_main} ${srcfile_type}" ${objdir}/${subdir}/${executable} executable [list debug additional_flags=-D$symbol]] != "" } { + untested ${testfile}.exp + return + } + clean_restart $executable + + gdb_test "show detach-on-fork" "Whether gdb will detach the child of a fork is on\\." + gdb_test_no_output "set follow-fork-mode $type" + gdb_test "show follow-fork-mode" "Debugger response to a program call of fork or vfork is \"$type\"\\." + # Testcase uses it for the `follow-fork-mode child' type. + gdb_test "handle SIGUSR1 nostop noprint pass" "No\[ \t\]+No\[ \t\]+Yes.*" + + if ![runto_main] { + return + } + + gdb_test "watch var" "atchpoint \[0-9\]+: var" "Set the watchpoint" + + # It is never hit but it should not be left over in the fork()ed-off child. + set hbreak "hbreak" + set test "hbreak marker" + gdb_test_multiple $test $test { + -re "Hardware assisted breakpoint \[0-9\]+ at .*\r\n$gdb_prompt $" { + pass $test + } + -re "(No hardware breakpoint support in the target\\.|Hardware breakpoints used exceeds limit\\.)\r\n$gdb_prompt $" { + pass $test + set hbreak "break" + gdb_test "break marker" + } + } + + gdb_breakpoint "mark_exit" + + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "hardware breakpoints work" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 0.*New value = 1.*forkoff *\\(1\\).*" "watchpoints work" + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "breakpoint after the first fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 1.*New value = 2.*forkoff *\\(2\\).*" "watchpoint after the first fork" + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "breakpoint after the second fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 2.*New value = 3.*mark_exit \\(\\);" "watchpoint after the second fork" + gdb_test "continue" "Continuing\\..*\r\nBreakpoint \[0-9\]+, mark_exit .*" "finish" + + + # threads + + set pf_prefix $prefix_mt + lappend pf_prefix "multithreaded:" + + set executable ${testfile}-${type}-mt + set srcfile_main ${srcdir}/${subdir}/${testfile}-mt.c + if { [gdb_compile_pthreads "${srcfile_main} ${srcfile_type}" ${objdir}/${subdir}/${executable} executable [list debug "additional_flags=-D$symbol -DTHREAD"]] != "" } { + untested ${testfile}.exp + return + } + clean_restart $executable + + gdb_test_no_output "set follow-fork-mode $type" + # Testcase uses it for the `follow-fork-mode child' type. + gdb_test "handle SIGUSR1 nostop noprint pass" "No\[ \t\]+No\[ \t\]+Yes.*" + + if ![runto_main] { + return + } + + gdb_test "watch var" "atchpoint \[0-9\]+: var" "Set the watchpoint" + + # It should not be left over in the fork()ed-off child. + gdb_test "$hbreak marker" {reakpoint [0-9]+.*} + + gdb_breakpoint "mark_exit" + + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "hardware breakpoints work" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 0.*New value = 1.*validity-first.*" "singlethread watchpoints work" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 1.*New value = 2.*validity-thread-A.*" "multithreaded watchpoints work at A" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 2.*New value = 3.*validity-thread-B.*" "multithreaded watchpoints work at B" + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "breakpoint (A) after the first fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 3.*New value = 4.*after-fork1-A.*" "watchpoint A after the first fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 4.*New value = 5.*after-fork1-B.*" "watchpoint B after the first fork" + gdb_test "continue" \ + "reakpoint \[0-9\]+, marker.*" "breakpoint (A) after the second fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 5.*New value = 6.*after-fork2-A.*" "watchpoint A after the second fork" + gdb_test "continue" \ + "atchpoint \[0-9\]+: var.*Old value = 6.*New value = 7.*after-fork2-B.*" "watchpoint B after the second fork" + gdb_test "continue" "Continuing\\..*\r\nBreakpoint \[0-9\]+, mark_exit .*" "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*"] && ![is_remote target]} { + test child FOLLOW_CHILD +} else { + untested "child" +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchpoint-fork.h @@ -0,0 +1,32 @@ +/* Test case for forgotten hw-watchpoints after fork()-off of a process. + + Copyright 2008, 2009, 2010 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. */ + +#ifdef THREAD +#include + +extern volatile int step; +extern pthread_t thread; +#endif /* THREAD */ + +extern volatile int var; + +extern void marker (void); +extern void forkoff (int nr);