Author: Armin Rigo <ar...@tunes.org>
Branch: reverse-debugger
Changeset: r85447:155739abc604
Date: 2016-06-29 12:44 +0200
http://bitbucket.org/pypy/pypy/changeset/155739abc604/

Log:    in-progress

diff --git a/pypy/interpreter/executioncontext.py 
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -152,6 +152,9 @@
         Like bytecode_trace() but doesn't invoke any other events besides the
         trace function.
         """
+        if self.space.config.translation.reverse_debugger:
+            from pypy.interpreter.reverse_debugging import potential_stop_point
+            potential_stop_point(frame)
         if (frame.get_w_f_trace() is None or self.is_tracing or
             self.gettrace() is None):
             return
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
@@ -1,11 +1,11 @@
 import sys
 from rpython.rlib import revdb
 from rpython.rlib.debug import make_sure_not_resized
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.rtyper.annlowlevel import cast_gcref_to_instance
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter import gateway, typedef
+from pypy.interpreter import gateway, typedef, pycode
 
 
 class DBState:
@@ -45,6 +45,90 @@
     #revdb.register_debug_command(revdb.CMD_WATCHVALUES, lambda_watchvalues)
 
 
+pycode.PyCode.co_revdb_linestarts = None   # or a string: a list of bits
+
+
+def potential_stop_point(frame):
+    if not we_are_translated():
+        return
+    #
+    # We only record a stop_point at every line, not every bytecode.
+    # Uses roughly the same algo as ExecutionContext.run_trace_func()
+    # to know where the line starts are, but tweaked for speed,
+    # avoiding the quadratic complexity when run N times with a large
+    # code object.  A potential difference is that we only record
+    # where the line starts are; the "We jumped backwards in the same
+    # line" case of run_trace_func() is not fully reproduced.
+    #
+    code = frame.pycode
+    lstart = code.co_revdb_linestarts
+    if lstart is None:
+        lstart = build_co_revdb_linestarts(code)
+    index = frame.last_instr
+    c = lstart[index >> 3]
+    if ord(c) & (1 << (index & 7)):
+        stop_point_at_start_of_line()
+
+def build_co_revdb_linestarts(code):
+    # inspired by findlinestarts() in the 'dis' standard module
+    assert len(code.co_code) > 0
+    bits = [False] * len(code.co_code)
+    bits[0] = True
+    lnotab = code.co_lnotab
+    addr = 0
+    p = 0
+    newline = True
+    while p + 1 < len(lnotab):
+        byte_incr = ord(lnotab[p])
+        line_incr = ord(lnotab[p+1])
+        if byte_incr:
+            if newline:
+                if addr < len(bits):
+                    bits[addr] = True
+                newline = False
+            addr += byte_incr
+        if line_incr:
+            newline = True
+        p += 2
+    if newline:
+        if addr < len(bits):
+            bits[addr] = True
+    #
+    byte_list = []
+    pending = 0
+    nextval = 1
+    for bit_is_set in bits:
+        if bit_is_set:
+            pending |= nextval
+        if nextval < 128:
+            nextval <<= 1
+        else:
+            byte_list.append(chr(pending))
+            pending = 0
+            nextval = 1
+    if nextval != 1:
+        byte_list.append(chr(pending))
+    lstart = ''.join(byte_list)
+    code.co_revdb_linestarts = lstart
+    return lstart
+
+
+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
+        revdb.watch_restore_state(any_watch_point)
+        if watch_id != -1:
+            revdb.breakpoint(watch_id)
+    revdb.stop_point()
+
+
 def load_metavar(index):
     assert index >= 0
     space = dbstate.space
@@ -113,7 +197,11 @@
         dbstate.printed_objects[uid] = w_obj
         revdb.send_nextnid(uid)   # outputs '$NUM = '
     space.setitem(space.builtin.w_dict, space.wrap('_'), w_obj)
-    revdb.send_output(space.str_w(space.repr(w_obj)))
+    # do str_w(repr()) only now: if w_obj was produced successfully,
+    # but its repr crashes because it tries to do I/O, then we already
+    # have it recorded in '_' and in '$NUM ='.
+    s = space.str_w(space.repr(w_obj))
+    revdb.send_output(s)
     revdb.send_output("\n")
 
 @specialize.memo()
diff --git a/pypy/interpreter/test/test_reverse_debugging.py 
b/pypy/interpreter/test/test_reverse_debugging.py
new file mode 100644
--- /dev/null
+++ b/pypy/interpreter/test/test_reverse_debugging.py
@@ -0,0 +1,28 @@
+import dis
+from pypy.interpreter.reverse_debugging import *
+
+
+class FakeCode:
+    def __init__(self, co_code, co_lnotab):
+        self.co_firstlineno = 43
+        self.co_code = co_code
+        self.co_lnotab = co_lnotab
+        self.co_revdb_linestarts = None
+
+
+def test_build_co_revdb_linestarts():
+    fake_lnotab = ("\x01\x02\x03\x04"
+                   "\x00\xFF\x20\x30\x00\xFF\x00\x40"
+                   "\xFF\x00\x0A\x0B\xFF\x00\x0C\x00")
+    code = FakeCode("?" * sum(map(ord, fake_lnotab[0::2])), fake_lnotab)
+    lstart = build_co_revdb_linestarts(code)
+    assert lstart is code.co_revdb_linestarts
+
+    expected_starts = set()
+    for addr, lineno in dis.findlinestarts(code):
+        expected_starts.add(addr)
+
+    for index in range(len(code.co_code)):
+        c = lstart[index >> 3]
+        found = ord(c) & (1 << (index & 7))
+        assert (found != 0) == (index in expected_starts)
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
@@ -91,15 +91,15 @@
 
     def expect(self, cmd, arg1=0, arg2=0, arg3=0, extra=""):
         msg = self.recv()
-        assert msg.cmd == cmd
+        assert msg.cmd == cmd, msg
         if arg1 is not Ellipsis:
-            assert msg.arg1 == arg1
+            assert msg.arg1 == arg1, msg
         if arg2 is not Ellipsis:
-            assert msg.arg2 == arg2
+            assert msg.arg2 == arg2, msg
         if arg3 is not Ellipsis:
-            assert msg.arg3 == arg3
+            assert msg.arg3 == arg3, msg
         if extra is not Ellipsis:
-            assert msg.extra == extra
+            assert msg.extra == extra, msg
         return msg
 
     def expect_ready(self):
@@ -207,6 +207,9 @@
         child = ReplayProcess(initial_subproc.pid, s1)
         msg = child.expect(ANSWER_INIT, INIT_VERSION_NUMBER, Ellipsis)
         self.total_stop_points = msg.arg2
+        if self.total_stop_points == 0:
+            raise ValueError("%r does not contain any stop point" %
+                             (revdb_log_filename,))
         child.expect_ready()
         self.initial_uid = child.currently_created_objects
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to