--- /dev/null
+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
+
--- /dev/null
+/* Copyright 2007, Red Hat Inc. */
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <string.h>
+#include <execinfo.h>
+#include <thread_db.h>
+#include <elfutils/libdwfl.h>
+
+#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 </dev/null;rm -f /tmp/debugger.%d",
+ (int) getpid(), (int) getpid(), (int) getpid(), (int) getpid(),
+ (int) getpid());
+ system (command);
+ fputs (">>> 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 <PID>\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+
+ pid = atoi (argv[1]);
+
+ stopped = attach (pid);
+
+ detach (pid, stopped);
+
+ return EXIT_SUCCESS;
+}
+
+#endif /* !LIBRARY */
--- /dev/null
+--- 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;
--- /dev/null
+/* Copyright 2007, Red Hat Inc. */
+
+#include <sys/types.h>
+
+
+extern int attach (pid_t pid);
+extern void detach (pid_t pid, int stopped);
+
+extern int attach_redelivered;
+extern void delay (void);
--- /dev/null
+/* Copyright 2007, Red Hat Inc. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <linux/unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <pthread.h>
+
+#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;
+}