[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