Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r85217:829c0b70159d
Date: 2016-06-17 21:17 +0200
http://bitbucket.org/pypy/pypy/changeset/829c0b70159d/

Log:    Change the model: now that the basics work, a more flexible model
        involving a regular Python controller program makes more sense

diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -1,11 +1,13 @@
 import sys
 from rpython.rlib.objectmodel import we_are_translated, fetch_translated_config
 from rpython.rlib.objectmodel import specialize
+from rpython.rlib.rarithmetic import r_longlong
 from rpython.rtyper.lltypesystem import lltype, llmemory, rstr
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.rtyper.extregistry import ExtRegistryEntry
 from rpython.rtyper.annlowlevel import llhelper, hlstr
 from rpython.rtyper.annlowlevel import cast_gcref_to_instance
+from rpython.rtyper.lltypesystem import rffi
 
 
 def stop_point():
@@ -20,9 +22,9 @@
 def register_debug_command(command, lambda_func):
     """Register the extra RPython-implemented debug command."""
 
-def send_output(string):
-    """For RPython debug commands: writes the string to stdout."""
-    llop.revdb_send_output(lltype.Void, string)
+def send_answer(cmd, arg1=0, arg2=0, arg3=0, extra=""):
+    """For RPython debug commands: writes an answer block to stdout"""
+    llop.revdb_send_answer(lltype.Void, cmd, arg1, arg2, arg3, extra)
 
 def current_time():
     """For RPython debug commands: returns the current time."""
@@ -33,41 +35,11 @@
     this is the target time at which we'll stop going forward."""
     return llop.revdb_get_value(lltype.SignedLongLong, 'b')
 
-def most_recent_fork():
-    """For RPython debug commands: returns the time of the most
-    recent fork.  Going back to that time is fast; going back to a time
-    just before is slow."""
-    return llop.revdb_get_value(lltype.SignedLongLong, 'f')
-
 def total_time():
     """For RPython debug commands: returns the total time (measured
     as the total number of stop-points)."""
     return llop.revdb_get_value(lltype.SignedLongLong, 't')
 
[email protected](1)
-def go_forward(time_delta, callback, arg_string):
-    """For RPython debug commands: tells that after this function finishes,
-    the debugger should run the 'forward <time_delta>' command and then
-    invoke the 'callback' with no argument.
-    """
-    _change_time('f', time_delta, callback, arg_string)
-
[email protected](0)
-def breakpoint(callback, arg_string):
-    _change_time('k', 1, callback, arg_string)
-
[email protected](1)
-def jump_in_time(target_time, callback, arg_string, exact=True):
-    """For RPython debug commands: the debugger should run the
-    'go <target_time>' command.  This will reset the memory and fork again,
-    so you can't save any RPython state and read it back.  You can only
-    encode the state you want to save into a string.  In the reloaded
-    process, 'callback(arg_string)' is called.  If 'exact' is False, go to
-    the fork point before target_time but don't go_forward to exactly
-    target_time afterwards.
-    """
-    _change_time('g' if exact else 'b', target_time, callback, arg_string)
-
 def currently_created_objects():
     """For RPython debug commands: returns the current value of
     the object creation counter.  All objects created so far have
@@ -75,11 +47,13 @@
     unique id greater or equal."""
     return llop.revdb_get_value(lltype.SignedLongLong, 'u')
 
-def first_created_object_uid():
-    """Returns the creation number of the first object dynamically created
-    by the program.  Older objects are either prebuilt or created before
-    the first stop point."""
-    return llop.revdb_get_value(lltype.SignedLongLong, '1')
[email protected](1)
+def go_forward(time_delta, callback):
+    """For RPython debug commands: tells that after this function finishes,
+    the debugger should run the 'forward <time_delta>' command and then
+    invoke the 'callback' with no argument.
+    """
+    _change_time('f', time_delta, callback)
 
 @specialize.argtype(0)
 def get_unique_id(x):
@@ -111,10 +85,10 @@
 
 
 @specialize.arg(2)
-def _change_time(mode, time, callback, arg_string):
+def _change_time(mode, time, callback):
     callback_wrapper = _make_callback(callback)
     ll_callback = llhelper(_CALLBACK_ARG_FNPTR, callback_wrapper)
-    llop.revdb_change_time(lltype.Void, mode, time, ll_callback, arg_string)
+    llop.revdb_change_time(lltype.Void, mode, time, ll_callback)
 
 @specialize.memo()
 def _make_callback(callback):
@@ -125,16 +99,23 @@
                                                  lltype.Void))
 _CALLBACK_GCREF_FNPTR = lltype.Ptr(lltype.FuncType([llmemory.GCREF],
                                                    lltype.Void))
+_CMDPTR = rffi.CStructPtr('rpy_revdb_command_s',
+                          ('cmd', rffi.INT),
+                          ('arg1', lltype.SignedLongLong),
+                          ('arg2', lltype.SignedLongLong),
+                          ('arg3', lltype.SignedLongLong))
 
 
 class RegisterDebugCommand(ExtRegistryEntry):
     _about_ = register_debug_command
 
-    def compute_result_annotation(self, s_command, s_lambda_func):
+    def compute_result_annotation(self, s_command_num, s_lambda_func):
         from rpython.annotator import model as annmodel
-        command = s_command.const
+        from rpython.rtyper import llannotation
+
+        command_num = s_command_num.const
         lambda_func = s_lambda_func.const
-        assert isinstance(command, str)
+        assert isinstance(command_num, int)
         t = self.bookkeeper.annotator.translator
         if t.config.translation.reverse_debugger:
             func = lambda_func()
@@ -142,10 +123,12 @@
                 cmds = t.revdb_commands
             except AttributeError:
                 cmds = t.revdb_commands = []
-            cmds.append((command, func))
+            cmds.append((command_num, func))
             s_func = self.bookkeeper.immutablevalue(func)
+            s_ptr1 = llannotation.SomePtr(ll_ptrtype=_CMDPTR)
+            s_ptr2 = llannotation.SomePtr(ll_ptrtype=rffi.CCHARP)
             self.bookkeeper.emulate_pbc_call(self.bookkeeper.position_key,
-                                             s_func, [annmodel.s_Str0])
+                                             s_func, [s_ptr1, s_ptr2])
 
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -566,13 +566,13 @@
     'instrument_count':     LLOp(),
 
     'revdb_stop_point':     LLOp(),
-    'revdb_send_output':    LLOp(),
+    'revdb_send_answer':    LLOp(),
     'revdb_change_time':    LLOp(),
     'revdb_get_value':      LLOp(sideeffects=False),
-    'revdb_set_value':      LLOp(),
-    'revdb_identityhash':   LLOp(),
-    'revdb_get_unique_id':  LLOp(sideeffects=False),
-    'revdb_track_object':   LLOp(),
+    ## 'revdb_set_value':      LLOp(),
+    ## 'revdb_identityhash':   LLOp(),
+    ## 'revdb_get_unique_id':  LLOp(sideeffects=False),
+    ## 'revdb_track_object':   LLOp(),
 }
 # ***** Run test_lloperation after changes. *****
 
diff --git a/rpython/translator/c/src/g_prerequisite.h 
b/rpython/translator/c/src/g_prerequisite.h
--- a/rpython/translator/c/src/g_prerequisite.h
+++ b/rpython/translator/c/src/g_prerequisite.h
@@ -23,3 +23,7 @@
 # define RPY_LENGTH0     1       /* array decl [0] are bad */
 # define RPY_DUMMY_VARLENGTH     /* nothing */
 #endif
+
+#ifdef RPY_REVERSE_DEBUGGER
+#include "src-revdb/revdb_preinclude.h"
+#endif
diff --git a/rpython/translator/revdb/revdb_genc.py 
b/rpython/translator/revdb/revdb_genc.py
--- a/rpython/translator/revdb/revdb_genc.py
+++ b/rpython/translator/revdb/revdb_genc.py
@@ -1,7 +1,7 @@
 import py
 from rpython.rtyper.lltypesystem import lltype, rffi, rstr
 from rpython.translator.c.support import cdecl
-from rpython.rlib import exports
+from rpython.rlib import exports, revdb
 
 
 def extra_files():
@@ -23,12 +23,13 @@
 
 
 def prepare_database(db):
-    FUNCPTR = lltype.Ptr(lltype.FuncType([lltype.Ptr(rstr.STR)], lltype.Void))
+    FUNCPTR = lltype.Ptr(lltype.FuncType([revdb._CMDPTR,
+                                          rffi.CCHARP], lltype.Void))
 
     bk = db.translator.annotator.bookkeeper
     cmds = getattr(db.translator, 'revdb_commands', [])
 
-    array_names = lltype.malloc(rffi.CArray(rffi.CCHARP), len(cmds) + 1,
+    array_names = lltype.malloc(rffi.CArray(rffi.INT), len(cmds) + 1,
                                 flavor='raw', immortal=True, zero=True)
     array_funcs = lltype.malloc(rffi.CArray(FUNCPTR), len(cmds),
                                 flavor='raw', immortal=True, zero=True)
@@ -36,7 +37,8 @@
     for i, (name, func) in enumerate(cmds):
         fnptr = lltype.getfunctionptr(bk.getdesc(func).getuniquegraph())
         assert lltype.typeOf(fnptr) == FUNCPTR
-        array_names[i] = rffi.str2charp(name)
+        assert isinstance(name, int) and name != 0
+        array_names[i] = rffi.cast(rffi.INT, name)
         array_funcs[i] = fnptr
 
     exports.EXPORTS_obj2name[array_names._as_obj()] = 'rpy_revdb_command_names'
diff --git a/rpython/translator/revdb/src-revdb/ancillary.h 
b/rpython/translator/revdb/src-revdb/ancillary.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/revdb/src-revdb/ancillary.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * libancillary - black magic on Unix domain sockets
+ * (C) Nicolas George
+ * ancillary.h - public header
+ ***************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANCILLARY_H__
+#define ANCILLARY_H__
+
+/***************************************************************************
+ * Start of the readable part.
+ ***************************************************************************/
+
+#define ANCIL_MAX_N_FDS 960
+/*
+ * Maximum number of fds that can be sent or received using the "esay"
+ * functions; this is so that all can fit in one page.
+ */
+
+extern int
+ancil_send_fds_with_buffer(int, const int *, unsigned, void *);
+/*
+ * ancil_send_fds_with_buffer(sock, n_fds, fds, buffer)
+ *
+ * Sends the file descriptors in the array pointed by fds, of length n_fds
+ * on the socket sock.
+ * buffer is a writeable memory area large enough to hold the required data
+ * structures.
+ * Returns: -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fds_with_buffer(int, int *, unsigned, void *);
+/*
+ * ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer)
+ *
+ * Receives *n_fds file descriptors into the array pointed by fds
+ * from the socket sock.
+ * buffer is a writeable memory area large enough to hold the required data
+ * structures.
+ * Returns: -1 and errno in case of error, the actual number of received fd
+ * in case of success
+ */
+
+#define ANCIL_FD_BUFFER(n) \
+    struct { \
+       struct cmsghdr h; \
+       int fd[n]; \
+    }
+/* ANCIL_FD_BUFFER(n)
+ *
+ * A structure type suitable to be used as buffer for n file descriptors.
+ * Requires <sys/socket.h>.
+ * Example:
+ * ANCIL_FD_BUFFER(42) buffer;
+ * ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer);
+ */
+
+extern int
+ancil_send_fds(int, const int *, unsigned);
+/*
+ * ancil_send_fds(sock, n_fds, fds)
+ *
+ * Sends the file descriptors in the array pointed by fds, of length n_fds
+ * on the socket sock.
+ * n_fds must not be greater than ANCIL_MAX_N_FDS.
+ * Returns: -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fds(int, int *, unsigned);
+/*
+ * ancil_recv_fds(sock, n_fds, fds)
+ *
+ * Receives *n_fds file descriptors into the array pointed by fds
+ * from the socket sock.
+ * *n_fds must not be greater than ANCIL_MAX_N_FDS.
+ * Returns: -1 and errno in case of error, the actual number of received fd
+ * in case of success.
+ */
+
+
+extern int
+ancil_send_fd(int, int);
+/* ancil_recv_fd(sock, fd);
+ *
+ * Sends the file descriptor fd on the socket sock.
+ * Returns : -1 and errno in case of error, 0 in case of success.
+ */
+
+extern int
+ancil_recv_fd(int, int *);
+/* ancil_send_fd(sock, &fd);
+ *
+ * Receives the file descriptor fd from the socket sock.
+ * Returns : -1 and errno in case of error, 0 in case of success.
+ */
+
+#endif /* ANCILLARY_H__ */
diff --git a/rpython/translator/revdb/src-revdb/fd_recv.c 
b/rpython/translator/revdb/src-revdb/fd_recv.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/revdb/src-revdb/fd_recv.c
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * libancillary - black magic on Unix domain sockets
+ * (C) Nicolas George
+ * fd_send.c - receiving file descriptors
+ ***************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XPG4_2 /* Solaris sucks */
+# define _XPG4_2
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <assert.h>
+#if defined(__FreeBSD__)
+# include <sys/param.h> /* FreeBSD sucks */
+#endif
+
+#include "ancillary.h"
+
+int
+ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
+{
+    struct msghdr msghdr;
+    char nothing;
+    struct iovec nothing_ptr;
+    struct cmsghdr *cmsg;
+    int i;
+
+    nothing_ptr.iov_base = &nothing;
+    nothing_ptr.iov_len = 1;
+    msghdr.msg_name = NULL;
+    msghdr.msg_namelen = 0;
+    msghdr.msg_iov = &nothing_ptr;
+    msghdr.msg_iovlen = 1;
+    msghdr.msg_flags = 0;
+    msghdr.msg_control = buffer;
+    msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
+    cmsg = CMSG_FIRSTHDR(&msghdr);
+    cmsg->cmsg_len = msghdr.msg_controllen;
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    for(i = 0; i < n_fds; i++)
+       ((int *)CMSG_DATA(cmsg))[i] = -1;
+    
+    if(recvmsg(sock, &msghdr, 0) < 0)
+       return(-1);
+    for(i = 0; i < n_fds; i++)
+       fds[i] = ((int *)CMSG_DATA(cmsg))[i];
+    n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
+    return(n_fds);
+}
+
+#ifndef SPARE_RECV_FDS
+int
+ancil_recv_fds(int sock, int *fd, unsigned n_fds)
+{
+    ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
+
+    assert(n_fds <= ANCIL_MAX_N_FDS);
+    return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
+}
+#endif /* SPARE_RECV_FDS */
+
+#ifndef SPARE_RECV_FD
+int
+ancil_recv_fd(int sock, int *fd)
+{
+    ANCIL_FD_BUFFER(1) buffer;
+
+    return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
+}
+#endif /* SPARE_RECV_FD */
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
@@ -7,6 +7,7 @@
 #include <unistd.h>
 #include <ctype.h>
 #include <setjmp.h>
+#include <signal.h>
 
 #include "structdef.h"
 #include "forwarddecl.h"
@@ -175,70 +176,42 @@
 /* ------------------------------------------------------------ */
 
 
-/* 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]
-
-   The main process's job, once reloading is finished, is only to
-   active debugging processes, one at a time.  To go to a specific
-   target time, it activates the right frozen process by sending
-   'target_time' over the corresponding pipe.  The frozen process
-   forks the debugging process, and the debugging process goes forward
-   until it reaches 'target_time'.
-
-   The debugging process is then interacting with the user on
-   stdin/stdout.
-
-   A few commands like 'go <target_time>' will cause the debugging
-   process to send the 'target_time' back over a signalling pipe to
-   the main process, and then finish.  The main process receives that
-   'target_time', and the loop closes: it activates the right frozen
-   process, which will go forward and re-enter interactive mode.
+/* How it works: we run the same executable with different flags to
+   run it in "replay" mode.  In this mode, it reads commands from
+   stdin (in binary format) and writes the results to stdout.
+   Notably, there is a command to ask it to fork, passing a new pair
+   of pipes to the forked copy as its new stdin/stdout.  This is how
+   we implement the illusion of going backward: we throw away the
+   current fork, start from an earlier fork, make a new fork again,
+   and go forward by the correct number of steps.  This is all
+   controlled by a pure Python wrapper that is roughly generic
+   (i.e. able to act as a debugger for any language).
 */
 
-#define NUM_FROZEN_PROCESSES   30
-#define STEP_RATIO             0.25
+#include "src-revdb/fd_recv.c"
 
-#define RD_SIDE   0
-#define WR_SIDE   1
+#define INIT_VERSION_NUMBER   0xd80100
 
-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];
+#define CMD_FORK     (-1)
+#define CMD_QUIT     (-2)
+#define CMD_FORWARD  (-3)
 
-enum { PK_MAIN_PROCESS, PK_FROZEN_PROCESS, PK_DEBUG_PROCESS };
-static unsigned char process_kind = PK_MAIN_PROCESS;
-static jmp_buf jmp_buf_cancel_execution;
-static uint64_t most_recent_fork;
-static uint64_t total_stop_points;
+#define ANSWER_INIT    (-20)
+#define ANSWER_STD     (-21)
+#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 const char *rpy_rev_filename;
 static uint64_t stopped_time;
 static uint64_t stopped_uid;
-static uint64_t first_created_uid;
-
-static void (*invoke_after_forward)(RPyString *);
-static RPyString *invoke_argument;
-
-struct jump_in_time_s {
-    uint64_t target_time;
-    char mode;
-    void *callback;
-    size_t arg_length;
-};
-
+static uint64_t total_stop_points;
+static jmp_buf jmp_buf_cancel_execution;
+static void (*pending_after_forward)(void);
 
 static void attach_gdb(void)
 {
@@ -256,11 +229,9 @@
         ssize_t rsize = read(rpy_rev_fileno, buf + result, count_max - result);
         if (rsize <= 0) {
             if (rsize == 0)
-                fprintf(stderr, "[%d] RevDB file appears truncated\n",
-                        process_kind);
+                fprintf(stderr, "RevDB file appears truncated\n");
             else
-                fprintf(stderr, "[%d] RevDB file read error: %m\n",
-                        process_kind);
+                fprintf(stderr, "RevDB file read error: %m\n");
             exit(1);
         }
         result += rsize;
@@ -273,11 +244,63 @@
     (void)read_at_least(buf, count, count);
 }
 
+
+static void read_pipe(int fd, void *buf, ssize_t count)
+{
+    while (count > 0) {
+        ssize_t got = read(fd, buf, count);
+        if (got <= 0) {
+            fprintf(stderr, "subprocess: cannot read pipe %d\n", fd);
+            exit(1);
+        }
+        count -= got;
+        buf += got;
+    }
+}
+
+static void write_pipe(int fd, const void *buf, ssize_t count)
+{
+    while (count > 0) {
+        ssize_t wrote = write(fd, buf, count);
+        if (wrote <= 0) {
+            fprintf(stderr, "subprocess: cannot write to pipe %d\n", fd);
+            exit(1);
+        }
+        count -= wrote;
+        buf += wrote;
+    }
+}
+
+static void write_answer(int cmd, int64_t arg1, int64_t arg2, int64_t arg3)
+{
+    rpy_revdb_command_t c;
+    memset(&c, 0, sizeof(c));
+    c.cmd = cmd;
+    c.arg1 = arg1;
+    c.arg2 = arg2;
+    c.arg3 = arg3;
+    write_pipe(RDB_STDOUT, &c, sizeof(c));
+}
+
+static void answer_std(void)
+{
+    write_answer(ANSWER_STD, rpy_revdb.stop_point_seen,
+                 rpy_revdb.unique_id_seen, 0);
+}
+
+static void reopen_revdb_file(const char *filename)
+{
+    rpy_rev_fileno = open(filename, O_RDONLY | O_NOCTTY);
+    if (rpy_rev_fileno < 0) {
+        fprintf(stderr, "Can't open file '%s': %m\n", filename);
+        exit(1);
+    }
+}
+
 static void setup_replay_mode(int *argc_p, char **argv_p[])
 {
     int argc = *argc_p;
     char **argv = *argv_p;
-    char *filename;
     rdb_header_t h;
     char input[16];
     ssize_t count;
@@ -286,20 +309,15 @@
         fprintf(stderr, "syntax: %s --revdb-replay <RevDB-file>\n", argv[0]);
         exit(2);
     }
-    filename = argv[2];
-
-    rpy_rev_fileno = open(filename, O_RDONLY | O_NOCTTY);
-    if (rpy_rev_fileno < 0) {
-        fprintf(stderr, "Can't open file '%s': %m\n", filename);
-        exit(1);
-    }
+    rpy_rev_filename = argv[2];
+    reopen_revdb_file(rpy_rev_filename);
 
     assert(RPY_RDB_REPLAY == 1);
 
     read_all(input, strlen(RDB_SIGNATURE));
     if (strncmp(input, RDB_SIGNATURE, strlen(RDB_SIGNATURE)) != 0) {
         fprintf(stderr, "'%s' is not a RevDB file (or wrong platform)\n",
-                filename);
+                rpy_rev_filename);
         exit(1);
     }
     fprintf(stderr, "%s", RDB_SIGNATURE);
@@ -321,7 +339,7 @@
             read(rpy_rev_fileno, &total_stop_points,
                  sizeof(uint64_t)) != sizeof(uint64_t) ||
             lseek(rpy_rev_fileno, count, SEEK_SET) != count) {
-        fprintf(stderr, "%s: %m\n", filename);
+        fprintf(stderr, "%s: %m\n", rpy_rev_filename);
         exit(1);
     }
 
@@ -330,10 +348,12 @@
     rpy_revdb.stop_point_break = 1;
     rpy_revdb.unique_id_seen = 1;
 
-    if (pipe(frozen_pipe_signal) < 0) {
-        perror("pipe");
-        exit(1);
-    }
+    write_answer(ANSWER_INIT, INIT_VERSION_NUMBER, total_stop_points, 0);
+    pending_after_forward = &answer_std;
+
+    /* ignore the SIGCHLD signals so that child processes don't become
+       zombies */
+    signal(SIGCHLD, SIG_IGN);
 }
 
 RPY_EXTERN
@@ -379,9 +399,10 @@
     flag_io_disabled = 0;
 
     if (pypy_g_ExcData.ed_exc_type != NULL) {
-        printf("Command crashed with %.*s\n",
-               (int)(pypy_g_ExcData.ed_exc_type->ov_name->rs_chars.length),
-               pypy_g_ExcData.ed_exc_type->ov_name->rs_chars.items);
+        fprintf(stderr, "Command crashed with %.*s\n",
+                (int)(pypy_g_ExcData.ed_exc_type->ov_name->rs_chars.length),
+                pypy_g_ExcData.ed_exc_type->ov_name->rs_chars.items);
+        exit(1);
     }
     /* restore the complete struct, with the exception of '*_break' */
     v1 = rpy_revdb.stop_point_break;
@@ -393,24 +414,7 @@
     pypy_g_ExcData.ed_exc_value = dinfo->saved_exc[1];
 }
 
-/* generated by RPython */
-extern char *rpy_revdb_command_names[];
-extern void (*rpy_revdb_command_funcs[])(RPyString *);
-
-static RPyString *make_rpy_string(size_t length)
-{
-    RPyString *s = malloc(sizeof(RPyString) + length);
-    if (s == NULL) {
-        fprintf(stderr, "out of memory for a string of %llu chars\n",
-                (unsigned long long)length);
-        exit(1);
-    }
-    /* xxx assumes Boehm here for now */
-    memset(s, 0, sizeof(RPyString));
-    RPyString_Size(s) = length;
-    return s;
-}
-
+/*
 static void execute_rpy_function(void func(RPyString *), RPyString *arg);
 
 static void execute_rpy_command(long index, char *arguments)
@@ -425,152 +429,27 @@
 
     execute_rpy_function(rpy_revdb_command_funcs[index], s);
 }
+*/
 
-static void execute_rpy_function(void func(RPyString *), RPyString *arg)
+static void execute_rpy_function(rpy_revdb_command_fn func,
+                                 rpy_revdb_command_t *cmd,
+                                 char *extra)
 {
     rpy_revdb_t dinfo;
     disable_io(&dinfo);
-    invoke_after_forward = NULL;
-    invoke_argument = NULL;
     if (setjmp(jmp_buf_cancel_execution) == 0)
-        func(arg);
+        func(cmd, extra);
     enable_io(&dinfo);
 }
 
-struct action_s {
-    const char *name;
-    void (*act)(char *);
-};
-
-static void process_input(char *input, const char *kind, int rpycmd,
-                          struct action_s actions[])
-{
-    char *p;
-    struct action_s *a;
-
-    while (isspace(*input))
-        input++;
-    p = input;
-    while (*p != 0 && !isspace(*p))
-        p++;
-    if (*p != 0) {
-        *p = 0;
-        do {
-            p++;
-        } while (isspace(*p));
-    }
-
-    if (rpycmd) {
-        long i;
-        for (i = 0; rpy_revdb_command_names[i] != NULL; i++) {
-            if (strcmp(rpy_revdb_command_names[i], input) == 0) {
-                execute_rpy_command(i, p);
-                return;
-            }
-        }
-    }
-
-    for (a = actions; a->name != NULL; a++) {
-        if (strcmp(a->name, input) == 0) {
-            a->act(p);
-            return;
-        }
-    }
-    if (strcmp(input, "help") == 0) {
-        printf("select %s:\n", kind);
-        if (rpycmd) {
-            char **p;
-            for (p = rpy_revdb_command_names; *p != NULL; p++)
-                printf("\t%s\n", *p);
-        }
-        for (a = actions; a->name != NULL; a++) {
-            if (*a->name != 0)
-                printf("\t%s\n", a->name);
-        }
-    }
-    else {
-        printf("bad %s '%s', try 'help'\n", kind, 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 int copy_pipe(int dst_fd, int src_fd, ssize_t count)
-{
-    char buffer[16384];
-    while (count > 0) {
-        ssize_t count1 = count > sizeof(buffer) ? sizeof(buffer) : count;
-        if (read_pipe(src_fd, buffer, count1) < 0 ||
-            write_pipe(dst_fd, buffer, count1) < 0)
-            return -1;
-        count -= count1;
-    }
-    return 0;
-}
-
-static void cmd_go(uint64_t target_time, void callback(RPyString *),
-                   RPyString *arg, char mode)
-{
-    struct jump_in_time_s header;
-
-    header.target_time = target_time;
-    header.mode = mode;
-    header.callback = callback;    /* may be NULL */
-    /* ^^^ assumes the fn address is the same in the various forks */
-    header.arg_length = arg == NULL ? 0 : RPyString_Size(arg);
-
-    assert(process_kind == PK_DEBUG_PROCESS);
-    write_pipe(frozen_pipe_signal[WR_SIDE], &header, sizeof(header));
-    if (header.arg_length > 0) {
-        write_pipe(frozen_pipe_signal[WR_SIDE], _RPyString_AsString(arg),
-                   header.arg_length);
-    }
-    exit(0);
-}
-
 static void check_at_end(uint64_t stop_points)
 {
     char dummy[1];
-    struct jump_in_time_s jump_in_time;
-
-    if (process_kind == PK_DEBUG_PROCESS) {
-        printf("At end.\n");
-        cmd_go(stop_points, NULL, NULL, 'g');
-        abort();   /* unreachable */
-    }
-
-    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 "
-                "(seen %llu, recorded %llu)\n",
-                (unsigned long long)rpy_revdb.stop_point_seen,
-                (unsigned long long)stop_points);
+                "(seen %lld, recorded %lld)\n",
+                (long long)rpy_revdb.stop_point_seen,
+                (long long)stop_points);
         exit(1);
     }
     if (rpy_revdb.buf_p != rpy_revdb.buf_limit ||
@@ -582,134 +461,21 @@
         fprintf(stderr, "RevDB file modified while reading?\n");
         exit(1);
     }
-    if (frozen_num_pipes == 0) {
-        fprintf(stderr, "RevDB file does not contain any stop points\n");
-        exit(1);
-    }
 
-    fprintf(stderr, "\n");
-    fflush(stderr);
-    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;
-
-    memset(&jump_in_time, 0, sizeof(jump_in_time));
-    jump_in_time.target_time = frozen_time[frozen_num_pipes-1];
-
-    while (jump_in_time.target_time != (uint64_t)-1) {
-        int p = frozen_num_pipes - 1;
-        if (jump_in_time.target_time > frozen_time[p])
-            jump_in_time.target_time = frozen_time[p];
-        while (frozen_time[p] > jump_in_time.target_time)
-            p--;
-        if (write_pipe(frozen_pipes[p][WR_SIDE],
-                       &jump_in_time, sizeof(jump_in_time)) < 0 ||
-            copy_pipe(frozen_pipes[p][WR_SIDE],
-                      frozen_pipe_signal[RD_SIDE],
-                      jump_in_time.arg_length) < 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], &jump_in_time,
-                      sizeof(jump_in_time)) < 0) {
-            fprintf(stderr, "broken signal pipe\n");
-            exit(1);
-        }
-    }
+    write_answer(ANSWER_AT_END, 0, 0, 0);
     exit(0);
 }
 
-static void run_frozen_process(int frozen_pipe_fd)
+static void command_fork(void)
 {
-    struct jump_in_time_s jump_in_time;
-    pid_t child_pid;
+    int child_pipes[2];
+    int child_pid;
+    off_t rev_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR);
 
-    while (1) {
-        if (read_pipe(frozen_pipe_fd, &jump_in_time, sizeof(jump_in_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(jump_in_time.target_time >= rpy_revdb.stop_point_seen);
-            most_recent_fork = rpy_revdb.stop_point_seen;
-            switch (jump_in_time.mode) {
-            case 'b':    /* go non-exact: stay at most_recent_fork */
-                rpy_revdb.stop_point_break = most_recent_fork;
-                break;
-            default:     /* other modes: go exact */
-                rpy_revdb.stop_point_break = jump_in_time.target_time;
-            }
-
-            if (jump_in_time.callback == NULL) {
-                assert(jump_in_time.arg_length == 0);
-                assert(invoke_after_forward == NULL);
-            }
-            else {
-                RPyString *s = make_rpy_string(jump_in_time.arg_length);
-                if (read_pipe(frozen_pipe_fd, _RPyString_AsString(s),
-                              jump_in_time.arg_length) < 0) {
-                    fprintf(stderr, "broken pipe to debug subprocess\n");
-                    exit(1);
-                }
-                invoke_after_forward = jump_in_time.callback;
-                invoke_argument = s;
-            }
-            /* 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 {
-                fprintf(stderr, "debugging subprocess died\n");
-                cmd_go((uint64_t)-1, NULL, NULL, 'q');
-                abort();    /* unreachable */
-            }
-        }
-    }
-}
-
-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");
+    if (ancil_recv_fds(RDB_STDIN, child_pipes, 2) != 2) {
+        fprintf(stderr, "cannot read child stdin/stdout pipes\n");
         exit(1);
     }
-    if (frozen_num_pipes == 0)
-        first_created_uid = rpy_revdb.unique_id_seen;
-
-    fprintf(stderr, "[%llu]",
-            (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) {
@@ -717,97 +483,113 @@
         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);
+        /* in the child */
+        if (dup2(child_pipes[0], RDB_STDIN) < 0 ||
+            dup2(child_pipes[1], RDB_STDOUT) < 0) {
+            perror("dup2");
+            exit(1);
+        }
+        /* 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
+           that the revdb log file is still the same.  So for Linux,
+           we try to open "/proc/self/fd/%d" instead. */
+        char fd_filename[48];
+        struct stat st;
+        const char *filename;
+        int old_fd = rpy_rev_fileno;
+
+        sprintf(fd_filename, "/proc/self/fd/%d", old_fd);
+        if (lstat(fd_filename, &st) == 0)
+            filename = fd_filename;
+        else
+            filename = rpy_rev_filename;
+        reopen_revdb_file(filename);
+
+        if (close(old_fd) < 0) {
+            perror("close");
+            exit(1);
+        }
+        if (lseek(rpy_rev_fileno, rev_offset, SEEK_SET) < 0) {
+            perror("lseek");
+            exit(1);
+        }
     }
     else {
-        /* in the main process: continue reloading the revdb log */
-        uint64_t remaining = total_stop_points - rpy_revdb.stop_point_break;
-        uint64_t delta;
-        double step = STEP_RATIO;
-        int remaining_freezes = NUM_FROZEN_PROCESSES - frozen_num_pipes;
-        if (step * remaining_freezes < 1.0)
-            step = 1.0 / remaining_freezes;
-        delta = (uint64_t)(remaining * step);
-        if (delta == 0 || delta > remaining || remaining_freezes == 1)
-            rpy_revdb.stop_point_break = total_stop_points;
-        else
-            rpy_revdb.stop_point_break += delta;
-        close(fds[RD_SIDE]);
-        fds[RD_SIDE] = -1;
+        /* 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);
     }
 }
 
-static void act_quit(char *p)
+static void command_forward(rpy_revdb_command_t *cmd)
 {
-    cmd_go((uint64_t)-1, NULL, NULL, 'q');
+    if (cmd->arg1 < rpy_revdb.stop_point_seen) {
+        fprintf(stderr, "CMD_FORWARD: target time %lld < current time %lld\n",
+                (long long)cmd->arg1, (long long)rpy_revdb.stop_point_seen);
+        exit(1);
+    }
+    rpy_revdb.stop_point_break = cmd->arg1;
+    pending_after_forward = &answer_std;
 }
 
-static void act_go(char *p)
+static void command_default(rpy_revdb_command_t *cmd, char *extra)
 {
-    int64_t target_time = strtoll(p, NULL, 10);
-    if (target_time <= 0) {
-        printf("usage: go <target_time>\n");
-        return;
+    int i;
+    for (i = 0; rpy_revdb_command_names[i] != cmd->cmd; i++) {
+        if (rpy_revdb_command_names[i] == 0) {
+            fprintf(stderr, "unknown command %d\n", cmd->cmd);
+            exit(1);
+        }
     }
-    cmd_go(target_time, NULL, NULL, 'g');
+    execute_rpy_function(rpy_revdb_command_funcs[i], cmd, extra);
 }
 
-static void act_info(char *p)
+RPY_EXTERN
+void rpy_reverse_db_stop_point(void)
 {
-    char cmd = *p;
-    if (cmd == 0)
-        cmd = '?';
-    printf("info %c=%lld\n", cmd, (long long)rpy_reverse_db_get_value(cmd));
-}
-
-static void act_nop(char *p)
-{
-}
-
-static void act_forward(char *p)
-{
-    int64_t delta = strtoll(p, NULL, 10);
-    if (delta <= 0) {
-        if (delta < 0 || *p == 0)
-            printf("usage: forward <time_steps>\n");
-        return;
-    }
-    rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + delta;
-}
-
-static void run_debug_process(void)
-{
-    static struct action_s actions_1[] = {
-        { "info", act_info },
-        { "quit", act_quit },
-        { "__go", act_go },
-        { "__forward", act_forward },
-        { "", act_nop },
-        { NULL }
-    };
     while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
         stopped_time = rpy_revdb.stop_point_seen;
         stopped_uid = rpy_revdb.unique_id_seen;
         rpy_revdb.unique_id_seen = (-1ULL) << 63;
-        if (invoke_after_forward != NULL) {
-            execute_rpy_function(invoke_after_forward, invoke_argument);
+
+        if (pending_after_forward) {
+            void (*fn)(void) = pending_after_forward;
+            pending_after_forward = NULL;
+            fn();
         }
         else {
-            char input[256];
-            printf("(%llu)$ ", (unsigned long long)stopped_time);
-            fflush(stdout);
-            if (fgets(input, sizeof(input), stdin) != input) {
-                fprintf(stderr, "\n");
-                act_quit("");
-                abort();   /* unreachable */
+            rpy_revdb_command_t cmd;
+            read_pipe(RDB_STDIN, &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);
+
+            switch (cmd.cmd) {
+
+            case CMD_FORK:
+                command_fork();
+                break;
+
+            case CMD_QUIT:
+                exit(0);
+                break;
+
+            case CMD_FORWARD:
+                command_forward(&cmd);
+                break;
+
+            default:
+                command_default(&cmd, extra);
+                break;
             }
-            process_input(input, "command", 1, actions_1);
         }
         rpy_revdb.unique_id_seen = stopped_uid;
         stopped_time = 0;
@@ -816,30 +598,28 @@
 }
 
 RPY_EXTERN
-void rpy_reverse_db_stop_point(void)
+void rpy_reverse_db_send_answer(int cmd, int64_t arg1, int64_t arg2,
+                                int64_t arg3, RPyString *extra)
 {
-    if (process_kind == PK_MAIN_PROCESS) {
-        make_new_frozen_process();
-        if (process_kind == PK_MAIN_PROCESS)
-            return;
-    }
-    assert(process_kind == PK_DEBUG_PROCESS);
-    run_debug_process();
-}
-
-RPY_EXTERN
-void rpy_reverse_db_send_output(RPyString *output)
-{
-    fwrite(_RPyString_AsString(output), 1, RPyString_Size(output), stdout);
+    rpy_revdb_command_t c;
+    memset(&c, 0, sizeof(c));
+    c.cmd = cmd;
+    c.extra_size = RPyString_Size(extra);
+    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);
 }
 
 RPY_EXTERN
 void rpy_reverse_db_change_time(char mode, long long time,
-                                void callback(RPyString *), RPyString *arg)
+                                void callback(void))
 {
     switch (mode) {
 
-    case 'f': {      /* forward */
+    case 'f':        /* forward */
         if (time < 0) {
             fprintf(stderr, "revdb.go_forward(): negative amount of steps\n");
             exit(1);
@@ -849,25 +629,22 @@
             exit(1);
         }
         rpy_revdb.stop_point_break = stopped_time + time;
-        invoke_after_forward = callback;
-        invoke_argument = arg;
+        pending_after_forward = callback;
         break;
-    }
-    case 'g':       /* go */
-    case 'b':       /* go non exact */
-        cmd_go(time >= 1 ? time : 1, callback, arg, mode);
-        abort();    /* unreachable */
 
     case 'k':       /* breakpoint */
-        assert(time > 0);
+        if (time <= 0) {
+            fprintf(stderr, "revdb.breakpoint(): non-positive amount of "
+                            "steps\n");
+            exit(1);
+        }
         if (stopped_time != 0) {
             fprintf(stderr, "revdb.breakpoint(): cannot be called from a "
                             "debug command\n");
             exit(1);
         }
         rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + time;
-        invoke_after_forward = callback;
-        invoke_argument = arg;
+        pending_after_forward = callback;
         break;
 
     default:
@@ -881,16 +658,12 @@
     switch (value_id) {
     case 'c':       /* current_time() */
         return stopped_time ? stopped_time : rpy_revdb.stop_point_seen;
-    case 'f':       /* most_recent_fork() */
-        return most_recent_fork;
     case 't':       /* total_time() */
         return total_stop_points;
     case 'b':       /* current_break_time() */
         return rpy_revdb.stop_point_break;
     case 'u':       /* currently_created_objects() */
         return stopped_uid ? stopped_uid : rpy_revdb.unique_id_seen;
-    case '1':       /* first_created_object_uid() */
-        return first_created_uid;
     default:
         return -1;
     }
diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h 
b/rpython/translator/revdb/src-revdb/revdb_include.h
--- a/rpython/translator/revdb/src-revdb/revdb_include.h
+++ b/rpython/translator/revdb/src-revdb/revdb_include.h
@@ -1,5 +1,4 @@
 #include <string.h>
-#include <stdint.h>
 
 /* By default, this makes an executable which supports both recording
    and replaying.  It should help avoid troubles like using for
@@ -85,8 +84,8 @@
 #define OP_REVDB_SEND_OUTPUT(ll_string, r)                              \
     rpy_reverse_db_send_output(ll_string)
 
-#define OP_REVDB_CHANGE_TIME(mode, time, callback, ll_string, r)        \
-    rpy_reverse_db_change_time(mode, time, callback, ll_string)
+#define OP_REVDB_CHANGE_TIME(mode, time, callback, r)                   \
+    rpy_reverse_db_change_time(mode, time, callback)
 
 #define OP_REVDB_GET_VALUE(value_id, r)                                 \
     r = rpy_reverse_db_get_value(value_id)
@@ -107,8 +106,7 @@
 RPY_EXTERN void rpy_reverse_db_send_output(RPyString *output);
 RPY_EXTERN Signed rpy_reverse_db_identityhash(struct pypy_header0 *obj);
 RPY_EXTERN void rpy_reverse_db_change_time(char mode, long long time,
-                                           void callback(RPyString *),
-                                           RPyString *arg);
+                                           void callback(void));
 RPY_EXTERN long long rpy_reverse_db_get_value(char value_id);
 RPY_EXTERN uint64_t rpy_reverse_db_unique_id_break(void *new_object);
 RPY_EXTERN void rpy_reverse_db_track_object(long long unique_id,
diff --git a/rpython/translator/revdb/src-revdb/revdb_preinclude.h 
b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
@@ -0,0 +1,9 @@
+#include <stdint.h>
+
+typedef struct rpy_revdb_command_s {
+    int cmd;      /* neg for standard commands, pos for interp-specific */
+    size_t extra_size;
+    int64_t arg1;
+    int64_t arg2;
+    int64_t arg3;
+} rpy_revdb_command_t;
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to