if (target_is_non_stop_p () && !tp->executing ())
{
if (tp->has_pending_waitstatus ())
- signo = tp->pending_waitstatus ().sig ();
+ {
+ /* If the thread has a pending event, and it was stopped with a
+ signal, use that signal to resume it. If it has a pending
+ event of another kind, it was not stopped with a signal, so
+ resume it without a signal. */
+ if (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED)
+ signo = tp->pending_waitstatus ().sig ();
+ else
+ signo = GDB_SIGNAL_0;
+ }
else
signo = tp->stop_signal ();
}
gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
+ /* If the lwp/thread we are about to detach has a pending fork event,
+ there is a process GDB is attached to that the core of GDB doesn't know
+ about. Detach from it. */
+
+ /* Check in lwp_info::status. */
+ if (WIFSTOPPED (lp->status) && linux_is_extended_waitstatus (lp->status))
+ {
+ int event = linux_ptrace_get_extended_event (lp->status);
+
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
+ {
+ unsigned long child_pid;
+ int ret = ptrace (PTRACE_GETEVENTMSG, lp->ptid.lwp (), 0, &child_pid);
+ if (ret == 0)
+ detach_one_pid (child_pid, 0);
+ else
+ perror_warning_with_name (_("Failed to detach fork child"));
+ }
+ }
+
+ /* Check in lwp_info::waitstatus. */
+ if (lp->waitstatus.kind () == TARGET_WAITKIND_VFORKED
+ || lp->waitstatus.kind () == TARGET_WAITKIND_FORKED)
+ detach_one_pid (lp->waitstatus.child_ptid ().pid (), 0);
+
+
+ /* Check in thread_info::pending_waitstatus. */
+ thread_info *tp = find_thread_ptid (linux_target, lp->ptid);
+ if (tp->has_pending_waitstatus ())
+ {
+ const target_waitstatus &ws = tp->pending_waitstatus ();
+
+ if (ws.kind () == TARGET_WAITKIND_VFORKED
+ || ws.kind () == TARGET_WAITKIND_FORKED)
+ detach_one_pid (ws.child_ptid ().pid (), 0);
+ }
+
+ /* Check in thread_info::pending_follow. */
+ if (tp->pending_follow.kind () == TARGET_WAITKIND_VFORKED
+ || tp->pending_follow.kind () == TARGET_WAITKIND_FORKED)
+ detach_one_pid (tp->pending_follow.child_ptid ().pid (), 0);
+
if (lp->status != 0)
linux_nat_debug_printf ("Pending %s for %s on detach.",
strsignal (WSTOPSIG (lp->status)),
if (from_tty && !rs->extended && number_of_live_inferiors (this) == 1)
puts_filtered (_("Ending remote debugging.\n"));
+ /* See if any thread of the inferior we are detaching has a pending fork
+ status. In that case, we must detach from the child resulting from
+ that fork. */
+ for (thread_info *thread : inf->non_exited_threads ())
+ {
+ const target_waitstatus *ws = thread_pending_fork_status (thread);
+
+ if (ws == nullptr)
+ continue;
+
+ remote_detach_pid (ws->child_ptid ().pid ());
+ }
+
+ /* Check also for any pending fork events in the stop reply queue. */
+ remote_notif_get_pending_events (¬if_client_stop);
+ for (stop_reply_up &reply : rs->stop_reply_queue)
+ {
+ if (reply->ptid.pid () != pid)
+ continue;
+
+ if (!is_fork_status (reply->ws.kind ()))
+ continue;
+
+ remote_detach_pid (reply->ws.child_ptid ().pid ());
+ }
+
thread_info *tp = find_thread_ptid (this, inferior_ptid);
/* Check to see if we are detaching a fork parent. Note that if we
/* Leave the notification pending, since the server expects that
we acknowledge it with vStopped. But clear its contents, so
that later on when we acknowledge it, we also discard it. */
+ remote_debug_printf
+ ("discarding in-flight notification: ptid: %s, ws: %s\n",
+ reply->ptid.to_string().c_str(),
+ reply->ws.to_string ().c_str ());
reply->ws.set_ignore ();
-
- if (remote_debug)
- fprintf_unfiltered (gdb_stdlog,
- "discarded in-flight notification\n");
}
/* Discard the stop replies we have already pulled with
{
return event->ptid.pid () == inf->pid;
});
+ for (auto it = iter; it != rs->stop_reply_queue.end (); ++it)
+ remote_debug_printf
+ ("discarding queued stop reply: ptid: %s, ws: %s\n",
+ reply->ptid.to_string().c_str(),
+ reply->ws.to_string ().c_str ());
rs->stop_reply_queue.erase (iter, rs->stop_reply_queue.end ());
}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define NUM_FORKING_THREADS 12
+
+static pthread_barrier_t barrier;
+static volatile int should_exit = 0;
+
+static void
+sigusr1_handler (int sig, siginfo_t *siginfo, void *context)
+{
+ should_exit = 1;
+}
+
+static void *
+forking_thread (void *arg)
+{
+ /* Wait for all forking threads to have spawned before fork-spamming. */
+ pthread_barrier_wait (&barrier);
+
+ while (!should_exit)
+ {
+ pid_t pid = fork ();
+ if (pid == 0)
+ {
+ /* Child */
+ exit (8);
+ }
+ else
+ {
+ /* Parent */
+ int status;
+ int ret = waitpid (pid, &status, 0);
+ assert (ret == pid);
+ assert (WIFEXITED (status));
+ assert (WEXITSTATUS (status) == 8);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+break_here_first (void)
+{
+}
+
+static pid_t my_pid;
+
+int
+main (void)
+{
+ pthread_t forking_threads[NUM_FORKING_THREADS];
+ int ret;
+ struct sigaction sa;
+ int i;
+
+ /* Just to make sure we don't run for ever. */
+ alarm (30);
+
+ my_pid = getpid ();
+
+ break_here_first ();
+
+ pthread_barrier_init (&barrier, NULL, NUM_FORKING_THREADS);
+
+ memset (&sa, 0, sizeof (sa));
+ sa.sa_sigaction = sigusr1_handler;
+ ret = sigaction (SIGUSR1, &sa, NULL);
+ assert (ret == 0);
+
+ for (i = 0; i < NUM_FORKING_THREADS; ++i)
+ {
+ ret = pthread_create (&forking_threads[i], NULL, forking_thread, NULL);
+ assert (ret == 0);
+ }
+
+ for (i = 0; i < NUM_FORKING_THREADS; ++i)
+ {
+ ret = pthread_join (forking_threads[i], NULL);
+ assert (ret == 0);
+ }
+
+ FILE *f = fopen (TOUCH_FILE_PATH, "w");
+ assert (f != NULL);
+ ret = fclose (f);
+ assert (ret == 0);
+
+ return 0;
+}
--- /dev/null
+# Copyright 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/>.
+
+# Detach a running program that constantly forks, verify that we correctly
+# detach all fork children, for which events are pending.
+#
+# The strategy is:
+#
+# - Resume a program in background (continue &) with many threads that
+# constantly fork and wait for their fork children to exit.
+# - Detach the program. If testing against GDBserver, hope that the detach
+# CLI command is processed while there is a stop reply pending in the
+# remote target.
+# - Signal the parent program to exit, by sending it a SIGUSR1 signal.
+# - Have the parent write a flag file to the filesystem just before exiting.
+# - If a pending fork child is mistakenly still attached, it will prevent its
+# parent thread from waitpid'ing it, preventing the main thread from joining
+# it, prevent it from writing the flag file, failing the test.
+
+standard_testfile
+
+if { [is_remote target] } {
+ # If the target is remote, write the file in whatever the current working
+ # directory is, with a somewhat unique name.
+ set touch_file_path ${testfile}-flag
+} else {
+ set touch_file_path [standard_output_file flag]
+}
+
+if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+ executable [list debug "additional_flags=-DTOUCH_FILE_PATH=\"$touch_file_path\""]] != "" } {
+ return
+}
+
+proc do_test { } {
+ remote_file target delete $::touch_file_path
+ gdb_assert { ![remote_file target exists $::touch_file_path] } "file does not exist before test"
+
+ save_vars { ::GDBFLAGS } {
+ append ::GDBFLAGS " -ex \"set non-stop on\""
+ clean_restart $::binfile
+ }
+
+ if { ![runto break_here_first] } {
+ return
+ }
+
+ set pid [get_integer_valueof "my_pid" -1]
+ if { $pid == -1 } {
+ error "could not get inferior pid"
+ }
+
+ gdb_test_no_output "set print inferior-events off"
+
+ gdb_test_multiple "continue &" "" {
+ -re "Continuing.\r\n$::gdb_prompt " {
+ pass $gdb_test_name
+ }
+ }
+
+ set wait_time_s 2
+
+ if { [info exists ::server_spawn_id] } {
+ # Let the program run for 2 seconds, during which it will fork many times.
+ # When running against GDBserver, this makes server print a ton of
+ # "Detaching from process X" message, to the point where its output buffer
+ # gets full and it hangs in a write to stdout. During these 2 seconds,
+ # drain the messages from GDBserver to keep that from happening.
+ gdb_test_multiple "" "flush server output" {
+ -timeout $wait_time_s
+ -i $::server_spawn_id
+ -re ".+" {
+ exp_continue -continue_timer
+ }
+
+ timeout {
+ pass $gdb_test_name
+ }
+ }
+ } else {
+ # Not using GDBserver, just sleep 2 seconds.
+ sleep $wait_time_s
+ }
+
+ gdb_test "detach" "Detaching from program: .*"
+
+ if { [info exists ::server_spawn_id] } {
+ # Drain GDBserver's output buffer, in the (unlikely) event that enough
+ # messages were output to fill the buffer between the moment we stopped
+ # consuming it and the moment GDBserver detached the process.
+ gdb_test_multiple "" "" {
+ -i $::server_spawn_id
+ -re ".+" {
+ exp_continue
+ }
+ -re "^$" {}
+ }
+ }
+
+ remote_exec target "kill -USR1 ${pid}"
+ gdb_assert { [target_file_exists_with_timeout $::touch_file_path] } "file exists after detach"
+
+ # Don't leave random files on the target system.
+ if { [is_remote target] } {
+ remote_file target delete $::touch_file_path
+ }
+}
+
+do_test
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <stdio.h>
+
+int
+main (void)
+{
+ FILE *f = fopen (TOUCH_FILE_PATH, "w");
+ fclose (f);
+ return 0;
+}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <assert.h>
+
+static volatile int release_forking_thread = 0;
+static int x;
+static pthread_barrier_t barrier;
+
+static void
+break_here (void)
+{
+ x++;
+}
+
+static void
+do_fork (void)
+{
+ while (!release_forking_thread);
+
+ if (FORK_FUNCTION () == 0)
+ {
+ /* We create the file in a separate program that we exec: if FORK_FUNCTION
+ is vfork, we shouldn't do anything more than an exec. */
+ execl (TOUCH_FILE_BIN, TOUCH_FILE_BIN, NULL);
+ }
+
+}
+
+static void *
+thread_func (void *p)
+{
+ pthread_barrier_wait (&barrier);
+
+#if defined(MAIN_THREAD_FORKS)
+ break_here ();
+#elif defined(OTHER_THREAD_FORKS)
+ do_fork ();
+#else
+# error "MAIN_THREAD_FORKS or OTHER_THREAD_FORKS must be defined"
+#endif
+}
+
+
+int
+main (void)
+{
+ pthread_t thread;
+ int ret;
+
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ alarm (30);
+ ret = pthread_create (&thread, NULL, thread_func, NULL);
+ assert (ret == 0);
+
+ pthread_barrier_wait (&barrier);
+
+#if defined(MAIN_THREAD_FORKS)
+ do_fork ();
+#elif defined(OTHER_THREAD_FORKS)
+ break_here ();
+#else
+# error "MAIN_THREAD_FORKS or OTHER_THREAD_FORKS must be defined"
+#endif
+
+ pthread_join (thread, NULL);
+
+ return 0;
+}
--- /dev/null
+# 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/>.
+
+# Then, test that if we detach an inferior with a pending fork child, that
+# child is correctly detached and resumes execution normally. There are two
+# kinds of "pending fork child" we test:
+#
+# - resulting of a fork catchpoint: we stop at a fork catchpoint and detach.
+# - resulting of an all-stop stop on top of a non-stop target, where a fork
+# event is saved as a pending wait status. To test this, we stepi a thread
+# while another one forks. The stepi generally completes at least as fast
+# as the fork, so we have a chance that the stop due to the stepi being
+# complete is shown to the user while the fork event is saved for later.
+#
+# To verify that the child process is detached and resumes execution, we have
+# it write a file on the filesystem. If we don't see the file after a certain
+# delay, it means the child was likely not detached, and the test fails.
+#
+# At the same time, this tests that having this pending fork event does not
+# cause other problems in general. For example, 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 .c -touch-file.c
+
+set touch_file_bin $binfile-touch-file
+
+if { [is_remote target] } {
+ # If the target is remote, write the file in whatever the current working
+ # directory is, with a somewhat unique name.
+ set touch_file_path ${testfile}-flag
+} else {
+ set touch_file_path [standard_output_file flag]
+}
+
+set opts [list debug "additional_flags=-DTOUCH_FILE_PATH=\"$touch_file_path\""]
+if { [gdb_compile "$srcdir/$subdir/$srcfile2" $touch_file_bin executable $opts] != "" } {
+ return
+}
+
+proc do_test { target-non-stop who_forks fork_function stop_mode } {
+ set opts [list \
+ debug \
+ "additional_flags=-DFORK_FUNCTION=$fork_function" \
+ "additional_flags=-DTOUCH_FILE_BIN=\"$::touch_file_bin\""]
+
+ # 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
+ }
+
+ remote_file target delete $::touch_file_path
+ gdb_assert { ![remote_file target exists $::touch_file_path] } "file does not exist before test"
+
+ 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"
+
+ # There are two "pending fork child" modes we can test here:
+ #
+ # - catch: set up a "catch fork" / "catch vfork" and run to it.
+ # - stepi: stepi the non-forking thread while the forking thread,
+ # well, forks.
+ if { $stop_mode == "catch" } {
+ gdb_test "catch fork"
+ gdb_test "catch vfork"
+ gdb_test "continue" "hit Catchpoint $::decimal.*fork.*"
+ } elseif { $stop_mode == "stepi" } {
+ # stepi the non-forking thread.
+ gdb_test "stepi"
+ } else {
+ error "invalid stop_mode value: $stop_mode"
+ }
+
+ # Make sure there's still a single inferior.
+ gdb_test "info inferior" {\* 1 [^\r\n]+}
+
+ gdb_test "detach"
+
+ # After being detached, the fork child creates file ::TOUCH_FILE_PATH.
+ # Seeing this file tells us the fork child was detached and executed
+ # successfully.
+ gdb_assert { [target_file_exists_with_timeout $::touch_file_path] } "file exists after detach"
+
+ # Don't leave random files on the target system.
+ if { [is_remote target] } {
+ remote_file target delete $::touch_file_path
+ }
+}
+
+foreach_with_prefix target-non-stop { auto on off } {
+ foreach_with_prefix who_forks { main other } {
+ foreach_with_prefix fork_function { fork vfork } {
+ foreach_with_prefix stop_mode { stepi catch } {
+ do_test ${target-non-stop} $who_forks $fork_function $stop_mode
+ }
+ }
+ }
+}
+++ /dev/null
-/* This testcase is part of GDB, the GNU debugger.
-
- Copyright 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/>. */
-
-#include <pthread.h>
-#include <unistd.h>
-#include <assert.h>
-
-static volatile int release_forking_thread = 0;
-static int x;
-static pthread_barrier_t barrier;
-
-static void
-break_here (void)
-{
- x++;
-}
-
-static void
-do_fork (void)
-{
- pthread_barrier_wait (&barrier);
-
- while (!release_forking_thread);
-
- if (FORK_FUNCTION () == 0)
- _exit (0);
-
-}
-
-static void *
-thread_func (void *p)
-{
-#if defined(MAIN_THREAD_FORKS)
- break_here ();
-#elif defined(OTHER_THREAD_FORKS)
- do_fork ();
-#else
-# error "MAIN_THREAD_FORKS or OTHER_THREAD_FORKS must be defined"
-#endif
-}
-
-
-int
-main (void)
-{
- pthread_t thread;
- int ret;
-
- pthread_barrier_init (&barrier, NULL, 2);
-
- alarm (30);
- ret = pthread_create (&thread, NULL, thread_func, NULL);
- assert (ret == 0);
-
- pthread_barrier_wait (&barrier);
-
-#if defined(MAIN_THREAD_FORKS)
- do_fork ();
-#elif defined(OTHER_THREAD_FORKS)
- break_here ();
-#else
-# error "MAIN_THREAD_FORKS or OTHER_THREAD_FORKS must be defined"
-#endif
-
- pthread_join (thread, NULL);
-
- return 0;
-}
+++ /dev/null
-# 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
- }
- }
-}
return -code return 0
}
+# Wait up to ::TIMEOUT seconds for file PATH to exist on the target system.
+# Return 1 if it does exist, 0 otherwise.
+
+proc target_file_exists_with_timeout { path } {
+ for {set i 0} {$i < $::timeout} {incr i} {
+ if { [remote_file target exists $path] } {
+ return 1
+ }
+
+ sleep 1
+ }
+
+ return 0
+}
+
# Always load compatibility stuff.
load_lib future.exp
return get_lwp_thread (parent);
}
+thread_info *
+linux_process_target::thread_pending_child (thread_info *thread)
+{
+ lwp_info *child = get_thread_lwp (thread)->pending_child ();
+
+ if (child == nullptr)
+ return nullptr;
+
+ return get_lwp_thread (child);
+}
+
/* Default implementation of linux_target_ops method "set_pc" for
32-bit pc register which is literally named "pc". */
#endif
thread_info *thread_pending_parent (thread_info *thread) override;
+ thread_info *thread_pending_child (thread_info *thread) override;
bool supports_catch_syscall () override;
return this->fork_relative;
}
+ /* If this LWP is the parent of a fork child we haven't reported to GDB yet,
+ return that child, else nullptr. */
+ lwp_info *pending_child () const
+ {
+ if (this->fork_relative == nullptr)
+ return nullptr;
+
+ gdb_assert (this->fork_relative->fork_relative == this);
+
+ /* In a fork parent/child relationship, the parent has a status pending and
+ the child does not, and a thread can only be in one such relationship
+ at most. So we can recognize who is the parent based on which one has
+ a pending status. */
+ gdb_assert (!!this->status_pending_p
+ != !!this->fork_relative->status_pending_p);
+
+ if (!this->status_pending_p)
+ return nullptr;
+
+ const target_waitstatus &ws = this->waitstatus;
+ gdb_assert (ws.kind () == TARGET_WAITKIND_FORKED
+ || ws.kind () == TARGET_WAITKIND_VFORKED);
+
+ return this->fork_relative;
+ }
+
/* Backlink to the parent object. */
struct thread_info *thread = nullptr;
/* We'll need this after PROCESS has been destroyed. */
int pid = process->pid;
+ /* If this process has an unreported fork child, that child is not known to
+ GDB, so GDB won't take care of detaching it. We must do it here.
+
+ Here, we specifically don't want to use "safe iteration", as detaching
+ another process might delete the next thread in the iteration, which is
+ the one saved by the safe iterator. We will never delete the currently
+ iterated on thread, so standard iteration should be safe. */
+ for (thread_info *thread : all_threads)
+ {
+ /* Only threads that are of the process we are detaching. */
+ if (thread->id.pid () != pid)
+ continue;
+
+ /* Only threads that have a pending fork event. */
+ thread_info *child = target_thread_pending_child (thread);
+ if (child == nullptr)
+ continue;
+
+ process_info *fork_child_process = get_thread_process (child);
+ gdb_assert (fork_child_process != nullptr);
+
+ int fork_child_pid = fork_child_process->pid;
+
+ if (detach_inferior (fork_child_process) != 0)
+ warning (_("Failed to detach fork child %s, child of %s"),
+ target_pid_to_str (ptid_t (fork_child_pid)).c_str (),
+ target_pid_to_str (thread->id).c_str ());
+ }
+
if (detach_inferior (process) != 0)
write_enn (own_buf);
else
return nullptr;
}
+thread_info *
+process_stratum_target::thread_pending_child (thread_info *thread)
+{
+ return nullptr;
+}
+
bool
process_stratum_target::supports_software_single_step ()
{
else nullptr. */
virtual thread_info *thread_pending_parent (thread_info *thread);
+ /* If THREAD is the parent of a fork child that was not reported to GDB,
+ return this child, else nullptr. */
+ virtual thread_info *thread_pending_child (thread_info *thread);
+
/* Returns true if the target can software single step. */
virtual bool supports_software_single_step ();
return the_target->thread_pending_parent (thread);
}
+static inline thread_info *
+target_thread_pending_child (thread_info *thread)
+{
+ return the_target->thread_pending_child (thread);
+}
+
int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len);
int set_desired_thread ();