--- /dev/null
+/* 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;
+ }
+}
--- /dev/null
+# 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}
+ }
+ }
+ }
+}