Author: Armin Rigo <ar...@tunes.org>
Branch: reverse-debugger
Changeset: r85419:9b947e1ab238
Date: 2016-06-28 10:07 +0200
http://bitbucket.org/pypy/pypy/changeset/9b947e1ab238/

Log:    Clean up the breakpoint system, with a single 'stop_point_break'
        controlled from several internal variables in revdb.c

diff --git a/rpython/translator/revdb/interact.py 
b/rpython/translator/revdb/interact.py
--- a/rpython/translator/revdb/interact.py
+++ b/rpython/translator/revdb/interact.py
@@ -65,6 +65,13 @@
                     runner(argument)
                 except Exception as e:
                     traceback.print_exc()
+                    print >> sys.stderr
+                    print >> sys.stderr, 'Something went wrong.  You are now',
+                    print >> sys.stderr, 'in a pdb; press Ctrl-D to continue.'
+                    import pdb; pdb.post_mortem(sys.exc_info()[2])
+                    print >> sys.stderr
+                    print >> sys.stderr, 'You are back running %s.' % (
+                        sys.argv[0],)
 
     def command_help(self, argument):
         """Display commands summary"""
diff --git a/rpython/translator/revdb/message.py 
b/rpython/translator/revdb/message.py
--- a/rpython/translator/revdb/message.py
+++ b/rpython/translator/revdb/message.py
@@ -8,6 +8,7 @@
 CMD_QUIT      = -2     # Message(CMD_QUIT)
 CMD_FORWARD   = -3     # Message(CMD_FORWARD, steps, breakpoint_mode)
 CMD_FUTUREIDS = -4     # Message(CMD_FUTUREIDS, extra=list-of-8bytes-uids)
+CMD_PING      = -5     # Message(CMD_PING)
 # extra commands which are not handled by revdb.c, but
 # by revdb.register_debug_command()
 CMD_PRINT       = 1    # Message(CMD_PRINT, extra=expression)
diff --git a/rpython/translator/revdb/process.py 
b/rpython/translator/revdb/process.py
--- a/rpython/translator/revdb/process.py
+++ b/rpython/translator/revdb/process.py
@@ -139,6 +139,14 @@
         Returns the Breakpoint or None.
         """
         assert not self.tainted
+
+        # <DEBUGGING ONLY>
+        currents = self.current_time, self.currently_created_objects
+        self.send(Message(CMD_PING))
+        self.expect_ready()
+        assert currents == (self.current_time, self.currently_created_objects)
+        # </DEBUGGING ONLY>
+
         self.send(Message(CMD_FORWARD, steps, ord(breakpoint_mode)))
         #
         # record the first ANSWER_BREAKPOINT, drop the others
@@ -150,7 +158,7 @@
                 break
             if bkpt is None:
                 bkpt = Breakpoint(msg.arg1, msg.arg3)
-        assert msg.cmd == ANSWER_READY
+        assert msg.cmd == ANSWER_READY, msg
         self.update_times(msg)
         return bkpt
 
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
@@ -28,6 +28,10 @@
 
 #define ASYNC_FINALIZER_TRIGGER    ((int16_t)0xff46)
 
+#define FID_REGULAR_MODE           'R'
+#define FID_SAVED_STATE            'S'
+#define FID_JMPBUF_PROTECTED       'J'
+
 
 typedef struct {
     Signed version;
@@ -40,7 +44,7 @@
 rpy_revdb_t rpy_revdb;
 static char rpy_rev_buffer[16384];    /* max. 32768 */
 static int rpy_rev_fileno = -1;
-static unsigned char flag_io_disabled;
+static char flag_io_disabled = FID_REGULAR_MODE;
 
 
 static void setup_record_mode(int argc, char *argv[]);
@@ -199,6 +203,7 @@
        They are only invoked when we call GC_invoke_finalizers(),
        which we only do at stop points in the case of revdb. 
     */
+    assert(!RPY_RDB_REPLAY);
     assert(rpy_revdb.stop_point_break <= rpy_revdb.stop_point_seen + 1);
     rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + 1;
 }
@@ -214,26 +219,21 @@
 
 static void record_stop_point(void)
 {
-    /* Invoke the finalizers now.  This will call boehm_fq_callback(),
-       which will enqueue the objects in the correct FinalizerQueue.
-       Then, call boehm_fq_trigger(), which calls finalizer_trigger().
-    */
+    /* ===== FINALIZERS =====
+
+       When the GC wants to invoke some finalizers, it causes this
+       to be called at the stop point.  The new-style finalizers
+       are only enqueued at this point.  The old-style finalizers
+       run immediately, conceptually just *after* the stop point.
+     */
     int i;
     char *p = rpy_rev_buffer;
     int64_t done;
 
+    /* Write an ASYNC_FINALIZER_TRIGGER packet */
     rpy_reverse_db_flush();
+    assert(current_packet_size() == 0);
 
-    fq_trigger();
-
-    /* This should be done without emitting anything to the rdb
-       log.  We check that, and emit just a ASYNC_FINALIZER_TRIGGER.
-    */
-    if (current_packet_size() != 0) {
-        fprintf(stderr,
-                "record_stop_point emitted unexpected data into the rdb 
log\n");
-        exit(1);
-    }
     *(int16_t *)p = ASYNC_FINALIZER_TRIGGER;
     memcpy(rpy_revdb.buf_p, &rpy_revdb.stop_point_seen, sizeof(uint64_t));
     rpy_revdb.buf_p += sizeof(uint64_t);
@@ -251,6 +251,10 @@
     GC_invoke_finalizers();
     in_invoke_finalizers--;
     RPY_REVDB_EMIT(done = -1;, int64_t _e, done);
+
+    /* Now we're back in normal mode.  We trigger the finalizer 
+       queues here. */
+    fq_trigger();
 }
 
 RPY_EXTERN
@@ -276,7 +280,7 @@
     /* Boehm only */
     if (obj->h_hash == 0) {
         Signed h;
-        if (flag_io_disabled) {
+        if (flag_io_disabled != FID_REGULAR_MODE) {
             /* This is when running debug commands.  Don't cache the
                hash on the object at all. */
             return ~((Signed)obj);
@@ -378,7 +382,7 @@
     r->re_addr = target;
     r->re_off_prev = 0;
 
-    if (!flag_io_disabled) {
+    if (flag_io_disabled == FID_REGULAR_MODE) {
         char alive;
         /* Emit WEAKREF_AFTERWARDS_DEAD, but remember where we emit it.
            If we deref the weakref and it is still alive, we will patch
@@ -415,7 +419,7 @@
 {
     struct WEAKLINK *r = (struct WEAKLINK *)weakref;
     void *result = r->re_addr;
-    if (result && !flag_io_disabled) {
+    if (result && flag_io_disabled == FID_REGULAR_MODE) {
         char alive;
         if (!RPY_RDB_REPLAY) {
             if (r->re_off_prev <= 0) {
@@ -471,6 +475,7 @@
 #define CMD_QUIT      (-2)
 #define CMD_FORWARD   (-3)
 #define CMD_FUTUREIDS (-4)
+#define CMD_PING      (-5)
 
 #define ANSWER_INIT       (-20)
 #define ANSWER_READY      (-21)
@@ -482,16 +487,14 @@
 
 static int rpy_rev_sockfd;
 static const char *rpy_rev_filename;
-static uint64_t stopped_time;
-static uint64_t stopped_uid;
+static uint64_t interactive_break = 1, finalizer_break = -1, uid_break = -1;
 static uint64_t total_stop_points;
-static uint64_t finalizer_trigger_saved_break;
 static jmp_buf jmp_buf_cancel_execution;
 static void (*pending_after_forward)(void);
 static RPyString *empty_string;
 static uint64_t last_recorded_breakpoint_loc;
 static int last_recorded_breakpoint_num;
-static char breakpoint_mode;
+static char breakpoint_mode = 'i';
 static uint64_t *future_ids, *future_next_id;
 static void *finalizer_tree, *destructor_tree;
 
@@ -586,6 +589,15 @@
     }
 }
 
+static void set_revdb_breakpoints(void)
+{
+    /* note: these are uint64_t, so '-1' is bigger than positive values */
+    rpy_revdb.stop_point_break = (interactive_break < finalizer_break ?
+                                  interactive_break : finalizer_break);
+    rpy_revdb.unique_id_break = uid_break;
+    rpy_revdb.watch_enabled = (breakpoint_mode != 'i');
+}
+
 static void setup_replay_mode(int *argc_p, char **argv_p[])
 {
     int argc = *argc_p;
@@ -637,8 +649,9 @@
     rpy_revdb.buf_p = rpy_rev_buffer;
     rpy_revdb.buf_limit = rpy_rev_buffer;
     rpy_revdb.buf_readend = rpy_rev_buffer;
-    rpy_revdb.stop_point_break = 1;
+    rpy_revdb.stop_point_seen = 0;
     rpy_revdb.unique_id_seen = 1;
+    set_revdb_breakpoints();
 
     empty_string = make_rpy_string(0);
 
@@ -670,11 +683,15 @@
 RPY_EXTERN
 void rpy_reverse_db_fetch(const char *file, int line)
 {
-    if (!flag_io_disabled) {
+    if (flag_io_disabled == FID_REGULAR_MODE) {
         ssize_t keep;
         ssize_t full_packet_size;
         int16_t header;
 
+        if (finalizer_break != (uint64_t)-1) {
+            fprintf(stderr, "reverse_db_fetch: finalizer_break != -1\n");
+            exit(1);
+        }
         if (rpy_revdb.buf_limit != rpy_revdb.buf_p) {
             fprintf(stderr, "bad log format: incomplete packet\n");
             exit(1);
@@ -695,7 +712,8 @@
             switch (header) {
 
             case ASYNC_FINALIZER_TRIGGER:
-                if (finalizer_trigger_saved_break != 0) {
+                //fprintf(stderr, "ASYNC_FINALIZER_TRIGGER\n");
+                if (finalizer_break != (uint64_t)-1) {
                     fprintf(stderr, "unexpected multiple "
                                     "ASYNC_FINALIZER_TRIGGER\n");
                     exit(1);
@@ -709,10 +727,10 @@
                     fprintf(stderr, "invalid finalizer break point\n");
                     exit(1);
                 }
-                finalizer_trigger_saved_break = rpy_revdb.stop_point_break;
-                rpy_revdb.stop_point_break = bp;
+                finalizer_break = bp;
+                set_revdb_breakpoints();
                 /* Now we should not fetch anything more until we reach
-                   that finalizer_trigger_saved_break point. */
+                   that finalizer_break point. */
                 rpy_revdb.buf_limit = rpy_revdb.buf_p;
                 return;
 
@@ -734,64 +752,94 @@
         */
         fprintf(stderr, "%s:%d: Attempted to do I/O or access raw memory\n",
                 file, line);
-        longjmp(jmp_buf_cancel_execution, 1);
+        if (flag_io_disabled == FID_JMPBUF_PROTECTED) {
+            longjmp(jmp_buf_cancel_execution, 1);
+        }
+        else {
+            fprintf(stderr, "but we are not in a jmpbuf_protected section\n");
+            exit(1);
+        }
     }
 }
 
-static rpy_revdb_t disabled_state;
-static void *disabled_exc[2];
+static rpy_revdb_t saved_state;
+static void *saved_exc[2];
 
-static void disable_io(void)
+static void change_flag_io_disabled(char oldval, char newval)
 {
-    if (flag_io_disabled) {
-        fprintf(stderr, "unexpected recursive disable_io()\n");
+    if (flag_io_disabled != oldval) {
+        fprintf(stderr, "change_flag_io_disabled(%c, %c) but got %c\n",
+                oldval, newval, flag_io_disabled);
         exit(1);
     }
-    disabled_state = rpy_revdb;   /* save the complete struct */
-    disabled_exc[0] = pypy_g_ExcData.ed_exc_type;
-    disabled_exc[1] = pypy_g_ExcData.ed_exc_value;
+    flag_io_disabled = newval;
+}
+
+static void save_state(void)
+{
+    /* The program is switching from replaying execution to 
+       time-paused mode.  In time-paused mode, we can run more
+       app-level code like watch points or interactive prints,
+       but they must not be matched against the log, and they must
+       not involve generic I/O.
+    */
+    change_flag_io_disabled(FID_REGULAR_MODE, FID_SAVED_STATE);
+
+    saved_state = rpy_revdb;   /* save the complete struct */
+
+    rpy_revdb.unique_id_seen = (-1ULL) << 63;
+    rpy_revdb.watch_enabled = 0;
+    rpy_revdb.stop_point_break = -1;
+    rpy_revdb.unique_id_break = -1;
+    rpy_revdb.buf_p = rpy_rev_buffer;       /* anything readable */
+    rpy_revdb.buf_limit = rpy_rev_buffer;   /* same as buf_p */
+}
+
+static void restore_state(void)
+{
+    /* The program is switching from time-paused mode to replaying
+       execution. */
+    change_flag_io_disabled(FID_SAVED_STATE, FID_REGULAR_MODE);
+
+    /* restore the complete struct */
+    rpy_revdb = saved_state;
+
+    /* set the breakpoint fields to the current value of the *_break
+       global variables, which may be different from what is in
+       'save_state' */
+    set_revdb_breakpoints();
+}
+
+static void protect_jmpbuf(void)
+{
+    change_flag_io_disabled(FID_SAVED_STATE, FID_JMPBUF_PROTECTED);
+    saved_exc[0] = pypy_g_ExcData.ed_exc_type;
+    saved_exc[1] = pypy_g_ExcData.ed_exc_value;
     pypy_g_ExcData.ed_exc_type = NULL;
     pypy_g_ExcData.ed_exc_value = NULL;
-    rpy_revdb.buf_p = rpy_rev_buffer;       /* anything readable */
-    rpy_revdb.buf_limit = rpy_rev_buffer;   /* same as buf_p */
-    flag_io_disabled = 1;
 }
 
-static void enable_io(int restore_breaks)
+static void unprotect_jmpbuf(void)
 {
-    uint64_t v1, v2;
-    if (!flag_io_disabled) {
-        fprintf(stderr, "unexpected enable_io()\n");
-        exit(1);
-    }
-    flag_io_disabled = 0;
-
+    change_flag_io_disabled(FID_JMPBUF_PROTECTED, FID_SAVED_STATE);
     if (pypy_g_ExcData.ed_exc_type != NULL) {
         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;
-    v2 = rpy_revdb.unique_id_break;
-    rpy_revdb = disabled_state;
-    if (!restore_breaks) {
-        rpy_revdb.stop_point_break = v1;
-        rpy_revdb.unique_id_break = v2;
-    }
-    pypy_g_ExcData.ed_exc_type = disabled_exc[0];
-    pypy_g_ExcData.ed_exc_value = disabled_exc[1];
+    pypy_g_ExcData.ed_exc_type = saved_exc[0];
+    pypy_g_ExcData.ed_exc_value = saved_exc[1];
 }
 
 static void execute_rpy_function(rpy_revdb_command_fn func,
                                  rpy_revdb_command_t *cmd,
                                  RPyString *extra)
 {
-    disable_io();
+    protect_jmpbuf();
     if (setjmp(jmp_buf_cancel_execution) == 0)
         func(cmd, extra);
-    enable_io(0);
+    unprotect_jmpbuf();
 }
 
 static void check_at_end(uint64_t stop_points)
@@ -888,13 +936,13 @@
         fprintf(stderr, "CMD_FORWARD: negative step\n");
         exit(1);
     }
-    rpy_revdb.stop_point_break = stopped_time + cmd->arg1;
+    assert(flag_io_disabled == FID_SAVED_STATE);
+    interactive_break = saved_state.stop_point_seen + cmd->arg1;
     breakpoint_mode = (char)cmd->arg2;
     if (breakpoint_mode == 'r') {
         last_recorded_breakpoint_loc = 0;
         pending_after_forward = &answer_recorded_breakpoint;
     }
-    rpy_revdb.watch_enabled = (breakpoint_mode != 'i');
 }
 
 static void command_future_ids(rpy_revdb_command_t *cmd, char *extra)
@@ -902,7 +950,7 @@
     free(future_ids);
     if (cmd->extra_size == 0) {
         future_ids = NULL;
-        rpy_revdb.unique_id_break = 0;
+        uid_break = 0;
     }
     else {
         assert(cmd->extra_size % sizeof(uint64_t) == 0);
@@ -914,7 +962,7 @@
         }
         memcpy(future_ids, extra, cmd->extra_size);
         future_ids[cmd->extra_size / sizeof(uint64_t)] = 0;
-        rpy_revdb.unique_id_break = *future_ids;
+        uid_break = *future_ids;
     }
     future_next_id = future_ids;
 }
@@ -940,41 +988,16 @@
     execute_rpy_function(rpy_revdb_commands.rp_funcs[i], cmd, s);
 }
 
-static void save_state(void)
-{
-    stopped_time = rpy_revdb.stop_point_seen;
-    stopped_uid = rpy_revdb.unique_id_seen;
-    rpy_revdb.unique_id_seen = (-1ULL) << 63;
-    rpy_revdb.watch_enabled = 0;
-}
-
-static void restore_state(void)
-{
-    rpy_revdb.stop_point_seen = stopped_time;
-    rpy_revdb.unique_id_seen = stopped_uid;
-    stopped_time = 0;
-    stopped_uid = 0;
-}
-
 RPY_EXTERN
 void rpy_reverse_db_watch_save_state(void)
 {
-    if (stopped_time != 0) {
-        fprintf(stderr, "unexpected recursive watch_save_state\n");
-        exit(1);
-    }
     save_state();
-    disable_io();
-    rpy_revdb.stop_point_break = 0;
-    rpy_revdb.unique_id_break = 0;
 }
 
 RPY_EXTERN
 void rpy_reverse_db_watch_restore_state(bool_t any_watch_point)
 {
-    enable_io(1);
     restore_state();
-    assert(!rpy_revdb.watch_enabled);
     rpy_revdb.watch_enabled = any_watch_point;
 }
 
@@ -982,12 +1005,17 @@
 
 static void replay_stop_point(void)
 {
-    if (finalizer_trigger_saved_break != 0)
+    if (finalizer_break != (uint64_t)-1)
         replay_call_destructors();
 
+    if (rpy_revdb.stop_point_break != interactive_break) {
+        fprintf(stderr, "mismatch between interactive_break and "
+                        "stop_point_break\n");
+        exit(1);
+    }
+
     while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
         save_state();
-        breakpoint_mode = 0;
 
         if (pending_after_forward) {
             void (*fn)(void) = pending_after_forward;
@@ -996,7 +1024,10 @@
         }
         else {
             rpy_revdb_command_t cmd;
-            write_answer(ANSWER_READY, stopped_time, stopped_uid, 0);
+            write_answer(ANSWER_READY,
+                         saved_state.stop_point_seen,
+                         saved_state.unique_id_seen,
+                         0);
             read_sock(&cmd, sizeof(cmd));
 
             char extra[cmd.extra_size + 1];
@@ -1022,6 +1053,9 @@
                 command_future_ids(&cmd, extra);
                 break;
 
+            case CMD_PING:     /* to get only the ANSWER_READY */
+                break;
+
             default:
                 command_default(&cmd, extra);
                 break;
@@ -1054,7 +1088,7 @@
 RPY_EXTERN
 void rpy_reverse_db_breakpoint(int64_t num)
 {
-    if (stopped_time != 0) {
+    if (flag_io_disabled != FID_REGULAR_MODE) {
         fprintf(stderr, "revdb.breakpoint(): cannot be called from a "
                         "debug command\n");
         exit(1);
@@ -1069,9 +1103,16 @@
         last_recorded_breakpoint_num = num;
         return;
 
-    default:      /* 'b' or '\0' for default handling of breakpoints */
-        rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + 1;
+    case 'b':     /* default handling of breakpoints */
+        interactive_break = rpy_revdb.stop_point_seen + 1;
+        set_revdb_breakpoints();
         write_answer(ANSWER_BREAKPOINT, rpy_revdb.stop_point_break, 0, num);
+        return;
+
+    default:
+        fprintf(stderr, "bad value %d of breakpoint_mode\n",
+                (int)breakpoint_mode);
+        exit(1);
     }
 }
 
@@ -1080,13 +1121,17 @@
 {
     switch (value_id) {
     case 'c':       /* current_time() */
-        return stopped_time ? stopped_time : rpy_revdb.stop_point_seen;
+        return (flag_io_disabled == FID_REGULAR_MODE ?
+                rpy_revdb.stop_point_seen :
+                saved_state.stop_point_seen);
     case 't':       /* total_time() */
         return total_stop_points;
     case 'b':       /* current_break_time() */
-        return rpy_revdb.stop_point_break;
+        return interactive_break;
     case 'u':       /* currently_created_objects() */
-        return stopped_uid ? stopped_uid : rpy_revdb.unique_id_seen;
+        return (flag_io_disabled == FID_REGULAR_MODE ?
+                rpy_revdb.unique_id_seen :
+                saved_state.unique_id_seen);
     default:
         return -1;
     }
@@ -1096,18 +1141,23 @@
 uint64_t rpy_reverse_db_unique_id_break(void *new_object)
 {
     uint64_t uid = rpy_revdb.unique_id_seen;
+    bool_t watch_enabled = rpy_revdb.watch_enabled;
+
     if (!new_object) {
         fprintf(stderr, "out of memory: allocation failed, cannot continue\n");
         exit(1);
     }
+
+    save_state();
     if (rpy_revdb_commands.rp_alloc) {
-        bool_t watch_enabled = rpy_revdb.watch_enabled;
-        rpy_reverse_db_watch_save_state();
+        protect_jmpbuf();
         if (setjmp(jmp_buf_cancel_execution) == 0)
             rpy_revdb_commands.rp_alloc(uid, new_object);
-        rpy_reverse_db_watch_restore_state(watch_enabled);
+        unprotect_jmpbuf();
     }
-    rpy_revdb.unique_id_break = *future_next_id++;
+    uid_break = *future_next_id++;
+    restore_state();
+    rpy_revdb.watch_enabled = watch_enabled;
     return uid;
 }
 
@@ -1221,14 +1271,15 @@
 
 static void replay_call_destructors(void)
 {
-    rpy_revdb.stop_point_break = finalizer_trigger_saved_break;
-    finalizer_trigger_saved_break = 0;
     fq_trigger();
 
     /* Re-enable fetching (disabled when we saw ASYNC_FINALIZER_TRIGGER),
        and fetch the uid's of dying objects with old-style destructors.
     */
+    finalizer_break = -1;
+    set_revdb_breakpoints();
     rpy_reverse_db_fetch(__FILE__, __LINE__);
+
     while (1) {
         int64_t uid;
         struct destructor_s d_dummy, *entry, **item;
diff --git a/rpython/translator/revdb/test/test_weak.py 
b/rpython/translator/revdb/test/test_weak.py
--- a/rpython/translator/revdb/test/test_weak.py
+++ b/rpython/translator/revdb/test/test_weak.py
@@ -327,3 +327,9 @@
         child = self.replay()
         child.send(Message(CMD_FORWARD, 3001))
         child.expect(ANSWER_AT_END)
+
+    def test_bug1(self):
+        child = self.replay()
+        for i in range(50):
+            child.send(Message(CMD_FORWARD, i))
+            child.expect_ready()
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to