gdb/
authorPedro Alves <palves@redhat.com>
Sun, 17 May 2009 19:20:33 +0000 (19:20 +0000)
committerPedro Alves <palves@redhat.com>
Sun, 17 May 2009 19:20:33 +0000 (19:20 +0000)
2009-05-17  Pedro Alves  <pedro@codesourcery.com>

* infrun.c (handle_inferior_event): When handling a
TARGET_WAITKIND_FORKED, detach breakpoints from the fork child
immediatelly.
* linux-nat.c (linux_child_follow_fork): Only detach breakpoint
from the child if vforking.
* inf-ptrace.c (inf_ptrace_follow_fork): No need to detach
breakpoints from the child here.

gdb/testsuite/
2009-05-17  Pedro Alves  <pedro@codesourcery.com>

* gdb.base/foll-fork.c: Include stdlib.h.  Add markers for
`gdb_get_line_number'.  Call `callee' in both parent and child.
* gdb.base/foll-fork.exp (catch_fork_child_follow): Use
`gdb_get_line_number' instead of hardcoding line numbers.
(catch_fork_unpatch_child): New procedure to test detaching
breakpoints from child fork.
(tcatch_fork_parent_follow): Use `gdb_get_line_number' instead of
hardcoding line numbers.
(do_fork_tests): Run `catch_fork_unpatch_child'.

gdb/ChangeLog
gdb/inf-ptrace.c
gdb/infrun.c
gdb/linux-nat.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/foll-fork.c
gdb/testsuite/gdb.base/foll-fork.exp

index f35064c8a120956b5fe6b17563b2eb3abf5dd2dd..f8a7287218e7d42741feaf1001d772a627b41ebe 100644 (file)
@@ -1,3 +1,13 @@
+2009-05-17  Pedro Alves  <pedro@codesourcery.com>
+
+       * infrun.c (handle_inferior_event): When handling a
+       TARGET_WAITKIND_FORKED, detach breakpoints from the fork child
+       immediatelly.
+       * linux-nat.c (linux_child_follow_fork): Only detach breakpoint
+       from the child if vforking.
+       * inf-ptrace.c (inf_ptrace_follow_fork): No need to detach
+       breakpoints from the child here.
+
 2009-05-17  Pedro Alves  <pedro@codesourcery.com>
 
        * infrun.c (pending_follow): Remove execd_pathname member.
index f088ffdc6e74c42bc3a2dffb91b3667fb373adb5..7849f2436f165e9a973dd49fa668a889d2568274 100644 (file)
@@ -111,7 +111,9 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
   else
     {
       inferior_ptid = pid_to_ptid (pid);
-      detach_breakpoints (fpid);
+
+      /* Breakpoints have already been detached from the child by
+        infrun.c.  */
 
       if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
        perror_with_name (("ptrace"));
index dbaf02bbd611d8dfd94628e66d8a8af922c13bc5..99873b180aa09c558076426d6780b8b46333e163 100644 (file)
@@ -2418,6 +2418,27 @@ handle_inferior_event (struct execution_control_state *ecs)
          reinit_frame_cache ();
        }
 
+      /* Immediately detach breakpoints from the child before there's
+        any chance of letting the user delete breakpoints from the
+        breakpoint lists.  If we don't do this early, it's easy to
+        leave left over traps in the child, vis: "break foo; catch
+        fork; c; <fork>; del; c; <child calls foo>".  We only follow
+        the fork on the last `continue', and by that time the
+        breakpoint at "foo" is long gone from the breakpoint table.
+        If we vforked, then we don't need to unpatch here, since both
+        parent and child are sharing the same memory pages; we'll
+        need to unpatch at follow/detach time instead to be certain
+        that new breakpoints added between catchpoint hit time and
+        vfork follow are detached.  */
+      if (ecs->ws.kind != TARGET_WAITKIND_VFORKED)
+       {
+         int child_pid = ptid_get_pid (ecs->ws.value.related_pid);
+
+         /* This won't actually modify the breakpoint list, but will
+            physically remove the breakpoints from the child.  */
+         detach_breakpoints (child_pid);
+       }
+
       stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
 
       ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
index d67fcc3b2dbab2017d4813c7292e37891845b6cb..beff012fab63b67d604b1038d7fdedca2f8855bb 100644 (file)
@@ -593,11 +593,15 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
       /* We're already attached to the parent, by default. */
 
       /* Before detaching from the child, remove all breakpoints from
-         it.  (This won't actually modify the breakpoint list, but will
-         physically remove the breakpoints from the child.) */
-      /* If we vforked this will remove the breakpoints from the parent
-        also, but they'll be reinserted below.  */
-      detach_breakpoints (child_pid);
+         it.  If we forked, then this has already been taken care of
+         by infrun.c.  If we vforked however, any breakpoint inserted
+         in the parent is visible in the child, even those added while
+         stopped in a vfork catchpoint.  This won't actually modify
+         the breakpoint list, but will physically remove the
+         breakpoints from the child.  This will remove the breakpoints
+         from the parent also, but they'll be reinserted below.  */
+      if (has_vforked)
+       detach_breakpoints (child_pid);
 
       /* Detach new forked process?  */
       if (detach_fork)
@@ -701,10 +705,6 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
         breakpoint.  */
       last_tp->step_resume_breakpoint = NULL;
 
-      /* Needed to keep the breakpoint lists in sync.  */
-      if (! has_vforked)
-       detach_breakpoints (child_pid);
-
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
 
index 54d1de64ea977fdfb22322dfc37bc3503e6ed386..1fb17ff9819caf92ca4e446902e62580495bf4df 100644 (file)
@@ -1,3 +1,15 @@
+2009-05-17  Pedro Alves  <pedro@codesourcery.com>
+
+       * gdb.base/foll-fork.c: Include stdlib.h.  Add markers for
+       `gdb_get_line_number'.  Call `callee' in both parent and child.
+       * gdb.base/foll-fork.exp (catch_fork_child_follow): Use
+       `gdb_get_line_number' instead of hardcoding line numbers.
+       (catch_fork_unpatch_child): New procedure to test detaching
+       breakpoints from child fork.
+       (tcatch_fork_parent_follow): Use `gdb_get_line_number' instead of
+       hardcoding line numbers.
+       (do_fork_tests): Run `catch_fork_unpatch_child'.
+
 2009-05-17  Vladimir Prus  <vladimir@codesourcery.com>
 
        * gdb.mi/mi-cmd-var.exp: Check that when varobj
index 841258f147ca5c4665b5877cdb6030d9540b5e0e..b7e69abfb1254c28cc037cc17b6b2a76fcbee884 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <unistd.h>
+#include <stdlib.h>
 
 #ifdef PROTOTYPES
 void callee (int i)
@@ -21,14 +22,18 @@ main ()
   int  v = 5;
 
   pid = fork ();
-  if (pid == 0)
+  if (pid == 0) /* set breakpoint here */
     {
       v++;
       /* printf ("I'm the child!\n"); */
+      callee (getpid ());
     }
   else
     {
       v--;
       /* printf ("I'm the proud parent of child #%d!\n", pid); */
+      callee (getpid ());
     }
+
+  exit (0); /* at exit */
 }
index 76475ad7a7fd10953884cce8bb5908e5d271706d..08a0f49e93fa94dc1d0b1e05e2d6763081e1b8dd 100644 (file)
@@ -147,6 +147,8 @@ proc catch_fork_child_follow {} {
    global gdb_prompt
    global srcfile
 
+   set bp_after_fork [gdb_get_line_number "set breakpoint here"]
+
    send_gdb "catch fork\n"
    gdb_expect {
       -re "Catchpoint .*(fork).*$gdb_prompt $"\
@@ -188,21 +190,21 @@ proc catch_fork_child_follow {} {
       -re "$gdb_prompt $" {pass "set follow child"}
       timeout         {fail "(timeout) set follow child"}
    }
-   send_gdb "tbreak ${srcfile}:24\n"
+   send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
    gdb_expect {
-      -re "Temporary breakpoint.*, line 24.*$gdb_prompt $"\
+      -re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
                       {pass "set follow child, tbreak"}
       -re "$gdb_prompt $" {fail "set follow child, tbreak"}
       timeout         {fail "(timeout) set follow child, tbreak"}
    }
    send_gdb "continue\n"
    gdb_expect {
-      -re "Attaching after fork to.* at .*24.*$gdb_prompt $"\
+      -re "Attaching after fork to.* at .*$bp_after_fork.*$gdb_prompt $"\
                       {pass "set follow child, hit tbreak"}
       -re "$gdb_prompt $" {fail "set follow child, hit tbreak"}
       timeout         {fail "(timeout) set follow child, hit tbreak"}
    }
-   # The child has been detached; allow time for any output it might
+   # The parent has been detached; allow time for any output it might
    # generate to arrive, so that output doesn't get confused with
    # any expected debugger output from a subsequent testpoint.
    #
@@ -222,10 +224,61 @@ proc catch_fork_child_follow {} {
    }
 }
 
+proc catch_fork_unpatch_child {} {
+   global gdb_prompt
+   global srcfile
+
+   set bp_exit [gdb_get_line_number "at exit"]
+
+   gdb_test "break callee" "file .*$srcfile, line .*" "unpatch child, break at callee"
+   gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "unpatch child, set catch fork"
+
+   gdb_test "continue" \
+       "Catchpoint.*\\(forked process.*\\).*,.*in .*(fork|__kernel_v?syscall).*" \
+       "unpatch child, catch fork"
+
+   # Delete all breakpoints and catchpoints.
+   delete_breakpoints
+
+   gdb_test "break $bp_exit" \
+       "Breakpoint .*file .*$srcfile, line .*" \
+       "unpatch child, breakpoint at exit call"
+
+   gdb_test "set follow child" "" "unpatch child, set follow child"
+
+   set test "unpatch child, unpatched parent breakpoints from child"
+   gdb_test_multiple "continue" $test {
+      -re "at exit.*$gdb_prompt $" {
+         pass "$test"
+      }
+      -re "SIGTRAP.*$gdb_prompt $" {
+         fail "$test"
+
+         # Explicitly kill this child, so we can continue gracefully
+         # with further testing...
+         send_gdb "kill\n"
+         gdb_expect {
+             -re ".*Kill the program being debugged.*y or n. $" {
+                 send_gdb "y\n"
+                 gdb_expect -re "$gdb_prompt $" {}
+             }
+         }
+      }
+      -re ".*$gdb_prompt $" {
+         fail "$test (unknown output)"
+      }
+      timeout {
+         fail "$test (timeout)"
+      }
+   }
+}
+
 proc tcatch_fork_parent_follow {} {
    global gdb_prompt
    global srcfile
 
+   set bp_after_fork [gdb_get_line_number "set breakpoint here"]
+
    send_gdb "catch fork\n"
    gdb_expect {
       -re "Catchpoint .*(fork).*$gdb_prompt $"\
@@ -249,16 +302,16 @@ proc tcatch_fork_parent_follow {} {
       -re "$gdb_prompt $" {pass "set follow parent"}
       timeout         {fail "(timeout) set follow parent"}
    }
-   send_gdb "tbreak ${srcfile}:24\n"
+   send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
    gdb_expect {
-      -re "Temporary breakpoint.*, line 24.*$gdb_prompt $"\
+      -re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
                       {pass "set follow parent, tbreak"}
       -re "$gdb_prompt $" {fail "set follow parent, tbreak"}
       timeout         {fail "(timeout) set follow child, tbreak"}
    }
    send_gdb "continue\n"
    gdb_expect {
-      -re ".*Detaching after fork from.* at .*24.*$gdb_prompt $"\
+      -re ".*Detaching after fork from.* at .*$bp_after_fork.*$gdb_prompt $"\
                       {pass "set follow parent, hit tbreak"}
       -re "$gdb_prompt $" {fail "set follow parent, hit tbreak"}
       timeout         {fail "(timeout) set follow parent, hit tbreak"}
@@ -362,6 +415,11 @@ By default, the debugger will follow the parent process..*$gdb_prompt $"\
    #
    if [runto_main] then { catch_fork_child_follow }
 
+   # Test that parent breakpoints are successfully detached from the
+   # child at fork time, even if the user removes them from the
+   # breakpoints list after stopping at a fork catchpoint.
+   if [runto_main] then { catch_fork_unpatch_child }
+
    # Test the ability to catch a fork, specify via a -do clause that
    # the parent be followed, and continue.  Make the catchpoint temporary.
    #