]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/testsuite/gdb.threads/watchthreads-reorder.c
0f5731da8e1c72f3b159a030c12f675dfe5bb342
[thirdparty/binutils-gdb.git] / gdb / testsuite / gdb.threads / watchthreads-reorder.c
1 /* This testcase is part of GDB, the GNU debugger.
2
3 Copyright 2009-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 #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
37 static pid_t thread1_tid;
38 static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER;
39 static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
40
41 static pid_t thread2_tid;
42 static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER;
43 static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
44
45 static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
46
47 static 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
52 static int unused1_rwatch;
53 static int unused2_rwatch;
54
55 static volatile int thread1_rwatch;
56 static 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
61 static 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
86 static void *
87 thread1_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
114 static void *
115 thread2_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
142 static const char *
143 proc_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
185 static unsigned long
186 proc_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
203 static void
204 state_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
250 static volatile pid_t tracer = 0;
251 static pthread_t thread1, thread2;
252
253 static void
254 cleanup (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
270 int
271 main (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 }