This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch 2/4] hw watchpoints across fork()


Hi,

current FSF GDB has two problems with hw watchpoints:

(a) after inferior fork()s, GDB detaches+ignores the child and everything
    looks OK.  But in the debugged parent the hw watchpoints are now disarmed,
    they never trigger again.

(b) The hardware registers in the detached child remain set.  Therefore if the
    child triggers the hw watchpoint formerly set for the parent the child
    SIGTRAPs - as it is no longer under GDB control.


Thanks,
Jan


gdb/
2010-12-06  Jan Kratochvil  <jan.kratochvil@redhat.com>

	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 (i386_inferior_data, struct i386_inferior_data)
	(i386_inferior_data_get): New.
	(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  <jan.kratochvil@redhat.com>

	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,
-		     (void *) control);
+  linux_nat_iterate_watchpoint_lwps (amd64_linux_dr_set_control_callback,
+				     (void *) 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,
 				     (void *) 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,
 				     (void *) 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,
 				     (void *) mask);
 }
 
--- a/gdb/i386-nat.c
+++ b/gdb/i386-nat.c
@@ -220,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 dr_mirror dr_mirror;
+  };
+
+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;
+
+  if (inf->pid != ptid_get_pid (inferior_ptid))
+    {
+      if (detached_inf_pid != ptid_get_pid (inferior_ptid))
+	{
+	  xfree (detached_inf_data);
+	  detached_inf_pid = ptid_get_pid (inferior_ptid);
+	  detached_inf_data = xmalloc (sizeof (*detached_inf_data));
+
+	  /* Forked processes get a copy of the debug registers.  */
+	  memcpy (detached_inf_data, inf_data, sizeof (*detached_inf_data));
+	}
+
+      gdb_assert (detached_inf_data != NULL);
+      inf_data = detached_inf_data;
+    }
+
+  return inf_data;
+}
+
 /* Clear the reference counts and forget everything we knew about the
    debug registers.  */
 
 static struct dr_mirror *
 dr_mirror_get (void)
 {
-  /* Intermediate patch stub.  */
-  static struct 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+
+#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 <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define gettid() syscall (__NR_gettid)
+
+#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 <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#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 <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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 <http://www.gnu.org/licenses/>.
+
+# Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+proc test {type symbol} {
+    global objdir subdir srcdir 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 <pthread.h>
+
+extern volatile int step;
+extern pthread_t thread;
+#endif /* THREAD */
+
+extern volatile int var;
+
+extern void marker (void);
+extern void forkoff (int nr);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]