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