Author: Armin Rigo <ar...@tunes.org> Branch: reverse-debugger Changeset: r85335:fb7901990151 Date: 2016-06-22 17:30 +0200 http://bitbucket.org/pypy/pypy/changeset/fb7901990151/
Log: watchpoints, in-progress diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py --- a/rpython/rlib/revdb.py +++ b/rpython/rlib/revdb.py @@ -16,12 +16,12 @@ CMD_BREAKPOINTS = 4 CMD_MOREINFO = 5 CMD_ATTACHID = 6 -CMD_WATCH = 7 -CMD_EXPECTED = 8 +CMD_CHECKWATCH = 7 +CMD_WATCHVALUES = 8 ANSWER_TEXT = 20 ANSWER_MOREINFO = 21 ANSWER_NEXTNID = 22 -ANSWER_COMPILED = 23 +ANSWER_WATCH = 23 def stop_point(): @@ -46,6 +46,9 @@ def send_nextnid(unique_id): send_answer(ANSWER_NEXTNID, unique_id) +def send_watch(text, ok_flag): + send_answer(ANSWER_WATCH, ok_flag, extra=text) + def current_time(): """For RPython debug commands: returns the current time.""" return llop.revdb_get_value(lltype.SignedLongLong, 'c') @@ -103,6 +106,12 @@ ll_callback = llhelper(_CALLBACK_GCREF_FNPTR, callback) llop.revdb_track_object(lltype.Void, unique_id, ll_callback) +def save_state(): + return llop.revdb_save_state(lltype.Bool) + +def restore_state(): + llop.revdb_restore_state(lltype.Void) + # ____________________________________________________________ @@ -157,7 +166,7 @@ llannotation.lltype_to_annotation(llmemory.GCREF)] def arguments_WATCHING(self): - return [] + raise Exception("XXX remove me") def specialize_call(self, hop): hop.exception_cannot_occur() diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -571,7 +571,8 @@ 'revdb_breakpoint': LLOp(), 'revdb_get_value': LLOp(sideeffects=False), 'revdb_get_unique_id': LLOp(sideeffects=False), - ## 'revdb_track_object': LLOp(), + 'revdb_save_state': LLOp(), + 'revdb_restore_state': LLOp(), } # ***** Run test_lloperation after changes. ***** diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py --- a/rpython/translator/revdb/gencsupp.py +++ b/rpython/translator/revdb/gencsupp.py @@ -27,7 +27,6 @@ lltype.Void)) ALLOCFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.LONGLONG, llmemory.GCREF], lltype.Void)) - WATCHFUNCPTR = lltype.Ptr(lltype.FuncType([], lltype.Signed)) bk = db.translator.annotator.bookkeeper cmds = getattr(db.translator, 'revdb_commands', {}) @@ -37,8 +36,7 @@ S = lltype.Struct('RPY_REVDB_COMMANDS', ('names', lltype.FixedSizeArray(rffi.INT, len(numcmds) + 1)), ('funcs', lltype.FixedSizeArray(FUNCPTR, len(numcmds))), - ('alloc', ALLOCFUNCPTR), - ('watch', WATCHFUNCPTR)) + ('alloc', ALLOCFUNCPTR)) s = lltype.malloc(S, flavor='raw', immortal=True, zero=True) i = 0 @@ -51,8 +49,6 @@ i += 1 elif name == "ALLOCATING": s.alloc = fnptr - elif name == "WATCHING": - s.watch = fnptr else: raise AssertionError("bad tag in register_debug_command(): %r" % (name,)) 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 @@ -109,14 +109,34 @@ lst = [str(n) for n in sorted(self.pgroup.paused)] print ', '.join(lst) + def _bp_kind(self, name): + if name[0] == 'B': + return 'breakpoint' + elif name[0] == 'W': + return 'watchpoint' + else: + return '?????point' + + def _bp_new(self, break_at, watchvalue=None): + b = self.pgroup.edit_breakpoints() + new = 1 + while new in b.num2name: + new += 1 + b.num2name[new] = break_at + if watchvalue is not None: + b.watchvalues[new] = watchvalue + print "%s %d added" % (self._bp_kind(break_at).capitalize(), new) + return new + def cmd_info_breakpoints(self): - """List current breakpoints""" + """List current breakpoints and watchpoints""" lst = self.pgroup.all_breakpoints.num2name.items() if lst: for num, name in sorted(lst): - print '%8d: %s' % (num, name) + print '\t%s %d: %s' % (self._bp_kind(name), num, name[1:]) else: print 'no breakpoints.' + cmd_info_watchpoints = cmd_info_breakpoints def move_forward(self, steps): self.remove_tainting() @@ -137,7 +157,10 @@ def hit_breakpoint(self, b, backward=False): if b.num != -1: - self.print_extra_pending_info = 'Hit breakpoint %d' % (b.num,) + name = self.pgroup.all_breakpoints.num2name.get(b.num, '??') + kind = self._bp_kind(name) + self.print_extra_pending_info = 'Hit %s %d: %s' % (kind, b.num, + name[1:]) elif backward: b.time -= 1 if self.pgroup.get_current_time() != b.time: @@ -240,20 +263,30 @@ if not argument: print "Break where?" return - b = self.pgroup.edit_breakpoints() - new = 1 - while new in b.num2name: - new += 1 - b.num2name[new] = argument - print "Breakpoint %d added" % (new,) + self._bp_new('B' + argument) command_b = command_break def command_delete(self, argument): - """Delete a breakpoint""" + """Delete a breakpoint/watchpoint""" arg = int(argument) b = self.pgroup.edit_breakpoints() if arg not in b.num2name: - print "No breakpoint number %d" % (arg,) + print "No breakpoint/watchpoint number %d" % (arg,) else: - del b.num2name[arg] - print "Breakpoint %d deleted" % (arg,) + name = b.num2name.pop(arg) + b.watchvalues.pop(arg, '') + kind = self._bp_kind(name) + print "%s %d deleted: %s" % (kind.capitalize(), arg, name[1:]) + + def command_watch(self, argument): + """Add a watchpoint (use $NUM in the expression to watch)""" + if not argument: + print "Watch what?" + return + ok_flag, text = self.pgroup.check_watchpoint_expr(argument) + if not ok_flag: + print text + print 'Watchpoint not added' + else: + print 'Current value:', text + self._bp_new('W' + argument, watchvalue=text) diff --git a/rpython/translator/revdb/message.py b/rpython/translator/revdb/message.py --- a/rpython/translator/revdb/message.py +++ b/rpython/translator/revdb/message.py @@ -17,6 +17,8 @@ # extra="\0-separated names") CMD_MOREINFO = 5 # Message(CMD_MOREINFO) CMD_ATTACHID = 6 # Message(CMD_ATTACHID, small-num, unique-id) +CMD_CHECKWATCH = 7 # Message(CMD_CHECKWATCH, extra=expression) +CMD_WATCHVALUES = 8 # Message(CMD_WATCHVALUES, extra=texts) # the first message sent by the first child: @@ -56,6 +58,10 @@ # Message(ANSWER_NEXTNID, unique-id) ANSWER_NEXTNID = 22 +# sent after CMD_CHECKWATCH: +# Message(ANSWER_WATCH, ok_flag, extra=result_of_expr) +ANSWER_WATCH = 23 + # ____________________________________________________________ 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 @@ -18,18 +18,22 @@ class AllBreakpoints(object): def __init__(self): - self.num2name = {} # {small number: function name} - self.stack_depth = 0 # breaks if the depth becomes lower than this + self.num2name = {} # {small number: break/watchpoint} + self.watchvalues = {} # {small number: resulting text} + self.stack_depth = 0 # breaks if the depth becomes lower than this def __repr__(self): return 'AllBreakpoints(%r, %d)' % (self.num2name, self.stack_depth) - def __eq__(self, other): - return (self.num2name == other.num2name and - self.stack_depth == other.stack_depth) - - def __ne__(self, other): - return not (self == other) + def compare(self, other): + if (self.num2name == other.num2name and + self.stack_depth == other.stack_depth): + if self.watchvalues == other.watchvalues: + return 2 # completely equal + else: + return 1 # equal, but watchvalues out-of-date + else: + return 0 # different def is_empty(self): return len(self.num2name) == 0 and self.stack_depth == 0 @@ -71,7 +75,7 @@ return ''.join(pieces) def send(self, msg): - #print 'SENT:', self.pid, msg + print 'SENT:', self.pid, msg binary = struct.pack("iIqqq", msg.cmd, len(msg.extra), msg.arg1, msg.arg2, msg.arg3) self.control_socket.sendall(binary + msg.extra) @@ -81,7 +85,7 @@ cmd, size, arg1, arg2, arg3 = struct.unpack("iIqqq", binary) extra = self._recv_all(size) msg = Message(cmd, arg1, arg2, arg3, extra) - #print 'RECV:', self.pid, msg + print 'RECV:', self.pid, msg return msg def expect(self, cmd, arg1=0, arg2=0, arg3=0, extra=""): @@ -129,7 +133,7 @@ except socket.error: pass - def forward(self, steps, breakpoint_mode='b'): + def forward(self, steps, breakpoint_mode, all_breakpoints): """Move this subprocess forward in time. Returns the Breakpoint or None. """ @@ -145,6 +149,8 @@ break if bkpt is None: bkpt = Breakpoint(msg.arg1, msg.arg3) + all_breakpoints.watchvalues = dict.fromkeys( + all_breakpoints.watchvalues) # set all values to None assert msg.cmd == ANSWER_READY self.update_times(msg) return bkpt @@ -249,7 +255,8 @@ 'r' = record the occurrence of a breakpoint but continue """ assert steps >= 0 - self.update_breakpoints() + if breakpoint_mode != 'i': + self.update_breakpoints() latest_bkpt = None while True: cur_time = self.get_current_time() @@ -261,7 +268,8 @@ break assert rel_next_clone >= 0 if rel_next_clone > 0: - bkpt = self.active.forward(rel_next_clone, breakpoint_mode) + bkpt = self.active.forward(rel_next_clone, breakpoint_mode, + self.all_breakpoints) if breakpoint_mode == 'r': latest_bkpt = bkpt or latest_bkpt elif bkpt: @@ -271,7 +279,8 @@ self.paused[self.active.current_time].close() clone = self.active.clone() self.paused[clone.current_time] = clone - bkpt = self.active.forward(steps, breakpoint_mode) + bkpt = self.active.forward(steps, breakpoint_mode, + self.all_breakpoints) if breakpoint_mode == 'r': bkpt = bkpt or latest_bkpt if bkpt: @@ -313,19 +322,50 @@ search_start_time -= time_range_to_search * 3 def update_breakpoints(self): - if self.active.breakpoints_cache == self.all_breakpoints: - return + cmp = self.all_breakpoints.compare(self.active.breakpoints_cache) + print 'compare:', cmp, self.all_breakpoints.watchvalues + if cmp == 2: + return # up-to-date + + # update the breakpoints/watchpoints + self.active.breakpoints_cache = None num2name = self.all_breakpoints.num2name - flat = [] - if num2name: - flat = [num2name.get(n, '') for n in range(max(num2name) + 1)] - arg1 = self.all_breakpoints.stack_depth - extra = '\x00'.join(flat) - # - self.active.breakpoints_cache = None - self.active.send(Message(CMD_BREAKPOINTS, arg1, extra=extra)) + N = (max(num2name) + 1) if num2name else 0 + if cmp == 0: + flat = [num2name.get(n, '') for n in range(N)] + arg1 = self.all_breakpoints.stack_depth + extra = '\x00'.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()): + watchvalues = self.all_breakpoints.watchvalues + flat = [] + for n in range(N): + text = '' + name = num2name.get(n, '') + if name.startswith('W'): + text = watchvalues[n] + if text is None: + _, text = self.check_watchpoint_expr(name[1:]) + print 'updating watchpoint value: %s => %s' % ( + name[1:], text) + watchvalues[n] = text + flat.append(text) + extra = '\x00'.join(flat) + self.active.send(Message(CMD_WATCHVALUES, extra=extra)) + self.active.expect_ready() + + self.active.breakpoints_cache = self.all_breakpoints.duplicate() + + def check_watchpoint_expr(self, expr): + self.active.send(Message(CMD_CHECKWATCH, extra=expr)) + msg = self.active.expect(ANSWER_WATCH, Ellipsis, extra=Ellipsis) self.active.expect_ready() - self.active.breakpoints_cache = self.all_breakpoints.duplicate() + return msg.arg1, msg.extra def _resume(self, from_time): clone_me = self.paused[from_time] diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c --- a/rpython/translator/revdb/src-revdb/revdb.c +++ b/rpython/translator/revdb/src-revdb/revdb.c @@ -585,14 +585,21 @@ execute_rpy_function(rpy_revdb_commands.rp_funcs[i], cmd, s); } -static void save_state(void) +RPY_EXTERN +bool_t rpy_reverse_db_save_state(void) { - stopped_time = rpy_revdb.stop_point_seen; - stopped_uid = rpy_revdb.unique_id_seen; - rpy_revdb.unique_id_seen = (-1ULL) << 63; + if (stopped_time == 0) { + stopped_time = rpy_revdb.stop_point_seen; + stopped_uid = rpy_revdb.unique_id_seen; + rpy_revdb.unique_id_seen = (-1ULL) << 63; + return 1; + } + else + return 0; } -static void restore_state(void) +RPY_EXTERN +void rpy_reverse_db_restore_state(void) { rpy_revdb.stop_point_seen = stopped_time; rpy_revdb.unique_id_seen = stopped_uid; @@ -604,7 +611,7 @@ void rpy_reverse_db_stop_point(void) { while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) { - save_state(); + rpy_reverse_db_save_state(); breakpoint_mode = 0; if (pending_after_forward) { @@ -645,7 +652,7 @@ break; } } - restore_state(); + rpy_reverse_db_restore_state(); } } @@ -744,12 +751,12 @@ } if (rpy_revdb_commands.rp_alloc) { rpy_revdb_t dinfo; - save_state(); + rpy_reverse_db_save_state(); disable_io(&dinfo); if (setjmp(jmp_buf_cancel_execution) == 0) rpy_revdb_commands.rp_alloc(uid, new_object); enable_io(&dinfo); - restore_state(); + rpy_reverse_db_restore_state(); } rpy_revdb.unique_id_break = *future_next_id++; return uid; diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -102,6 +102,12 @@ #define OP_REVDB_TRACK_OBJECT(uid, callback, r) \ rpy_reverse_db_track_object(uid, callback) +#define OP_REVDB_SAVE_STATE(r) \ + r = rpy_reverse_db_save_state() + +#define OP_REVDB_RESTORE_STATE(r) \ + rpy_reverse_db_restore_state() + RPY_EXTERN void rpy_reverse_db_flush(void); RPY_EXTERN char *rpy_reverse_db_fetch(int expected_size, const char *file, int line); @@ -114,5 +120,7 @@ RPY_EXTERN void rpy_reverse_db_breakpoint(int64_t num); RPY_EXTERN long long rpy_reverse_db_get_value(char value_id); RPY_EXTERN uint64_t rpy_reverse_db_unique_id_break(void *new_object); +RPY_EXTERN bool_t rpy_reverse_db_save_state(void); +RPY_EXTERN void rpy_reverse_db_restore_state(void); /* ------------------------------------------------------------ */ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit