diff options
Diffstat (limited to 'gdbserver')
-rw-r--r-- | gdbserver/linux-low.cc | 11 | ||||
-rw-r--r-- | gdbserver/linux-low.h | 27 | ||||
-rw-r--r-- | gdbserver/server.cc | 29 | ||||
-rw-r--r-- | gdbserver/target.cc | 6 | ||||
-rw-r--r-- | gdbserver/target.h | 10 |
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 (); |