[pypy-commit] pypy reverse-debugger: Ability to go forward programatically from RPython debug commands
arigo
pypy.commits at gmail.com
Sun Jun 12 10:46:59 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85105:3d318752b5b2
Date: 2016-06-12 16:47 +0200
http://bitbucket.org/pypy/pypy/changeset/3d318752b5b2/
Log: Ability to go forward programatically from RPython debug commands
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,6 +567,8 @@
'revdb_stop_point': LLOp(),
'revdb_send_output': LLOp(),
+ 'revdb_go_forward': LLOp(),
+ 'revdb_get_value': LLOp(sideeffects=False),
'revdb_identityhash': LLOp(),
}
# ***** Run test_lloperation after changes. *****
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
@@ -14,7 +14,7 @@
#include "src/rtyper.h"
#include "rdb-src/revdb_include.h"
-#define RDB_SIGNATURE "RDB:"
+#define RDB_SIGNATURE "RevDB:"
#define RDB_VERSION 0x00FF0001
@@ -41,7 +41,7 @@
{
/* init-time setup */
- int replay_asked = (*argc_p >= 2 && !strcmp((*argv_p)[1], "--replay"));
+ int replay_asked = (*argc_p >= 2 && !strcmp((*argv_p)[1],"--revdb-replay"));
#ifdef RPY_RDB_DYNAMIC_REPLAY
RPY_RDB_REPLAY = replay_asked;
@@ -219,11 +219,11 @@
enum { PK_MAIN_PROCESS, PK_FROZEN_PROCESS, PK_DEBUG_PROCESS };
static unsigned char process_kind = PK_MAIN_PROCESS;
-static unsigned char flag_exit_run_debug_process;
static jmp_buf jmp_buf_cancel_execution;
-static uint64_t latest_fork;
+static uint64_t most_recent_fork;
+static uint64_t total_stop_points;
-static uint64_t total_stop_points;
+static void (*invoke_after_forward)(void);
static void attach_gdb(void)
@@ -265,11 +265,11 @@
char **argv = *argv_p;
char *filename;
rdb_header_t h;
- char input[sizeof(rdb_header_t)];
+ char input[16];
ssize_t count;
if (argc != 3) {
- fprintf(stderr, "syntax: %s --replay <RevDB-file>\n", argv[0]);
+ fprintf(stderr, "syntax: %s --revdb-replay <RevDB-file>\n", argv[0]);
exit(2);
}
filename = argv[2];
@@ -289,16 +289,10 @@
exit(1);
}
fprintf(stderr, "%s", RDB_SIGNATURE);
- do {
- count = read_at_least(input, 1, sizeof(input) - 1);
- input[count] = 0;
- fprintf(stderr, "%s", input);
- } while (strlen(input) == count);
+ while ((read_all(input, 1), input[0] != 0))
+ fwrite(input, 1, 1, stderr);
- count -= (strlen(input) + 1);
- memcpy(&h, input + strlen(input) + 1, count);
-
- read_all(((char *)&h) + count, sizeof(h) - count);
+ read_all(&h, sizeof(h));
if (h.version != RDB_VERSION) {
fprintf(stderr, "RevDB file version mismatch (got %lx, expected %lx)\n",
(long)h.version, (long)RDB_VERSION);
@@ -362,19 +356,25 @@
static void enable_io(rpy_revdb_t *dinfo)
{
+ uint64_t v;
flag_io_disabled = 0;
+
+ /* restore the complete struct, with the exception of 'stop_point_break' */
+ v = rpy_revdb.stop_point_break;
rpy_revdb = *dinfo;
+ rpy_revdb.stop_point_break = v;
}
/* generated by RPython */
extern char *rpy_revdb_command_names[];
extern void (*rpy_revdb_command_funcs[])(RPyString *);
+static void execute_rpy_function(void func(RPyString *), RPyString *arg);
+
static void execute_rpy_command(long index, char *arguments)
{
size_t length = strlen(arguments);
RPyString *s;
- rpy_revdb_t dinfo;
while (length > 0 && isspace(arguments[length - 1]))
length--;
@@ -388,24 +388,32 @@
RPyString_Size(s) = length;
memcpy(_RPyString_AsString(s), arguments, length);
+ execute_rpy_function(rpy_revdb_command_funcs[index], s);
+}
+
+static void execute_rpy_function(void func(RPyString *), RPyString *arg)
+{
+ rpy_revdb_t dinfo;
+ void *saved_t = pypy_g_ExcData.ed_exc_type;
+ void *saved_v = pypy_g_ExcData.ed_exc_value;
+ pypy_g_ExcData.ed_exc_type = NULL;
+ pypy_g_ExcData.ed_exc_value = NULL;
disable_io(&dinfo);
+ invoke_after_forward = NULL;
+
if (setjmp(jmp_buf_cancel_execution) == 0) {
- void *saved_t = pypy_g_ExcData.ed_exc_type;
- void *saved_v = pypy_g_ExcData.ed_exc_value;
- pypy_g_ExcData.ed_exc_type = NULL;
- pypy_g_ExcData.ed_exc_value = NULL;
- rpy_revdb_command_funcs[index](s);
+ func(arg);
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);
}
- pypy_g_ExcData.ed_exc_type = saved_t;
- pypy_g_ExcData.ed_exc_value = saved_v;
}
enable_io(&dinfo);
+ pypy_g_ExcData.ed_exc_type = saved_t;
+ pypy_g_ExcData.ed_exc_value = saved_v;
}
struct action_s {
@@ -533,6 +541,8 @@
exit(1);
}
+ fprintf(stderr, "\n");
+ fflush(stderr);
printf("Replaying finished\n");
printf("stop_points=%lld\n", (long long)stop_points);
@@ -580,7 +590,7 @@
/* in the child: this is a debug process */
process_kind = PK_DEBUG_PROCESS;
assert(target_time >= rpy_revdb.stop_point_seen);
- latest_fork = rpy_revdb.stop_point_seen;
+ most_recent_fork = rpy_revdb.stop_point_seen;
rpy_revdb.stop_point_break = target_time;
/* continue "running" the RPython program until we reach
exactly the specified target_time */
@@ -616,7 +626,7 @@
exit(1);
}
- fprintf(stderr, "forking at time %llu\n",
+ fprintf(stderr, "[%llu]",
(unsigned long long)rpy_revdb.stop_point_seen);
fds = frozen_pipes[frozen_num_pipes];
@@ -678,7 +688,7 @@
static void act_info_fork(char *p)
{
- printf("latest_fork=%llu\n", (unsigned long long)latest_fork);
+ printf("most_recent_fork=%llu\n", (unsigned long long)most_recent_fork);
}
static void act_info(char *p)
@@ -703,7 +713,6 @@
return;
}
rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + delta;
- flag_exit_run_debug_process = 1;
}
static void run_debug_process(void)
@@ -716,17 +725,22 @@
{ "", act_nop },
{ NULL }
};
- flag_exit_run_debug_process = 0;
- while (!flag_exit_run_debug_process) {
- char input[256];
- printf("(%llu)$ ", (unsigned long long)rpy_revdb.stop_point_seen);
- fflush(stdout);
- if (fgets(input, sizeof(input), stdin) != input) {
- fprintf(stderr, "\n");
- act_quit("");
- abort(); /* unreachable */
+ 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);
}
- process_input(input, "command", 1, actions_1);
+ else {
+ char input[256];
+ printf("(%llu)$ ", (unsigned long long)rpy_revdb.stop_point_seen);
+ fflush(stdout);
+ if (fgets(input, sizeof(input), stdin) != input) {
+ fprintf(stderr, "\n");
+ act_quit("");
+ abort(); /* unreachable */
+ }
+ process_input(input, "command", 1, actions_1);
+ }
}
}
@@ -737,8 +751,6 @@
make_new_frozen_process();
if (process_kind == PK_MAIN_PROCESS)
return;
- if (rpy_revdb.stop_point_seen != rpy_revdb.stop_point_break)
- return;
}
assert(process_kind == PK_DEBUG_PROCESS);
run_debug_process();
@@ -750,5 +762,31 @@
fwrite(_RPyString_AsString(output), 1, RPyString_Size(output), stdout);
}
+RPY_EXTERN
+void rpy_reverse_db_go_forward(Signed steps, void callback(void))
+{
+ if (steps < 0) {
+ fprintf(stderr, "revdb.go_forward(): negative amount of steps\n");
+ exit(1);
+ }
+ rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + steps;
+ invoke_after_forward = callback;
+}
+
+RPY_EXTERN
+Signed rpy_reverse_db_get_value(char value_id)
+{
+ switch (value_id) {
+ case 'c': /* current_time() */
+ return rpy_revdb.stop_point_seen;
+ case 'm': /* most_recent_fork() */
+ return most_recent_fork;
+ case 't': /* total_time() */
+ return total_stop_points;
+ default:
+ return -1;
+ }
+}
+
/* ------------------------------------------------------------ */
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,6 +74,12 @@
#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_GET_VALUE(value_id, r) \
+ r = rpy_reverse_db_get_value(value_id)
+
#define OP_REVDB_IDENTITYHASH(obj, r) \
r = rpy_reverse_db_identityhash((struct pypy_header0 *)(obj))
@@ -83,6 +89,8 @@
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 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
@@ -18,7 +18,7 @@
with open(filename, 'rb') as f:
header = f.readline()
self.buffer = f.read()
- assert header == 'RDB: ' + ' '.join(expected_argv) + '\n'
+ assert header == 'RevDB: ' + ' '.join(expected_argv) + '\n'
#
self.cur = 0
x = self.next('c'); assert x == '\x00'
@@ -178,7 +178,7 @@
def replay(self, **kwds):
kwds.setdefault('timeout', 10)
child = pexpect.spawn(str(self.exename),
- ['--replay', str(self.rdbname)], **kwds)
+ ['--revdb-replay', str(self.rdbname)], **kwds)
child.logfile = sys.stdout
def expectx(s):
child.expect(re.escape(s))
@@ -202,8 +202,8 @@
def test_go(self):
child = self.replay()
- child.expectx('stop_points=3\r\n')
- child.expectx('(3)$ ')
+ child.expectx('stop_points=3\r\n'
+ '(3)$ ')
child.sendline('go 1')
child.expectx('(1)$ ')
child.sendline('')
@@ -223,7 +223,7 @@
def test_info_fork(self):
child = self.replay()
child.sendline('info fork')
- child.expectx('latest_fork=3\r\n')
+ child.expectx('most_recent_fork=3\r\n')
def test_quit(self):
child = self.replay()
@@ -239,12 +239,12 @@
child.sendline('forward 1')
child.expectx('(3)$ ')
child.sendline('info fork')
- child.expectx('latest_fork=1\r\n')
+ child.expectx('most_recent_fork=1\r\n')
child.sendline('forward 1')
- child.expectx('At end.\r\n')
- child.expectx('(3)$ ')
+ child.expectx('At end.\r\n'
+ '(3)$ ')
child.sendline('info fork')
- child.expectx('latest_fork=3\r\n')
+ child.expectx('most_recent_fork=3\r\n')
class TestDebugCommands(InteractiveTests):
@@ -256,6 +256,11 @@
raise ValueError
g._dont_inline_ = True
#
+ def went_fw():
+ revdb.send_output('went-fw -> %d\n' % revdb.current_time())
+ if revdb.current_time() != revdb.total_time():
+ revdb.go_forward(1, went_fw)
+ #
def blip(cmdline):
revdb.send_output('<<<' + cmdline + '>>>\n')
if cmdline == 'oops':
@@ -268,6 +273,12 @@
pass
if cmdline == 'crash':
raise ValueError
+ if cmdline == 'get-value':
+ revdb.send_output('%d,%d,%d\n' % (revdb.current_time(),
+ revdb.most_recent_fork(),
+ revdb.total_time()))
+ if cmdline == 'go-fw':
+ revdb.go_forward(1, went_fw)
revdb.send_output('blipped\n')
lambda_blip = lambda: blip
#
@@ -284,16 +295,17 @@
child = self.replay()
child.expectx('(3)$ ')
child.sendline('r foo bar baz ')
- child.expectx('<<<foo bar baz>>>\r\nblipped\r\n')
- child.expectx('(3)$ ')
+ child.expectx('<<<foo bar baz>>>\r\n'
+ 'blipped\r\n'
+ '(3)$ ')
def test_io_not_permitted(self):
child = self.replay()
child.expectx('(3)$ ')
child.sendline('r oops')
child.expectx('<<<oops>>>\r\n')
- child.expectx('Attempted to do I/O or access raw memory')
- child.expectx('(3)$ ')
+ child.expectx(': Attempted to do I/O or access raw memory\r\n'
+ '(3)$ ')
def test_interaction_with_forward(self):
child = self.replay()
@@ -302,22 +314,47 @@
child.expectx('(1)$ ')
child.sendline('r oops')
child.expectx('<<<oops>>>\r\n')
- child.expectx('Attempted to do I/O or access raw memory')
- child.expectx('(1)$ ')
+ child.expectx('Attempted to do I/O or access raw memory\r\n'
+ '(1)$ ')
child.sendline('forward 50')
- child.expectx('At end.\r\n')
- child.expectx('(3)$ ')
+ child.expectx('At end.\r\n'
+ '(3)$ ')
def test_raise_and_catch(self):
child = self.replay()
child.expectx('(3)$ ')
child.sendline('r raise-and-catch')
- child.expectx('<<<raise-and-catch>>>\r\nblipped\r\n')
- child.expectx('(3)$ ')
+ child.expectx('<<<raise-and-catch>>>\r\n'
+ 'blipped\r\n'
+ '(3)$ ')
def test_crash(self):
child = self.replay()
child.expectx('(3)$ ')
child.sendline('r crash')
- child.expectx('<<<crash>>>\r\nCommand crashed with ValueError')
+ child.expectx('<<<crash>>>\r\n'
+ 'Command crashed with ValueError\r\n'
+ '(3)$ ')
+
+ def test_get_value(self):
+ child = self.replay()
child.expectx('(3)$ ')
+ child.sendline('go 2')
+ child.expectx('(2)$ ')
+ child.sendline('r get-value')
+ child.expectx('<<<get-value>>>\r\n'
+ '2,1,3\r\n'
+ 'blipped\r\n'
+ '(2)$ ')
+
+ def test_go_fw(self):
+ child = self.replay()
+ child.expectx('(3)$ ')
+ child.sendline('go 1')
+ child.expectx('(1)$ ')
+ child.sendline('r go-fw')
+ child.expectx('<<<go-fw>>>\r\n'
+ 'blipped\r\n'
+ 'went-fw -> 2\r\n'
+ 'went-fw -> 3\r\n'
+ '(3)$ ')
More information about the pypy-commit
mailing list