[pypy-commit] pypy reverse-debugger: Watchpoints. Still buggy

arigo pypy.commits at gmail.com
Thu Jun 30 04:14:23 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85467:716b1a95fd64
Date: 2016-06-30 10:15 +0200
http://bitbucket.org/pypy/pypy/changeset/716b1a95fd64/

Log:	Watchpoints. Still buggy

diff --git a/pypy/interpreter/reverse_debugging.py b/pypy/interpreter/reverse_debugging.py
--- a/pypy/interpreter/reverse_debugging.py
+++ b/pypy/interpreter/reverse_debugging.py
@@ -6,6 +6,7 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter import gateway, typedef, pycode, pytraceback
+from pypy.module.marshal import interp_marshal
 
 
 class DBState:
@@ -41,8 +42,9 @@
     revdb.register_debug_command(revdb.CMD_STACKID, lambda_stackid)
     revdb.register_debug_command("ALLOCATING", lambda_allocating)
     revdb.register_debug_command(revdb.CMD_ATTACHID, lambda_attachid)
-    #revdb.register_debug_command(revdb.CMD_CHECKWATCH, lambda_checkwatch)
-    #revdb.register_debug_command(revdb.CMD_WATCHVALUES, lambda_watchvalues)
+    revdb.register_debug_command(revdb.CMD_COMPILEWATCH, lambda_compilewatch)
+    revdb.register_debug_command(revdb.CMD_CHECKWATCH, lambda_checkwatch)
+    revdb.register_debug_command(revdb.CMD_WATCHVALUES, lambda_watchvalues)
 
 
 pycode.PyCode.co_revdb_linestarts = None   # or a string: an array of bits
@@ -138,13 +140,13 @@
 def stop_point_at_start_of_line():
     if revdb.watch_save_state():
         any_watch_point = False
-        #for prog, watch_id, expected in dbstate.watch_progs:
-        #    any_watch_point = True
-        #    got = _watch_expr(prog)
-        #    if got != expected:
-        #        break
-        #else:
-        watch_id = -1
+        for prog, watch_id, expected in dbstate.watch_progs:
+            any_watch_point = True
+            got = _run_watch(prog)
+            if got != expected:
+                break
+        else:
+            watch_id = -1
         revdb.watch_restore_state(any_watch_point)
         if watch_id != -1:
             revdb.breakpoint(watch_id)
@@ -274,6 +276,7 @@
                         break
                     show_frame(tb.frame, tb.get_lineno(), indent='  ')
                     tb = tb.next
+            revdb.send_output('%s\n' % operationerr.errorstr(space))
 
             # set the sys.last_xxx attributes
             w_type = operationerr.w_type
@@ -283,9 +286,6 @@
             space.setitem(space.sys.w_dict, space.wrap('last_value'), w_value)
             space.setitem(space.sys.w_dict, space.wrap('last_traceback'), w_tb)
 
-            # re-raise, catch me in the outside "except OperationError"
-            raise
-
     except OperationError as e:
         revdb.send_output('%s\n' % e.errorstr(space, use_repr=True))
 lambda_print = lambda: command_print
@@ -384,26 +384,33 @@
 
 
 def command_breakpoints(cmd, extra):
+    space = dbstate.space
     dbstate.breakpoint_stack_id = cmd.c_arg1
     funcnames = None
-    for i, name in enumerate(extra.split('\x00')):
-        if name:
-            if name[0] == 'B':
-                if funcnames is None:
-                    funcnames = {}
-                funcnames[name[1:]] = i
-            elif name[0] == 'W':
-                pass
-                ## try:
-                ##     prog = compiler.parse(dbstate.space, name[1:])
-                ## except DuhtonError, e:
-                ##     revdb.send_output('compiling "%s": %s\n' %
-                ##                       (name[1:], e.msg))
-                ## else:
-                ##     watch_progs.append((prog, i, ''))
+    watch_progs = []
+    for i, kind, name in revdb.split_breakpoints_arg(extra):
+        if kind == 'B':
+            if funcnames is None:
+                funcnames = {}
+            funcnames[name] = i
+        elif kind == 'W':
+            code = interp_marshal.loads(space, space.wrap(name))
+            watch_progs.append((code, i, ''))
     dbstate.breakpoint_funcnames = funcnames
+    dbstate.watch_progs = watch_progs[:]
 lambda_breakpoints = lambda: command_breakpoints
 
+
+def command_watchvalues(cmd, extra):
+    expected = extra.split('\x00')
+    for j in range(len(dbstate.watch_progs)):
+        prog, i, _ = dbstate.watch_progs[j]
+        if i >= len(expected):
+            raise IndexError
+        dbstate.watch_progs[j] = prog, i, expected[i]
+lambda_watchvalues = lambda: command_watchvalues
+
+
 def command_stackid(cmd, extra):
     frame = fetch_cur_frame()
     if frame is not None and cmd.c_arg1 != 0:     # parent_flag
@@ -440,3 +447,38 @@
         w_obj = dbstate.w_future
     set_metavar(index_metavar, w_obj)
 lambda_attachid = lambda: command_attachid
+
+
+def command_compilewatch(cmd, expression):
+    space = dbstate.space
+    try:
+        code = compile(expression, 'eval')
+        marshalled_code = space.str_w(interp_marshal.dumps(
+            space, space.wrap(code),
+            space.wrap(interp_marshal.Py_MARSHAL_VERSION)))
+    except OperationError as e:
+        revdb.send_watch(e.errorstr(space), ok_flag=0)
+    else:
+        revdb.send_watch(marshalled_code, ok_flag=1)
+lambda_compilewatch = lambda: command_compilewatch
+
+def command_checkwatch(cmd, marshalled_code):
+    space = dbstate.space
+    try:
+        code = interp_marshal.loads(space, space.wrap(marshalled_code))
+        w_res = code.exec_code(space, space.builtin, space.builtin)
+        text = space.str_w(space.repr(w_res))
+    except OperationError as e:
+        revdb.send_watch(e.errorstr(space), ok_flag=0)
+    else:
+        revdb.send_watch(text, ok_flag=1)
+lambda_checkwatch = lambda: command_checkwatch
+
+def _run_watch(code):
+    space = dbstate.space
+    try:
+        w_res = code.exec_code(space, space.builtin, space.builtin)
+        text = space.str_w(space.repr(w_res))
+    except OperationError as e:
+        return e.errorstr(space)
+    return text
diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -118,6 +118,25 @@
     llop.revdb_watch_restore_state(lltype.Void, any_watch_point)
 
 
+def split_breakpoints_arg(breakpoints):
+    # RPython generator to help in splitting the string arg in CMD_BREAKPOINTS
+    n = 0
+    i = 0
+    while i < len(breakpoints):
+        kind = breakpoints[i]
+        i += 1
+        if kind != '\x00':
+            length = (ord(breakpoints[i]) |
+                      (ord(breakpoints[i + 1]) << 8) |
+                      (ord(breakpoints[i + 2]) << 16))
+            assert length >= 0
+            i += 3
+            yield n, kind, breakpoints[i : i + length]
+            i += length
+        n += 1
+    assert i == len(breakpoints)
+
+
 # ____________________________________________________________
 
 
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
@@ -121,26 +121,32 @@
         print ', '.join(lst)
 
     def _bp_kind(self, num):
-        name = self.pgroup.all_breakpoints.num2name.get(num, '??')
-        if name[0] == 'B':
+        break_at = self.pgroup.all_breakpoints.num2break.get(num, '??')
+        if break_at[0] == 'B':
             kind = 'breakpoint'
-            name = name[1:]
-        elif name[0] == 'W':
+            name = break_at[4:]
+        elif break_at[0] == 'W':
             kind = 'watchpoint'
             name = self.pgroup.all_breakpoints.sources.get(num, '??')
         else:
             kind = '?????point'
-            name = repr(name)
+            name = repr(break_at)
         return kind, name
 
-    def _bp_new(self, break_at, nids=None, source_expr=None):
+    def _bp_new(self, source_expr, break_code, break_at, nids=None):
         b = self.pgroup.edit_breakpoints()
         new = 1
-        while new in b.num2name:
+        while new in b.num2break:
             new += 1
-        b.num2name[new] = break_at
+        if len(break_at) > 0xFFFFFF:
+            raise OverflowError("break/watchpoint too complex")
+        b.num2break[new] = (break_code +
+                            chr(len(break_at) & 0xFF) +
+                            chr((len(break_at) >> 8) & 0xFF) +
+                            chr(len(break_at) >> 16) +
+                            break_at)
         b.sources[new] = source_expr
-        if break_at.startswith('W'):
+        if break_code == 'W':
             b.watchvalues[new] = ''
             if nids:
                 b.watchuids[new] = self.pgroup.nids_to_uids(nids)
@@ -149,7 +155,7 @@
 
     def cmd_info_breakpoints(self):
         """List current breakpoints and watchpoints"""
-        lst = self.pgroup.all_breakpoints.num2name.keys()
+        lst = self.pgroup.all_breakpoints.num2break.keys()
         if lst:
             for num in sorted(lst):
                 kind, name = self._bp_kind(num)
@@ -179,7 +185,7 @@
 
     def hit_breakpoint(self, b, backward=False):
         if b.num != -1:
-            kind, name = self._bp_kind(d.num)
+            kind, name = self._bp_kind(b.num)
             self.print_extra_pending_info = 'Hit %s %d: %s' % (kind, b.num,
                                                                name)
         elif backward:
@@ -304,18 +310,18 @@
         if not argument:
             print "Break where?"
             return
-        self._bp_new('B' + argument)
+        self._bp_new(argument, 'B', argument)
     command_b = command_break
 
     def command_delete(self, argument):
         """Delete a breakpoint/watchpoint"""
         arg = int(argument)
         b = self.pgroup.edit_breakpoints()
-        if arg not in b.num2name:
+        if arg not in b.num2break:
             print "No breakpoint/watchpoint number %d" % (arg,)
         else:
             kind, name = self._bp_kind(arg)
-            b.num2name.pop(arg, '')
+            b.num2break.pop(arg, '')
             b.sources.pop(arg, '')
             b.watchvalues.pop(arg, '')
             b.watchuids.pop(arg, '')
@@ -340,5 +346,5 @@
             print 'Watchpoint not added'
             return
         #
-        self._bp_new('W' + compiled_code, nids=nids, source_expr=argument)
+        self._bp_new(argument, 'W', compiled_code, nids=nids)
         self.pgroup.update_watch_values()
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
@@ -16,8 +16,8 @@
 class AllBreakpoints(object):
 
     def __init__(self):
-        self.num2name = {}     # {small number: break/watchpoint}
-        self.sources = {}      # {small number: src text or None}
+        self.num2break = {}    # {small number: encoded break/watchpoint}
+        self.sources = {}      # {small number: src text}
         self.watchvalues = {}  # {small number: resulting text}
         self.watchuids = {}    # {small number: [uid...]}
         self.stack_id = 0      # breaks when leaving/entering a frame from/to
@@ -25,11 +25,11 @@
 
     def __repr__(self):
         return 'AllBreakpoints(%r, %r, %r, %r)' % (
-            self.num2name, self.watchvalues, self.watchuids,
+            self.num2break, self.watchvalues, self.watchuids,
             self.stack_id)
 
     def compare(self, other):
-        if (self.num2name == other.num2name and
+        if (self.num2break == other.num2break and
             self.stack_id == other.stack_id):
             if self.watchvalues == other.watchvalues:
                 return 2     # completely equal
@@ -39,11 +39,11 @@
             return 0     # different
 
     def is_empty(self):
-        return len(self.num2name) == 0 and self.stack_id == 0
+        return len(self.num2break) == 0 and self.stack_id == 0
 
     def duplicate(self):
         a = AllBreakpoints()
-        a.num2name.update(self.num2name)
+        a.num2break.update(self.num2break)
         a.stack_id = self.stack_id
         return a
 
@@ -362,24 +362,24 @@
 
         # update the breakpoints/watchpoints
         self.active.breakpoints_cache = None
-        num2name = self.all_breakpoints.num2name
-        N = (max(num2name) + 1) if num2name else 0
+        num2break = self.all_breakpoints.num2break
+        N = (max(num2break) + 1) if num2break else 0
         if cmp == 0:
-            flat = [num2name.get(n, '') for n in range(N)]
+            flat = [num2break.get(n, '\x00') for n in range(N)]
             arg1 = self.all_breakpoints.stack_id
-            extra = '\x00'.join(flat)
+            extra = ''.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()):
+        if any(name.startswith('W') for name in num2break.values()):
             watchvalues = self.all_breakpoints.watchvalues
             flat = []
             for n in range(N):
                 text = ''
-                name = num2name.get(n, '')
+                name = num2break.get(n, '')
                 if name.startswith('W'):
                     text = watchvalues[n]
                 flat.append(text)
@@ -392,13 +392,13 @@
     def update_watch_values(self):
         self._update_watchpoints_uids()
         seen = set()
-        for num, name in self.all_breakpoints.num2name.items():
+        for num, name in self.all_breakpoints.num2break.items():
             if name.startswith('W'):
-                _, text = self.check_watchpoint_expr(name[1:])
+                _, text = self.check_watchpoint_expr(name[4:])
                 if text != self.all_breakpoints.watchvalues[num]:
                     #print self.active.pid
                     print 'updating watchpoint value: %s => %s' % (
-                        name[1:], text)
+                        self.all_breakpoints.sources[num], text)
                     self.all_breakpoints.watchvalues[num] = text
                 seen.add(num)
         assert set(self.all_breakpoints.watchvalues) == seen


More information about the pypy-commit mailing list