Author: Armin Rigo <ar...@tunes.org>
Branch: reverse-debugger
Changeset: r85335:fb7901990151
Date: 2016-06-22 17:30 +0200
http://bitbucket.org/pypy/pypy/changeset/fb7901990151/

Log:    watchpoints, in-progress

diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -16,12 +16,12 @@
 CMD_BREAKPOINTS = 4
 CMD_MOREINFO    = 5
 CMD_ATTACHID    = 6
-CMD_WATCH       = 7
-CMD_EXPECTED    = 8
+CMD_CHECKWATCH  = 7
+CMD_WATCHVALUES = 8
 ANSWER_TEXT     = 20
 ANSWER_MOREINFO = 21
 ANSWER_NEXTNID  = 22
-ANSWER_COMPILED = 23
+ANSWER_WATCH    = 23
 
 
 def stop_point():
@@ -46,6 +46,9 @@
 def send_nextnid(unique_id):
     send_answer(ANSWER_NEXTNID, unique_id)
 
+def send_watch(text, ok_flag):
+    send_answer(ANSWER_WATCH, ok_flag, extra=text)
+
 def current_time():
     """For RPython debug commands: returns the current time."""
     return llop.revdb_get_value(lltype.SignedLongLong, 'c')
@@ -103,6 +106,12 @@
     ll_callback = llhelper(_CALLBACK_GCREF_FNPTR, callback)
     llop.revdb_track_object(lltype.Void, unique_id, ll_callback)
 
+def save_state():
+    return llop.revdb_save_state(lltype.Bool)
+
+def restore_state():
+    llop.revdb_restore_state(lltype.Void)
+
 
 # ____________________________________________________________
 
@@ -157,7 +166,7 @@
                 llannotation.lltype_to_annotation(llmemory.GCREF)]
 
     def arguments_WATCHING(self):
-        return []
+        raise Exception("XXX remove me")
 
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
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
@@ -571,7 +571,8 @@
     'revdb_breakpoint':     LLOp(),
     'revdb_get_value':      LLOp(sideeffects=False),
     'revdb_get_unique_id':  LLOp(sideeffects=False),
-    ## 'revdb_track_object':   LLOp(),
+    'revdb_save_state':     LLOp(),
+    'revdb_restore_state':  LLOp(),
 }
 # ***** Run test_lloperation after changes. *****
 
diff --git a/rpython/translator/revdb/gencsupp.py 
b/rpython/translator/revdb/gencsupp.py
--- a/rpython/translator/revdb/gencsupp.py
+++ b/rpython/translator/revdb/gencsupp.py
@@ -27,7 +27,6 @@
                                          lltype.Void))
     ALLOCFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.LONGLONG, llmemory.GCREF],
                                               lltype.Void))
-    WATCHFUNCPTR = lltype.Ptr(lltype.FuncType([], lltype.Signed))
 
     bk = db.translator.annotator.bookkeeper
     cmds = getattr(db.translator, 'revdb_commands', {})
@@ -37,8 +36,7 @@
     S = lltype.Struct('RPY_REVDB_COMMANDS',
                   ('names', lltype.FixedSizeArray(rffi.INT, len(numcmds) + 1)),
                   ('funcs', lltype.FixedSizeArray(FUNCPTR, len(numcmds))),
-                  ('alloc', ALLOCFUNCPTR),
-                  ('watch', WATCHFUNCPTR))
+                  ('alloc', ALLOCFUNCPTR))
     s = lltype.malloc(S, flavor='raw', immortal=True, zero=True)
 
     i = 0
@@ -51,8 +49,6 @@
             i += 1
         elif name == "ALLOCATING":
             s.alloc = fnptr
-        elif name == "WATCHING":
-            s.watch = fnptr
         else:
             raise AssertionError("bad tag in register_debug_command(): %r"
                                  % (name,))
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
@@ -109,14 +109,34 @@
         lst = [str(n) for n in sorted(self.pgroup.paused)]
         print ', '.join(lst)
 
+    def _bp_kind(self, name):
+        if name[0] == 'B':
+            return 'breakpoint'
+        elif name[0] == 'W':
+            return 'watchpoint'
+        else:
+            return '?????point'
+
+    def _bp_new(self, break_at, watchvalue=None):
+        b = self.pgroup.edit_breakpoints()
+        new = 1
+        while new in b.num2name:
+            new += 1
+        b.num2name[new] = break_at
+        if watchvalue is not None:
+            b.watchvalues[new] = watchvalue
+        print "%s %d added" % (self._bp_kind(break_at).capitalize(), new)
+        return new
+
     def cmd_info_breakpoints(self):
-        """List current breakpoints"""
+        """List current breakpoints and watchpoints"""
         lst = self.pgroup.all_breakpoints.num2name.items()
         if lst:
             for num, name in sorted(lst):
-                print '%8d: %s' % (num, name)
+                print '\t%s %d: %s' % (self._bp_kind(name), num, name[1:])
         else:
             print 'no breakpoints.'
+    cmd_info_watchpoints = cmd_info_breakpoints
 
     def move_forward(self, steps):
         self.remove_tainting()
@@ -137,7 +157,10 @@
 
     def hit_breakpoint(self, b, backward=False):
         if b.num != -1:
-            self.print_extra_pending_info = 'Hit breakpoint %d' % (b.num,)
+            name = self.pgroup.all_breakpoints.num2name.get(b.num, '??')
+            kind = self._bp_kind(name)
+            self.print_extra_pending_info = 'Hit %s %d: %s' % (kind, b.num,
+                                                               name[1:])
         elif backward:
             b.time -= 1
         if self.pgroup.get_current_time() != b.time:
@@ -240,20 +263,30 @@
         if not argument:
             print "Break where?"
             return
-        b = self.pgroup.edit_breakpoints()
-        new = 1
-        while new in b.num2name:
-            new += 1
-        b.num2name[new] = argument
-        print "Breakpoint %d added" % (new,)
+        self._bp_new('B' + argument)
     command_b = command_break
 
     def command_delete(self, argument):
-        """Delete a breakpoint"""
+        """Delete a breakpoint/watchpoint"""
         arg = int(argument)
         b = self.pgroup.edit_breakpoints()
         if arg not in b.num2name:
-            print "No breakpoint number %d" % (arg,)
+            print "No breakpoint/watchpoint number %d" % (arg,)
         else:
-            del b.num2name[arg]
-            print "Breakpoint %d deleted" % (arg,)
+            name = b.num2name.pop(arg)
+            b.watchvalues.pop(arg, '')
+            kind = self._bp_kind(name)
+            print "%s %d deleted: %s" % (kind.capitalize(), arg, name[1:])
+
+    def command_watch(self, argument):
+        """Add a watchpoint (use $NUM in the expression to watch)"""
+        if not argument:
+            print "Watch what?"
+            return
+        ok_flag, text = self.pgroup.check_watchpoint_expr(argument)
+        if not ok_flag:
+            print text
+            print 'Watchpoint not added'
+        else:
+            print 'Current value:', text
+            self._bp_new('W' + argument, watchvalue=text)
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
@@ -17,6 +17,8 @@
                        #         extra="\0-separated names")
 CMD_MOREINFO    = 5    # Message(CMD_MOREINFO)
 CMD_ATTACHID    = 6    # Message(CMD_ATTACHID, small-num, unique-id)
+CMD_CHECKWATCH  = 7    # Message(CMD_CHECKWATCH, extra=expression)
+CMD_WATCHVALUES = 8    # Message(CMD_WATCHVALUES, extra=texts)
 
 
 # the first message sent by the first child:
@@ -56,6 +58,10 @@
 #    Message(ANSWER_NEXTNID, unique-id)
 ANSWER_NEXTNID    = 22
 
+# sent after CMD_CHECKWATCH:
+#    Message(ANSWER_WATCH, ok_flag, extra=result_of_expr)
+ANSWER_WATCH      = 23
+
 
 # ____________________________________________________________
 
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
@@ -18,18 +18,22 @@
 class AllBreakpoints(object):
 
     def __init__(self):
-        self.num2name = {}    # {small number: function name}
-        self.stack_depth = 0     # breaks if the depth becomes lower than this
+        self.num2name = {}     # {small number: break/watchpoint}
+        self.watchvalues = {}  # {small number: resulting text}
+        self.stack_depth = 0   # breaks if the depth becomes lower than this
 
     def __repr__(self):
         return 'AllBreakpoints(%r, %d)' % (self.num2name, self.stack_depth)
 
-    def __eq__(self, other):
-        return (self.num2name == other.num2name and
-                self.stack_depth == other.stack_depth)
-
-    def __ne__(self, other):
-        return not (self == other)
+    def compare(self, other):
+        if (self.num2name == other.num2name and
+            self.stack_depth == other.stack_depth):
+            if self.watchvalues == other.watchvalues:
+                return 2     # completely equal
+            else:
+                return 1     # equal, but watchvalues out-of-date
+        else:
+            return 0     # different
 
     def is_empty(self):
         return len(self.num2name) == 0 and self.stack_depth == 0
@@ -71,7 +75,7 @@
         return ''.join(pieces)
 
     def send(self, msg):
-        #print 'SENT:', self.pid, msg
+        print 'SENT:', self.pid, msg
         binary = struct.pack("iIqqq", msg.cmd, len(msg.extra),
                              msg.arg1, msg.arg2, msg.arg3)
         self.control_socket.sendall(binary + msg.extra)
@@ -81,7 +85,7 @@
         cmd, size, arg1, arg2, arg3 = struct.unpack("iIqqq", binary)
         extra = self._recv_all(size)
         msg = Message(cmd, arg1, arg2, arg3, extra)
-        #print 'RECV:', self.pid, msg
+        print 'RECV:', self.pid, msg
         return msg
 
     def expect(self, cmd, arg1=0, arg2=0, arg3=0, extra=""):
@@ -129,7 +133,7 @@
         except socket.error:
             pass
 
-    def forward(self, steps, breakpoint_mode='b'):
+    def forward(self, steps, breakpoint_mode, all_breakpoints):
         """Move this subprocess forward in time.
         Returns the Breakpoint or None.
         """
@@ -145,6 +149,8 @@
                 break
             if bkpt is None:
                 bkpt = Breakpoint(msg.arg1, msg.arg3)
+                all_breakpoints.watchvalues = dict.fromkeys(
+                    all_breakpoints.watchvalues)    # set all values to None
         assert msg.cmd == ANSWER_READY
         self.update_times(msg)
         return bkpt
@@ -249,7 +255,8 @@
           'r' = record the occurrence of a breakpoint but continue
         """
         assert steps >= 0
-        self.update_breakpoints()
+        if breakpoint_mode != 'i':
+            self.update_breakpoints()
         latest_bkpt = None
         while True:
             cur_time = self.get_current_time()
@@ -261,7 +268,8 @@
                 break
             assert rel_next_clone >= 0
             if rel_next_clone > 0:
-                bkpt = self.active.forward(rel_next_clone, breakpoint_mode)
+                bkpt = self.active.forward(rel_next_clone, breakpoint_mode,
+                                           self.all_breakpoints)
                 if breakpoint_mode == 'r':
                     latest_bkpt = bkpt or latest_bkpt
                 elif bkpt:
@@ -271,7 +279,8 @@
                 self.paused[self.active.current_time].close()
             clone = self.active.clone()
             self.paused[clone.current_time] = clone
-        bkpt = self.active.forward(steps, breakpoint_mode)
+        bkpt = self.active.forward(steps, breakpoint_mode,
+                                   self.all_breakpoints)
         if breakpoint_mode == 'r':
             bkpt = bkpt or latest_bkpt
         if bkpt:
@@ -313,19 +322,50 @@
             search_start_time -= time_range_to_search * 3
 
     def update_breakpoints(self):
-        if self.active.breakpoints_cache == self.all_breakpoints:
-            return
+        cmp = self.all_breakpoints.compare(self.active.breakpoints_cache)
+        print 'compare:', cmp, self.all_breakpoints.watchvalues
+        if cmp == 2:
+            return      # up-to-date
+
+        # update the breakpoints/watchpoints
+        self.active.breakpoints_cache = None
         num2name = self.all_breakpoints.num2name
-        flat = []
-        if num2name:
-            flat = [num2name.get(n, '') for n in range(max(num2name) + 1)]
-        arg1 = self.all_breakpoints.stack_depth
-        extra = '\x00'.join(flat)
-        #
-        self.active.breakpoints_cache = None
-        self.active.send(Message(CMD_BREAKPOINTS, arg1, extra=extra))
+        N = (max(num2name) + 1) if num2name else 0
+        if cmp == 0:
+            flat = [num2name.get(n, '') for n in range(N)]
+            arg1 = self.all_breakpoints.stack_depth
+            extra = '\x00'.join(flat)
+            self.active.send(Message(CMD_BREAKPOINTS, arg1, extra=extra))
+            self.active.expect_ready()
+        else:
+            assert cmp == 1
+
+        # update the watchpoint values
+        if any(name.startswith('W') for name in num2name.values()):
+            watchvalues = self.all_breakpoints.watchvalues
+            flat = []
+            for n in range(N):
+                text = ''
+                name = num2name.get(n, '')
+                if name.startswith('W'):
+                    text = watchvalues[n]
+                    if text is None:
+                        _, text = self.check_watchpoint_expr(name[1:])
+                        print 'updating watchpoint value: %s => %s' % (
+                            name[1:], text)
+                        watchvalues[n] = text
+                flat.append(text)
+            extra = '\x00'.join(flat)
+            self.active.send(Message(CMD_WATCHVALUES, extra=extra))
+            self.active.expect_ready()
+
+        self.active.breakpoints_cache = self.all_breakpoints.duplicate()
+
+    def check_watchpoint_expr(self, expr):
+        self.active.send(Message(CMD_CHECKWATCH, extra=expr))
+        msg = self.active.expect(ANSWER_WATCH, Ellipsis, extra=Ellipsis)
         self.active.expect_ready()
-        self.active.breakpoints_cache = self.all_breakpoints.duplicate()
+        return msg.arg1, msg.extra
 
     def _resume(self, from_time):
         clone_me = self.paused[from_time]
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
@@ -585,14 +585,21 @@
     execute_rpy_function(rpy_revdb_commands.rp_funcs[i], cmd, s);
 }
 
-static void save_state(void)
+RPY_EXTERN
+bool_t rpy_reverse_db_save_state(void)
 {
-    stopped_time = rpy_revdb.stop_point_seen;
-    stopped_uid = rpy_revdb.unique_id_seen;
-    rpy_revdb.unique_id_seen = (-1ULL) << 63;
+    if (stopped_time == 0) {
+        stopped_time = rpy_revdb.stop_point_seen;
+        stopped_uid = rpy_revdb.unique_id_seen;
+        rpy_revdb.unique_id_seen = (-1ULL) << 63;
+        return 1;
+    }
+    else
+        return 0;
 }
 
-static void restore_state(void)
+RPY_EXTERN
+void rpy_reverse_db_restore_state(void)
 {
     rpy_revdb.stop_point_seen = stopped_time;
     rpy_revdb.unique_id_seen = stopped_uid;
@@ -604,7 +611,7 @@
 void rpy_reverse_db_stop_point(void)
 {
     while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
-        save_state();
+        rpy_reverse_db_save_state();
         breakpoint_mode = 0;
 
         if (pending_after_forward) {
@@ -645,7 +652,7 @@
                 break;
             }
         }
-        restore_state();
+        rpy_reverse_db_restore_state();
     }
 }
 
@@ -744,12 +751,12 @@
     }
     if (rpy_revdb_commands.rp_alloc) {
         rpy_revdb_t dinfo;
-        save_state();
+        rpy_reverse_db_save_state();
         disable_io(&dinfo);
         if (setjmp(jmp_buf_cancel_execution) == 0)
             rpy_revdb_commands.rp_alloc(uid, new_object);
         enable_io(&dinfo);
-        restore_state();
+        rpy_reverse_db_restore_state();
     }
     rpy_revdb.unique_id_break = *future_next_id++;
     return uid;
diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h 
b/rpython/translator/revdb/src-revdb/revdb_include.h
--- a/rpython/translator/revdb/src-revdb/revdb_include.h
+++ b/rpython/translator/revdb/src-revdb/revdb_include.h
@@ -102,6 +102,12 @@
 #define OP_REVDB_TRACK_OBJECT(uid, callback, r)                         \
     rpy_reverse_db_track_object(uid, callback)
 
+#define OP_REVDB_SAVE_STATE(r)                                          \
+    r = rpy_reverse_db_save_state()
+
+#define OP_REVDB_RESTORE_STATE(r)                                       \
+    rpy_reverse_db_restore_state()
+
 RPY_EXTERN void rpy_reverse_db_flush(void);
 RPY_EXTERN char *rpy_reverse_db_fetch(int expected_size,
                                       const char *file, int line);
@@ -114,5 +120,7 @@
 RPY_EXTERN void rpy_reverse_db_breakpoint(int64_t num);
 RPY_EXTERN long long rpy_reverse_db_get_value(char value_id);
 RPY_EXTERN uint64_t rpy_reverse_db_unique_id_break(void *new_object);
+RPY_EXTERN bool_t rpy_reverse_db_save_state(void);
+RPY_EXTERN void rpy_reverse_db_restore_state(void);
 
 /* ------------------------------------------------------------ */
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to