1/* This testcase is part of GDB, the GNU debugger.
2
3   Copyright 2015-2023 Free Software Foundation, Inc.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18#include <assert.h>
19#include <pthread.h>
20#include <unistd.h>
21#include <stdio.h>
22#include <sys/types.h>
23#include <sys/wait.h>
24#include <stdlib.h>
25#include <errno.h>
26
27/* Number of threads.  Each thread continuously spawns a fork and wait
28   for it.  If we have another thread continuously start a step over,
29   gdbserver should end up finding new forks while suspending
30   threads.  */
31#define NTHREADS 10
32
33pthread_t threads[NTHREADS];
34
35pthread_barrier_t barrier;
36
37#define NFORKS 10
38
39/* Used to create a conditional breakpoint that always fails.  */
40volatile int zero;
41
42static void *
43thread_forks (void *arg)
44{
45  int i;
46
47  pthread_barrier_wait (&barrier);
48
49  for (i = 0; i < NFORKS; i++)
50    {
51      pid_t pid;
52
53      do
54	{
55	  pid = fork ();
56	}
57      while (pid == -1 && errno == EINTR);
58
59      if (pid > 0)
60	{
61	  int status;
62
63	  /* Parent.  */
64	  do
65	    {
66	      pid = waitpid (pid, &status, 0);
67	    }
68	  while (pid == -1 && errno == EINTR);
69
70	  if (pid == -1)
71	    {
72	      perror ("wait");
73	      exit (1);
74	    }
75
76	  if (!WIFEXITED (status))
77	    {
78	      printf ("Unexpected wait status 0x%x from child %d\n",
79		      status, pid);
80	    }
81	}
82      else if (pid == 0)
83	{
84	  /* Child.  */
85	  exit (0);
86	}
87      else
88	{
89	  perror ("fork");
90	  exit (1);
91	}
92    }
93
94  return NULL;
95}
96
97/* Set this to tell the thread_breakpoint thread to exit.  */
98volatile int break_out;
99
100static void *
101thread_breakpoint (void *arg)
102{
103  pthread_barrier_wait (&barrier);
104
105  while (!break_out)
106    {
107      usleep (1); /* set break here */
108    }
109
110  return NULL;
111}
112
113pthread_barrier_t barrier;
114
115int
116main (void)
117{
118  int i;
119  int ret;
120  pthread_t bp_thread;
121
122  /* Don't run forever.  */
123  alarm (180);
124
125  pthread_barrier_init (&barrier, NULL, NTHREADS + 1);
126
127  /* Start the threads that constantly fork.  */
128  for (i = 0; i < NTHREADS; i++)
129    {
130      ret = pthread_create (&threads[i], NULL, thread_forks, NULL);
131      assert (ret == 0);
132    }
133
134  /* Start the thread that constantly hit a conditional breakpoint
135     that needs to be stepped over.  */
136  ret = pthread_create (&bp_thread, NULL, thread_breakpoint, NULL);
137  assert (ret == 0);
138
139  /* Wait for forking to stop.  */
140  for (i = 0; i < NTHREADS; i++)
141    {
142      ret = pthread_join (threads[i], NULL);
143      assert (ret == 0);
144    }
145
146  break_out = 1;
147  pthread_join (bp_thread, NULL);
148  assert (ret == 0);
149
150  return 0;
151}
152