Fixed SIGALRM race.
[debugger.git] / testsuite.c
index 242b53d..76a3d09 100644 (file)
@@ -1,5 +1,7 @@
 /* Copyright 2007, Red Hat Inc.  */
 
+#define _GNU_SOURCE 1
+
 #include <unistd.h>
 #include <stdlib.h>
 #include <assert.h>
@@ -10,6 +12,7 @@
 #include <string.h>
 #include <pthread.h>
 #include <sys/syscall.h>
+#include <dlfcn.h>
 
 #include "debugger.h"
 
 #include "debugger.c"
 
 
+#define TIMEOUT_SECS 4
+#undef LOOPS_MIN
+#define LOOPS_MIN 1000
+
+
 #define tkill(tid, sig) syscall (SYS_tkill, (tid), (sig))
 
 
-static int attach_checked (pid_t pid, int redelivered_expect)
+static void detach_checked (struct attach_state_struct *attach_state)
 {
-  int stopped;
+  pid_t pid = attach_state_pid_get (attach_state);
 
-  STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
-  stopped = attach (pid);
-  if (attach_redelivered != redelivered_expect)
+  if (attach_state_stopped_get (attach_state) != 0)
+    {
+      STATE (pid, 1 << STATE_STOPPED);
+      detach (attach_state);
+      STATE (pid, 1 << STATE_STOPPED);
+    }
+  else
     {
-      fprintf (stderr, "Expecting redelivery of %d but found %d\n",
-              redelivered_expect, attach_redelivered);
-      abort ();
+      STATE (pid, (1 << STATE_PTRACED));
+      detach (attach_state);
+      STATE (pid, ((1 << STATE_SLEEPING) | (1 << STATE_RUNNING)));
     }
-  /* FIXME: Why also STATE_STOPPED?  */
-  STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
-  return stopped;
 }
 
-static void detach_checked (pid_t pid, int stopped)
+static volatile int spawned_threads_count;     /* Set for the specific spawner.  */
+
+static struct attach_state_struct *attach_checked (pid_t pid, int redelivered_expect)
 {
-  /* FIXME: Why STATE_STOPPED?  */
-  STATE (pid, (stopped ? 1 << STATE_STOPPED : 1 << STATE_PTRACED));
-  detach (pid, stopped);
-  STATE (pid, (stopped ? 1 << STATE_STOPPED : (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)));
+  struct attach_state_struct *attach_state;
+  time_t timeout = time (NULL) + TIMEOUT_SECS;
+  unsigned loops = 0;
+
+  STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
+  do
+    {
+      attach_state = attach (pid);
+      if (redelivered_expect != attach_state_redelivered_get (attach_state))
+       {
+         fprintf (stderr, "Expecting redelivery of %d but found %d\n",
+                  redelivered_expect,
+                  attach_state_redelivered_get (attach_state));
+         abort ();
+       }
+      /* During the inferior's initialization we may catch less threads.  */
+      assert (attach_state_threads_count_get (attach_state)
+             <= spawned_threads_count);
+      if (attach_state_threads_count_get (attach_state)
+         == spawned_threads_count
+         /* Inferior may got stopped before it could initialize.  */
+         || attach_state_stopped_get (attach_state) == 1)
+       {
+         /* FIXME: Why also STATE_STOPPED?  */
+         STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
+         return attach_state;
+       }
+      /* WARNING: Currently we never use REDELIVERED_EXPECT but we would have to
+         probably reset it back to 0 otherwise.  */
+      assert (redelivered_expect == 0);
+      detach_checked (attach_state);
+    }
+  while (loops++ < LOOPS_MIN || time (NULL) < timeout);
+  abort ();
 }
 
 struct registry
@@ -122,7 +163,7 @@ static void registry_handler (int signo)
   raise (signo);
 }
 
-static void child_pause (void)
+static __attribute__((__noreturn__)) void child_pause (void)
 {
   for (;;)
     pause ();
@@ -136,16 +177,14 @@ static void child_alrm_handler (int signo)
   raise (SIGALRM);
 }
 
-static void child_alrm (void)
+static __attribute__((__noreturn__)) void child_alrm (void)
 {
-  void (*handler_orig) (int signo);
 #if 0
   int i;
   sigset_t oldset;
 #endif
 
-  handler_orig = signal (SIGALRM, child_alrm_handler);
-  assert (handler_orig == SIG_DFL);
+  /* Assumed already setup SIGALRM for CHILD_ALRM_HANDLER.  */
 
 #if 0
   i = sigprocmask (SIG_BLOCK, NULL, &oldset);
@@ -227,7 +266,7 @@ struct child_spawner
     int fd;
   };
 
-static void *child_spawner (void *param_voidpointer, void *input)
+static __attribute__((__noreturn__)) void *child_spawner (void *param_voidpointer, void *input)
 {
   struct child_spawner *param = param_voidpointer;
   pid_t inferior;
@@ -300,16 +339,17 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
                          void *input)
 {
   pid_t inferior;
-  int stopped;
+  struct attach_state_struct *attach_state;
   int i;
+  void (*handler_orig) (int signo);
 
   assert (input == NULL);
 
   /* Plain attach/detach.  */
   inferior = (unsigned long) (*child) (data, child_pause);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 0);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 0);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a process stopped by standard kill(2).  */
@@ -318,9 +358,9 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   i = kill (inferior, SIGSTOP);
   assert (i == 0);
   STATE (inferior, 1 << STATE_STOPPED);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a process stopped by Linux specific tkill(2).  */
@@ -329,13 +369,18 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   i = tkill (inferior, SIGSTOP);
   assert (i == 0);
   STATE (inferior, 1 << STATE_STOPPED);
-  stopped = attach_checked (inferior, 0);
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  attach_state = attach_checked (inferior, 0);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   murder (inferior);
 
   /* Attach to a stopped process with already pending SIGALRM.  */
+  /* Setup the handler already in the parent to avoid the child race.  */
+  handler_orig = signal (SIGALRM, child_alrm_handler);
+  assert (handler_orig == SIG_DFL);
   inferior = (unsigned long) (*child) (data, child_alrm);
+  handler_orig = signal (SIGALRM, handler_orig);
+  assert (handler_orig == child_alrm_handler);
   STATE (inferior, 1 << STATE_SLEEPING);
   delay ();
   i = tkill (inferior, SIGSTOP);
@@ -348,12 +393,12 @@ static void body_spawner (void *(*child) (void *data, void *input), void *data,
   STATE (inferior, 1 << STATE_STOPPED);
   /* FIXME: SIGALRM did not get redelivered?  */
 #if 0
-  stopped = attach_checked (inferior, SIGALRM);
+  attach_state = attach_checked (inferior, SIGALRM);
 #else
-  stopped = attach_checked (inferior, 0);
+  attach_state = attach_checked (inferior, 0);
 #endif
-  assert (stopped == 1);
-  detach_checked (inferior, stopped);
+  assert (attach_state_stopped_get (attach_state) == 1);
+  detach_checked (attach_state);
   STATE (inferior, 1 << STATE_STOPPED);
   delay ();
   i = tkill (inferior, SIGCONT);
@@ -397,7 +442,7 @@ static void *spawn_singlethreaded (void *data, void *input)
   return pass (data, input);
 }
 
-static void *spawn_threaded_parent_start (void *arg)
+static __attribute__((__noreturn__)) void *spawn_threaded_parent_start (void *arg)
 {
   for (;;)
     pause ();
@@ -405,12 +450,46 @@ static void *spawn_threaded_parent_start (void *arg)
   abort ();
 }
 
+static void *libpthread_handle;
+static int (*libpthread_create_pointer) (pthread_t *thread,
+                                        const pthread_attr_t *attr,
+                                        void *(*start_routine) (void *),
+                                        void *arg);
+static int (*libpthread_join_pointer) (pthread_t thread, void **value_ptr);
+
+static void libpthread_open (void)
+{
+  assert (libpthread_handle == NULL);
+
+  libpthread_handle = dlopen ("libpthread.so.0", RTLD_LAZY);
+  assert (libpthread_handle != NULL);
+
+  libpthread_create_pointer = dlsym (libpthread_handle, "pthread_create");
+  assert (libpthread_create_pointer != NULL);
+  libpthread_join_pointer = dlsym (libpthread_handle, "pthread_join");
+  assert (libpthread_join_pointer != NULL);
+
+}
+
+static void libpthread_close (void)
+{
+  int i;
+
+  assert (libpthread_handle != NULL);
+
+  i = dlclose (libpthread_handle);
+  assert (i == 0);
+  libpthread_handle = NULL;
+}
+
 static void *spawn_threaded_parent (void *data, void *input)
 {
   pthread_t thread;
   int i;
 
-  i = pthread_create (&thread, NULL, spawn_threaded_parent_start, NULL);
+  assert (libpthread_create_pointer != NULL);
+  i = (*libpthread_create_pointer) (&thread, NULL, spawn_threaded_parent_start,
+                                   NULL);
   assert (i == 0);
   return pass (data, input);
 }
@@ -426,10 +505,9 @@ static void *spawn_threaded_child_start (void *arg_voidpointer)
   struct spawn_threaded_child_start *arg = arg_voidpointer;
 
   return pass (arg->data, arg->input);
-  /* NOTREACHED */
 }
 
-static void *spawn_threaded_child (void *data, void *input)
+static __attribute__((__noreturn__)) void *spawn_threaded_child (void *data, void *input)
 {
   pthread_t thread;
   int i;
@@ -437,12 +515,17 @@ static void *spawn_threaded_child (void *data, void *input)
 
   arg_local.data = data;
   arg_local.input = input;
-  i = pthread_create (&thread, NULL, spawn_threaded_child_start, &arg_local);
+  assert (libpthread_create_pointer != NULL);
+  i = (*libpthread_create_pointer) (&thread, NULL, spawn_threaded_child_start,
+                                   &arg_local);
   assert (i == 0);
-  i = pthread_join (thread, NULL);
+  assert (libpthread_join_pointer != NULL);
+  i = (*libpthread_join_pointer) (thread, NULL);
   assert (i == 0);
 
   _exit (EXIT_SUCCESS);
+  /* NOTREACHED */
+  abort ();
 }
 
 static void body_maywaiter (void *(*child) (void *data, void *input),
@@ -466,11 +549,34 @@ static void handler_sigusr1 (int signo)
   loops_print++;
 }
 
+static __attribute__((__noreturn__)) void *tests_threaded (void *data, void *input)
+{
+  libpthread_open ();
+
+  spawned_threads_count = 2;
+  body_maywaiter (spawn_threaded_parent, NULL, NULL);
+
+  spawned_threads_count = 2;
+  body_maywaiter (spawn_threaded_child, NULL, NULL);
+
+  libpthread_close ();
+
+  registry_cleanup ();
+  _exit (EXIT_SUCCESS);
+  /* NOTREACHED */
+  abort ();
+}
+
 int main (int argc, char **argv)
 {
   int loop = 0;
   int i;
 
+#ifdef MEASURE_STATE_PERFORMANCE
+  STATE (1, 1 << STATE_ZOMBIE);
+  return 0;
+#endif /* MEASURE_STATE_PERFORMANCE */
+
   if (argc == 1)
     ;
   else if (argc == 2 && strcmp (argv[1], "-l") == 0)
@@ -489,19 +595,36 @@ int main (int argc, char **argv)
 
   do
     {
+      pid_t pid_threaded, pid_threaded_got;
+      int status;
+
       while (loops_print > 0)
         {
          printf ("%lu\n", loops);
          loops_print--;
        }
+
+      spawned_threads_count = 0;
       body_maywaiter (spawn_singlethreaded, NULL, NULL);
-      body_maywaiter (spawn_threaded_parent, NULL, NULL);
-      body_maywaiter (spawn_threaded_child, NULL, NULL);
+
+      /* We cannot rely on LIBPTHREAD_CLOSE as libpthread would be still loaded
+         with ATTACH_STATE_REDELIVERED_GET () reporting 1 for
+         SPAWN_SINGLETHREADED.  */
+      pid_threaded = spawn (tests_threaded, NULL, NULL, -1);
+      pid_threaded_got = waitpid (pid_threaded, &status, 0);
+      assert (pid_threaded_got == pid_threaded);
+      assert (!WIFSIGNALED (status));  /* Improve readability in the failed case.  */
+      assert (!WIFSTOPPED (status));   /* Improve readability in the failed case.  */
+      assert (WIFEXITED (status));
+      assert (WEXITSTATUS (status) == 0);
+      STATE (pid_threaded, 1 << STATE_ENOENT);
+      registry_remove (pid_threaded);
 
       registry_cleanup ();
       loops++;
     }
-  while (loop != 0);
+  /* Run at least twice to test libpthread_close ().  */
+  while (loop != 0 || loops == 1);
 
   return EXIT_SUCCESS;
 }