Use fork instead of vfork on Solaris
authorRainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
Thu, 25 Jun 2020 15:48:14 +0000 (17:48 +0200)
committerRainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
Thu, 25 Jun 2020 15:48:14 +0000 (17:48 +0200)
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   (7fffbffff7567fffbfffec587fffbfffec90, 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.

gdb/ChangeLog
gdb/procfs.c

index 7298617b80edc8abca2c07f2f88ba6adc7ae1743..982f23a315770cd48702cbdd826c36bb9bc54746 100644 (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
index 65243b1e7c584a3f2a70ad5289faadd3a51265c9..d3085a20fce23e9538a0328fd0b12958e2ae370b 100644 (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
This page took 0.030274 seconds and 4 git commands to generate.