/* This testcase is part of GDB, the GNU debugger. Copyright 2015-2019 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 . */ #include #include #include #include #include #include #include #include /* 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; }