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