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

Reply via email to