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

Reply via email to