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