Automatic Copyright Year update after running gdb/copyright.py
[deliverable/binutils-gdb.git] / gdb / testsuite / gdb.threads / siginfo-threads.c
1 /* This testcase is part of GDB, the GNU debugger.
2
3 Copyright 2010-2022 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 #define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig)
33
34 /* Terminate always in the main task. It can lock up with SIGSTOPped
35 GDB otherwise. */
36 #define TIMEOUT (gettid () == getpid() ? 10 : 15)
37
38 static pid_t thread1_tid;
39 static pthread_cond_t thread1_tid_cond
40 = PTHREAD_COND_INITIALIZER;
41 static pthread_mutex_t thread1_tid_mutex
42 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
43 static int thread1_sigusr1_hit;
44 static int thread1_sigusr2_hit;
45
46 static pid_t thread2_tid;
47 static pthread_cond_t thread2_tid_cond
48 = PTHREAD_COND_INITIALIZER;
49 static pthread_mutex_t thread2_tid_mutex
50 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
51 static int thread2_sigusr1_hit;
52 static int thread2_sigusr2_hit;
53
54 static pthread_mutex_t terminate_mutex
55 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
56
57 static pthread_barrier_t threads_started_barrier;
58
59 /* Do not use alarm as it would create a ptrace event which would hang
60 us up if we are being traced by GDB, which we stopped
61 ourselves. */
62
63 static void
64 timed_mutex_lock (pthread_mutex_t *mutex)
65 {
66 int i;
67 struct timespec start, now;
68
69 i = clock_gettime (CLOCK_MONOTONIC, &start);
70 assert (i == 0);
71
72 do
73 {
74 i = pthread_mutex_trylock (mutex);
75 if (i == 0)
76 return;
77 assert (i == EBUSY);
78
79 i = clock_gettime (CLOCK_MONOTONIC, &now);
80 assert (i == 0);
81 assert (now.tv_sec >= start.tv_sec);
82 }
83 while (now.tv_sec - start.tv_sec < TIMEOUT);
84
85 fprintf (stderr, "Timed out waiting for internal lock!\n");
86 exit (EXIT_FAILURE);
87 }
88
89 static void
90 handler (int signo, siginfo_t *siginfo, void *exception)
91 {
92 int *varp;
93
94 assert (siginfo->si_signo == signo);
95 assert (siginfo->si_code == SI_TKILL);
96 assert (siginfo->si_pid == getpid ());
97
98 if (gettid () == thread1_tid)
99 {
100 if (signo == SIGUSR1)
101 varp = &thread1_sigusr1_hit;
102 else if (signo == SIGUSR2)
103 varp = &thread1_sigusr2_hit;
104 else
105 assert (0);
106 }
107 else if (gettid () == thread2_tid)
108 {
109 if (signo == SIGUSR1)
110 varp = &thread2_sigusr1_hit;
111 else if (signo == SIGUSR2)
112 varp = &thread2_sigusr2_hit;
113 else
114 assert (0);
115 }
116 else
117 assert (0);
118
119 if (*varp)
120 {
121 fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo,
122 (unsigned long) gettid ());
123 exit (EXIT_FAILURE);
124 }
125 *varp = 1;
126 }
127
128 static void *
129 thread1_func (void *unused)
130 {
131 int i;
132
133 pthread_barrier_wait (&threads_started_barrier);
134
135 timed_mutex_lock (&thread1_tid_mutex);
136
137 /* THREAD1_TID_MUTEX must be already locked to avoid a race. */
138 thread1_tid = gettid ();
139
140 i = pthread_cond_signal (&thread1_tid_cond);
141 assert (i == 0);
142 i = pthread_mutex_unlock (&thread1_tid_mutex);
143 assert (i == 0);
144
145 /* Be sure the "t (tracing stop)" test can proceed for both
146 threads. */
147 timed_mutex_lock (&terminate_mutex);
148 i = pthread_mutex_unlock (&terminate_mutex);
149 assert (i == 0);
150
151 if (!thread1_sigusr1_hit)
152 {
153 fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n");
154 exit (EXIT_FAILURE);
155 }
156 if (!thread1_sigusr2_hit)
157 {
158 fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n");
159 exit (EXIT_FAILURE);
160 }
161
162 return NULL;
163 }
164
165 static void *
166 thread2_func (void *unused)
167 {
168 int i;
169
170 pthread_barrier_wait (&threads_started_barrier);
171
172 timed_mutex_lock (&thread2_tid_mutex);
173
174 /* THREAD2_TID_MUTEX must be already locked to avoid a race. */
175 thread2_tid = gettid ();
176
177 i = pthread_cond_signal (&thread2_tid_cond);
178 assert (i == 0);
179 i = pthread_mutex_unlock (&thread2_tid_mutex);
180 assert (i == 0);
181
182 /* Be sure the "t (tracing stop)" test can proceed for both
183 threads. */
184 timed_mutex_lock (&terminate_mutex);
185 i = pthread_mutex_unlock (&terminate_mutex);
186 assert (i == 0);
187
188 if (!thread2_sigusr1_hit)
189 {
190 fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n");
191 exit (EXIT_FAILURE);
192 }
193 if (!thread2_sigusr2_hit)
194 {
195 fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n");
196 exit (EXIT_FAILURE);
197 }
198
199 return NULL;
200 }
201
202 static const char *
203 proc_string (const char *filename, const char *line)
204 {
205 FILE *f;
206 static char buf[LINE_MAX];
207 size_t line_len = strlen (line);
208
209 f = fopen (filename, "r");
210 if (f == NULL)
211 {
212 fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
213 strerror (errno));
214 exit (EXIT_FAILURE);
215 }
216 while (errno = 0, fgets (buf, sizeof (buf), f))
217 {
218 char *s;
219
220 s = strchr (buf, '\n');
221 assert (s != NULL);
222 *s = 0;
223
224 if (strncmp (buf, line, line_len) != 0)
225 continue;
226
227 if (fclose (f))
228 {
229 fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
230 strerror (errno));
231 exit (EXIT_FAILURE);
232 }
233
234 return &buf[line_len];
235 }
236 if (errno != 0)
237 {
238 fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
239 exit (EXIT_FAILURE);
240 }
241 fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
242 exit (EXIT_FAILURE);
243 }
244
245 static unsigned long
246 proc_ulong (const char *filename, const char *line)
247 {
248 const char *s = proc_string (filename, line);
249 long retval;
250 char *end;
251
252 errno = 0;
253 retval = strtol (s, &end, 10);
254 if (retval < 0 || retval >= LONG_MAX || (end && *end))
255 {
256 fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
257 strerror (errno));
258 exit (EXIT_FAILURE);
259 }
260 return retval;
261 }
262
263 static void
264 state_wait (pid_t process, const char *wanted)
265 {
266 char *filename;
267 int i;
268 struct timespec start, now;
269 const char *state;
270
271 i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
272 assert (i > 0);
273
274 i = clock_gettime (CLOCK_MONOTONIC, &start);
275 assert (i == 0);
276
277 do
278 {
279 state = proc_string (filename, "State:\t");
280
281 /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0
282 has changed "T (tracing stop)" to "t (tracing stop)". Make the GDB
283 testcase backward compatible with older Linux kernels. */
284 if (strcmp (state, "T (tracing stop)") == 0)
285 state = "t (tracing stop)";
286
287 if (strcmp (state, wanted) == 0)
288 {
289 free (filename);
290 return;
291 }
292
293 if (sched_yield ())
294 {
295 perror ("sched_yield()");
296 exit (EXIT_FAILURE);
297 }
298
299 i = clock_gettime (CLOCK_MONOTONIC, &now);
300 assert (i == 0);
301 assert (now.tv_sec >= start.tv_sec);
302 }
303 while (now.tv_sec - start.tv_sec < TIMEOUT);
304
305 fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
306 (unsigned long) process, wanted, state);
307 exit (EXIT_FAILURE);
308 }
309
310 static volatile pid_t tracer = 0;
311 static pthread_t thread1, thread2;
312
313 static void
314 cleanup (void)
315 {
316 printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
317
318 if (tracer)
319 {
320 int i;
321 int tracer_save = tracer;
322
323 tracer = 0;
324
325 i = kill (tracer_save, SIGCONT);
326 assert (i == 0);
327 }
328 }
329
330 int
331 main (int argc, char **argv)
332 {
333 int i;
334 int standalone = 0;
335 struct sigaction act;
336
337 if (argc == 2 && strcmp (argv[1], "-s") == 0)
338 standalone = 1;
339 else
340 assert (argc == 1);
341
342 setbuf (stdout, NULL);
343
344 timed_mutex_lock (&thread1_tid_mutex);
345 timed_mutex_lock (&thread2_tid_mutex);
346
347 timed_mutex_lock (&terminate_mutex);
348
349 errno = 0;
350 memset (&act, 0, sizeof (act));
351 act.sa_sigaction = handler;
352 act.sa_flags = SA_RESTART | SA_SIGINFO;
353 i = sigemptyset (&act.sa_mask);
354 assert_perror (errno);
355 assert (i == 0);
356 i = sigaction (SIGUSR1, &act, NULL);
357 assert_perror (errno);
358 assert (i == 0);
359 i = sigaction (SIGUSR2, &act, NULL);
360 assert_perror (errno);
361 assert (i == 0);
362
363 pthread_barrier_init (&threads_started_barrier, NULL, 3);
364
365 i = pthread_create (&thread1, NULL, thread1_func, NULL);
366 assert (i == 0);
367
368 i = pthread_create (&thread2, NULL, thread2_func, NULL);
369 assert (i == 0);
370
371 if (!standalone)
372 {
373 tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
374 if (tracer == 0)
375 {
376 fprintf (stderr, "The testcase must be run by GDB!\n");
377 exit (EXIT_FAILURE);
378 }
379 if (tracer != getppid ())
380 {
381 fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
382 exit (EXIT_FAILURE);
383 }
384 }
385
386 /* SIGCONT our debugger in the case of our crash as we would deadlock
387 otherwise. */
388
389 atexit (cleanup);
390
391 /* Wait until all threads are seen running. On Linux (at least),
392 new threads start stopped, and the debugger must resume them.
393 Need to wait for that before stopping GDB. */
394 pthread_barrier_wait (&threads_started_barrier);
395
396 printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
397
398 if (tracer)
399 {
400 i = kill (tracer, SIGSTOP);
401 assert (i == 0);
402 state_wait (tracer, "T (stopped)");
403 }
404
405 /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex)
406 and so they could not trigger the signals before GDB is unstopped
407 later. Threads get resumed by the pthread_cond_wait below. Use
408 `while' loops for protection against spurious pthread_cond_wait
409 wakeups. */
410
411 printf ("Waiting till the threads initialize their TIDs.\n");
412
413 while (thread1_tid == 0)
414 {
415 i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
416 assert (i == 0);
417 }
418
419 while (thread2_tid == 0)
420 {
421 i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
422 assert (i == 0);
423 }
424
425 printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
426 (unsigned long) thread1_tid, (unsigned long) thread2_tid,
427 (unsigned long) getpid ());
428
429 errno = 0;
430 i = tgkill (getpid (), thread1_tid, SIGUSR1);
431 assert_perror (errno);
432 assert (i == 0);
433 i = tgkill (getpid (), thread1_tid, SIGUSR2);
434 assert_perror (errno);
435 assert (i == 0);
436 i = tgkill (getpid (), thread2_tid, SIGUSR1);
437 assert_perror (errno);
438 assert (i == 0);
439 i = tgkill (getpid (), thread2_tid, SIGUSR2);
440 assert_perror (errno);
441 assert (i == 0);
442
443 printf ("Waiting till the threads are trapped by the signals.\n");
444
445 if (tracer)
446 {
447 /* s390x-unknown-linux-gnu will fail with "R (running)". */
448
449 state_wait (thread1_tid, "t (tracing stop)");
450
451 state_wait (thread2_tid, "t (tracing stop)");
452 }
453
454 cleanup ();
455
456 printf ("Joining the threads.\n");
457
458 i = pthread_mutex_unlock (&terminate_mutex);
459 assert (i == 0);
460
461 i = pthread_join (thread1, NULL);
462 assert (i == 0);
463
464 i = pthread_join (thread2, NULL);
465 assert (i == 0);
466
467 printf ("Exiting.\n"); /* break-at-exit */
468
469 return EXIT_SUCCESS;
470 }
This page took 0.039795 seconds and 4 git commands to generate.