aboutsummaryrefslogtreecommitdiff
path: root/gdbserver
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@efficios.com>2021-12-01 09:40:03 -0500
committerSimon Marchi <simon.marchi@efficios.com>2021-12-08 21:00:39 -0500
commitdf5ad102009c41ab4dfadbb8cfb8c8b2a02a4f78 (patch)
treed3e4ea03469af069618f83928fdc275f6d5bfa83 /gdbserver
parent577d2167bbed078e99fe8b704f936be8ac7cf83d (diff)
downloadgdb-df5ad102009c41ab4dfadbb8cfb8c8b2a02a4f78.zip
gdb-df5ad102009c41ab4dfadbb8cfb8c8b2a02a4f78.tar.gz
gdb-df5ad102009c41ab4dfadbb8cfb8c8b2a02a4f78.tar.bz2
gdb, gdbserver: detach fork child when detaching from fork parent
While working with pending fork events, I wondered what would happen if the user detached an inferior while a thread of that inferior had a pending fork event. What happens with the fork child, which is ptrace-attached by the GDB process (or by GDBserver), but not known to the core? Sure enough, neither the core of GDB or the target detach the child process, so GDB (or GDBserver) just stays ptrace-attached to the process. The result is that the fork child process is stuck, while you would expect it to be detached and run. Make GDBserver detach of fork children it knows about. That is done in the generic handle_detach function. Since a process_info already exists for the child, we can simply call detach_inferior on it. GDB-side, make the linux-nat and remote targets detach of fork children known because of pending fork events. These pending fork events can be stored in: - thread_info::pending_waitstatus, if the core has consumed the event but then saved it for later (for example, because it got the event while stopping all threads, to present an all-stop stop on top of a non-stop target) - thread_info::pending_follow: if we ran to a "catch fork" and we detach at that moment Additionally, pending fork events can be in target-specific fields: - For linux-nat, they can be in lwp_info::status and lwp_info::waitstatus. - For the remote target, they could be stored as pending stop replies, saved in `remote_state::notif_state::pending_event`, if not acknowledged yet, or in `remote_state::stop_reply_queue`, if acknowledged. I followed the model of remove_new_fork_children for this: call remote_notif_get_pending_events to process / acknowledge any unacknowledged notification, then look through stop_reply_queue. Update the gdb.threads/pending-fork-event.exp test (and rename it to gdb.threads/pending-fork-event-detach.exp) to try to detach the process while it is stopped with a pending fork event. In order to verify that the fork child process is correctly detached and resumes execution outside of GDB's control, make that process create a file in the test output directory, and make the test wait $timeout seconds for that file to appear (it happens instantly if everything goes well). This test catches a bug in linux-nat.c, also reported as PR 28512 ("waitstatus.h:300: internal-error: gdb_signal target_waitstatus::sig() const: Assertion `m_kind == TARGET_WAITKIND_STOPPED || m_kind == TARGET_WAITKIND_SIGNALLED' failed.). When detaching a thread with a pending event, get_detach_signal unconditionally fetches the signal stored in the waitstatus (`tp->pending_waitstatus ().sig ()`). However, that is only valid if the pending event is of type TARGET_WAITKIND_STOPPED, and this is now enforced using assertions (iit would also be valid for TARGET_WAITKIND_SIGNALLED, but that would mean the thread does not exist anymore, so we wouldn't be detaching it). Add a condition in get_detach_signal to access the signal number only if the wait status is of kind TARGET_WAITKIND_STOPPED, and use GDB_SIGNAL_0 instead (since the thread was not stopped with a signal to begin with). Add another test, gdb.threads/pending-fork-event-ns.exp, specifically to verify that we consider events in pending stop replies in the remote target. This test has many threads constantly forking, and we detach from the program while the program is executing. That gives us some chance that we detach while a fork stop reply is stored in the remote target. To verify that we correctly detach all fork children, we ask the parent to exit by sending it a SIGUSR1 signal and have it write a file to the filesystem before exiting. Because the parent's main thread joins the forking threads, and the forking threads wait for their fork children to exit, if some fork child is not detach by GDB, the parent will not write the file, and the test will time out. If I remove the new remote_detach_pid calls in remote.c, the test fails eventually if I run it in a loop. There is a known limitation: we don't remove breakpoints from the children before detaching it. So the children, could hit a trap instruction after being detached and crash. I know this is wrong, and it should be fixed, but I would like to handle that later. The current patch doesn't fix everything, but it's a step in the right direction. Change-Id: I6d811a56f520e3cb92d5ea563ad38976f92e93dd Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28512
Diffstat (limited to 'gdbserver')
-rw-r--r--gdbserver/linux-low.cc11
-rw-r--r--gdbserver/linux-low.h27
-rw-r--r--gdbserver/server.cc29
-rw-r--r--gdbserver/target.cc6
-rw-r--r--gdbserver/target.h10
5 files changed, 83 insertions, 0 deletions
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 34991df..8788804 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -7130,6 +7130,17 @@ linux_process_target::thread_pending_parent (thread_info *thread)
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". */
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 819f915..b563537 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -312,6 +312,7 @@ public:
#endif
thread_info *thread_pending_parent (thread_info *thread) override;
+ thread_info *thread_pending_child (thread_info *thread) override;
bool supports_catch_syscall () override;
@@ -750,6 +751,32 @@ struct lwp_info
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;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 8dde6fb..27e2aba 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1250,6 +1250,35 @@ handle_detach (char *own_buf)
/* 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
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 136b510..aa3d424 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -841,6 +841,12 @@ process_stratum_target::thread_pending_parent (thread_info *thread)
return nullptr;
}
+thread_info *
+process_stratum_target::thread_pending_child (thread_info *thread)
+{
+ return nullptr;
+}
+
bool
process_stratum_target::supports_software_single_step ()
{
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 1b0a120..331a21a 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -492,6 +492,10 @@ public:
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 ();
@@ -708,6 +712,12 @@ target_thread_pending_parent (thread_info *thread)
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 ();