gdbserver: hide fork child threads from GDB
[deliverable/binutils-gdb.git] / gdb / testsuite / gdb.threads / pending-fork-event.exp
diff --git a/gdb/testsuite/gdb.threads/pending-fork-event.exp b/gdb/testsuite/gdb.threads/pending-fork-event.exp
new file mode 100644 (file)
index 0000000..51af07f
--- /dev/null
@@ -0,0 +1,79 @@
+# Copyright (C) 2021 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 that we handle well, in all-stop, a fork happening in a thread B while
+# doing a step-like command in a thread A.
+#
+# A buggy GDB / GDBserver combo would notice the thread of the child process
+# of the (still unprocessed) fork event, and erroneously create a new inferior
+# for it.  Once fixed, the child process' thread is hidden by whoever holds the
+# pending fork event.
+
+standard_testfile
+
+proc do_test { target-non-stop who_forks fork_function } {
+
+    set opts [list debug "additional_flags=-DFORK_FUNCTION=$fork_function"]
+
+    # WHO_FORKS says which of the main or other thread calls (v)fork.  The
+    # thread that does not call (v)fork is the one who tries to step.
+    if { $who_forks == "main" } {
+       lappend opts "additional_flags=-DMAIN_THREAD_FORKS"
+       set this_binfile ${::binfile}-main-${fork_function}
+    } elseif { $who_forks == "other" } {
+       lappend opts "additional_flags=-DOTHER_THREAD_FORKS"
+       set this_binfile ${::binfile}-other-${fork_function}
+    } else {
+       error "invalid who_forks value: $who_forks"
+    }
+
+    if { [gdb_compile_pthreads "$::srcdir/$::subdir/$::srcfile" $this_binfile executable $opts] != "" } {
+       return
+    }
+
+    save_vars { ::GDBFLAGS } {
+       append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\""
+       clean_restart $this_binfile
+    }
+
+    if {![runto_main]} {
+       fail "could not run to main"
+       return
+    }
+
+    # Run until breakpoint in the second thread.
+    gdb_test "break break_here" "Breakpoint $::decimal.*"
+    gdb_continue_to_breakpoint "thread started"
+
+    # Delete the breakpoint so the thread doesn't do a step-over.
+    delete_breakpoints
+
+    # Let the forking thread make progress during the step.
+    gdb_test "p release_forking_thread = 1" " = 1"
+
+    # stepi the non-forking thread.
+    gdb_test "stepi"
+
+    # Make sure there's still a single inferior.
+    gdb_test "info inferior" {\* 1 [^\r\n]+}
+}
+
+foreach_with_prefix target-non-stop { auto on off } {
+    foreach_with_prefix who_forks { main other } {
+       foreach_with_prefix fork_function { fork vfork } {
+           do_test ${target-non-stop} $who_forks $fork_function
+       }
+    }
+}
This page took 0.030337 seconds and 4 git commands to generate.