Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r85063:502859b18e51
Date: 2016-06-10 00:14 +0200
http://bitbucket.org/pypy/pypy/changeset/502859b18e51/
Log: Pipes and forked subprocesses
diff --git a/rpython/translator/revdb/rdb-src/revdb.c
b/rpython/translator/revdb/rdb-src/revdb.c
--- a/rpython/translator/revdb/rdb-src/revdb.c
+++ b/rpython/translator/revdb/rdb-src/revdb.c
@@ -71,16 +71,16 @@
/* ------------------------------------------------------------ */
-static void write_all(int fd, const char *buf, ssize_t count)
+static void write_all(const void *buf, ssize_t count)
{
while (count > 0) {
- ssize_t wsize = write(fd, buf, count);
+ ssize_t wsize = write(rpy_rev_fileno, buf, count);
if (wsize <= 0) {
if (wsize == 0)
- fprintf(stderr, "Writing to PYPYREVDB file: "
+ fprintf(stderr, "writing to RevDB file: "
"unexpected non-blocking mode\n");
else
- fprintf(stderr, "Fatal error: writing to PYPYREVDB file:
%m\n");
+ fprintf(stderr, "Fatal error: writing to RevDB file: %m\n");
abort();
}
buf += wsize;
@@ -114,7 +114,7 @@
h.version = RDB_VERSION;
h.argc = argc;
h.argv = argv;
- write_all(rpy_rev_fileno, (const char *)&h, sizeof(h));
+ write_all((const char *)&h, sizeof(h));
}
RPY_EXTERN
@@ -128,7 +128,7 @@
if (size == 0 || rpy_rev_fileno < 0)
return;
- write_all(rpy_rev_fileno, rpy_rev_buffer, size);
+ write_all(rpy_rev_buffer, size);
}
@@ -182,24 +182,44 @@
writing more data.
*/
-#define FROZEN_PROCESSES 30
-#define GOLDEN_RATIO 0.618034
+#define NUM_FROZEN_PROCESSES 30
+#define GOLDEN_RATIO 0.618034
+
+#define RD_SIDE 0
+#define WR_SIDE 1
+
+static int frozen_num_pipes = 0;
+static int frozen_pipes[NUM_FROZEN_PROCESSES][2];
+static uint64_t frozen_time[NUM_FROZEN_PROCESSES];
+static int frozen_pipe_signal[2];
+
+enum { PK_MAIN_PROCESS, PK_FROZEN_PROCESS, PK_DEBUG_PROCESS };
+static int process_kind = PK_MAIN_PROCESS;
static uint64_t total_stop_points;
-static ssize_t read_at_least(int fd, char *buf,
- ssize_t count_min, ssize_t count_max)
+static void attach_gdb(void)
+{
+ char cmdline[80];
+ sprintf(cmdline, "term -c \"gdb --pid=%d\"", getpid());
+ system(cmdline);
+ sleep(1);
+}
+
+static ssize_t read_at_least(void *buf, ssize_t count_min, ssize_t count_max)
{
ssize_t result = 0;
assert(count_min <= count_max);
while (result < count_min) {
- ssize_t rsize = read(fd, buf + result, count_max - result);
+ ssize_t rsize = read(rpy_rev_fileno, buf + result, count_max - result);
if (rsize <= 0) {
if (rsize == 0)
- fprintf(stderr, "RevDB file appears truncated\n");
+ fprintf(stderr, "[%d] RevDB file appears truncated\n",
+ process_kind);
else
- fprintf(stderr, "RevDB file read error: %m\n");
+ fprintf(stderr, "[%d] RevDB file read error: %m\n",
+ process_kind);
exit(1);
}
result += rsize;
@@ -207,9 +227,9 @@
return result;
}
-static void read_all(int fd, char *buf, ssize_t count)
+static void read_all(void *buf, ssize_t count)
{
- (void)read_at_least(fd, buf, count, count);
+ (void)read_at_least(buf, count, count);
}
static void setup_replay_mode(int *argc_p, char **argv_p[])
@@ -233,7 +253,7 @@
assert(RPY_RDB_REPLAY == 1);
- read_all(rpy_rev_fileno, (char *)&h, sizeof(h));
+ read_all(&h, sizeof(h));
if (h.signature != RDB_SIGNATURE) {
fprintf(stderr, "'%s' is not a RevDB file (or wrong platform)\n",
@@ -259,6 +279,11 @@
rpy_revdb.buf_p = rpy_rev_buffer;
rpy_revdb.buf_limit = rpy_rev_buffer;
rpy_revdb.stop_point_break = 1;
+
+ if (pipe(frozen_pipe_signal) < 0) {
+ perror("pipe");
+ exit(1);
+ }
}
RPY_EXTERN
@@ -267,8 +292,7 @@
ssize_t rsize, keep = rpy_revdb.buf_limit - rpy_revdb.buf_p;
assert(keep >= 0);
memmove(rpy_rev_buffer, rpy_revdb.buf_p, keep);
- rsize = read_at_least(rpy_rev_fileno,
- rpy_rev_buffer + keep,
+ rsize = read_at_least(rpy_rev_buffer + keep,
expected_size - keep,
sizeof(rpy_rev_buffer) - keep);
rpy_revdb.buf_p = rpy_rev_buffer;
@@ -276,9 +300,82 @@
return rpy_rev_buffer;
}
+struct action_s {
+ const char *name;
+ void (*act)(char *);
+};
+
+static void process_input(struct action_s actions[])
+{
+ char input[256], *p;
+ struct action_s *a;
+
+ if (fgets(input, sizeof(input), stdin) != input) {
+ fprintf(stderr, "\n");
+ strcpy(input, "exit");
+ }
+
+ p = input;
+ while (*p > ' ')
+ p++;
+ if (*p != 0) {
+ *p = 0;
+ p++;
+ }
+ a = actions;
+ while (a->name != NULL && strcmp(a->name, input) != 0) {
+ a++;
+ }
+ if (a->name != NULL) {
+ a->act(p);
+ }
+ else if (strcmp(input, "help") == 0) {
+ a = actions;
+ printf("available commands:\n");
+ while (a->name != NULL) {
+ printf("\t%s\n", a->name);
+ a++;
+ }
+ }
+ else if (input[0] != 0) {
+ printf("bad command '%s', try 'help'\n", input);
+ }
+}
+
+static int read_pipe(int fd, void *buf, ssize_t count)
+{
+ while (count > 0) {
+ ssize_t got = read(fd, buf, count);
+ if (got <= 0)
+ return -1;
+ count -= got;
+ buf += got;
+ }
+ return 0;
+}
+
+static int write_pipe(int fd, const void *buf, ssize_t count)
+{
+ while (count > 0) {
+ ssize_t wrote = write(fd, buf, count);
+ if (wrote <= 0)
+ return -1;
+ count -= wrote;
+ buf += wrote;
+ }
+ return 0;
+}
+
static void check_at_end(uint64_t stop_points)
{
char dummy[1];
+ uint64_t target_time;
+
+ if (process_kind != PK_MAIN_PROCESS) {
+ fprintf(stderr, "[%d] Unexpectedly falling off the end\n",
+ process_kind);
+ exit(1);
+ }
if (stop_points != rpy_revdb.stop_point_seen) {
fprintf(stderr, "Bad number of stop points\n");
exit(1);
@@ -292,15 +389,176 @@
fprintf(stderr, "RevDB file modified while reading?\n");
exit(1);
}
- printf("Replaying finished, %lld stop points\n", (long long)stop_points);
+ if (frozen_num_pipes == 0) {
+ fprintf(stderr, "RevDB file does not contain any stop points\n");
+ exit(1);
+ }
+
+ printf("Replaying finished\n");
+ printf("stop_points=%lld\n", (long long)stop_points);
+
+ close(frozen_pipe_signal[WR_SIDE]);
+ frozen_pipe_signal[WR_SIDE] = -1;
+
+ target_time = frozen_time[frozen_num_pipes-1];
+ while (target_time != (uint64_t)-1) {
+ int p = frozen_num_pipes - 1;
+ if (target_time > frozen_time[p])
+ target_time = frozen_time[p];
+ while (frozen_time[p] > target_time)
+ p--;
+ if (write_pipe(frozen_pipes[p][WR_SIDE],
+ &target_time, sizeof(target_time)) < 0) {
+ fprintf(stderr, "broken pipe to frozen subprocess\n");
+ exit(1);
+ }
+ /* blocking here while the p'th frozen process spawns a debug process
+ and the user interacts with it; then: */
+ if (read_pipe(frozen_pipe_signal[RD_SIDE], &target_time,
+ sizeof(target_time)) < 0) {
+ fprintf(stderr, "broken signal pipe\n");
+ exit(1);
+ }
+ }
exit(0);
}
+static void run_frozen_process(int frozen_pipe_fd)
+{
+ uint64_t target_time;
+ pid_t child_pid;
+
+ while (1) {
+ if (read_pipe(frozen_pipe_fd, &target_time, sizeof(target_time)) < 0)
+ exit(1);
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ exit(1);
+ }
+ if (child_pid == 0) {
+ /* in the child: this is a debug process */
+ process_kind = PK_DEBUG_PROCESS;
+ assert(target_time >= rpy_revdb.stop_point_seen);
+ rpy_revdb.stop_point_break = target_time;
+ /* continue "running" the RPython program until we reach
+ exactly the specified target_time */
+ break;
+ }
+ else {
+ /* in the parent: the frozen process, which waits for
+ the debug process to finish to reclaim the pid,
+ and then loops to wait for the next wake-up */
+ int status;
+ waitpid(child_pid, &status, 0);
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ ; /* normal exit */
+ else {
+ target_time = (uint64_t)-1;
+ fprintf(stderr, "debugging subprocess died\n");
+ write_pipe(frozen_pipe_signal[WR_SIDE], &target_time,
+ sizeof(target_time));
+ exit(1); /* error */
+ }
+ }
+ }
+}
+
+static void make_new_frozen_process(void)
+{
+ pid_t child_pid;
+ int *fds;
+ off_t fileno_offset;
+
+ if (frozen_num_pipes >= NUM_FROZEN_PROCESSES) {
+ fprintf(stderr, "stop_point_break overflow?\n");
+ exit(1);
+ }
+
+ fprintf(stderr, "forking at time %llu\n",
+ (unsigned long long)rpy_revdb.stop_point_seen);
+
+ fds = frozen_pipes[frozen_num_pipes];
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+ frozen_time[frozen_num_pipes] = rpy_revdb.stop_point_seen;
+ frozen_num_pipes += 1;
+
+ fileno_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR);
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ exit(1);
+ }
+ if (child_pid == 0) {
+ /* in the child: this is a frozen process */
+ process_kind = PK_FROZEN_PROCESS;
+ close(fds[WR_SIDE]);
+ fds[WR_SIDE] = -1;
+ run_frozen_process(fds[RD_SIDE]);
+ /* when we reach that point, we are in the debugging process */
+ lseek(rpy_rev_fileno, fileno_offset, SEEK_SET);
+ }
+ else {
+ /* in the main process: continue reloading the revdb log */
+ uint64_t delta = total_stop_points - rpy_revdb.stop_point_break;
+ delta = (uint64_t)(delta * (1 - GOLDEN_RATIO));
+ if (delta == 0)
+ rpy_revdb.stop_point_break = total_stop_points;
+ else
+ rpy_revdb.stop_point_break += delta;
+ close(fds[RD_SIDE]);
+ fds[RD_SIDE] = -1;
+ }
+}
+
+static void act_exit(char *p)
+{
+ uint64_t target_time = (uint64_t)-1;
+ write_pipe(frozen_pipe_signal[WR_SIDE], &target_time,
+ sizeof(target_time));
+ exit(0);
+}
+
+static void act_go(char *p)
+{
+ uint64_t target_time = strtoull(p, NULL, 10);
+ if (target_time == 0) {
+ printf("usage: go <target time>\n");
+ return;
+ }
+ write_pipe(frozen_pipe_signal[WR_SIDE], &target_time,
+ sizeof(target_time));
+ exit(0);
+}
+
+static void run_debug_process(void)
+{
+ static struct action_s actions_1[] = {
+ { "go", act_go },
+ { "exit", act_exit },
+ { NULL }
+ };
+ while (1) {
+ printf("(%llu)$ ", (unsigned long long)rpy_revdb.stop_point_seen);
+ fflush(stdout);
+ process_input(actions_1);
+ }
+}
+
RPY_EXTERN
void rpy_reverse_db_break(long stop_point)
{
- printf("break #%ld after %lld stop points\n", stop_point,
- (long long)rpy_revdb.stop_point_seen);
+ if (process_kind == PK_MAIN_PROCESS)
+ make_new_frozen_process();
+
+ if (process_kind == PK_DEBUG_PROCESS)
+ if (rpy_revdb.stop_point_seen == rpy_revdb.stop_point_break)
+ run_debug_process();
}
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit