Author: Armin Rigo <ar...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit