Use fork instead of vfork on Solaris

The gdb.mi/mi-exec-run.exp test never completed/timed out on Solaris for
quite some time:

FAIL: gdb.mi/mi-exec-run.exp: inferior-tty=main: mi=main: force-fail=1: run failure detected (timeout)

This is for gdb trying to exec mi-exec-run.nox, a copy of mi-exec-run
with execute permissions removed.

The process tree at this point looks like this:

          21254 /vol/gcc/bin/expect -- /vol/gcc/share/dejagnu/runtest.exp GDB_PARALLEL=yes --outdir=outputs/gdb.mi/mi-exec-run-vfork gdb.mi/mi-exec-run.exp
            21300 <defunct>
            21281 <defunct>
            21294 $obj/gdb/testsuite/../../gdb/gdb -nw -nx -data-directory $obj/gdb/testsuite/../data-directory -i=mi
              21297 $obj/gdb/testsuite/../../gdb/gdb -nw -nx -data-directory $obj/gdb/testsuite/../data-directory -i=mi

The parent gdb hangs here:

21294:  $obj/gdb/testsuite/../../gdb/gdb -nw
------------  lwp# 1 / thread# 1  ---------------
 0000000000000000 SYS#0    ()
 0000000000daeccd procfs_target::create_inferior(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char**, int) () + 97 (procfs.c:2853)
 0000000000ca63a7 run_command_1(char const*, int, run_how) () + 349 (basic_string.h:187)
 0000000000ca6516 start_command(char const*, int) () + 26 (infcmd.c:584)
 0000000000b3ca8e do_const_cfunc(cmd_list_element*, char const*, int) () + f (cli-decode.c:96)
 0000000000b3ed77 cmd_func(cmd_list_element*, char const*, int) () + 32 (cli-decode.c:2113)
 0000000000f2d219 execute_command(char const*, int) () + 455 (top.c:657)
 0000000000d4ad77 mi_execute_cli_command(char const*, int, char const*) () + 242 (basic_string.h:187)
 0000000000d4ae80 mi_cmd_exec_run(char const*, char**, int) () + ba (mi-main.c:473)

with these process flags

21294:	$obj/gdb/testsuite/../../gdb/gdb -nw
	data model = _LP64  flags = VFORKP|ORPHAN|MSACCT|MSFORK
	sigpend = 0x00004103,0x00000000,0x00000000
 /1:	flags = 0
	sigmask = 0xffbffeff,0xffffffff,0x000000ff
	cursig = SIGKILL
 /2:	flags = DETACH|STOPPED|ASLEEP  lwp_park(0x0,0x0,0x0)
	why = PR_SUSPENDED
	sigmask = 0x000a2002,0x00000000,0x00000000
[...]

while the child sits at

21297:  $obj/gdb/testsuite/../../gdb/gdb -nw
 00007fffbf078a0b execve   (7fffbffff756, 7fffbfffec58, 7fffbfffec90, 0)
 00007fffbef84cf6 execvpex () + f6
 00007fffbef84f45 execvp () + 15
 0000000000d60a44 fork_inferior(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char**, void (*)(), gdb::function_view<void (int)>, void (*)(), char const*, void (*)(char const*, char* const*, char* const*)) () + 47f (fork-inferior.c:423)
 0000000000daeccd procfs_target::create_inferior(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char**, int) () + 97 (procfs.c:2853)
 0000000000ca63a7 run_command_1(char const*, int, run_how) () + 349 (basic_string.h:187)
 0000000000ca6516 start_command(char const*, int) () + 26 (infcmd.c:584)
 0000000000b3ca8e do_const_cfunc(cmd_list_element*, char const*, int) () + f (cli-decode.c:96)
 0000000000b3ed77 cmd_func(cmd_list_element*, char const*, int) () + 32 (cli-decode.c:2113)
 0000000000f2d219 execute_command(char const*, int) () + 455 (top.c:657)
 0000000000d4ad77 mi_execute_cli_command(char const*, int, char const*) () + 242 (basic_string.h:187)
 0000000000d4ae80 mi_cmd_exec_run(char const*, char**, int) () + ba (mi-main.c:473)

with

21297:	$obj/gdb/testsuite/../../gdb/gdb -nw
	data model = _LP64  flags = MSACCT|MSFORK
	exitset  = 0x00000000 0x04000000 0x00000000 0x00000000
	           0x00000000 0x00000000 0x00000000 0x00000000
 /1:	flags = STOPPED|ISTOP  execve(0x7fffbffff756,0x7fffbfffec58,0x7fffbfffec90,0x0)
	why = PR_SYSEXIT  what = execve

We have a deadlock here: the execve in the child cannot return until the
parent has handled the PR_SYSEXIT while the parent cannot run with a
vfork'ed child as documented in proc(4):

       The child of a vfork(2) borrows the  parent's  address  space.  When  a
       vfork(2) is executed by a traced process, all watched areas established
       for the parent are suspended until the child terminates or performs  an
       exec(2).  Any  watched areas established independently in the child are
       cancelled when the parent resumes  after  the  child's  termination  or
       exec(2).  PCWATCH  fails  with  EBUSY  if  applied  to  the parent of a
       vfork(2) before the child has terminated or performed an  exec(2).  The
       PR_VFORKP  flag  is  set  in  the  pstatus  structure for such a parent
       process.

In that situation, the parent cannot be killed even with SIGKILL (as
runtest will attempt once the timeout occurs; the pending signal can be
seen in the pflags output above), so the whole test hangs until one
manually kills the child process.

Fortunately, there's an easy way out: when using fork instead of vfork,
the problem doesn't occur, and this is what the current patch does: it
calls fork_inferior with a dummy pre_trace_fun arg.

Tested on amd64-pc-solaris2.11 and sparcv9-sun-solaris2.11.

	* procfs.c (procfs_pre_trace): New function.
	(procfs_target::create_inferior): Pass it to fork_inferior.
This commit is contained in:
Rainer Orth 2020-06-25 17:48:14 +02:00
parent a7e6196bb8
commit c6d3683661
2 changed files with 13 additions and 1 deletions

View File

@ -1,3 +1,8 @@
2020-06-25 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* procfs.c (procfs_pre_trace): New function.
(procfs_target::create_inferior): Pass it to fork_inferior.
2020-06-25 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* configure.tgt <sparc-*-linux*> (gdb_target_obs): Remove

View File

@ -2759,6 +2759,13 @@ procfs_set_exec_trap (void)
/*destroy_procinfo (pi);*/
}
/* Dummy function to be sure fork_inferior uses fork(2) and not vfork(2).
This avoids a possible deadlock gdb and its vfork'ed child. */
static void
procfs_pre_trace (void)
{
}
/* This function is called BEFORE gdb forks the inferior process. Its
only real responsibility is to set things up for the fork, and tell
GDB which two functions to call after the fork (one for the parent,
@ -2851,7 +2858,7 @@ procfs_target::create_inferior (const char *exec_file,
push_target (this);
pid = fork_inferior (exec_file, allargs, env, procfs_set_exec_trap,
NULL, NULL, shell_file, NULL);
NULL, procfs_pre_trace, shell_file, NULL);
/* We have something that executes now. We'll be running through
the shell at this point (if startup-with-shell is true), but the