Fixes for tracing across exec
diff --git a/handle_event.c b/handle_event.c
index 5a49387..bdda4d1 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -1,18 +1,19 @@
 #define _GNU_SOURCE
 #include "config.h"
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
 #include <assert.h>
-#include <sys/time.h>
 #include <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
 
-#include "common.h"
 #include "breakpoint.h"
-#include "proc.h"
+#include "common.h"
 #include "library.h"
+#include "proc.h"
 
 static void handle_signal(Event *event);
 static void handle_exit(Event *event);
@@ -441,21 +442,26 @@
 handle_exec(Event * event) {
 	Process * proc = event->proc;
 
+	/* Save the PID so that we can use it after unsuccessful
+	 * process_exec.  */
+	pid_t pid = proc->pid;
+
 	debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid);
 	if (proc->state == STATE_IGNORED) {
-		untrace_pid(proc->pid);
+	untrace:
+		untrace_pid(pid);
 		remove_process(proc);
 		free(proc);
 		return;
 	}
 	output_line(proc, "--- Called exec() ---");
-	proc->mask_32bit = 0;
-	proc->personality = 0;
-	proc->arch_ptr = NULL;
-	free(proc->filename);
-	proc->filename = pid2name(proc->pid);
-	breakpoints_init(proc, 0);
-	proc->callstack_depth = 0;
+
+	if (process_exec(proc) < 0) {
+		error(0, errno,
+		      "couldn't reinitialize process %d after exec", pid);
+		goto untrace;
+	}
+
 	continue_process(proc->pid);
 
 	/* After the exec, we expect to hit the first executable
diff --git a/proc.c b/proc.c
index c525359..1634764 100644
--- a/proc.c
+++ b/proc.c
@@ -17,25 +17,28 @@
 #include "breakpoint.h"
 #include "proc.h"
 
-static void add_process(struct Process *proc);
+static void add_process(struct Process *proc, int was_exec);
 
 static int
-process_bare_init(struct Process *proc, const char *filename, pid_t pid)
+process_bare_init(struct Process *proc, const char *filename,
+		  pid_t pid, int was_exec)
 {
-	memset(proc, 0, sizeof(*proc));
+	if (!was_exec) {
+		memset(proc, 0, sizeof(*proc));
 
-	proc->filename = strdup(filename);
-	if (proc->filename == NULL) {
-	fail:
-		free(proc->filename);
-		if (proc->breakpoints != NULL)
-			dict_clear(proc->breakpoints);
-		return -1;
+		proc->filename = strdup(filename);
+		if (proc->filename == NULL) {
+		fail:
+			free(proc->filename);
+			if (proc->breakpoints != NULL)
+				dict_clear(proc->breakpoints);
+			return -1;
+		}
 	}
 
 	/* Add process so that we know who the leader is.  */
 	proc->pid = pid;
-	add_process(proc);
+	add_process(proc, was_exec);
 	if (proc->leader == NULL)
 		goto fail;
 
@@ -57,36 +60,29 @@
 }
 
 static void
-process_bare_destroy(struct Process *proc)
+process_bare_destroy(struct Process *proc, int was_exec)
 {
-	free(proc->filename);
 	dict_clear(proc->breakpoints);
-	remove_process(proc);
+	if (!was_exec) {
+		free(proc->filename);
+		remove_process(proc);
+	}
 }
 
-int
-process_init(struct Process *proc, const char *filename, pid_t pid, int enable)
+static int
+process_init_main(struct Process *proc)
 {
-	if (process_bare_init(proc, filename, pid) < 0) {
-		error(0, errno, "init process %d", pid);
-		return -1;
-	}
-
-	/* For secondary threads, this is all that we need to do.  */
-	if (proc->leader != proc)
-		return 0;
-
 	target_address_t entry;
 	target_address_t interp_bias;
 	if (process_get_entry(proc, &entry, &interp_bias) < 0) {
 		fprintf(stderr, "Couldn't get entry points of process %d\n",
 			proc->pid);
 	fail:
-		process_bare_destroy(proc);
+		process_bare_destroy(proc, 0);
 		return -1;
 	}
 
-	if (breakpoints_init(proc, enable) < 0) {
+	if (breakpoints_init(proc) < 0) {
 		fprintf(stderr, "failed to init breakpoints %d\n",
 			proc->pid);
 		goto fail;
@@ -95,6 +91,69 @@
 	return 0;
 }
 
+int
+process_init(struct Process *proc, const char *filename, pid_t pid)
+{
+	if (process_bare_init(proc, filename, pid, 0) < 0) {
+		error(0, errno, "init process %d", pid);
+		return -1;
+	}
+
+	if (proc->leader == proc)
+		return process_init_main(proc);
+	else
+		return 0;
+}
+
+static void
+destroy_breakpoint_cb(void *key, void *value, void *data)
+{
+	struct breakpoint *bp = value;
+	breakpoint_destroy(bp);
+	free(bp);
+}
+
+static void
+private_process_destroy(struct Process *proc, int keep_filename)
+{
+	if (!keep_filename)
+		free(proc->filename);
+
+	/* Libraries and symbols.  */
+	struct library *lib;
+	for (lib = proc->libraries; lib != NULL; ) {
+		struct library *next = lib->next;
+		library_destroy(lib);
+		free(lib);
+		lib = next;
+	}
+	proc->libraries = NULL;
+
+	/* Breakpoints.  */
+	dict_apply_to_all(proc->breakpoints, destroy_breakpoint_cb, NULL);
+	dict_clear(proc->breakpoints);
+	proc->breakpoints = NULL;
+}
+
+void
+process_destroy(struct Process *proc)
+{
+	private_process_destroy(proc, 0);
+}
+
+int
+process_exec(struct Process *proc)
+{
+	private_process_destroy(proc, 1);
+	if (process_bare_init(proc, NULL, proc->pid, 1) < 0)
+		return -1;
+	if (process_init_main(proc) < 0) {
+		process_bare_destroy(proc, 1);
+		return -1;
+	}
+	return 0;
+}
+
 struct Process *
 open_program(const char *filename, pid_t pid)
 {
@@ -137,7 +196,7 @@
 int
 process_clone(struct Process *retp, struct Process *proc, pid_t pid)
 {
-	if (process_bare_init(retp, proc->filename, pid) < 0) {
+	if (process_bare_init(retp, proc->filename, pid, 0) < 0) {
 	fail:
 		error(0, errno, "clone process %d->%d", proc->pid, pid);
 		return -1;
@@ -158,7 +217,7 @@
 		if (*nlibp == NULL
 		    || library_clone(*nlibp, lib) < 0) {
 		fail2:
-			process_bare_destroy(retp);
+			process_bare_destroy(retp, 0);
 
 			/* Error when cloning.  Unroll what was done.  */
 			for (lib = retp->libraries; lib != NULL; ) {
@@ -372,7 +431,7 @@
 }
 
 static void
-add_process(Process * proc)
+add_process(struct Process *proc, int was_exec)
 {
 	Process ** leaderp = &list_of_processes;
 	if (proc->pid) {
@@ -390,8 +449,11 @@
 				leaderp = &leader->next;
 		}
 	}
-	proc->next = *leaderp;
-	*leaderp = proc;
+
+	if (!was_exec) {
+		proc->next = *leaderp;
+		*leaderp = proc;
+	}
 }
 
 void
diff --git a/proc.h b/proc.h
index 96739e0..b5a08a8 100644
--- a/proc.h
+++ b/proc.h
@@ -140,6 +140,19 @@
  * and add the process to an internal chain of traced processes.  */
 int process_init(struct Process *proc, const char *filename, pid_t pid);
 
+/* PROC underwent an exec.  This is a bit like process_destroy
+ * followed by process_init, except that some state is kept and the
+ * process doesn't lose it's place in the list of processes.  */
+int process_exec(struct Process *proc);
+
+/* Release any memory allocated for PROC (but not PROC itself).  Does
+ * NOT remove PROC from internal chain.
+ *
+ * XXX clearly this init/destroy pair is different than others and
+ * should be fixed.  process_init should presumably be separate from
+ * process_add.  */
+void process_destroy(struct Process *proc);
+
 struct Process *open_program(const char *filename, pid_t pid);
 void open_pid(pid_t pid);
 Process * pid2proc(pid_t pid);