From 21acf600ec5492bfdb5451443e4f2e20b70d1971 Mon Sep 17 00:00:00 2001 From: lace <> Date: Wed, 11 Apr 2007 20:57:31 +0000 Subject: [PATCH] Non-working threaded version. --- Makefile | 23 +++ debugger.c | 462 ++++++++++++++++++++++++++++++++++++++++++ debugger.c-childhandler.patch | 57 ++++++ debugger.h | 10 + testsuite.c | 425 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 977 insertions(+) create mode 100644 Makefile create mode 100644 debugger.c create mode 100644 debugger.c-childhandler.patch create mode 100644 debugger.h create mode 100644 testsuite.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8929e34 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +CC=gcc -ggdb3 -Wall -Werror + +all: debugger check + +debugger: debugger.c debugger.h + $(CC) -o $@ $< -lthread_db -pthread -ldw + +testsuite: testsuite.c debugger.c debugger.h + $(CC) -o $@ $< -lthread_db -pthread -ldw + +.PHONY: check +check: testsuite + ./testsuite + @echo OK + +.PHONY: still +still: + FIXME ./testsuite && false + +.PHONY: clean +clean: + $(RM) debugger testsuite + diff --git a/debugger.c b/debugger.c new file mode 100644 index 0000000..f880038 --- /dev/null +++ b/debugger.c @@ -0,0 +1,462 @@ +/* Copyright 2007, Red Hat Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugger.h" + + +#define USLEEP (1000000 / 2) + + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) + +void delay (void) +{ +#ifdef USLEEP + int i; + + i = usleep (USLEEP); + assert (i == 0); +#endif +} + +static __attribute__((__noreturn__)) void crash (void) +{ +#if 0 + void *array[0x100]; + int count; +#endif + char command[256]; + + fputs (">>> CRASH START\n", stderr); +#if 0 + count = backtrace (array, ARRAY_SIZE (array)); + backtrace_symbols_fd (array, count, STDERR_FILENO); +#endif + snprintf (command, sizeof (command), "echo -e \"bt\\nquit\"" + " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d" + " /proc/%d/exe %d >> CRASH FINISH\n", stderr); + abort (); +} + +enum state + { + /* Separate the valid `1 << enum state' and `enum state' ranges. */ + STATE_FIRST = 4, + STATE_ENOENT, + STATE_SLEEPING, + STATE_RUNNING, + STATE_STOPPED, + STATE_PTRACED, + STATE_ZOMBIE, + STATE_LAST + }; + +static const char *state_to_name (enum state state) +{ + switch (state) + { + case STATE_ENOENT: return "STATE_ENOENT"; + case STATE_SLEEPING: return "STATE_SLEEPING"; + case STATE_RUNNING: return "STATE_RUNNING"; + case STATE_STOPPED: return "STATE_STOPPED"; + case STATE_PTRACED: return "STATE_PTRACED"; + case STATE_ZOMBIE: return "STATE_ZOMBIE"; + default: crash (); + } + /* NOTREACHED */ + crash (); +} + +#define STATE(pid, expect_mask) state ((pid), (expect_mask), #expect_mask ) + +static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mask_string) +{ + char status_name[32]; + char line[LINE_MAX]; + FILE *f; + enum state found; + + delay (); + + /* Sanity check `1 << enum state' was not misplaced with `enum state'. */ + assert (1 << (STATE_FIRST + 1) >= STATE_LAST); + assert (expect_mask != 0); +#define MASK_UNDER_EXCLUSIVE(bit) ((1 << (bit)) - 1) +#define MASK_ABOVE_INCLUSIVE(bit) (~MASK_UNDER_EXCLUSIVE (bit)) + assert ((expect_mask & MASK_UNDER_EXCLUSIVE (STATE_FIRST + 1)) == 0); + assert ((expect_mask & MASK_ABOVE_INCLUSIVE (STATE_LAST)) == 0); +#undef MASK_ABOVE_INCLUSIVE +#undef MASK_UNDER_EXCLUSIVE + + snprintf (status_name, sizeof (status_name), "/proc/%d/status", (int) pid); + f = fopen (status_name, "r"); + if (f == NULL && errno == ENOENT) + found = STATE_ENOENT; + else if (f == NULL) + crash (); + else + { + int i; + + found = STATE_ENOENT; + while (errno = 0, fgets (line, sizeof (line), f) != NULL) + { + const char *const string = "State:\t"; + const size_t length = sizeof "State:\t" - 1; + + if (strncmp (line, string, length) != 0) + continue; + if (strcmp (line + length, "S (sleeping)\n") == 0) + found = STATE_SLEEPING; + else if (strcmp (line + length, "R (running)\n") == 0) + found = STATE_RUNNING; + else if (strcmp (line + length, "T (stopped)\n") == 0) + found = STATE_STOPPED; + else if (strcmp (line + length, "T (tracing stop)\n") == 0) + found = STATE_PTRACED; + else if (strcmp (line + length, "Z (zombie)\n") == 0) + found = STATE_ZOMBIE; + else + { + fprintf (stderr, "Found an unknown state: %s", line + length); + crash (); + } + } + assert (found != STATE_ENOENT); + i = fclose (f); + assert (i == 0); + } + if (((1 << found) & expect_mask) == 0) + { + fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n", + (int) pid, state_to_name (found), expect_mask_string); + crash (); + } + return found; +} + +/* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH. */ +int attach_redelivered; + +static int attach_single (pid_t pid) +{ + int i; + int status; + int stopped; + + attach_redelivered = 0; + + delay (); + +#if 1 + stopped = (STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) + | (1 << STATE_STOPPED)) == STATE_STOPPED); +#endif + + i = ptrace (PTRACE_ATTACH, pid, NULL, NULL); + assert (i == 0); + + /* FIXME: Why it does not work? + Disable also the STATE () call above. */ +#if 0 + delay(); + + i = ptrace (PTRACE_CONT, pid, (void *) 1, (void *) SIGSTOP); + /* `STOPPED == 1' may be false, even if the process was not stopped. */ + if (i == 0) + stopped = 1; + else if (errno == ESRCH) + stopped = 0; + else + crash (); +#endif + + for (;;) + { + delay (); + + i = waitpid (pid, &status, 0); + assert (i == pid); + if (!WIFSTOPPED (status)) + { + /* Process may have exited. */ + fprintf (stderr, "PID %d waitpid(2) status %d\n", (int) pid, + status); + exit (EXIT_FAILURE); + } + if (WSTOPSIG (status) == SIGSTOP) + break; + + if (attach_redelivered == 0) + attach_redelivered = WSTOPSIG (status); + else + attach_redelivered = -1; + + delay (); + + /* Re-deliver the signal received before SIGSTOP. + It happens with about only 1:200000 probability. */ + i = ptrace (PTRACE_CONT, pid, (void *) 1, + (void *) (unsigned long) WSTOPSIG (status)); + assert (i == 0); + } + + return stopped; +} + +struct ps_prochandle_module + { + struct ps_prochandle_module *next; + Dwfl_Module *module; + }; + +struct ps_prochandle + { + pid_t pid; + Dwfl *dwfl; + struct ps_prochandle_module *module_list; + }; + +static int get_dwfl_find_debuginfo (Dwfl_Module *mod, void **userdata, + const char *modname, GElf_Addr base, + const char *file_name, + const char *debuglink_file, + GElf_Word debuglink_crc, + char **debuginfo_file_name) +{ + printf("modname=<%s>,file_name=<%s>\n",modname,file_name); + return dwfl_standard_find_debuginfo (mod, userdata, modname, base, file_name, + debuglink_file, debuglink_crc, + debuginfo_file_name); +} + +static Dwfl *get_dwfl (struct ps_prochandle *proc_handle) +{ + static char *debuginfo_path; + + static const Dwfl_Callbacks proc_callbacks = + { + .find_debuginfo = get_dwfl_find_debuginfo, + .debuginfo_path = &debuginfo_path, + + .find_elf = dwfl_linux_proc_find_elf, + }; + + if (proc_handle->dwfl == NULL) + { + proc_handle->dwfl = dwfl_begin (&proc_callbacks); + assert (proc_handle->dwfl != NULL); + + if (dwfl_linux_proc_report (proc_handle->dwfl, proc_handle->pid) != 0 + || dwfl_report_end (proc_handle->dwfl, NULL, NULL) != 0) + { + dwfl_end (proc_handle->dwfl); + proc_handle->dwfl = NULL; + crash (); + } + } + return proc_handle->dwfl; +} + +/* Functions in this interface return one of these status codes. */ +typedef enum +{ + PS_OK, /* Generic "call succeeded". */ + PS_ERR, /* Generic error. */ + PS_BADPID, /* Bad process handle. */ + PS_BADLID, /* Bad LWP identifier. */ + PS_BADADDR, /* Bad address. */ + PS_NOSYM, /* Could not find given symbol. */ + PS_NOFREGS /* FPU register set not available for given LWP. */ +} ps_err_e; + +ps_err_e ps_pdread (struct ps_prochandle *proc_handle, psaddr_t addr, + void *buffer, size_t length) +{ + crash (); +} + +ps_err_e ps_pdwrite (struct ps_prochandle *proc_handle, psaddr_t addr, + const void *buffer, size_t length) +{ + crash (); +} + +ps_err_e ps_lgetregs (struct ps_prochandle *proc_handle, lwpid_t lwp, + prgregset_t regs) +{ + crash (); +} + +ps_err_e ps_lsetregs (struct ps_prochandle *proc_handle, lwpid_t lwp, + const prgregset_t regs) +{ + crash (); +} + +ps_err_e ps_lgetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp, + prfpregset_t *fpregs) +{ + crash (); +} + +ps_err_e ps_lsetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp, + const prfpregset_t *fpregs) +{ + crash (); +} + +/* Return the PID of the process. */ +pid_t ps_getpid (struct ps_prochandle *proc_handle) +{ + crash (); +} + +/* Look up the named symbol in the named DSO in the symbol tables + associated with the process being debugged, filling in *SYM_ADDR + with the corresponding run-time address. */ +ps_err_e ps_pglobal_lookup (struct ps_prochandle *proc_handle, + const char *object_name, const char *sym_name, + psaddr_t *sym_addr) +{ + Dwfl *dwfl = get_dwfl (proc_handle); + dwfl = dwfl; + dwfl_addrmodule (dwfl, 0x3571d00000); + +#if 0 + Elf *elf = FIXME; + Elf_Scn *scn = NULL; + int sym_count, ndx; + + sym_count = dwfl_module_getsymtab (Dwfl_Module *mod); + assert (sym_count >= 0); + for (ndx = 0; ndx < sym_count; ndx++) + { + GElf_Sym sym; + const char *name_got; + + name_got = dwfl_module_getsym (Dwfl_Module *mod, ndx, &sym, NULL); + assert (name_got != NULL); + if (strcmp (name_got, sym_name) == 0) + break; + } + assert (ndx < sym_count); + +extern Elf *dwarf_getelf (Dwarf *dwarf); + + + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* Handle the section if it is a symbol table. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL && shdr->sh_type == SHT_SYMTAB) + handle_symtab (ebl, scn, shdr); + } + + + for (modulei = 0; modulei < dwfl->nmodules; modulei++) + { + Dwfl_Module *m = dwfl->modules[modulei]; + + puts (m->name); + } +#if 0 + if (strcmp (sym_name, "nptl_version") == 0) + { + extern const char nptl_version[]; + *sym_addr = (psaddr_t) nptl_version; + return PS_OK; + } +#endif +#endif + crash (); +} + +int attach (pid_t pid) +{ + int stopped; + td_err_e err; + td_thragent_t *thread_agent; + struct ps_prochandle proc_handle_local; + static int thread_db_init = 0; + + stopped = attach_single (pid); + + if (thread_db_init == 0) + { + err = td_init (); + assert (err == TD_OK); + thread_db_init = 1; + } + + proc_handle_local.pid = pid; + proc_handle_local.dwfl = NULL; + err = td_ta_new (&proc_handle_local, &thread_agent); + if (proc_handle_local.dwfl != NULL) + dwfl_end (proc_handle_local.dwfl); + if (err == TD_NOLIBTHREAD) + { + delay (); + return stopped; + } + assert (err == TD_OK); + + assert (0); +} + +void detach (pid_t pid, int stopped) +{ + int i; + + delay (); + + i = ptrace (PTRACE_DETACH, pid, NULL, + (void *) (unsigned long) (stopped ? SIGSTOP : 0)); + assert (i == 0); + + delay (); +} + +#ifndef LIBRARY + +int main (int argc, char **argv) +{ + pid_t pid; + int stopped; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s \n", argv[0]); + exit (EXIT_FAILURE); + } + + pid = atoi (argv[1]); + + stopped = attach (pid); + + detach (pid, stopped); + + return EXIT_SUCCESS; +} + +#endif /* !LIBRARY */ diff --git a/debugger.c-childhandler.patch b/debugger.c-childhandler.patch new file mode 100644 index 0000000..4f07a79 --- /dev/null +++ b/debugger.c-childhandler.patch @@ -0,0 +1,57 @@ +--- debugger.c 2007-04-07 10:17:12.000000000 +0200 ++++ debugger.c-childhandler 2007-04-07 10:16:39.000000000 +0200 +@@ -26,6 +26,13 @@ + #endif + } + ++static int attach_handler_count; ++ ++static void attach_handler (int signo) ++{ ++ attach_handler_count++; ++} ++ + /* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH. */ + int attach_redelivered; + +@@ -40,6 +47,17 @@ + + delay (); + ++ attach_handler_count = 0; ++ /* Fill SA_SIGACTION _before_ SA_HANDLER as they may be in union! */ ++ act.sa_sigaction = NULL; ++ act.sa_handler = attach_handler; ++ i = sigemptyset (&act.sa_mask); ++ assert (i == 0); ++ act.sa_flags = ++ act.sa_restorer = NULL; ++ ++ attach_handler_orig = signal (SIGCHLD, attach_handler); ++ + i = ptrace (PTRACE_ATTACH, pid, NULL, NULL); + assert (i == 0); + +@@ -84,6 +102,22 @@ + assert (i == 0); + } + ++ attach_handler_orig = signal (SIGCHLD, attach_handler_orig); ++ assert (attach_handler_orig == attach_handler); ++ ++ if (stopped == 1 && attach_handler_count == 2) ++ stopped = 0; ++ else if (stopped == 1 && attach_handler_count == 1) ++ stopped = 1; ++ else if (stopped == 0 && attach_handler_count == 1) ++ stopped = 0; ++ else ++ { ++ fprintf (stderr, "stopped = %d, attach_handler_count = %d\n", stopped, ++ attach_handler_count); ++ abort (); ++ } ++ + delay (); + + return stopped; diff --git a/debugger.h b/debugger.h new file mode 100644 index 0000000..e64f2c6 --- /dev/null +++ b/debugger.h @@ -0,0 +1,10 @@ +/* Copyright 2007, Red Hat Inc. */ + +#include + + +extern int attach (pid_t pid); +extern void detach (pid_t pid, int stopped); + +extern int attach_redelivered; +extern void delay (void); diff --git a/testsuite.c b/testsuite.c new file mode 100644 index 0000000..fb07f45 --- /dev/null +++ b/testsuite.c @@ -0,0 +1,425 @@ +/* Copyright 2007, Red Hat Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugger.h" + +#define LIBRARY 1 +#include "debugger.c" + + +_syscall2(int, tkill, int, tid, int, sig) +int tkill(int tid, int sig); + + +static int attach_checked (pid_t pid, int redelivered_expect) +{ + int stopped; + + STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING) | (1 << STATE_STOPPED)); + stopped = attach (pid); + if (attach_redelivered != redelivered_expect) + { + fprintf (stderr, "Expecting redelivery of %d but found %d\n", + redelivered_expect, attach_redelivered); + abort (); + } + /* FIXME: Why also STATE_STOPPED? */ + STATE (pid, (1 << STATE_PTRACED) | (1 << STATE_STOPPED)); + return stopped; +} + +static void detach_checked (pid_t pid, int stopped) +{ + /* 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 registry + { + struct registry *next; + pid_t pid; + }; +struct registry *registry_list; + +static void registry_add (pid_t pid) +{ + struct registry *new; + + new = malloc (sizeof (*new)); + assert (new != NULL); + new->pid = pid; + new->next = registry_list; + registry_list = new; +} + +static void registry_remove (pid_t pid) +{ + struct registry **iter_pointer; + + for (iter_pointer = ®istry_list; *iter_pointer != NULL; + iter_pointer = &(*iter_pointer)->next) + { + struct registry *found = *iter_pointer; + if (found->pid != pid) + continue; + + *iter_pointer = found->next; + free (found); + return; + } + abort (); +} + +static void registry_atexit (void) +{ + struct registry *iter; + + for (iter = registry_list; iter != NULL; iter = iter->next) + kill (iter->pid, SIGKILL); +} + +static void registry_handler (int signo) +{ + signal (signo, SIG_DFL); + registry_atexit (); + raise (signo); +} + +static void child_pause (void) +{ + for (;;) + pause (); + /* NOTREACHED */ + abort (); +} + +static void child_alrm_handler (int signo) +{ + assert (signo == SIGALRM); + raise (SIGALRM); +} + +static 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); + +#if 0 + i = sigprocmask (SIG_BLOCK, NULL, &oldset); + assert (i == 0); + printf ("sigprocmask () -> sigismember (SIGALRM) == %d\n", sigismember (&oldset, SIGALRM)); +#endif + + for (;;) + pause (); + /* NOTREACHED */ + abort (); +} + +static pid_t spawn (void *(*child) (void *data, void *input), void *data, + void *input, int fd_close) +{ + pid_t pid; + + pid = fork(); + switch (pid) + { + case -1: + perror ("fork()"); + exit (EXIT_FAILURE); + /* NOTREACHED */ + case 0: + if (fd_close != -1) + { + int i; + + i = close (fd_close); + assert (i == 0); + } + child (data, input); + /* NOTREACHED */ + abort (); + default:; + /* PASSTHRU */ + } + /* Parent. */ + + registry_add (pid); + STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)); + return pid; +} + +static void murder (pid_t pid) +{ + int i; + pid_t pid_got; + int status; + + if (pid == 0) + return; + + i = kill (pid, SIGKILL); + assert (i == 0); + + pid_got = waitpid (pid, &status, 0); + if (!(pid_got == -1 && errno == ECHILD)) + { + assert (pid_got == pid); + assert ((WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL) + || (WIFEXITED (status) && WEXITSTATUS (status) == 0)); + STATE (pid, 1 << STATE_ENOENT); + } + else + STATE (pid, (1 << STATE_ENOENT) | (1 << STATE_ZOMBIE)); + + registry_remove (pid); +} + + +struct child_spawner + { + void *(*child) (void *data, void *input); + void *data; + int fd; + }; + +static void *child_spawner (void *param_voidpointer, void *input) +{ + struct child_spawner *param = param_voidpointer; + pid_t inferior; + ssize_t inferior_size; + int i; + + inferior = spawn (param->child, param->data, input, param->fd); + + inferior_size = write (param->fd, &inferior, sizeof (inferior)); + assert (inferior_size == sizeof (inferior)); + i = close (param->fd); + assert (i == 0); + + waitpid (inferior, NULL, 0); + _exit (EXIT_SUCCESS); + /* NOTREACHED */ + abort (); +} + +struct spawner + { + void *(*func) (void *data, void *input); + void *data; + }; + +static void *spawn_with_waiter (void *data, void *input) +{ + pid_t waiter; + struct child_spawner param_local; + int pipefds[2]; + int i; + pid_t inferior; + ssize_t inferior_size; + unsigned char buf; + ssize_t buf_size; + struct spawner *param = data; + + assert (data != NULL); + + i = pipe (pipefds); + assert (i == 0); + + param_local.child = param->func; + param_local.data = param->data; + param_local.fd = pipefds[1]; + waiter = spawn (child_spawner, ¶m_local, input, pipefds[0]); + + i = close (pipefds[1]); + assert (i == 0); + inferior_size = read (pipefds[0], &inferior, sizeof (inferior)); + assert (inferior_size == sizeof (inferior)); + buf_size = read (pipefds[0], &buf, sizeof (buf)); + assert (buf_size == 0); + i = close (pipefds[0]); + assert (i == 0); + + registry_add (inferior); + + return (void *) (unsigned long) inferior; +} + +static void *spawn_without_waiter (void *data, void *input) +{ + struct spawner *param = data; + + return (void *) (unsigned long) spawn (param->func, param->data, input, -1); +} + +static void body_spawner (void *(*child) (void *data, void *input), void *data, + void *input) +{ + pid_t inferior; + int stopped; + int i; + + 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); + murder (inferior); + + /* Attach to a process stopped by standard kill(2). */ + inferior = (unsigned long) (*child) (data, child_pause); + delay (); + i = kill (inferior, SIGSTOP); + assert (i == 0); + STATE (inferior, 1 << STATE_STOPPED); + stopped = attach_checked (inferior, 0); + assert (stopped == 1); + detach_checked (inferior, stopped); + murder (inferior); + + /* Attach to a process stopped by Linux specific tkill(2). */ + inferior = (unsigned long) (*child) (data, child_pause); + delay (); + i = tkill (inferior, SIGSTOP); + assert (i == 0); + STATE (inferior, 1 << STATE_STOPPED); + stopped = attach_checked (inferior, 0); + assert (stopped == 1); + detach_checked (inferior, stopped); + murder (inferior); + + /* Attach to a stopped process with already pending SIGALRM. */ + inferior = (unsigned long) (*child) (data, child_alrm); + STATE (inferior, 1 << STATE_SLEEPING); + delay (); + i = tkill (inferior, SIGSTOP); + assert (i == 0); + delay (); + i = tkill (inferior, SIGALRM); + assert (i == 0); + STATE (inferior, 1 << STATE_STOPPED); + /* FIXME: SIGALRM did not get redelivered? */ +#if 0 + stopped = attach_checked (inferior, SIGALRM); +#else + stopped = attach_checked (inferior, 0); +#endif + assert (stopped == 1); + detach_checked (inferior, stopped); + STATE (inferior, 1 << STATE_STOPPED); + delay (); + i = tkill (inferior, SIGCONT); + assert (i == 0); + STATE (inferior, 1 << STATE_RUNNING); + murder (inferior); +} + +static void *pass (void *data, void *input) +{ + struct spawner *param = data; + void (*input_func) (void) = input; + + if (param != NULL) + return (*param->func) (param->data, input); + + assert (input_func != NULL); + (*input_func) (); + /* NOTREACHED */ + abort (); +} + +static void *spawn_singlethreaded (void *data, void *input) +{ + return pass (data, input); +} + +static void *spawn_threaded_parent_start (void *arg) +{ + for (;;) + pause (); + /* NOTREACHED */ + abort (); +} + +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 (i == 0); + return pass (data, input); +} + +struct spawn_threaded_child_start + { + void *data; + void *input; + }; + +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) +{ + pthread_t thread; + int i; + struct spawn_threaded_child_start arg_local; + + arg_local.data = data; + arg_local.input = input; + i = pthread_create (&thread, NULL, spawn_threaded_child_start, &arg_local); + assert (i == 0); + i = pthread_join (thread, NULL); + assert (i == 0); + + _exit (EXIT_SUCCESS); +} + +static void body_maywaiter (void *(*child) (void *data, void *input), + void *data, void *input) +{ + struct spawner param_local; + + param_local.func = child; + param_local.data = data; + body_spawner (spawn_without_waiter, ¶m_local, NULL); + body_spawner (spawn_with_waiter, ¶m_local, NULL); +} + +int main (int argc, char **argv) +{ + atexit (registry_atexit); + signal (SIGINT, registry_handler); + signal (SIGABRT, registry_handler); + + body_maywaiter (spawn_singlethreaded, NULL, NULL); + body_maywaiter (spawn_threaded_parent, NULL, NULL); + body_maywaiter (spawn_threaded_child, NULL, NULL); + + return EXIT_SUCCESS; +} -- 1.8.3.1