[PATCH 3/3] gdb, gdbserver: detach fork child when detaching from fork parent
Simon Marchi
simon.marchi@polymtl.ca
Mon Nov 29 18:27:57 GMT 2021
> Yes, this can certainly happen. I don't see a way to reliably stop the
> program with an event held in the stop reply queue. GDB constantly drains the
> events asynchronously. So for a testcase, I think you'd need to take
> "constantly hammer" approach. Something like:
>
> - enable non-stop / target non-stop
> - make the test program have a few threads constantly fork and exit the child
> I say multiple threads so that the stop reply queue has a good chance of
> holding an event unprocessed
> - make the test program call alarm() to set a timeout. See why below.
> - make the testcase constantly attach / continue -a & / detach
>
> Determining whether the fork child was detached correctly would be done by:
>
> - the parent/child sharing a pipe. You'd have an array of pipes, one
> entry per thread.
> - the child writing to the pipe before exiting
> - the parent reading from the pipe after forking
>
> If GDB fails to detach the child, then the parent thread hangs in
> the pipe read, and eventually, the parent gets a SIGALRM.
>
> To avoid the case of the test failing, then the parent dying with SIGALRM, and
> GDB attaching to the wrong process (because the kernel reused the PID for
> something else), you'd make the SIGALRM signal handler set a "timed_out"
> global flag, and sleep for a while before exiting the process. Enough time
> so that GDB can always reattach before the process disappears due to SIGALRM.
>
> When GDB reattaches, it first always consults that "timed_out" global, to
> know whether the detach-from-children succeeded. If it is set, the test
> FAILs.
Ok, that sounds good (although not trivial!).
>> }
>> diff --git a/gdbserver/server.cc b/gdbserver/server.cc
>> index 7963f2faf267..2f3d5341758f 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 detach it as well.
>> +
>> + 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. */
>> + if (thread->fork_child == nullptr)
>> + continue;
>> +
>> + process_info *fork_child_process
>> + = find_process_pid (thread->fork_child->id.pid ());
>
> This can be written as:
>
> process_info *fork_child_process
> = get_thread_process (thread->fork_child);
Done.
>
>> + gdb_assert (fork_child_process != nullptr);
>
>
> Hmm, in my clone events work, I'm making clone events also set
> the fork_parent/fork_child link, since clone events and fork/vfork
> events are so similar. In this spot however, we'll need to skip
> detaching the child if the child is a clone child, not a fork child.
> I wonder how best to check that. See comments in the review of patch #1.
Right. In my new versions, the loop has:
/* Only threads that have a pending fork event. */
thread_info *child = target_thread_pending_child (thread);
if (child == nullptr)
continue;
In your series, you'll make target_thread_pending_child return the kind,
and you can add a check for the kind.
Simon
More information about the Gdb-patches
mailing list