[gdb/symtab] Fix gdb.base/fission-macro.exp with unix/-m32
[binutils-gdb.git] / gdb / testsuite / gdb.threads / siginfo-threads.c
blob22c6038206ba77fc3432da5ee30284c80641f305
1 /* This testcase is part of GDB, the GNU debugger.
3 Copyright 2010-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
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>
31 #define gettid() syscall (__NR_gettid)
32 #define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig)
34 /* Terminate always in the main task. It can lock up with SIGSTOPped
35 GDB otherwise. */
36 #define TIMEOUT (gettid () == getpid() ? 10 : 15)
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;
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;
54 static pthread_mutex_t terminate_mutex
55 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
57 static pthread_barrier_t threads_started_barrier;
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. */
63 static void
64 timed_mutex_lock (pthread_mutex_t *mutex)
66 int i;
67 struct timespec start, now;
69 i = clock_gettime (CLOCK_MONOTONIC, &start);
70 assert (i == 0);
74 i = pthread_mutex_trylock (mutex);
75 if (i == 0)
76 return;
77 assert (i == EBUSY);
79 i = clock_gettime (CLOCK_MONOTONIC, &now);
80 assert (i == 0);
81 assert (now.tv_sec >= start.tv_sec);
83 while (now.tv_sec - start.tv_sec < TIMEOUT);
85 fprintf (stderr, "Timed out waiting for internal lock!\n");
86 exit (EXIT_FAILURE);
89 static void
90 handler (int signo, siginfo_t *siginfo, void *exception)
92 int *varp;
94 assert (siginfo->si_signo == signo);
95 assert (siginfo->si_code == SI_TKILL);
96 assert (siginfo->si_pid == getpid ());
98 if (gettid () == thread1_tid)
100 if (signo == SIGUSR1)
101 varp = &thread1_sigusr1_hit;
102 else if (signo == SIGUSR2)
103 varp = &thread1_sigusr2_hit;
104 else
105 assert (0);
107 else if (gettid () == thread2_tid)
109 if (signo == SIGUSR1)
110 varp = &thread2_sigusr1_hit;
111 else if (signo == SIGUSR2)
112 varp = &thread2_sigusr2_hit;
113 else
114 assert (0);
116 else
117 assert (0);
119 if (*varp)
121 fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo,
122 (unsigned long) gettid ());
123 exit (EXIT_FAILURE);
125 *varp = 1;
128 static void *
129 thread1_func (void *unused)
131 int i;
133 pthread_barrier_wait (&threads_started_barrier);
135 timed_mutex_lock (&thread1_tid_mutex);
137 /* THREAD1_TID_MUTEX must be already locked to avoid a race. */
138 thread1_tid = gettid ();
140 i = pthread_cond_signal (&thread1_tid_cond);
141 assert (i == 0);
142 i = pthread_mutex_unlock (&thread1_tid_mutex);
143 assert (i == 0);
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);
151 if (!thread1_sigusr1_hit)
153 fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n");
154 exit (EXIT_FAILURE);
156 if (!thread1_sigusr2_hit)
158 fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n");
159 exit (EXIT_FAILURE);
162 return NULL;
165 static void *
166 thread2_func (void *unused)
168 int i;
170 pthread_barrier_wait (&threads_started_barrier);
172 timed_mutex_lock (&thread2_tid_mutex);
174 /* THREAD2_TID_MUTEX must be already locked to avoid a race. */
175 thread2_tid = gettid ();
177 i = pthread_cond_signal (&thread2_tid_cond);
178 assert (i == 0);
179 i = pthread_mutex_unlock (&thread2_tid_mutex);
180 assert (i == 0);
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);
188 if (!thread2_sigusr1_hit)
190 fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n");
191 exit (EXIT_FAILURE);
193 if (!thread2_sigusr2_hit)
195 fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n");
196 exit (EXIT_FAILURE);
199 return NULL;
202 static const char *
203 proc_string (const char *filename, const char *line)
205 FILE *f;
206 static char buf[LINE_MAX];
207 size_t line_len = strlen (line);
209 f = fopen (filename, "r");
210 if (f == NULL)
212 fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
213 strerror (errno));
214 exit (EXIT_FAILURE);
216 while (errno = 0, fgets (buf, sizeof (buf), f))
218 char *s;
220 s = strchr (buf, '\n');
221 assert (s != NULL);
222 *s = 0;
224 if (strncmp (buf, line, line_len) != 0)
225 continue;
227 if (fclose (f))
229 fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
230 strerror (errno));
231 exit (EXIT_FAILURE);
234 return &buf[line_len];
236 if (errno != 0)
238 fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
239 exit (EXIT_FAILURE);
241 fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
242 exit (EXIT_FAILURE);
245 static unsigned long
246 proc_ulong (const char *filename, const char *line)
248 const char *s = proc_string (filename, line);
249 long retval;
250 char *end;
252 errno = 0;
253 retval = strtol (s, &end, 10);
254 if (retval < 0 || retval >= LONG_MAX || (end && *end))
256 fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
257 strerror (errno));
258 exit (EXIT_FAILURE);
260 return retval;
263 static void
264 state_wait (pid_t process, const char *wanted)
266 char *filename;
267 int i;
268 struct timespec start, now;
269 const char *state;
271 i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
272 assert (i > 0);
274 i = clock_gettime (CLOCK_MONOTONIC, &start);
275 assert (i == 0);
279 state = proc_string (filename, "State:\t");
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)";
287 if (strcmp (state, wanted) == 0)
289 free (filename);
290 return;
293 if (sched_yield ())
295 perror ("sched_yield()");
296 exit (EXIT_FAILURE);
299 i = clock_gettime (CLOCK_MONOTONIC, &now);
300 assert (i == 0);
301 assert (now.tv_sec >= start.tv_sec);
303 while (now.tv_sec - start.tv_sec < TIMEOUT);
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);
310 static volatile pid_t tracer = 0;
311 static pthread_t thread1, thread2;
313 static void
314 cleanup (void)
316 printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
318 if (tracer)
320 int i;
321 int tracer_save = tracer;
323 tracer = 0;
325 i = kill (tracer_save, SIGCONT);
326 assert (i == 0);
331 main (int argc, char **argv)
333 int i;
334 int standalone = 0;
335 struct sigaction act;
337 if (argc == 2 && strcmp (argv[1], "-s") == 0)
338 standalone = 1;
339 else
340 assert (argc == 1);
342 setbuf (stdout, NULL);
344 timed_mutex_lock (&thread1_tid_mutex);
345 timed_mutex_lock (&thread2_tid_mutex);
347 timed_mutex_lock (&terminate_mutex);
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);
363 pthread_barrier_init (&threads_started_barrier, NULL, 3);
365 i = pthread_create (&thread1, NULL, thread1_func, NULL);
366 assert (i == 0);
368 i = pthread_create (&thread2, NULL, thread2_func, NULL);
369 assert (i == 0);
371 if (!standalone)
373 tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
374 if (tracer == 0)
376 fprintf (stderr, "The testcase must be run by GDB!\n");
377 exit (EXIT_FAILURE);
379 if (tracer != getppid ())
381 fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
382 exit (EXIT_FAILURE);
386 /* SIGCONT our debugger in the case of our crash as we would deadlock
387 otherwise. */
389 atexit (cleanup);
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);
396 printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
398 if (tracer)
400 i = kill (tracer, SIGSTOP);
401 assert (i == 0);
402 state_wait (tracer, "T (stopped)");
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. */
411 printf ("Waiting till the threads initialize their TIDs.\n");
413 while (thread1_tid == 0)
415 i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
416 assert (i == 0);
419 while (thread2_tid == 0)
421 i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
422 assert (i == 0);
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 ());
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);
443 printf ("Waiting till the threads are trapped by the signals.\n");
445 if (tracer)
447 /* s390x-unknown-linux-gnu will fail with "R (running)". */
449 state_wait (thread1_tid, "t (tracing stop)");
451 state_wait (thread2_tid, "t (tracing stop)");
454 cleanup ();
456 printf ("Joining the threads.\n");
458 i = pthread_mutex_unlock (&terminate_mutex);
459 assert (i == 0);
461 i = pthread_join (thread1, NULL);
462 assert (i == 0);
464 i = pthread_join (thread2, NULL);
465 assert (i == 0);
467 printf ("Exiting.\n"); /* break-at-exit */
469 return EXIT_SUCCESS;