d5f5a83a9731df75ede71b53246fc6b99d87cdcc
This commit fixes a bug introduced by this commit:
commit d8bbae6ea0
Date: Fri Jan 14 15:40:59 2022 -0500
gdb: fix handling of vfork by multi-threaded program (follow-fork-mode=parent, detach-on-fork=on)
The problem can be seen in this GDB session:
$ gdb -q
(gdb) set non-stop on
(gdb) file ./gdb/testsuite/outputs/gdb.base/foll-vfork/foll-vfork
Reading symbols from ./gdb/testsuite/outputs/gdb.base/foll-vfork/foll-vfork...
(gdb) tcatch vfork
Catchpoint 1 (vfork)
(gdb) run
Starting program: /tmp/gdb/testsuite/outputs/gdb.base/foll-vfork/foll-vfork
Temporary catchpoint 1 (vforked process 1375914), 0x00007ffff7d5043c in vfork () from /lib64/libc.so.6
(gdb) bt
#0 0x00007ffff7d5043c in vfork () from /lib64/libc.so.6
#1 0x00000000004011af in main (argc=1, argv=0x7fffffffad88) at .../gdb/testsuite/gdb.base/foll-vfork.c:32
(gdb) finish
Run till exit from #0 0x00007ffff7d5043c in vfork () from /lib64/libc.so.6
[Detaching after vfork from child process 1375914]
No unwaited-for children left.
(gdb)
Notice the "No unwaited-for children left." error. This is incorrect,
given where we are stopped there's no reason why we shouldn't be able
to use "finish" to return to the main frame.
When the inferior is stopped as a result of the 'tcatch vfork', the
inferior is in the process of performing the vfork, that is, GDB has
seen the VFORKED event, but has not yet attached to the new child
process, nor has the child process been resumed.
However, GDB has seen the VFORKED, and, as we are going to follow the
parent process, the inferior for the vfork parent will have its
thread_waiting_for_vfork_done member variable set, this will point to
the one and only thread that makes up the vfork parent process.
When the "finish" command is used GDB eventually ends up in the
proceed function (in infrun.c), in here we pass through all the
function until we eventually encounter this 'else if' condition:
else if (!cur_thr->resumed ()
&& !thread_is_in_step_over_chain (cur_thr)
/* In non-stop, forbid resuming a thread if some other thread of
that inferior is waiting for a vfork-done event (this means
breakpoints are out for this inferior). */
&& !(non_stop
&& cur_thr->inf->thread_waiting_for_vfork_done != nullptr))
{
The first two of these conditions will both be true, the thread is not
already resumed, and is not in the step-over chain, however, the third
condition, this one:
&& !(non_stop
&& cur_thr->inf->thread_waiting_for_vfork_done != nullptr))
is false, and this prevents the thread we are trying to finish from
being resumed. This condition is false because (a) non_stop is true,
and (b) cur_thr->inf->thread_waiting_for_vfork_done is not
nullptr (see above for why).
Now, if we check the comment embedded within the condition it says:
/* In non-stop, forbid resuming a thread if some other thread of
that inferior is waiting for a vfork-done event (this means
breakpoints are out for this inferior). */
And this makes sense, if we have a vfork parent with two thread, and
one thread has performed a vfork, then we shouldn't try to resume the
second thread.
However, if we are trying to resume the thread that actually performed
a vfork, then this is fine. If we never resume the vfork parent then
we'll never get a VFORK_DONE event, and so the vfork will never
complete.
Thus, the condition should actually be:
&& !(non_stop
&& cur_thr->inf->thread_waiting_for_vfork_done != nullptr
&& cur_thr->inf->thread_waiting_for_vfork_done != cur_thr))
This extra check will allow the vfork parent thread to resume, but
prevent any other thread in the vfork parent process from resuming.
This is the same condition that already exists in the all-stop on a
non-stop-target block earlier in the proceed function.
My actual fix is slightly different to the above, first, I've chosen
to use a nested 'if' check instead of extending the original 'else if'
check, this makes it easier to write a longer comment explaining
what's going on, and second, instead of checking 'non_stop' I've
switched to checking 'target_is_non_stop_p'. In this context this is
effectively the same thing, a previous 'else if' block in proceed
already handles '!non_stop && target_is_non_stop_p ()', so by the time
we get here, if 'target_is_non_stop_p ()' then we must be running in
non_stop mode.
Both of these tweaks will make the next patch easier, which is a
refactor to merge two parts of the proceed function, so this nested
'if' block is not going to exist for long.
For testing, there is no test included with this commit. The test was
exposed when using a modified version of the gdb.base/foll-vfork.exp
test script, however, there are other bugs that are exposed when using
the modified test script. These bugs will be addressed in subsequent
commits, and then I'll add the updated gdb.base/foll-vfork.exp.
If you wish to reproduce this failure then grab the updates to
gdb.base/foll-vfork.exp from the later commit and run this test, the
failure is always reproducible.
…
…
…
…
…
…
…
…
…
…
…
…
…
…
README for GNU development tools This directory contains various GNU compilers, assemblers, linkers, debuggers, etc., plus their support routines, definitions, and documentation. If you are receiving this as part of a GDB release, see the file gdb/README. If with a binutils release, see binutils/README; if with a libg++ release, see libg++/README, etc. That'll give you info about this package -- supported targets, how to use it, how to report bugs, etc. It is now possible to automatically configure and build a variety of tools with one command. To build all of the tools contained herein, run the ``configure'' script here, e.g.: ./configure make To install them (by default in /usr/local/bin, /usr/local/lib, etc), then do: make install (If the configure script can't determine your type of computer, give it the name as an argument, for instance ``./configure sun4''. You can use the script ``config.sub'' to test whether a name is recognized; if it is, config.sub translates it to a triplet specifying CPU, vendor, and OS.) If you have more than one compiler on your system, it is often best to explicitly set CC in the environment before running configure, and to also set CC when running make. For example (assuming sh/bash/ksh): CC=gcc ./configure make A similar example using csh: setenv CC gcc ./configure make Much of the code and documentation enclosed is copyright by the Free Software Foundation, Inc. See the file COPYING or COPYING.LIB in the various directories, for a description of the GNU General Public License terms under which you can copy the files. REPORTING BUGS: Again, see gdb/README, binutils/README, etc., for info on where and how to report problems.
Description