Files
binutils-gdb/gdb/testsuite/gdb.threads/process-dies-while-detaching.c
Pedro Alves f0fb2488c9 Fix gdb.threads/process-dies-while-detaching.exp
I noticed [1] a test bug in gdb.threads/process-dies-while-detaching.exp.
Simplified, the test code in question looks somewhat like this:

~~~
  # Detach from a process, and ensure that it exits after detaching.
  # This relies on inferior I/O.

  proc detach_and_expect_exit {test} {

      gdb_test_multiple "detach" $test ....

      set saw_prompt 0
      set saw_inf_exit 0
      while { !$saw_prompt && !$saw_inf_exit } {
          gdb_test_multiple "" $test {
              -re "exited, status=0" {
                  set saw_inf_exit 1
              }
              -re "$gdb_prompt " {
                  set saw_prompt 1
              }
          }
      }

      pass $test
  }
~~~

The bug is in the while loop's condition.  We want to make sure we see
both the inferior output and the prompt, so the loop's test should be:

   -    while { !$saw_prompt && !$saw_inf_exit } {
   +    while { !$saw_prompt || !$saw_inf_exit } {

If we just fix that, the test starts failing though, because it
exposes a couple latent problems:

- When called from test_detach_killed_outside, the parent doesn't
  print "exited, status=0", because in that case the child dies with a
  signal, and so detach_and_expect_exit times out.

  Fix it by making the parent print "signaled, sig=9" in that case,
  and have the .exp expect it.

- When testing against --target_board=native-gdbserver, sometimes we'd
  get this:

    ERROR: Process no longer exists
    ERROR: : spawn id exp9 not open
	while executing
    "expect {
    -i exp8 -timeout 220
	    -i $server_spawn_id
	    eof {
		pass $test
		wait -i $server_spawn_id
		unset server_spawn_id
	    }
	    timeout {
	       ..."
	("uplevel" body line 1)
	invoked from within
    "uplevel $body" NONE : spawn id exp9 not open

  The problem is that:

   - inferior_spawn_id and server_spawn_id are the same when testing
     with gdbserver.
   - gdbserver exits after "detach", so we get an eof for
     $inferior_spawn_id in the loop in detach_and_expect_exit.
     That's the first "ERROR: Process no longer exists".
   - and then when we reach test_server_exit, server_spawn_id
     is already closed (because server_spawn_id==inferior_spawn_id).

  To handle this, make the loop in detach_and_expect_exit use an
  indirect spawn id list and remove $inferior_spawn_id from the list
  as soon as we got the inferior output we're expecting, so that the
  "eof" is left unprocessed until we reach test_server_exit.

[1] I changed GDB in a way that should have made the test fail, but it
    didn't.

gdb/testsuite/ChangeLog:
2017-12-03  Pedro Alves  <palves@redhat.com>

	* gdb.threads/process-dies-while-detaching.c: Include <errno.h>
	and <string.h>.
	(parent_function): Print distinct messages when waitpid fails, or
	the child exits with a signal, or the child exits for an unhandled
	reason.
	* gdb.threads/process-dies-while-detaching.exp
	(detach_and_expect_exit): New 'inf_output_re' parameter and use
	it.  Wait for both inferior output and GDB's prompt.  Use an
	indirect spawn id list.
	(do_detach): New parameter 'child_exit'.  Use it to compute
	expected inferior output.
	(test_detach, test_detach_watch, test_detach_killed_outside):
	Adjust to pass down the expected child exit kind.
2017-12-03 15:32:08 +00:00

131 lines
2.8 KiB
C

/* This testcase is part of GDB, the GNU debugger.
Copyright 2016-2017 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
/* This barrier ensures we only reach the initial breakpoint after all
threads have started. */
pthread_barrier_t start_threads_barrier;
/* Many threads in order to be fairly sure the process exits while GDB
is detaching from each thread in the process, on targets that need
to detach from each thread individually. */
#define NTHREADS 256
/* GDB sets a watchpoint here. */
int globalvar = 1;
/* GDB reads this. */
int mypid;
/* Threads' entry point. */
void *
thread_function (void *arg)
{
pthread_barrier_wait (&start_threads_barrier);
_exit (0);
}
/* The fork child's entry point. */
void
child_function (void)
{
pthread_t threads[NTHREADS];
int i;
pthread_barrier_init (&start_threads_barrier, NULL, NTHREADS + 1);
for (i = 0; i < NTHREADS; i++)
pthread_create (&threads[i], NULL, thread_function, NULL);
pthread_barrier_wait (&start_threads_barrier);
exit (0);
}
/* This is defined by the .exp file if testing the multi-process
variant. */
#ifdef MULTIPROCESS
/* The fork parent's entry point. */
void
parent_function (pid_t child)
{
int status, ret;
alarm (300);
ret = waitpid (child, &status, 0);
if (ret == -1)
{
printf ("waitpid, errno=%d (%s)\n", errno, strerror (errno));
exit (1);
}
else if (WIFEXITED (status))
{
printf ("exited, status=%d\n", WEXITSTATUS (status));
exit (0);
}
else if (WIFSIGNALED (status))
{
printf ("signaled, sig=%d\n", WTERMSIG (status));
exit (2);
}
else
{
printf ("unexpected, status=%x\n", status);
exit (3);
}
}
#endif
int
main (void)
{
#ifdef MULTIPROCESS
pid_t child;
child = fork ();
if (child == -1)
return 1;
#endif
mypid = getpid ();
#ifdef MULTIPROCESS
if (child != 0)
parent_function (child);
else
#endif
child_function ();
/* Not reached. */
abort ();
}