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