Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r85107:535af90a99e0
Date: 2016-06-12 18:16 +0200
http://bitbucket.org/pypy/pypy/changeset/535af90a99e0/
Log: Add the jump_in_time() function
diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -4,34 +4,75 @@
from rpython.rtyper.lltypesystem import lltype, rstr
from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rtyper.extregistry import ExtRegistryEntry
-from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.annlowlevel import llhelper, hlstr
def stop_point(n):
+ """Indicates a point in the execution of the RPython program where
+ the reverse-debugger can stop. When reverse-debugging, we see
+ the "time" as the index of the stop-point that happened.
+ """
if we_are_translated():
if fetch_translated_config().translation.reverse_debugger:
llop.revdb_stop_point(lltype.Void, n)
def register_debug_command(command, lambda_func):
- pass
+ """Register the extra RPython-implemented debug command."""
-def send_output(string): # monkey-patch this for untranslated tests
+def send_output(string):
+ """For RPython debug commands: writes the string to stdout."""
llop.revdb_send_output(lltype.Void, string)
def current_time():
+ """For RPython debug commands: returns the current time."""
return llop.revdb_get_value(lltype.Signed, 'c')
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.Signed, 'm')
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.Signed, 't')
@specialize.arg(1)
-def go_forward(time_delta, callback):
- ll_callback = llhelper(_CALLBACK_FNPTR, callback)
- llop.revdb_go_forward(lltype.Void, time_delta, ll_callback)
-_CALLBACK_FNPTR = lltype.Ptr(lltype.FuncType([], lltype.Void))
+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](1)
+def jump_in_time(target_time, callback, arg_string):
+ """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.
+ """
+ _change_time('g', target_time, callback, arg_string)
+
+
+# ____________________________________________________________
+
+
[email protected](1)
+def _change_time(mode, time, callback, arg_string):
+ 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)
+
[email protected]()
+def _make_callback(callback):
+ def callback_wrapper(ll_string):
+ callback(hlstr(ll_string))
+ return callback_wrapper
+_CALLBACK_ARG_FNPTR = lltype.Ptr(lltype.FuncType([lltype.Ptr(rstr.STR)],
+ lltype.Void))
class RegisterDebugCommand(ExtRegistryEntry):
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
@@ -567,7 +567,7 @@
'revdb_stop_point': LLOp(),
'revdb_send_output': LLOp(),
- 'revdb_go_forward': LLOp(),
+ 'revdb_change_time': LLOp(),
'revdb_get_value': LLOp(sideeffects=False),
'revdb_identityhash': LLOp(),
}
diff --git a/rpython/translator/revdb/rdb-src/revdb.c
b/rpython/translator/revdb/rdb-src/revdb.c
--- a/rpython/translator/revdb/rdb-src/revdb.c
+++ b/rpython/translator/revdb/rdb-src/revdb.c
@@ -223,7 +223,14 @@
static uint64_t most_recent_fork;
static uint64_t total_stop_points;
-static void (*invoke_after_forward)(void);
+static void (*invoke_after_forward)(RPyString *);
+static RPyString *invoke_argument;
+
+struct jump_in_time_s {
+ uint64_t target_time;
+ void *callback;
+ size_t arg_length;
+};
static void attach_gdb(void)
@@ -369,6 +376,20 @@
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)
@@ -378,14 +399,7 @@
while (length > 0 && isspace(arguments[length - 1]))
length--;
- s = malloc(sizeof(RPyString) + length);
- if (s == NULL) {
- fprintf(stderr, "out of memory\n");
- exit(1);
- }
- /* xxx assumes Boehm here for now */
- memset(s, 0, sizeof(RPyString));
- RPyString_Size(s) = length;
+ s = make_rpy_string(length);
memcpy(_RPyString_AsString(s), arguments, length);
execute_rpy_function(rpy_revdb_command_funcs[index], s);
@@ -400,6 +414,7 @@
pypy_g_ExcData.ed_exc_value = NULL;
disable_io(&dinfo);
invoke_after_forward = NULL;
+ invoke_argument = NULL;
if (setjmp(jmp_buf_cancel_execution) == 0) {
@@ -496,22 +511,46 @@
return 0;
}
-static void cmd_go(uint64_t target_time)
+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)
+{
+ struct jump_in_time_s header;
+
+ header.target_time = target_time;
+ 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], &target_time,
- sizeof(target_time));
+ 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];
- uint64_t target_time;
+ struct jump_in_time_s jump_in_time;
if (process_kind == PK_DEBUG_PROCESS) {
printf("At end.\n");
- cmd_go(rpy_revdb.stop_point_seen);
+ cmd_go(rpy_revdb.stop_point_seen, NULL, NULL);
abort(); /* unreachable */
}
@@ -549,22 +588,27 @@
close(frozen_pipe_signal[WR_SIDE]);
frozen_pipe_signal[WR_SIDE] = -1;
- target_time = frozen_time[frozen_num_pipes-1];
- while (target_time != (uint64_t)-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 (target_time > frozen_time[p])
- target_time = frozen_time[p];
- while (frozen_time[p] > target_time)
+ 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],
- &target_time, sizeof(target_time)) < 0) {
+ &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], &target_time,
- sizeof(target_time)) < 0) {
+ if (read_pipe(frozen_pipe_signal[RD_SIDE], &jump_in_time,
+ sizeof(jump_in_time)) < 0) {
fprintf(stderr, "broken signal pipe\n");
exit(1);
}
@@ -574,11 +618,11 @@
static void run_frozen_process(int frozen_pipe_fd)
{
- uint64_t target_time;
+ struct jump_in_time_s jump_in_time;
pid_t child_pid;
while (1) {
- if (read_pipe(frozen_pipe_fd, &target_time, sizeof(target_time)) < 0)
+ if (read_pipe(frozen_pipe_fd, &jump_in_time, sizeof(jump_in_time)) < 0)
exit(1);
child_pid = fork();
@@ -589,9 +633,24 @@
if (child_pid == 0) {
/* in the child: this is a debug process */
process_kind = PK_DEBUG_PROCESS;
- assert(target_time >= rpy_revdb.stop_point_seen);
+ assert(jump_in_time.target_time >= rpy_revdb.stop_point_seen);
most_recent_fork = rpy_revdb.stop_point_seen;
- rpy_revdb.stop_point_break = target_time;
+ 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;
@@ -605,11 +664,9 @@
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
; /* normal exit */
else {
- target_time = (uint64_t)-1;
fprintf(stderr, "debugging subprocess died\n");
- write_pipe(frozen_pipe_signal[WR_SIDE], &target_time,
- sizeof(target_time));
- exit(1); /* error */
+ cmd_go((uint64_t)-1, NULL, NULL);
+ abort(); /* unreachable */
}
}
}
@@ -673,7 +730,7 @@
static void act_quit(char *p)
{
- cmd_go((uint64_t)-1);
+ cmd_go((uint64_t)-1, NULL, NULL);
}
static void act_go(char *p)
@@ -683,7 +740,7 @@
printf("usage: go <target_time>\n");
return;
}
- cmd_go(target_time);
+ cmd_go(target_time, NULL, NULL);
}
static void act_info_fork(char *p)
@@ -727,8 +784,7 @@
};
while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
if (invoke_after_forward != NULL) {
- execute_rpy_function((void(*)(RPyString *))invoke_after_forward,
- NULL);
+ execute_rpy_function(invoke_after_forward, invoke_argument);
}
else {
char input[256];
@@ -763,14 +819,28 @@
}
RPY_EXTERN
-void rpy_reverse_db_go_forward(Signed steps, void callback(void))
+void rpy_reverse_db_change_time(char mode, Signed time,
+ void callback(RPyString *), RPyString *arg)
{
- if (steps < 0) {
- fprintf(stderr, "revdb.go_forward(): negative amount of steps\n");
- exit(1);
+ switch (mode) {
+
+ case 'f': { /* forward */
+ if (time < 0) {
+ fprintf(stderr, "revdb.go_forward(): negative amount of steps\n");
+ exit(1);
+ }
+ rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + time;
+ invoke_after_forward = callback;
+ invoke_argument = arg;
+ break;
}
- rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + steps;
- invoke_after_forward = callback;
+ case 'g': { /* go */
+ cmd_go(time >= 1 ? time : 1, callback, arg);
+ abort(); /* unreachable */
+ }
+ default:
+ abort(); /* unreachable */
+ }
}
RPY_EXTERN
diff --git a/rpython/translator/revdb/rdb-src/revdb_include.h
b/rpython/translator/revdb/rdb-src/revdb_include.h
--- a/rpython/translator/revdb/rdb-src/revdb_include.h
+++ b/rpython/translator/revdb/rdb-src/revdb_include.h
@@ -74,8 +74,8 @@
#define OP_REVDB_SEND_OUTPUT(ll_string, r) \
rpy_reverse_db_send_output(ll_string)
-#define OP_REVDB_GO_FORWARD(time_delta, callback, r) \
- rpy_reverse_db_go_forward(time_delta, callback)
+#define OP_REVDB_CHANGE_TIME(mode, time, callback, ll_string, r) \
+ rpy_reverse_db_change_time(mode, time, callback, ll_string)
#define OP_REVDB_GET_VALUE(value_id, r) \
r = rpy_reverse_db_get_value(value_id)
@@ -89,7 +89,9 @@
RPY_EXTERN void rpy_reverse_db_break(long stop_point);
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_go_forward(Signed steps, void callback(void));
+RPY_EXTERN void rpy_reverse_db_change_time(char mode, Signed time,
+ void callback(RPyString *),
+ RPyString *arg);
RPY_EXTERN Signed rpy_reverse_db_get_value(char value_id);
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
@@ -256,10 +256,16 @@
raise ValueError
g._dont_inline_ = True
#
- def went_fw():
- revdb.send_output('went-fw -> %d\n' % revdb.current_time())
+ def went_fw(arg):
+ revdb.send_output('went-fw %s -> %d\n' % (arg,
+ revdb.current_time()))
if revdb.current_time() != revdb.total_time():
- revdb.go_forward(1, went_fw)
+ revdb.go_forward(1, went_fw, "yy")
+ def changed_time(arg):
+ revdb.send_output('changed-time %s -> %d\n' % (arg,
+ revdb.current_time()))
+ if revdb.current_time() != revdb.total_time():
+ revdb.go_forward(1, went_fw, "zz")
#
def blip(cmdline):
revdb.send_output('<<<' + cmdline + '>>>\n')
@@ -278,7 +284,9 @@
revdb.most_recent_fork(),
revdb.total_time()))
if cmdline == 'go-fw':
- revdb.go_forward(1, went_fw)
+ revdb.go_forward(1, went_fw, "xx")
+ if cmdline == 'change-time':
+ revdb.jump_in_time(2, changed_time, "xyzzy")
revdb.send_output('blipped\n')
lambda_blip = lambda: blip
#
@@ -355,6 +363,15 @@
child.sendline('r go-fw')
child.expectx('<<<go-fw>>>\r\n'
'blipped\r\n'
- 'went-fw -> 2\r\n'
- 'went-fw -> 3\r\n'
+ 'went-fw xx -> 2\r\n'
+ 'went-fw yy -> 3\r\n'
'(3)$ ')
+
+ def test_change_time(self):
+ child = self.replay()
+ child.expectx('(3)$ ')
+ child.sendline('r change-time')
+ child.expectx('<<<change-time>>>\r\n'
+ 'changed-time xyzzy -> 2\r\n'
+ 'went-fw zz -> 3\r\n'
+ '(3)$ ')
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit