1 /* Copyright 2007, Red Hat Inc. */
14 #include <sys/syscall.h>
23 #define tkill(tid, sig) syscall (SYS_tkill, (tid), (sig))
26 static volatile int spawned_threads_count; /* Set for the specific spawner. */
28 static struct attach_state_struct *attach_checked (pid_t pid, int redelivered_expect)
30 struct attach_state_struct *attach_state;
32 STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED));
33 attach_state = attach (pid);
34 if (redelivered_expect != attach_state_redelivered_get (attach_state))
36 fprintf (stderr, "Expecting redelivery of %d but found %d\n",
37 redelivered_expect, attach_state_redelivered_get (attach_state));
40 assert (spawned_threads_count == attach_state_threads_count_get (attach_state));
41 /* FIXME: Why also STATE_STOPPED? */
42 STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED));
46 static void detach_checked (struct attach_state_struct *attach_state)
48 pid_t pid = attach_state_pid_get (attach_state);
49 int stopped = attach_state_stopped_get (attach_state);
51 /* FIXME: Why STATE_STOPPED? */
52 STATE (pid, (stopped ? 1 << STATE_STOPPED : 1 << STATE_PTRACED));
53 detach (attach_state);
54 STATE (pid, (stopped ? 1 << STATE_STOPPED : (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)));
59 struct registry *next;
62 struct registry *registry_list;
64 static void registry_add (pid_t pid)
68 new = malloc (sizeof (*new));
71 new->next = registry_list;
75 static void registry_remove (pid_t pid)
77 struct registry **iter_pointer;
79 for (iter_pointer = ®istry_list; *iter_pointer != NULL;
80 iter_pointer = &(*iter_pointer)->next)
82 struct registry *found = *iter_pointer;
83 if (found->pid != pid)
86 *iter_pointer = found->next;
93 static void registry_atexit (void)
95 struct registry *iter;
97 for (iter = registry_list; iter != NULL; iter = iter->next)
99 tkill (iter->pid, SIGCONT);
100 tkill (iter->pid, SIGKILL);
101 kill (iter->pid, SIGKILL);
105 static void registry_cleanup (void)
107 struct registry *iter;
111 while ((pid = wait (NULL)) != -1)
113 for (iter = registry_list; iter != NULL; iter = iter->next)
114 if (iter->pid == pid)
116 assert (iter != NULL);
118 assert (errno == ECHILD);
119 while (registry_list)
121 iter = registry_list;
122 registry_list = iter->next;
127 static void registry_handler (int signo)
129 signal (signo, SIG_DFL);
134 static void child_pause (void)
142 static void child_alrm_handler (int signo)
144 assert (signo == SIGALRM);
148 static void child_alrm (void)
150 void (*handler_orig) (int signo);
156 handler_orig = signal (SIGALRM, child_alrm_handler);
157 assert (handler_orig == SIG_DFL);
160 i = sigprocmask (SIG_BLOCK, NULL, &oldset);
162 printf ("sigprocmask () -> sigismember (SIGALRM) == %d\n", sigismember (&oldset, SIGALRM));
171 static pid_t spawn (void *(*child) (void *data, void *input), void *data,
172 void *input, int fd_close)
188 i = close (fd_close);
200 STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING));
204 static void murder (pid_t pid)
213 i = kill (pid, SIGKILL);
216 pid_got = waitpid (pid, &status, 0);
217 if (!(pid_got == -1 && errno == ECHILD))
219 assert (pid_got == pid);
220 assert ((WIFSIGNALED (status) && (WTERMSIG (status) == SIGKILL
221 || WTERMSIG (status) == SIGUSR2))
222 || (WIFEXITED (status) && WEXITSTATUS (status) == 0));
223 STATE (pid, 1 << STATE_ENOENT);
226 STATE (pid, (1 << STATE_ENOENT) | (1 << STATE_ZOMBIE));
228 registry_remove (pid);
234 void *(*child) (void *data, void *input);
239 static void *child_spawner (void *param_voidpointer, void *input)
241 struct child_spawner *param = param_voidpointer;
243 ssize_t inferior_size;
246 inferior = spawn (param->child, param->data, input, param->fd);
248 inferior_size = write (param->fd, &inferior, sizeof (inferior));
249 assert (inferior_size == sizeof (inferior));
250 i = close (param->fd);
253 waitpid (inferior, NULL, 0);
254 _exit (EXIT_SUCCESS);
261 void *(*func) (void *data, void *input);
265 static void *spawn_with_waiter (void *data, void *input)
268 struct child_spawner param_local;
272 ssize_t inferior_size;
275 struct spawner *param = data;
277 assert (data != NULL);
282 param_local.child = param->func;
283 param_local.data = param->data;
284 param_local.fd = pipefds[1];
285 waiter = spawn (child_spawner, ¶m_local, input, pipefds[0]);
287 i = close (pipefds[1]);
289 inferior_size = read (pipefds[0], &inferior, sizeof (inferior));
290 assert (inferior_size == sizeof (inferior));
291 buf_size = read (pipefds[0], &buf, sizeof (buf));
292 assert (buf_size == 0);
293 i = close (pipefds[0]);
296 registry_add (inferior);
298 return (void *) (unsigned long) inferior;
301 static void *spawn_without_waiter (void *data, void *input)
303 struct spawner *param = data;
305 return (void *) (unsigned long) spawn (param->func, param->data, input, -1);
308 static void body_spawner (void *(*child) (void *data, void *input), void *data,
312 struct attach_state_struct *attach_state;
315 assert (input == NULL);
317 /* Plain attach/detach. */
318 inferior = (unsigned long) (*child) (data, child_pause);
319 attach_state = attach_checked (inferior, 0);
320 assert (attach_state_stopped_get (attach_state) == 0);
321 detach_checked (attach_state);
324 /* Attach to a process stopped by standard kill(2). */
325 inferior = (unsigned long) (*child) (data, child_pause);
327 i = kill (inferior, SIGSTOP);
329 STATE (inferior, 1 << STATE_STOPPED);
330 attach_state = attach_checked (inferior, 0);
331 assert (attach_state_stopped_get (attach_state) == 1);
332 detach_checked (attach_state);
335 /* Attach to a process stopped by Linux specific tkill(2). */
336 inferior = (unsigned long) (*child) (data, child_pause);
338 i = tkill (inferior, SIGSTOP);
340 STATE (inferior, 1 << STATE_STOPPED);
341 attach_state = attach_checked (inferior, 0);
342 assert (attach_state_stopped_get (attach_state) == 1);
343 detach_checked (attach_state);
346 /* Attach to a stopped process with already pending SIGALRM. */
347 inferior = (unsigned long) (*child) (data, child_alrm);
348 STATE (inferior, 1 << STATE_SLEEPING);
350 i = tkill (inferior, SIGSTOP);
352 /* Wait till it gets stopped otherwise we may get STATE_ENOENT below. */
353 STATE (inferior, 1 << STATE_STOPPED);
355 i = tkill (inferior, SIGALRM);
357 STATE (inferior, 1 << STATE_STOPPED);
358 /* FIXME: SIGALRM did not get redelivered? */
360 attach_state = attach_checked (inferior, SIGALRM);
362 attach_state = attach_checked (inferior, 0);
364 assert (attach_state_stopped_get (attach_state) == 1);
365 detach_checked (attach_state);
366 STATE (inferior, 1 << STATE_STOPPED);
368 i = tkill (inferior, SIGCONT);
370 /* This is a race, we may not prove the successful SIGALRM delivery by it.
371 Rather recheck it below. */
372 STATE (inferior, 1 << STATE_RUNNING);
374 i = tkill (inferior, SIGSTOP);
376 /* Wait till it gets stopped otherwise we may get STATE_ENOENT below. */
377 STATE (inferior, 1 << STATE_STOPPED);
379 i = tkill (inferior, SIGUSR2);
381 STATE (inferior, 1 << STATE_STOPPED);
383 i = tkill (inferior, SIGCONT);
385 /* Not just STATE_ZOMBIE as we can get spawn with waiter. FIXME. */
386 STATE (inferior, (1 << STATE_ENOENT) | (1 << STATE_ZOMBIE) | (1 << STATE_DEAD));
387 /* We would fail on: murder (inferior); */
390 static void *pass (void *data, void *input)
392 struct spawner *param = data;
393 void (*input_func) (void) = input;
396 return (*param->func) (param->data, input);
398 assert (input_func != NULL);
404 static void *spawn_singlethreaded (void *data, void *input)
406 return pass (data, input);
409 static void *spawn_threaded_parent_start (void *arg)
417 static void *spawn_threaded_parent (void *data, void *input)
423 int (*pthread_create_pointer) (pthread_t *thread,
424 const pthread_attr_t *attr,
425 void *(*start_routine) (void *),
428 handle = dlopen ("libpthread.so.0", RTLD_LAZY);
429 assert (handle != NULL);
430 pthread_create_pointer = dlsym (handle, "pthread_create");
431 assert (pthread_create_pointer != NULL);
433 i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_parent_start,
436 retval = pass (data, input);
438 i = dlclose (handle);
444 struct spawn_threaded_child_start
451 static void *spawn_threaded_child_start (void *arg_voidpointer)
453 struct spawn_threaded_child_start *arg = arg_voidpointer;
457 retval = pass (arg->data, arg->input);
459 i = dlclose (arg->lib_handle);
465 static void *spawn_threaded_child (void *data, void *input)
469 struct spawn_threaded_child_start arg_local;
470 int (*pthread_create_pointer) (pthread_t *thread,
471 const pthread_attr_t *attr,
472 void *(*start_routine) (void *),
474 int (*pthread_join_pointer) (pthread_t thread, void **value_ptr);
476 arg_local.lib_handle = dlopen ("libpthread.so.0", RTLD_LAZY);
477 assert (arg_local.lib_handle != NULL);
478 pthread_create_pointer = dlsym (arg_local.lib_handle, "pthread_create");
479 assert (pthread_create_pointer != NULL);
480 pthread_join_pointer = dlsym (arg_local.lib_handle, "pthread_join");
481 assert (pthread_join_pointer != NULL);
483 arg_local.data = data;
484 arg_local.input = input;
485 i = (*pthread_create_pointer) (&thread, NULL, spawn_threaded_child_start,
488 i = (*pthread_join_pointer) (thread, NULL);
491 _exit (EXIT_SUCCESS);
494 static void body_maywaiter (void *(*child) (void *data, void *input),
495 void *data, void *input)
497 struct spawner param_local;
499 param_local.func = child;
500 param_local.data = data;
501 body_spawner (spawn_without_waiter, ¶m_local, NULL);
502 body_spawner (spawn_with_waiter, ¶m_local, NULL);
505 static volatile unsigned long loops = 0;
506 static volatile int loops_print = 0;
508 static void handler_sigusr1 (int signo)
510 assert (signo == SIGUSR1);
515 int main (int argc, char **argv)
520 #ifdef MEASURE_STATE_PERFORMANCE
521 STATE (1, 1 << STATE_ZOMBIE);
523 #endif /* MEASURE_STATE_PERFORMANCE */
527 else if (argc == 2 && strcmp (argv[1], "-l") == 0)
535 atexit (registry_atexit);
536 signal (SIGINT, registry_handler);
537 signal (SIGABRT, registry_handler);
539 signal (SIGUSR1, handler_sigusr1);
543 while (loops_print > 0)
545 printf ("%lu\n", loops);
549 spawned_threads_count = 0;
550 body_maywaiter (spawn_singlethreaded, NULL, NULL);
552 spawned_threads_count = 2;
553 body_maywaiter (spawn_threaded_parent, NULL, NULL);
555 spawned_threads_count = 2;
556 body_maywaiter (spawn_threaded_child, NULL, NULL);