/* Copyright 2007, Red Hat Inc. */
+#define _GNU_SOURCE 1
+
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#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
raise (signo);
}
-static void child_pause (void)
+static __attribute__((__noreturn__)) void child_pause (void)
{
for (;;)
pause ();
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);
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;
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). */
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). */
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);
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);
return pass (data, input);
}
-static void *spawn_threaded_parent_start (void *arg)
+static __attribute__((__noreturn__)) void *spawn_threaded_parent_start (void *arg)
{
for (;;)
pause ();
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);
}
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;
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),
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)
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;
}