Basic support for tracing vfork

- allow even individual tasks to have their event handlers.  These are
  called in precedence to the thread group event handlers.
- distinguish CLONE and VFORK events
- add methods for dynamic changes of leader
- add new process status ps_sleeping
diff --git a/common.h b/common.h
index 1af2ac6..9989b29 100644
--- a/common.h
+++ b/common.h
@@ -255,6 +255,7 @@
 	ps_invalid,	/* Failure.  */
 	ps_stop,	/* Job-control stop.  */
 	ps_tracing_stop,
+	ps_sleeping,
 	ps_zombie,
 	ps_other,	/* Necessary other states can be added as needed.  */
 };
@@ -268,6 +269,7 @@
 extern Process * pid2proc(pid_t pid);
 extern void add_process(Process * proc);
 extern void remove_process(Process * proc);
+extern void change_process_leader(Process * proc, Process * leader);
 extern Process *each_process(Process * start,
 			     enum pcb_status (* cb)(Process * proc, void * data),
 			     void * data);
@@ -338,6 +340,7 @@
 extern void continue_process(pid_t pid);
 extern void continue_after_signal(pid_t pid, int signum);
 extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp);
+extern void continue_after_vfork(Process * proc);
 extern void ltrace_exiting(void);
 extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
 extern void save_register_args(enum tof type, Process * proc);
diff --git a/handle_event.c b/handle_event.c
index 0aa40f7..86788e0 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -35,6 +35,18 @@
 static char * sysname(Process *proc, int sysnum);
 static char * arch_sysname(Process *proc, int sysnum);
 
+static Event *
+call_handler(Process * proc, Event * event)
+{
+	assert(proc != NULL);
+
+	Event_Handler * handler = proc->event_handler;
+	if (handler == NULL)
+		return event;
+
+	return (*handler->on_event) (handler, event);
+}
+
 void
 handle_event(Event *event) {
 	if (exiting == 1) {
@@ -44,15 +56,22 @@
 	}
 	debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)",
 	      event->proc ? event->proc->pid : -1, event->type);
-	/* If the thread group defines an overriding event handler,
-	   give it a chance to kick in.  */
-	if (event->proc != NULL
-	    && event->proc->leader != NULL) {
-		Event_Handler * handler = event->proc->leader->event_handler;
-		if (handler != NULL) {
-			event = (*handler->on_event) (handler, event);
+
+	/* If the thread group or an individual task define an
+	   overriding event handler, give them a chance to kick in.
+	   We will end up calling both handlers, if the first one
+	   doesn't sink the event.  */
+	if (event->proc != NULL) {
+		event = call_handler(event->proc, event);
+		if (event == NULL)
+			/* It was handled.  */
+			return;
+
+		/* Note: the previous handler has a chance to alter
+		 * the event.  */
+		if (event->proc->leader != NULL) {
+			event = call_handler(event->proc->leader, event);
 			if (event == NULL)
-				/* It was handled.  */
 				return;
 		}
 	}
@@ -102,6 +121,7 @@
 		handle_arch_sysret(event);
 		return;
 	case EVENT_CLONE:
+	case EVENT_VFORK:
 		debug(1, "event: clone (%u)", event->e_un.newpid);
 		handle_clone(event);
 		return;
@@ -239,7 +259,11 @@
 		p->state = STATE_BEING_CREATED;
 		add_process(p);
 	}
-	continue_process(event->proc->pid);
+
+	if (event->type == EVENT_VFORK)
+		continue_after_vfork(p);
+	else
+		continue_process(event->proc->pid);
 }
 
 static void
@@ -253,8 +277,6 @@
 		pending_new_insert(event->e_un.newpid);
 	} else {
 		assert(proc->state == STATE_BEING_CREATED);
-		if (proc->event_handler != NULL)
-			destroy_event_handler(proc);
 		if (options.follow) {
 			proc->state = STATE_ATTACHED;
 		} else {
diff --git a/ltrace.h b/ltrace.h
index 0ff4572..194704d 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -9,6 +9,7 @@
 	EVENT_ARCH_SYSCALL,
 	EVENT_ARCH_SYSRET,
 	EVENT_CLONE,
+	EVENT_VFORK,
 	EVENT_EXEC,
 	EVENT_BREAKPOINT,
 	EVENT_LIBCALL,
diff --git a/proc.c b/proc.c
index 0425e09..f4d3396 100644
--- a/proc.c
+++ b/proc.c
@@ -154,9 +154,30 @@
 	return each_process(NULL, &find_proc, (void *)(uintptr_t)pid);
 }
 
-
 static Process * list_of_processes = NULL;
 
+static void
+unlist_process(Process * proc)
+{
+	Process *tmp;
+
+	if (list_of_processes == proc) {
+		list_of_processes = list_of_processes->next;
+		return;
+	}
+
+	for (tmp = list_of_processes; ; tmp = tmp->next) {
+		/* If the following assert fails, the process wasn't
+		 * in the list.  */
+		assert(tmp->next != NULL);
+
+		if (tmp->next == proc) {
+			tmp->next = tmp->next->next;
+			return;
+		}
+	}
+}
+
 Process *
 each_process(Process * proc,
 	     enum pcb_status (* cb)(Process * proc, void * data),
@@ -213,6 +234,23 @@
 	*leaderp = proc;
 }
 
+void
+change_process_leader(Process * proc, Process * leader)
+{
+	Process ** leaderp = &list_of_processes;
+	if (proc->leader == leader)
+		return;
+
+	assert(leader != NULL);
+	unlist_process(proc);
+	if (proc != leader)
+		leaderp = &leader->next;
+
+	proc->leader = leader;
+	proc->next = *leaderp;
+	*leaderp = proc;
+}
+
 static enum pcb_status
 clear_leader(Process * proc, void * data)
 {
@@ -242,31 +280,14 @@
 void
 remove_process(Process *proc)
 {
-	Process *tmp, *tmp2;
-
 	debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid);
 
 	if (proc->leader == proc)
 		each_task(proc, &clear_leader, NULL);
 
-	if (list_of_processes == proc) {
-		tmp = list_of_processes;
-		list_of_processes = list_of_processes->next;
-		delete_events_for(tmp);
-		free(tmp);
-		return;
-	}
-	tmp = list_of_processes;
-	while (tmp->next) {
-		if (tmp->next == proc) {
-			tmp2 = tmp->next;
-			tmp->next = tmp->next->next;
-			delete_events_for(tmp2);
-			free(tmp2);
-			return;
-		}
-		tmp = tmp->next;
-	}
+	unlist_process(proc);
+	delete_events_for(proc);
+	free(proc);
 }
 
 void
@@ -283,7 +304,8 @@
 	Event_Handler * handler = proc->event_handler;
 	debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler);
 	assert(handler != NULL);
-	handler->destroy(handler);
+	if (handler->destroy != NULL)
+		handler->destroy(handler);
 	free(handler);
 	proc->event_handler = NULL;
 }
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index 8a79583..0685342 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -240,13 +240,20 @@
 			if (errno != 0)
 				perror("syscall_p");
 	}
-	if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) {
-		unsigned long data;
-		ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data);
-		event.type = EVENT_CLONE;
-		event.e_un.newpid = data;
-		debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data);
-		return &event;
+	if (WIFSTOPPED(status)) {
+		int what = status >> 16;
+		if (what == PTRACE_EVENT_VFORK
+		    || what == PTRACE_EVENT_FORK
+		    || what == PTRACE_EVENT_CLONE) {
+			unsigned long data;
+			event.type = what == PTRACE_EVENT_VFORK
+				? EVENT_VFORK : EVENT_CLONE;
+			ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data);
+			event.e_un.newpid = data;
+			debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d",
+			      pid, (int)data);
+			return &event;
+		}
 	}
 	if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) {
 		event.type = EVENT_EXEC;
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index e3b71e5..a99593c 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -148,7 +148,7 @@
 	switch (c) {
 	case 'Z': RETURN(ps_zombie);
 	case 't': RETURN(ps_tracing_stop);
-	case 'T': {
+	case 'T':
 		/* This can be either "T (stopped)" or, for older
 		 * kernels, "T (tracing stop)".  */
 		if (!strcmp(status, "T (stopped)\n"))
@@ -161,7 +161,8 @@
 			RETURN(ps_stop); /* Some sort of stop
 					  * anyway.  */
 		}
-	}
+	case 'D':
+	case 'S': RETURN(ps_sleeping);
 	}
 
 	RETURN(ps_other);
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index f8a1779..fef81df 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -164,9 +164,10 @@
 struct pid_task {
 	pid_t pid;	/* This may be 0 for tasks that exited
 			 * mid-handling.  */
-	int sigstopped;
-	int got_event;
-	int delivered;
+	int sigstopped : 1;
+	int got_event : 1;
+	int delivered : 1;
+	int vforked : 1;
 } * pids;
 
 struct pid_set {
@@ -213,23 +214,6 @@
 	struct pid_set pids;
 };
 
-static enum pcb_status
-task_stopped(Process * task, void * data)
-{
-	/* If the task is already stopped, don't worry about it.
-	 * Likewise if it managed to become a zombie or terminate in
-	 * the meantime.  This can happen when the whole thread group
-	 * is terminating.  */
-	switch (process_status(task->pid)) {
-	case ps_invalid:
-	case ps_tracing_stop:
-	case ps_zombie:
-		return pcb_cont;
-	default:
-		return pcb_stop;
-	}
-}
-
 static struct pid_task *
 get_task_info(struct pid_set * pids, pid_t pid)
 {
@@ -261,6 +245,57 @@
 }
 
 static enum pcb_status
+task_stopped(Process * task, void * data)
+{
+	enum process_status st = process_status(task->pid);
+	if (data != NULL)
+		*(enum process_status *)data = st;
+
+	/* If the task is already stopped, don't worry about it.
+	 * Likewise if it managed to become a zombie or terminate in
+	 * the meantime.  This can happen when the whole thread group
+	 * is terminating.  */
+	switch (st) {
+	case ps_invalid:
+	case ps_tracing_stop:
+	case ps_zombie:
+		return pcb_cont;
+	default:
+		return pcb_stop;
+	}
+}
+
+/* Task is blocked if it's stopped, or if it's a vfork parent.  */
+static enum pcb_status
+task_blocked(Process * task, void * data)
+{
+	struct pid_set * pids = data;
+	struct pid_task * task_info = get_task_info(pids, task->pid);
+	if (task_info != NULL
+	    && task_info->vforked)
+		return pcb_cont;
+
+	return task_stopped(task, NULL);
+}
+
+static Event * process_vfork_on_event(Event_Handler * super, Event * event);
+
+static enum pcb_status
+task_vforked(Process * task, void * data)
+{
+	if (task->event_handler != NULL
+	    && task->event_handler->on_event == &process_vfork_on_event)
+		return pcb_stop;
+	return pcb_cont;
+}
+
+static int
+is_vfork_parent(Process * task)
+{
+	return each_task(task->leader, &task_vforked, NULL) != NULL;
+}
+
+static enum pcb_status
 send_sigstop(Process * task, void * data)
 {
 	Process * leader = task->leader;
@@ -283,9 +318,11 @@
 		return pcb_cont;
 
 	/* Don't bother sending SIGSTOP if we are already stopped, or
-	 * if we sent the SIGSTOP already, which happens when we
-	 * inherit the handler from breakpoint re-enablement.  */
-	if (task_stopped(task, NULL) == pcb_cont)
+	 * if we sent the SIGSTOP already, which happens when we are
+	 * handling "onexit" and inherited the handler from breakpoint
+	 * re-enablement.  */
+	enum process_status st;
+	if (task_stopped(task, &st) == pcb_cont)
 		return pcb_cont;
 	if (task_info->sigstopped) {
 		if (!task_info->delivered)
@@ -293,6 +330,16 @@
 		task_info->delivered = 0;
 	}
 
+	/* Also don't attempt to stop the process if it's a parent of
+	 * vforked process.  We set up event handler specially to hint
+	 * us.  In that case parent is in D state, which we use to
+	 * weed out unnecessary looping.  */
+	if (st == ps_sleeping
+	    && is_vfork_parent (task)) {
+		task_info->vforked = 1;
+		return pcb_cont;
+	}
+
 	if (task_kill(task->pid, SIGSTOP) >= 0) {
 		debug(DEBUG_PROCESS, "send SIGSTOP to %d", task->pid);
 		task_info->sigstopped = 1;
@@ -536,7 +583,7 @@
 	switch (state) {
 	case psh_stopping:
 		/* If everyone is stopped, singlestep.  */
-		if (each_task(leader, &task_stopped, NULL) == NULL) {
+		if (each_task(leader, &task_blocked, &self->pids) == NULL) {
 			debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d",
 			      teb->pid);
 			if (sbp->enabled)
@@ -742,6 +789,86 @@
 	return 0;
 }
 
+/*
+ * When the traced process vforks, it's suspended until the child
+ * process calls _exit or exec*.  In the meantime, the two share the
+ * address space.
+ *
+ * The child process should only ever call _exit or exec*, but we
+ * can't count on that (it's not the role of ltrace to policy, but to
+ * observe).  In any case, we will _at least_ have to deal with
+ * removal of vfork return breakpoint (which we have to smuggle back
+ * in, so that the parent can see it, too), and introduction of exec*
+ * return breakpoint.  Since we already have both breakpoint actions
+ * to deal with, we might as well support it all.
+ *
+ * The gist is that we pretend that the child is in a thread group
+ * with its parent, and handle it as a multi-threaded case, with the
+ * exception that we know that the parent is blocked, and don't
+ * attempt to stop it.  When the child execs, we undo the setup.
+ *
+ * XXX The parent process could be un-suspended before ltrace gets
+ * child exec/exit event.  Make sure this is taken care of.
+ */
+
+static Event *
+process_vfork_on_event(Event_Handler * super, Event * event)
+{
+	struct process_vfork_handler * self = (void *)super;
+	assert(self != NULL);
+
+	switch (event->type) {
+	case EVENT_EXIT:
+	case EVENT_EXIT_SIGNAL:
+	case EVENT_EXEC:
+		/* Now is the time to remove the leader that we
+		 * artificially set up earlier.  XXX and do all the
+		 * other fun stuff.  */
+		change_process_leader(event->proc, event->proc);
+		destroy_event_handler(event->proc);
+
+		/* XXXXX this could happen in the middle of handling
+		 * multi-threaded breakpoint.  We must be careful to
+		 * undo the effects that we introduced above (vforked
+		 * = 1 et.al.).  */
+
+	default:
+		;
+	}
+
+	return event;
+}
+
+void
+continue_after_vfork(Process * proc)
+{
+	debug(DEBUG_PROCESS, "continue_after_vfork: pid=%d", proc->pid);
+	Event_Handler * handler = calloc(sizeof(*handler), 1);
+	if (handler == NULL) {
+		perror("malloc vfork handler");
+		/* Carry on not bothering to treat the process as
+		 * necessary.  */
+		continue_process(proc->parent->pid);
+		return;
+	}
+
+	/* We must set up custom event handler, so that we see
+	 * exec/exit events for the task itself.  */
+	handler->on_event = process_vfork_on_event;
+	install_event_handler(proc, handler);
+
+	/* Make sure that the child is sole thread.  */
+	assert(proc->leader == proc);
+	assert(proc->next == NULL || proc->next->leader != proc);
+
+	/* Make sure that the child's parent is properly set up.  */
+	assert(proc->parent != NULL);
+	assert(proc->parent->leader != NULL);
+
+	change_process_leader(proc, proc->parent->leader);
+	continue_process(proc->parent->pid);
+}
+
 /* If ltrace gets SIGINT, the processes directly or indirectly run by
  * ltrace get it too.  We just have to wait long enough for the signal
  * to be delivered and the process terminated, which we notice and
diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c
index 6e1c809..db1936d 100644
--- a/testsuite/ltrace.minor/trace-clone.c
+++ b/testsuite/ltrace.minor/trace-clone.c
@@ -3,7 +3,6 @@
    clone called.
 
    This file was written by Yao Qi <qiyao@cn.ibm.com>.  */
-
 #define _GNU_SOURCE
 #include <stdio.h>
 #include <sys/types.h>