[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