watchthreads-reorder.c revision 1.1
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 37static pid_t thread1_tid; 38static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER; 39static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; 40 41static pid_t thread2_tid; 42static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER; 43static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; 44 45static 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 50static int unused1_rwatch; 51static int unused2_rwatch; 52 53static volatile int thread1_rwatch; 54static 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 59static 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 84static void * 85thread1_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 110static void * 111thread2_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 136static const char * 137proc_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 179static unsigned long 180proc_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 197static void 198state_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 244static volatile pid_t tracer = 0; 245static pthread_t thread1, thread2; 246 247static void 248cleanup (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 264int 265main (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