7868401b7b
This patch addresses "fork:Interrupted system call" (or wait:) failures in gdb.threads/forking-threads-plus-breakpoint.exp. The test program spawns ten threads, each of which do ten fork/waitpid sequences. The cause of the problem was that when one of the fork children exited before the corresponding fork parent could initiate its waitpid for that child, a SIGCHLD and/or SIGSTOP was delivered and interrupted a fork or waitpid in another thread. The fix was to wrap the system calls in a loop to retry the call if it was interrupted, like: do { pid = fork (); } while (pid == -1 && errno == EINTR); Since this is a Linux-only test I figure it is OK to use errno and EINTR. I tried a number of alternative fixes using SIG_IGN, SA_RESTART, pthread_sigblock, and bsd_signal, but none of these worked as well. Tested on Nios II Linux target with x86 Linux host. gdb/testsuite/ChangeLog: 2016-03-16 Don Breazeal <donb@codesourcery.com> * gdb.threads/forking-threads-plus-breakpoint.c (thread_forks): Retry fork and waitpid on interrupted system call errors. * gdb.threads/forking-threads-plus-breakpoint.exp: (do_test): Use with_timeout_factor to increase timeout to 90.
149 lines
3 KiB
C
149 lines
3 KiB
C
/* This testcase is part of GDB, the GNU debugger.
|
|
|
|
Copyright 2015-2016 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 <assert.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
/* Number of threads. Each thread continuously spawns a fork and wait
|
|
for it. If we have another thread continuously start a step over,
|
|
gdbserver should end up finding new forks while suspending
|
|
threads. */
|
|
#define NTHREADS 10
|
|
|
|
pthread_t threads[NTHREADS];
|
|
|
|
pthread_barrier_t barrier;
|
|
|
|
#define NFORKS 10
|
|
|
|
/* Used to create a conditional breakpoint that always fails. */
|
|
volatile int zero;
|
|
|
|
static void *
|
|
thread_forks (void *arg)
|
|
{
|
|
int i;
|
|
|
|
pthread_barrier_wait (&barrier);
|
|
|
|
for (i = 0; i < NFORKS; i++)
|
|
{
|
|
pid_t pid;
|
|
|
|
do
|
|
{
|
|
pid = fork ();
|
|
}
|
|
while (pid == -1 && errno == EINTR);
|
|
|
|
if (pid > 0)
|
|
{
|
|
int status;
|
|
|
|
/* Parent. */
|
|
do
|
|
{
|
|
pid = waitpid (pid, &status, 0);
|
|
}
|
|
while (pid == -1 && errno == EINTR);
|
|
|
|
if (pid == -1)
|
|
{
|
|
perror ("wait");
|
|
exit (1);
|
|
}
|
|
|
|
if (!WIFEXITED (status))
|
|
{
|
|
printf ("Unexpected wait status 0x%x from child %d\n",
|
|
status, pid);
|
|
}
|
|
}
|
|
else if (pid == 0)
|
|
{
|
|
/* Child. */
|
|
exit (0);
|
|
}
|
|
else
|
|
{
|
|
perror ("fork");
|
|
exit (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set this to tell the thread_breakpoint thread to exit. */
|
|
volatile int break_out;
|
|
|
|
static void *
|
|
thread_breakpoint (void *arg)
|
|
{
|
|
pthread_barrier_wait (&barrier);
|
|
|
|
while (!break_out)
|
|
{
|
|
usleep (1); /* set break here */
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
pthread_barrier_t barrier;
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
int i;
|
|
int ret;
|
|
pthread_t bp_thread;
|
|
|
|
/* Don't run forever. */
|
|
alarm (180);
|
|
|
|
pthread_barrier_init (&barrier, NULL, NTHREADS + 1);
|
|
|
|
/* Start the threads that constantly fork. */
|
|
for (i = 0; i < NTHREADS; i++)
|
|
{
|
|
ret = pthread_create (&threads[i], NULL, thread_forks, NULL);
|
|
assert (ret == 0);
|
|
}
|
|
|
|
/* Start the thread that constantly hit a conditional breakpoint
|
|
that needs to be stepped over. */
|
|
ret = pthread_create (&bp_thread, NULL, thread_breakpoint, NULL);
|
|
assert (ret == 0);
|
|
|
|
/* Wait for forking to stop. */
|
|
for (i = 0; i < NTHREADS; i++)
|
|
{
|
|
ret = pthread_join (threads[i], NULL);
|
|
assert (ret == 0);
|
|
}
|
|
|
|
break_out = 1;
|
|
pthread_join (bp_thread, NULL);
|
|
assert (ret == 0);
|
|
|
|
return 0;
|
|
}
|