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