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 = ¬hing;
+ nothing_ptr.iov_len = 1;
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = ¬hing_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