diff --git a/gdb/infrun.c b/gdb/infrun.c index 899b2ae3cd0..0b88eb8cfdf 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -5734,7 +5734,18 @@ handle_inferior_event (struct execution_control_state *ecs) ecs->ptid = inferior_ptid; if (should_resume) - keep_going (ecs); + { + /* Never call switch_back_to_stepped_thread if we are waiting for + vfork-done (waiting for an external vfork child to exec or + exit). We will resume only the vforking thread for the purpose + of collecting the vfork-done event, and we will restart any + step once the critical shared address space window is done. */ + if ((!follow_child + && detach_fork + && parent->inf->thread_waiting_for_vfork_done != nullptr) + || !switch_back_to_stepped_thread (ecs)) + keep_going (ecs); + } else stop_waiting (ecs); return; @@ -5754,9 +5765,13 @@ handle_inferior_event (struct execution_control_state *ecs) if (handle_stop_requested (ecs)) return; - /* This also takes care of reinserting breakpoints in the - previously locked inferior. */ - keep_going (ecs); + if (!switch_back_to_stepped_thread (ecs)) + { + gdb_assert (inferior_thread () == ecs->event_thread); + /* This also takes care of reinserting breakpoints in the + previously locked inferior. */ + keep_going (ecs); + } return; case TARGET_WAITKIND_EXECD: diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.c b/gdb/testsuite/gdb.threads/next-fork-other-thread.c new file mode 100644 index 00000000000..377c1bc1a31 --- /dev/null +++ b/gdb/testsuite/gdb.threads/next-fork-other-thread.c @@ -0,0 +1,92 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 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 . */ + +#include +#include +#include +#include +#include +#include +#include + +/* Number of threads doing forks. */ +#define N_FORKERS 4 + +static void * +forker (void *arg) +{ + for (;;) + { + pid_t pid = FORK_FUNC (); + + if (pid == 0) + _exit (11); + + assert (pid > 0); + + /* Wait for children to exit. */ + int ret; + int stat; + do + { + ret = waitpid (pid, &stat, 0); + } while (ret == EINTR); + + assert (ret == pid); + assert (WIFEXITED (stat)); + assert (WEXITSTATUS (stat) == 11); + + /* We need a sleep, otherwise the forking threads spam events and the + stepping thread doesn't make progress. Sleep for a bit less than + `sleep_a_bit` does, so that forks are likely to interrupt a "next". */ + usleep (40 * 1000); + } + + return NULL; +} + +static void +sleep_a_bit (void) +{ + usleep (1000 * 50); +} + +int +main (void) +{ + int i; + + alarm (60); + + pthread_t thread[N_FORKERS]; + for (i = 0; i < N_FORKERS; ++i) + { + int ret = pthread_create (&thread[i], NULL, forker, NULL); + assert (ret == 0); + } + + for (i = 0; i < INT_MAX; ++i) /* for loop */ + { + sleep_a_bit (); /* break here */ + sleep_a_bit (); /* other line */ + } + + for (i = 0; i < N_FORKERS; ++i) + pthread_join (thread[i], NULL); + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp new file mode 100644 index 00000000000..5960bb9ba10 --- /dev/null +++ b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp @@ -0,0 +1,116 @@ +# Copyright 2022 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 . + +# Test doing a "next" on a thread during which forks or vforks happen in other +# threads. + +standard_testfile + +# Line where to stop the main thread. +set break_here_line [gdb_get_line_number "break here"] + +# Build executables, one for each fork flavor. +foreach_with_prefix fork_func {fork vfork} { + set opts [list debug pthreads additional_flags=-DFORK_FUNC=${fork_func}] + if { [build_executable "failed to prepare" \ + ${testfile}-${fork_func} ${srcfile} $opts] } { + return + } +} + +# If testing against GDBserver, consume all it its output. + +proc drain_gdbserver_output { } { + if { [info exists ::server_spawn_id] } { + gdb_test_multiple "" "" { + -i "$::server_spawn_id" + -timeout 0 + -re ".+" { + exp_continue + } + } + } +} + +# Run the test with the given parameters: +# +# - FORK_FUNC: fork flavor, "fork" or "vfork". +# - TARGET-NON-STOP: "maintenance set target-non-stop" value, "auto", "on" or +# "off". +# - NON-STOP: "set non-stop" value, "on" or "off". +# - DISPLACED-STEPPING: "set displaced-stepping" value, "auto", "on" or "off". + +proc do_test { fork_func target-non-stop non-stop displaced-stepping } { + save_vars { ::GDBFLAGS } { + append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" + append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" + clean_restart ${::binfile}-${fork_func} + } + + gdb_test_no_output "set displaced-stepping ${displaced-stepping}" + + if { ![runto_main] } { + return + } + + # The "Detached after (v)fork" messages get in the way in non-stop, disable + # them. + gdb_test_no_output "set print inferior-events off" + + # Advance the next-ing thread to the point where we'll execute the nexts. + # Leave the breakpoint in: it will force GDB to step over it while next-ing, + # which exercises some additional code paths. + gdb_test "break $::break_here_line" "Breakpoint $::decimal at $::hex.*" + gdb_test "continue" "hit Breakpoint $::decimal, main.*" + + # Next an arbitrary number of times over the lines of the loop. + # + # It is useful to bump this number to a larger value (e.g. 200) to stress + # test more, but it makes the test case run for considerably longer. If + # you increase the number of loops, you might want to adjust the alarm + # time in the .c file accordingly. + for { set i 0 } { $i < 20 } { incr i } { + # If testing against GDBserver, the forking threads cause a lot of + # "Detaching from process XYZ" messages to appear. If we don't consume + # that output, GDBserver eventually blocks on a full stderr. Drain it + # once every loop. It may not be needed for 20 iterations, but it's + # needed if you increase to 200 iterations. + drain_gdbserver_output + + with_test_prefix "i=$i" { + if { [gdb_test "next" "other line.*" "next to other line"] != 0 } { + return + } + + if { [gdb_test "next" "for loop.*" "next to for loop"] != 0 } { + return + } + + if { [gdb_test "next" "break here.*" "next to break here"] != 0} { + return + } + } + } +} + +foreach_with_prefix fork_func {fork vfork} { + foreach_with_prefix target-non-stop {auto on off} { + foreach_with_prefix non-stop {off on} { + foreach_with_prefix displaced-stepping {auto on off} { + do_test ${fork_func} ${target-non-stop} ${non-stop} ${displaced-stepping} + } + } + } +}