Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit