1/* This testcase is part of GDB, the GNU debugger.
2
3   Copyright 2009-2024 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#define _GNU_SOURCE
19#include <pthread.h>
20#include <stdio.h>
21#include <limits.h>
22#include <errno.h>
23#include <stdlib.h>
24#include <string.h>
25#include <assert.h>
26#include <sys/types.h>
27#include <signal.h>
28#include <unistd.h>
29#include <asm/unistd.h>
30
31#define gettid() syscall (__NR_gettid)
32
33/* Terminate always in the main task, it can lock up with SIGSTOPped GDB
34   otherwise.  */
35#define TIMEOUT (gettid () == getpid() ? 10 : 15)
36
37static pid_t thread1_tid;
38static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER;
39static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
40
41static pid_t thread2_tid;
42static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER;
43static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
44
45static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
46
47static pthread_barrier_t threads_started_barrier;
48
49/* These variables must have lower in-memory addresses than thread1_rwatch and
50   thread2_rwatch so that they take their watchpoint slots.  */
51
52static int unused1_rwatch;
53static int unused2_rwatch;
54
55static volatile int thread1_rwatch;
56static volatile int thread2_rwatch;
57
58/* Do not use alarm as it would create a ptrace event which would hang up us if
59   we are being traced by GDB which we stopped ourselves.  */
60
61static void timed_mutex_lock (pthread_mutex_t *mutex)
62{
63  int i;
64  struct timespec start, now;
65
66  i = clock_gettime (CLOCK_MONOTONIC, &start);
67  assert (i == 0);
68
69  do
70    {
71      i = pthread_mutex_trylock (mutex);
72      if (i == 0)
73	return;
74      assert (i == EBUSY);
75
76      i = clock_gettime (CLOCK_MONOTONIC, &now);
77      assert (i == 0);
78      assert (now.tv_sec >= start.tv_sec);
79    }
80  while (now.tv_sec - start.tv_sec < TIMEOUT);
81
82  fprintf (stderr, "Timed out waiting for internal lock!\n");
83  exit (EXIT_FAILURE);
84}
85
86static void *
87thread1_func (void *unused)
88{
89  int i;
90  volatile int rwatch_store;
91
92  pthread_barrier_wait (&threads_started_barrier);
93
94  timed_mutex_lock (&thread1_tid_mutex);
95
96  /* THREAD1_TID_MUTEX must be already locked to avoid race.  */
97  thread1_tid = gettid ();
98
99  i = pthread_cond_signal (&thread1_tid_cond);
100  assert (i == 0);
101  i = pthread_mutex_unlock (&thread1_tid_mutex);
102  assert (i == 0);
103
104  rwatch_store = thread1_rwatch;
105
106  /* Be sure the "t (tracing stop)" test can proceed for both threads.  */
107  timed_mutex_lock (&terminate_mutex);
108  i = pthread_mutex_unlock (&terminate_mutex);
109  assert (i == 0);
110
111  return NULL;
112}
113
114static void *
115thread2_func (void *unused)
116{
117  int i;
118  volatile int rwatch_store;
119
120  pthread_barrier_wait (&threads_started_barrier);
121
122  timed_mutex_lock (&thread2_tid_mutex);
123
124  /* THREAD2_TID_MUTEX must be already locked to avoid race.  */
125  thread2_tid = gettid ();
126
127  i = pthread_cond_signal (&thread2_tid_cond);
128  assert (i == 0);
129  i = pthread_mutex_unlock (&thread2_tid_mutex);
130  assert (i == 0);
131
132  rwatch_store = thread2_rwatch;
133
134  /* Be sure the "t (tracing stop)" test can proceed for both threads.  */
135  timed_mutex_lock (&terminate_mutex);
136  i = pthread_mutex_unlock (&terminate_mutex);
137  assert (i == 0);
138
139  return NULL;
140}
141
142static const char *
143proc_string (const char *filename, const char *line)
144{
145  FILE *f;
146  static char buf[LINE_MAX];
147  size_t line_len = strlen (line);
148
149  f = fopen (filename, "r");
150  if (f == NULL)
151    {
152      fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
153	       strerror (errno));
154      exit (EXIT_FAILURE);
155    }
156  while (errno = 0, fgets (buf, sizeof (buf), f))
157    {
158      char *s;
159
160      s = strchr (buf, '\n');
161      assert (s != NULL);
162      *s = 0;
163
164      if (strncmp (buf, line, line_len) != 0)
165	continue;
166
167      if (fclose (f))
168	{
169	  fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
170		   strerror (errno));
171	  exit (EXIT_FAILURE);
172	}
173
174      return &buf[line_len];
175    }
176  if (errno != 0)
177    {
178      fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
179      exit (EXIT_FAILURE);
180    }
181  fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
182  exit (EXIT_FAILURE);
183}
184
185static unsigned long
186proc_ulong (const char *filename, const char *line)
187{
188  const char *s = proc_string (filename, line);
189  long retval;
190  char *end;
191
192  errno = 0;
193  retval = strtol (s, &end, 10);
194  if (retval < 0 || retval >= LONG_MAX || (end && *end))
195    {
196      fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
197	       strerror (errno));
198      exit (EXIT_FAILURE);
199    }
200  return retval;
201}
202
203static void
204state_wait (pid_t process, const char *wanted)
205{
206  char *filename;
207  int i;
208  struct timespec start, now;
209  const char *state;
210
211  i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
212  assert (i > 0);
213
214  i = clock_gettime (CLOCK_MONOTONIC, &start);
215  assert (i == 0);
216
217  do
218    {
219      state = proc_string (filename, "State:\t");
220
221      /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0
222	 has changed "T (tracing stop)" to "t (tracing stop)".  Make the GDB
223	 testcase backward compatible with older Linux kernels.  */
224      if (strcmp (state, "T (tracing stop)") == 0)
225	state = "t (tracing stop)";
226
227      if (strcmp (state, wanted) == 0)
228	{
229	  free (filename);
230	  return;
231	}
232
233      if (sched_yield ())
234	{
235	  perror ("sched_yield()");
236	  exit (EXIT_FAILURE);
237	}
238
239      i = clock_gettime (CLOCK_MONOTONIC, &now);
240      assert (i == 0);
241      assert (now.tv_sec >= start.tv_sec);
242    }
243  while (now.tv_sec - start.tv_sec < TIMEOUT);
244
245  fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
246	   (unsigned long) process, wanted, state);
247  exit (EXIT_FAILURE);
248}
249
250static volatile pid_t tracer = 0;
251static pthread_t thread1, thread2;
252
253static void
254cleanup (void)
255{
256  printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
257
258  if (tracer)
259    {
260      int i;
261      int tracer_save = tracer;
262
263      tracer = 0;
264
265      i = kill (tracer_save, SIGCONT);
266      assert (i == 0);
267    }
268}
269
270int
271main (int argc, char **argv)
272{
273  int i;
274  int standalone = 0;
275
276  if (argc == 2 && strcmp (argv[1], "-s") == 0)
277    standalone = 1;
278  else
279    assert (argc == 1);
280
281  setbuf (stdout, NULL);
282
283  timed_mutex_lock (&thread1_tid_mutex);
284  timed_mutex_lock (&thread2_tid_mutex);
285
286  timed_mutex_lock (&terminate_mutex);
287
288  pthread_barrier_init (&threads_started_barrier, NULL, 3);
289
290  i = pthread_create (&thread1, NULL, thread1_func, NULL);
291  assert (i == 0);
292
293  i = pthread_create (&thread2, NULL, thread2_func, NULL);
294  assert (i == 0);
295
296  if (!standalone)
297    {
298      tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
299      if (tracer == 0)
300	{
301	  fprintf (stderr, "The testcase must be run by GDB!\n");
302	  exit (EXIT_FAILURE);
303	}
304      if (tracer != getppid ())
305	{
306	  fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
307	  exit (EXIT_FAILURE);
308	}
309    }
310
311  /* SIGCONT our debugger in the case of our crash as we would deadlock
312     otherwise.  */
313
314  atexit (cleanup);
315
316  /* Wait until all threads are seen running.  On Linux (at least),
317     new threads start stopped, and the debugger must resume them.
318     Need to wait for that before stopping GDB.  */
319  pthread_barrier_wait (&threads_started_barrier);
320
321  printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
322
323  if (tracer)
324    {
325      i = kill (tracer, SIGSTOP);
326      assert (i == 0);
327      state_wait (tracer, "T (stopped)");
328    }
329
330  /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex) and so
331     they could not trigger the watchpoints before GDB gets unstopped later.
332     Threads get resumed at pthread_cond_wait below.  Use `while' loops for
333     protection against spurious pthread_cond_wait wakeups.  */
334
335  printf ("Waiting till the threads initialize their TIDs.\n");
336
337  while (thread1_tid == 0)
338    {
339      i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
340      assert (i == 0);
341    }
342
343  while (thread2_tid == 0)
344    {
345      i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
346      assert (i == 0);
347    }
348
349  printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
350	  (unsigned long) thread1_tid, (unsigned long) thread2_tid,
351	  (unsigned long) getpid ());
352
353  printf ("Waiting till the threads get trapped by the watchpoints.\n");
354
355  if (tracer)
356    {
357      /* s390x-unknown-linux-gnu will fail with "R (running)".  */
358
359      state_wait (thread1_tid, "t (tracing stop)");
360
361      state_wait (thread2_tid, "t (tracing stop)");
362    }
363
364  cleanup ();
365
366  printf ("Joining the threads.\n");
367
368  i = pthread_mutex_unlock (&terminate_mutex);
369  assert (i == 0);
370
371  i = pthread_join (thread1, NULL);
372  assert (i == 0);
373
374  i = pthread_join (thread2, NULL);
375  assert (i == 0);
376
377  printf ("Exiting.\n");	/* break-at-exit */
378
379  /* Just prevent compiler `warning: unusedX_rwatch defined but not used'.  */
380  unused1_rwatch = 1;
381  unused2_rwatch = 2;
382
383  return EXIT_SUCCESS;
384}
385