1 /* This testcase is part of GDB, the GNU debugger.
2 
3    Copyright 2010-2020 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
timed_mutex_lock(pthread_mutex_t * mutex)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
handler(int signo,siginfo_t * siginfo,void * exception)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 *
thread1_func(void * unused)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 *
thread2_func(void * unused)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 *
proc_string(const char * filename,const char * line)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
proc_ulong(const char * filename,const char * line)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
state_wait(pid_t process,const char * wanted)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
cleanup(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
main(int argc,char ** argv)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 }
471