Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r85012:0e59d36285f8
Date: 2016-06-07 22:53 +0200
http://bitbucket.org/pypy/pypy/changeset/0e59d36285f8/

Log:    Documentation (not implemented yet). Other clean-ups.

diff --git a/rpython/translator/c/src/entrypoint.c 
b/rpython/translator/c/src/entrypoint.c
--- a/rpython/translator/c/src/entrypoint.c
+++ b/rpython/translator/c/src/entrypoint.c
@@ -104,7 +104,7 @@
     exitcode = STANDALONE_ENTRY_POINT(argc, argv);
 
 #ifdef RPY_REVERSE_DEBUGGER
-    rpy_reverse_db_teardown(&exitcode);
+    rpy_reverse_db_teardown();
 #endif
 
     pypy_debug_alloc_results();
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
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
 
@@ -11,6 +12,14 @@
 #define RDB_VERSION     0x00FF0001
 
 
+typedef struct {
+    Signed signature, version;
+    Signed reserved1, reserved2;
+    int argc;
+    char **argv;
+} rdb_header_t;
+
+
 rpy_revdb_t rpy_revdb;
 static char rpy_rev_buffer[16384];
 static int rpy_rev_fileno = -1;
@@ -18,7 +27,7 @@
 
 static void setup_record_mode(int argc, char *argv[]);
 static void setup_replay_mode(int *argc_p, char **argv_p[]);
-static void check_at_end(int exitcode, int *exitcode_p, uint64_t stop_points);
+static void check_at_end(uint64_t stop_points);
 
 RPY_EXTERN
 void rpy_reverse_db_setup(int *argc_p, char **argv_p[])
@@ -44,18 +53,16 @@
 }
 
 RPY_EXTERN
-void rpy_reverse_db_teardown(int *exitcode_p)
+void rpy_reverse_db_teardown(void)
 {
-    int exitcode;
     uint64_t stop_points;
-    RPY_REVDB_EMIT(exitcode = *exitcode_p; , int _e, exitcode);
     RPY_REVDB_EMIT(stop_points = rpy_revdb.stop_point_seen; ,
                    uint64_t _e, stop_points);
 
     if (!RPY_RDB_REPLAY)
         rpy_reverse_db_flush();
     else
-        check_at_end(exitcode, exitcode_p, stop_points);
+        check_at_end(stop_points);
 }
 
 
@@ -64,10 +71,27 @@
 /* ------------------------------------------------------------ */
 
 
+static void write_all(int fd, const char *buf, ssize_t count)
+{
+    while (count > 0) {
+        ssize_t wsize = write(fd, buf, count);
+        if (wsize <= 0) {
+            if (wsize == 0)
+                fprintf(stderr, "Writing to PYPYREVDB file: "
+                                "unexpected non-blocking mode\n");
+            else
+                fprintf(stderr, "Fatal error: writing to PYPYREVDB file: 
%m\n");
+            abort();
+        }
+        buf += wsize;
+        count -= wsize;
+    }
+}
+
 static void setup_record_mode(int argc, char *argv[])
 {
     char *filename = getenv("PYPYREVDB");
-    Signed x;
+    rdb_header_t h;
 
     assert(RPY_RDB_REPLAY == 0);
     rpy_revdb.buf_p = rpy_rev_buffer;
@@ -85,12 +109,12 @@
         atexit(rpy_reverse_db_flush);
     }
 
-    RPY_REVDB_EMIT(x = RDB_SIGNATURE; , Signed _e, x);
-    RPY_REVDB_EMIT(x = RDB_VERSION;   , Signed _e, x);
-    RPY_REVDB_EMIT(x = 0;             , Signed _e, x);
-    RPY_REVDB_EMIT(x = 0;             , Signed _e, x);
-    RPY_REVDB_EMIT(x = argc;          , Signed _e, x);
-    RPY_REVDB_EMIT(x = (Signed)argv;  , Signed _e, x);
+    memset(&h, 0, sizeof(h));
+    h.signature = RDB_SIGNATURE;
+    h.version = RDB_VERSION;
+    h.argc = argc;
+    h.argv = argv;
+    write_all(rpy_rev_fileno, (const char *)&h, sizeof(h));
 }
 
 RPY_EXTERN
@@ -104,22 +128,7 @@
     if (size == 0 || rpy_rev_fileno < 0)
         return;
 
-    p = rpy_rev_buffer;
- retry:
-    wsize = write(rpy_rev_fileno, p, size);
-    if (wsize >= size)
-        return;
-    if (wsize <= 0) {
-        if (wsize == 0)
-            fprintf(stderr, "Writing to PYPYREVDB file: "
-                            "unexpected non-blocking mode\n");
-        else
-            fprintf(stderr, "Fatal error: writing to PYPYREVDB file: %m\n");
-        abort();
-    }
-    p += wsize;
-    size -= wsize;
-    goto retry;
+    write_all(rpy_rev_fileno, rpy_rev_buffer, size);
 }
 
 
@@ -128,12 +137,87 @@
 /* ------------------------------------------------------------ */
 
 
+/* How it works: the main process reads the RevDB file and
+   reconstructs the GC objects, in effect replaying their content, for
+   the complete duration of the original program.  During this
+   replaying, it forks a fixed number of frozen processes which sit
+   around, each keeping the version of the GC objects contents at some
+   known version.  We have one pipe for every frozen process, which
+   the frozen process is blocked reading.
+
+    [main process]
+        [frozen process 1]
+        [frozen process 2]
+            [debugging process]
+        [frozen process 3]
+        [frozen process 4]
+        ...
+        [frozen process n]
+
+   When all frozen processes are made, the main process enters
+   interactive mode.  In interactive mode, the main process reads from
+   stdin a version number to go to.  It picks the correct frozen
+   process (the closest one that is before in time); let's say it is
+   process #p.  It sends the version number to it by writing to pipe
+   #p.  The corresponding frozen process wakes up, and forks again
+   into a debugging process.  The main and the frozen process then
+   block.
+
+   The debugging process first goes forward in time until it reaches
+   the right version number.  Then it interacts with the user (or a
+   pdb-like program outside) on stdin/stdout.  This goes on until an
+   "exit" command is received and the debugging process dies.  At that
+   point its parent (the frozen process) continues and signals its own
+   parent (the main process) by writing to a separate signalling pipe.
+   The main process then wakes up again, and the loop closes: it reads
+   on stdin the next version number that we're interested in, and asks
+   the right frozen process to make a debugging process again.
+
+   Note how we have, over time, several processes that read and
+   process stdin; it should work because they are strictly doing that
+   in sequence, never concurrently.  To avoid the case where stdin is
+   buffered inside one process but a different process should read it,
+   we write markers to stdout when such switches occur.  The outside
+   controlling program must wait until it sees these markers before
+   writing more data.
+*/
+
+#define FROZEN_PROCESSES   30
+#define GOLDEN_RATIO       0.618034
+
+static uint64_t total_stop_points;
+
+
+static ssize_t read_at_least(int fd, char *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);
+        if (rsize <= 0) {
+            if (rsize == 0)
+                fprintf(stderr, "RevDB file appears truncated\n");
+            else
+                fprintf(stderr, "RevDB file read error: %m\n");
+            exit(1);
+        }
+        result += rsize;
+    }
+    return result;
+}
+
+static void read_all(int fd, char *buf, ssize_t count)
+{
+    (void)read_at_least(fd, buf, count, count);
+}
+
 static void setup_replay_mode(int *argc_p, char **argv_p[])
 {
-    Signed x;
     int argc = *argc_p;
     char **argv = *argv_p;
     char *filename;
+    rdb_header_t h;
 
     if (argc != 3) {
         fprintf(stderr, "syntax: %s --replay <RevDB-file>\n", argv[0]);
@@ -148,37 +232,51 @@
     }
 
     assert(RPY_RDB_REPLAY == 1);
+
+    read_all(rpy_rev_fileno, (char *)&h, sizeof(h));
+
+    if (h.signature != RDB_SIGNATURE) {
+        fprintf(stderr, "'%s' is not a RevDB file (or wrong platform)\n",
+                filename);
+        exit(1);
+    }
+    if (h.version != RDB_VERSION) {
+        fprintf(stderr, "RevDB file version mismatch (got %lx, expected 
%lx)\n",
+                (long)h.version, (long)RDB_VERSION);
+        exit(1);
+    }
+    *argc_p = h.argc;
+    *argv_p = h.argv;
+
+    if (lseek(rpy_rev_fileno, -sizeof(uint64_t), SEEK_END) < 0 ||
+            read(rpy_rev_fileno, &total_stop_points,
+                 sizeof(uint64_t)) != sizeof(uint64_t) ||
+            lseek(rpy_rev_fileno, sizeof(h), SEEK_SET) != sizeof(h)) {
+        fprintf(stderr, "%s: %m\n", filename);
+        exit(1);
+    }
+
     rpy_revdb.buf_p = rpy_rev_buffer;
     rpy_revdb.buf_limit = rpy_rev_buffer;
-
-    RPY_REVDB_EMIT(abort();, Signed _e, x);
-    if (x != RDB_SIGNATURE) {
-        fprintf(stderr, "stdin is not a RevDB file (or wrong platform)\n");
-        exit(1);
-    }
-    RPY_REVDB_EMIT(abort();, Signed _e, x);
-    if (x != RDB_VERSION) {
-        fprintf(stderr, "RevDB file version mismatch (got %lx, expected 
%lx)\n",
-                (long)x, (long)RDB_VERSION);
-        exit(1);
-    }
-    RPY_REVDB_EMIT(abort();, Signed _e, x);   /* ignored */
-    RPY_REVDB_EMIT(abort();, Signed _e, x);   /* ignored */
-
-    RPY_REVDB_EMIT(abort();, Signed _e, x);
-    if (x <= 0) {
-        fprintf(stderr, "RevDB file is bogus\n");
-        exit(1);
-    }
-    *argc_p = x;
-
-    RPY_REVDB_EMIT(abort();, Signed _e, x);
-    *argv_p = (char **)x;
-
     rpy_revdb.stop_point_break = 1;
 }
 
-static void check_at_end(int exitcode, int *exitcode_p, uint64_t stop_points)
+RPY_EXTERN
+char *rpy_reverse_db_fetch(int expected_size)
+{
+    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,
+                          expected_size - keep,
+                          sizeof(rpy_rev_buffer) - keep);
+    rpy_revdb.buf_p = rpy_rev_buffer;
+    rpy_revdb.buf_limit = rpy_rev_buffer + keep + rsize;
+    return rpy_rev_buffer;
+}
+
+static void check_at_end(uint64_t stop_points)
 {
     char dummy[1];
     if (stop_points != rpy_revdb.stop_point_seen) {
@@ -190,41 +288,12 @@
         fprintf(stderr, "RevDB file error: corrupted file (too much data?)\n");
         exit(1);
     }
-    if (*exitcode_p != exitcode) {
-        fprintf(stderr, "Bogus exit code\n");
+    if (stop_points != total_stop_points) {
+        fprintf(stderr, "RevDB file modified while reading?\n");
         exit(1);
     }
-    printf("Replaying finished (exit code %d)\n", exitcode);
-    rpy_reverse_db_break(0);
-    *exitcode_p = 0;
-}
-
-RPY_EXTERN
-char *rpy_reverse_db_fetch(int expected_size)
-{
-    ssize_t rsize, keep = rpy_revdb.buf_limit - rpy_revdb.buf_p;
-    assert(keep >= 0);
-    memmove(rpy_rev_buffer, rpy_revdb.buf_p, keep);
-
- retry:
-    rsize = read(rpy_rev_fileno, rpy_rev_buffer + keep,
-                 sizeof(rpy_rev_buffer) - keep);
-    if (rsize <= 0) {
-        if (rsize == 0)
-            fprintf(stderr, "RevDB file appears truncated\n");
-        else
-            fprintf(stderr, "RevDB file read error: %m\n");
-        exit(1);
-    }
-    keep += rsize;
-
-    rpy_revdb.buf_p = rpy_rev_buffer;
-    rpy_revdb.buf_limit = rpy_rev_buffer + keep;
-
-    if (rpy_revdb.buf_limit - rpy_revdb.buf_p < expected_size)
-        goto retry;
-
-    return rpy_rev_buffer;
+    printf("Replaying finished, %lld stop points\n", (long long)stop_points);
+    exit(0);
 }
 
 RPY_EXTERN
diff --git a/rpython/translator/revdb/rdb-src/revdb_include.h 
b/rpython/translator/revdb/rdb-src/revdb_include.h
--- a/rpython/translator/revdb/rdb-src/revdb_include.h
+++ b/rpython/translator/revdb/rdb-src/revdb_include.h
@@ -26,7 +26,7 @@
 /* ------------------------------------------------------------ */
 
 RPY_EXTERN void rpy_reverse_db_setup(int *argc_p, char **argv_p[]);
-RPY_EXTERN void rpy_reverse_db_teardown(int *exitcode_p);
+RPY_EXTERN void rpy_reverse_db_teardown(void);
 
 
 #define RPY_REVDB_EMIT(normal_code, decl_e, variable)                   \
diff --git a/rpython/translator/revdb/test/test_basic.py 
b/rpython/translator/revdb/test/test_basic.py
--- a/rpython/translator/revdb/test/test_basic.py
+++ b/rpython/translator/revdb/test/test_basic.py
@@ -93,17 +93,15 @@
         # write() call
         x = rdb.next(); assert x == len('[abc, d]\n')
         x = rdb.next('i'); assert x == 0      # errno
-        x = rdb.next('i'); assert x == 9      # exitcode
         x = rdb.next('q'); assert x == 0      # number of stop points
-        # that's all that should get from this simple example
+        # that's all we should get from this simple example
         assert rdb.done()
         #
         assert got == [self.exename, 'abc', 'd']
         #
         # Now try the replay mode (just "doesn't crash" for now)
         out = replay()
-        assert out == ("Replaying finished (exit code 9)\n"
-                       "break #0 after 0 stop points\n")
+        assert out == "Replaying finished, 0 stop points\n"
 
     def test_simple_interpreter(self):
         def main(argv):
@@ -116,5 +114,4 @@
         assert self.fetch_rdb().number_of_stop_points() == 3
         out = replay()
         assert out == ("break #42 after 1 stop points\n"
-                       "Replaying finished (exit code 9)\n"
-                       "break #0 after 3 stop points\n")
+                       "Replaying finished, 3 stop points\n")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to