diff options
-rw-r--r-- | gdb/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/infrun.c | 11 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/fork-running-state.c | 83 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/fork-running-state.exp | 163 |
5 files changed, 269 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4cfabfb..476bb5e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,11 @@ 2016-01-25 Pedro Alves <palves@redhat.com> + PR threads/19461 + * infrun.c (handle_inferior_event_1) <fork/vfork>: Update + parent/child running states. + +2016-01-25 Pedro Alves <palves@redhat.com> + PR gdb/19494 * linux-nat.c (kill_one_lwp): New, factored out from ... (kill_callback): ... this. diff --git a/gdb/infrun.c b/gdb/infrun.c index 33981d2..15210c9 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -5198,6 +5198,17 @@ Cannot fill $_exitsignal with the correct signal number.\n")); parent = ecs->ptid; child = ecs->ws.value.related_pid; + /* At this point, the parent is marked running, and the + child is marked stopped. */ + + /* If not resuming the parent, mark it stopped. */ + if (follow_child && !detach_fork && !non_stop && !sched_multi) + set_running (parent, 0); + + /* If resuming the child, mark it running. */ + if (follow_child || (!detach_fork && (non_stop || sched_multi))) + set_running (child, 1); + /* In non-stop mode, also resume the other branch. */ if (!detach_fork && (non_stop || (sched_multi && target_is_non_stop_p ()))) diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index a6daf80..ef4b046 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,5 +1,11 @@ 2016-01-25 Pedro Alves <palves@redhat.com> + PR threads/19461 + * gdb.base/fork-running-state.c: New file. + * gdb.base/fork-running-state.exp: New file. + +2016-01-25 Pedro Alves <palves@redhat.com> + PR gdb/19494 * gdb.base/catch-fork-kill.c: New file. * gdb.base/catch-fork-kill.exp: New file. diff --git a/gdb/testsuite/gdb.base/fork-running-state.c b/gdb/testsuite/gdb.base/fork-running-state.c new file mode 100644 index 0000000..c7103fd --- /dev/null +++ b/gdb/testsuite/gdb.base/fork-running-state.c @@ -0,0 +1,83 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2015-2016 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/>. */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/wait.h> + +int save_parent; + +/* The fork child. Just runs forever. */ + +static int +fork_child (void) +{ + while (1) + { + sleep (1); + + /* Exit if GDB kills the parent. */ + if (getppid () != save_parent) + break; + if (kill (getppid (), 0) != 0) + break; + } + + return 0; +} + +/* The fork parent. Just runs forever waiting for the child to + exit. */ + +static int +fork_parent (void) +{ + if (wait (NULL) == -1) + { + perror ("wait"); + return 1; + } + + return 0; +} + +int +main (void) +{ + pid_t pid; + + save_parent = getpid (); + + /* Don't run forever. */ + alarm (180); + + /* The parent and child should basically run forever without + tripping on any debug event. We want to check that GDB updates + the parent and child running states correctly right after the + fork. */ + pid = fork (); + if (pid > 0) + return fork_parent (); + else if (pid == 0) + return fork_child (); + else + { + perror ("fork"); + return 1; + } +} diff --git a/gdb/testsuite/gdb.base/fork-running-state.exp b/gdb/testsuite/gdb.base/fork-running-state.exp new file mode 100644 index 0000000..be81be6 --- /dev/null +++ b/gdb/testsuite/gdb.base/fork-running-state.exp @@ -0,0 +1,163 @@ +# Copyright (C) 2016 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/>. + +# Regression test for PR threads/19461 (strange "info thread" behavior +# in non-stop). GDB used to miss updating the parent/child running +# states after a fork. + +standard_testfile + +# The test proper. + +proc do_test { detach_on_fork follow_fork non_stop schedule_multiple } { + global GDBFLAGS + global srcfile testfile + global gdb_prompt + + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set non-stop $non_stop\"" + + if {[prepare_for_testing "failed to prepare" \ + $testfile $srcfile {debug}] == -1} { + return -1 + } + } + + if ![runto_main] then { + fail "Can't run to main" + return 0 + } + + # If debugging with target remote, check whether the all-stop + # variant of the RSP is being used. If so, we can't run the + # all-stop tests. + if { [target_info exists gdb_protocol] + && ([target_info gdb_protocol] == "remote" + || [target_info gdb_protocol] == "extended-remote")} { + + set test "maint show target-non-stop" + gdb_test_multiple "maint show target-non-stop" $test { + -re "(is|currently) on.*$gdb_prompt $" { + } + -re "(is|currently) off.*$gdb_prompt $" { + unsupported "can't issue info threads while target is running" + return 0 + } + } + } + + # We want to catch "[New inferior ...]" below, to avoid sleeping. + if {$detach_on_fork == "off" || $follow_fork == "child"} { + gdb_test_no_output "set print inferior-events on" + } + + gdb_test_no_output "set detach-on-fork $detach_on_fork" + + gdb_test_no_output "set follow-fork $follow_fork" + if {$non_stop == "off"} { + gdb_test_no_output "set schedule-multiple $schedule_multiple" + } + + set test "continue &" + gdb_test_multiple $test $test { + -re "$gdb_prompt " { + pass $test + } + } + + if {$detach_on_fork == "off" || $follow_fork == "child"} { + set test "fork child appears" + gdb_test_multiple "" $test { + -re "\\\[New inferior " { + pass $test + } + } + } else { + # All we can do is wait a little bit for the parent to fork. + sleep 1 + } + + set not_nl "\[^\r\n\]*" + + if {$detach_on_fork == "on" && $non_stop == "on" && $follow_fork == "child"} { + gdb_test "info threads" \ + " 2.1 ${not_nl}\\\(running\\\).*No selected thread.*" + } elseif {$detach_on_fork == "on" && $follow_fork == "child"} { + gdb_test "info threads" \ + "\\\* 2.1 ${not_nl}\\\(running\\\)" + } elseif {$detach_on_fork == "on"} { + gdb_test "info threads" \ + "\\\* 1 ${not_nl}\\\(running\\\)" + } elseif {$non_stop == "on" + || ($schedule_multiple == "on" && $follow_fork == "parent")} { + # Both parent and child should be marked running, and the + # parent should be selected. + gdb_test "info threads" \ + [multi_line \ + "\\\* 1.1 ${not_nl} \\\(running\\\)${not_nl}" \ + " 2.1 ${not_nl} \\\(running\\\)"] + } elseif {$schedule_multiple == "on" && $follow_fork == "child"} { + # Both parent and child should be marked running, and the + # child should be selected. + gdb_test "info threads" \ + [multi_line \ + " 1.1 ${not_nl} \\\(running\\\)${not_nl}" \ + "\\\* 2.1 ${not_nl} \\\(running\\\)"] + } else { + set test "only $follow_fork marked running" + gdb_test_multiple "info threads" $test { + -re "\\\(running\\\)${not_nl}\\\(running\\\)\r\n$gdb_prompt $" { + fail $test + } + -re "\\\* 1.1 ${not_nl}\\\(running\\\)\r\n 2.1 ${not_nl}\r\n$gdb_prompt $" { + gdb_assert [string eq $follow_fork "parent"] $test + } + -re "1.1 ${not_nl}\r\n\\\* 2.1 ${not_nl}\\\(running\\\)\r\n$gdb_prompt $" { + gdb_assert [string eq $follow_fork "child"] $test + } + } + } + + # We don't want to see "Inferior exited" in reaction to the kills. + gdb_test_no_output "set print inferior-events off" + + # Kill both parent and child. + if {$detach_on_fork == "off" || $follow_fork == "parent"} { + gdb_test_no_output "kill inferior 1" "kill parent" + } + if {$detach_on_fork == "off" || $follow_fork == "child"} { + gdb_test_no_output "kill inferior 2" "kill child" + } +} + +# Exercise all permutations of: +# +# set detach-on-fork off|on +# set follow-fork parent|child +# set non-stop on|off +# set schedule-multiple on|off + +foreach_with_prefix detach-on-fork {"off" "on"} { + foreach_with_prefix follow-fork {"parent" "child"} { + with_test_prefix "non-stop" { + do_test ${detach-on-fork} ${follow-fork} "on" "-" + } + with_test_prefix "all-stop" { + foreach_with_prefix schedule-multiple {"on" "off"} { + do_test ${detach-on-fork} ${follow-fork} "off" ${schedule-multiple} + } + } + } +} |