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);