[pypy-commit] pypy reverse-debugger: fixes, progress

arigo pypy.commits at gmail.com
Sat Jun 18 01:49:56 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85220:c89a87929045
Date: 2016-06-18 07:22 +0200
http://bitbucket.org/pypy/pypy/changeset/c89a87929045/

Log:	fixes, progress

diff --git a/rpython/translator/revdb/revmsg.py b/rpython/translator/revdb/revmsg.py
--- a/rpython/translator/revdb/revmsg.py
+++ b/rpython/translator/revdb/revmsg.py
@@ -1,4 +1,5 @@
-import struct
+import os, struct, socket, errno
+from rpython.translator.revdb import ancillary
 
 
 INIT_VERSION_NUMBER   = 0xd80100
@@ -33,19 +34,29 @@
 
 
 class ReplayProcess(object):
-    def __init__(self, stdin, stdout):
-        self.stdin = stdin
-        self.stdout = stdout
+    def __init__(self, pid, control_socket):
+        self.pid = pid
+        self.control_socket = control_socket
+
+    def _recv_all(self, size):
+        pieces = []
+        while size > 0:
+            data = self.control_socket.recv(size)
+            if not data:
+                raise EOFError
+            size -= len(data)
+            pieces.append(data)
+        return ''.join(pieces)
 
     def send(self, msg):
-        binary = struct.pack("iLqqq", msg.cmd, len(msg.extra),
+        binary = struct.pack("iIqqq", msg.cmd, len(msg.extra),
                              msg.arg1, msg.arg2, msg.arg3)
-        self.stdin.write(binary + msg.extra)
+        self.control_socket.sendall(binary + msg.extra)
 
     def recv(self):
-        binary = self.stdout.read(struct.calcsize("iLqqq"))
-        cmd, size, arg1, arg2, arg3 = struct.unpack("iLqqq", binary)
-        extra = self.stdout.read(size) if size > 0 else ""
+        binary = self._recv_all(struct.calcsize("iIqqq"))
+        cmd, size, arg1, arg2, arg3 = struct.unpack("iIqqq", binary)
+        extra = self._recv_all(size)
         return Message(cmd, arg1, arg2, arg3, extra)
 
     def expect(self, cmd, arg1=0, arg2=0, arg3=0, extra=""):
@@ -60,3 +71,15 @@
         if extra is not Ellipsis:
             assert msg.extra == extra
         return msg
+
+    def fork(self):
+        self.send(Message(CMD_FORK))
+        s1, s2 = socket.socketpair()
+        ancillary.send_fds(self.control_socket.fileno(), [s2.fileno()])
+        s2.close()
+        msg = self.expect(ANSWER_FORKED, Ellipsis)
+        child_pid = msg.arg1
+        return ReplayProcess(child_pid, s1)
+
+    def close(self):
+        self.send(Message(CMD_QUIT))
diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c
--- a/rpython/translator/revdb/src-revdb/revdb.c
+++ b/rpython/translator/revdb/src-revdb/revdb.c
@@ -201,11 +201,9 @@
 #define ANSWER_FORKED  (-22)
 #define ANSWER_AT_END  (-23)
 
-#define RDB_STDIN   0
-#define RDB_STDOUT  1
-
 typedef void (*rpy_revdb_command_fn)(rpy_revdb_command_t *, char *);
 
+static int rpy_rev_sockfd;
 static const char *rpy_rev_filename;
 static uint64_t stopped_time;
 static uint64_t stopped_uid;
@@ -244,13 +242,12 @@
     (void)read_at_least(buf, count, count);
 }
 
-
-static void read_pipe(int fd, void *buf, ssize_t count)
+static void read_sock(void *buf, ssize_t count)
 {
     while (count > 0) {
-        ssize_t got = read(fd, buf, count);
+        ssize_t got = read(rpy_rev_sockfd, buf, count);
         if (got <= 0) {
-            fprintf(stderr, "subprocess: cannot read pipe %d\n", fd);
+            fprintf(stderr, "subprocess: cannot read from control socket\n");
             exit(1);
         }
         count -= got;
@@ -258,12 +255,12 @@
     }
 }
 
-static void write_pipe(int fd, const void *buf, ssize_t count)
+static void write_sock(const void *buf, ssize_t count)
 {
     while (count > 0) {
-        ssize_t wrote = write(fd, buf, count);
+        ssize_t wrote = write(rpy_rev_sockfd, buf, count);
         if (wrote <= 0) {
-            fprintf(stderr, "subprocess: cannot write to pipe %d\n", fd);
+            fprintf(stderr, "subprocess: cannot write to control socket\n");
             exit(1);
         }
         count -= wrote;
@@ -279,7 +276,7 @@
     c.arg1 = arg1;
     c.arg2 = arg2;
     c.arg3 = arg3;
-    write_pipe(RDB_STDOUT, &c, sizeof(c));
+    write_sock(&c, sizeof(c));
 }
 
 static void answer_std(void)
@@ -305,12 +302,14 @@
     char input[16];
     ssize_t count;
 
-    if (argc != 3) {
-        fprintf(stderr, "syntax: %s --revdb-replay <RevDB-file>\n", argv[0]);
+    if (argc != 4) {
+        fprintf(stderr, "syntax: %s --revdb-replay <RevDB-file> <socket_fd>\n",
+                argv[0]);
         exit(2);
     }
     rpy_rev_filename = argv[2];
     reopen_revdb_file(rpy_rev_filename);
+    rpy_rev_sockfd = atoi(argv[3]);
 
     assert(RPY_RDB_REPLAY == 1);
 
@@ -468,15 +467,14 @@
 
 static void command_fork(void)
 {
-    int child_pipes[2];
+    int child_sockfd;
     int child_pid;
     off_t rev_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR);
 
-    if (ancil_recv_fds(RDB_STDIN, child_pipes, 2) != 2) {
-        fprintf(stderr, "cannot read child stdin/stdout pipes\n");
+    if (ancil_recv_fd(rpy_rev_sockfd, &child_sockfd) < 0) {
+        fprintf(stderr, "cannot fetch child control socket: %m\n");
         exit(1);
     }
-
     child_pid = fork();
     if (child_pid == -1) {
         perror("fork");
@@ -484,11 +482,12 @@
     }
     if (child_pid == 0) {
         /* in the child */
-        if (dup2(child_pipes[0], RDB_STDIN) < 0 ||
-            dup2(child_pipes[1], RDB_STDOUT) < 0) {
-            perror("dup2");
+        if (close(rpy_rev_sockfd) < 0) {
+            perror("close");
             exit(1);
         }
+        rpy_rev_sockfd = child_sockfd;
+
         /* Close and re-open the revdb log file in the child process.
            This is the simplest way I found to give 'rpy_rev_fileno'
            its own offset, independent from the parent.  It assumes
@@ -518,12 +517,7 @@
     else {
         /* in the parent */
         write_answer(ANSWER_FORKED, child_pid, 0, 0);
-    }
-    /* common code in the parent and the child */
-    if (close(child_pipes[0]) < 0 ||
-        close(child_pipes[1]) < 0) {
-        perror("close");
-        exit(1);
+        close(child_sockfd);
     }
 }
 
@@ -564,12 +558,12 @@
         }
         else {
             rpy_revdb_command_t cmd;
-            read_pipe(RDB_STDIN, &cmd, sizeof(cmd));
+            read_sock(&cmd, sizeof(cmd));
 
             char extra[cmd.extra_size + 1];
             extra[cmd.extra_size] = 0;
             if (cmd.extra_size > 0)
-                read_pipe(RDB_STDIN, extra, cmd.extra_size);
+                read_sock(extra, cmd.extra_size);
 
             switch (cmd.cmd) {
 
@@ -601,15 +595,19 @@
                                 int64_t arg3, RPyString *extra)
 {
     rpy_revdb_command_t c;
-    memset(&c, 0, sizeof(c));
+    size_t extra_size = RPyString_Size(extra);
     c.cmd = cmd;
-    c.extra_size = RPyString_Size(extra);
+    c.extra_size = extra_size;
+    if (c.extra_size != extra_size) {
+        fprintf(stderr, "string too large (more than 4GB)\n");
+        exit(1);
+    }
     c.arg1 = arg1;
     c.arg2 = arg2;
     c.arg3 = arg3;
-    write_pipe(RDB_STDOUT, &c, sizeof(c));
-    if (c.extra_size > 0)
-        write_pipe(RDB_STDOUT, _RPyString_AsString(extra), c.extra_size);
+    write_sock(&c, sizeof(c));
+    if (extra_size > 0)
+        write_sock(_RPyString_AsString(extra), extra_size);
 }
 
 RPY_EXTERN
diff --git a/rpython/translator/revdb/src-revdb/revdb_preinclude.h b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
--- a/rpython/translator/revdb/src-revdb/revdb_preinclude.h
+++ b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
@@ -1,8 +1,8 @@
 #include <stdint.h>
 
 typedef struct rpy_revdb_command_s {
-    int cmd;      /* neg for standard commands, pos for interp-specific */
-    size_t extra_size;
+    int32_t cmd;      /* neg for standard commands, pos for interp-specific */
+    uint32_t extra_size;
     int64_t arg1;
     int64_t arg2;
     int64_t arg3;
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
@@ -1,5 +1,5 @@
 import py
-import os, sys, subprocess
+import os, sys, subprocess, socket
 import re, array, struct
 from rpython.tool.udir import udir
 from rpython.translator.interactive import Translation
@@ -211,12 +211,13 @@
 class InteractiveTests(object):
 
     def replay(self):
+        s1, s2 = socket.socketpair()
         subproc = subprocess.Popen(
-            [str(self.exename), '--revdb-replay', str(self.rdbname)],
-            stdin  = subprocess.PIPE,
-            stdout = subprocess.PIPE)
+            [str(self.exename), '--revdb-replay', str(self.rdbname),
+             str(s2.fileno())])
+        s2.close()
         self.subproc = subproc
-        return ReplayProcess(subproc.stdin, subproc.stdout)
+        return ReplayProcess(subproc.pid, s1)
 
 
 class TestSimpleInterpreter(InteractiveTests):
@@ -253,6 +254,19 @@
         child.send(Message(CMD_QUIT))
         assert self.subproc.wait() == 0
 
+    def test_fork(self):
+        child = self.replay()
+        child.expect(ANSWER_INIT, INIT_VERSION_NUMBER, 3)
+        child.expect(ANSWER_STD, 1, Ellipsis)
+        child2 = child.fork()
+        child.send(Message(CMD_FORWARD, 2))
+        child.expect(ANSWER_STD, 3, Ellipsis)
+        child2.send(Message(CMD_FORWARD, 1))
+        child2.expect(ANSWER_STD, 2, Ellipsis)
+        #
+        child.close()
+        child2.close()
+
 
 class TestDebugCommands(InteractiveTests):
 


More information about the pypy-commit mailing list