[pypy-commit] pypy reverse-debugger: 'next', 'bnext', 'finish', 'bfinish' for Python

arigo pypy.commits at gmail.com
Wed Jun 29 17:48:18 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85464:3c619ff91d4b
Date: 2016-06-29 23:49 +0200
http://bitbucket.org/pypy/pypy/changeset/3c619ff91d4b/

Log:	'next', 'bnext', 'finish', 'bfinish' for Python

diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -64,6 +64,9 @@
         return frame
 
     def enter(self, frame):
+        if self.space.config.translation.reverse_debugger:
+            from pypy.interpreter.reverse_debugging import enter_call
+            enter_call(self.topframeref(), frame)
         frame.f_backref = self.topframeref
         self.topframeref = jit.virtual_ref(frame)
 
@@ -84,6 +87,9 @@
                 # be accessed also later
                 frame_vref()
             jit.virtual_ref_finish(frame_vref, frame)
+        if self.space.config.translation.reverse_debugger:
+            from pypy.interpreter.reverse_debugging import leave_call
+            leave_call(self.topframeref(), frame)
 
     # ________________________________________________________________
 
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
@@ -10,6 +10,7 @@
 
 class DBState:
     extend_syntax_with_dollar_num = False
+    breakpoint_stack_id = 0
     breakpoint_funcnames = []
     printed_objects = {}
     metavars = []
@@ -37,8 +38,8 @@
     revdb.register_debug_command(revdb.CMD_PRINT, lambda_print)
     revdb.register_debug_command(revdb.CMD_BACKTRACE, lambda_backtrace)
     revdb.register_debug_command(revdb.CMD_LOCALS, lambda_locals)
-    #revdb.register_debug_command(revdb.CMD_BREAKPOINTS, lambda_breakpoints)
-    #revdb.register_debug_command(revdb.CMD_MOREINFO, lambda_moreinfo)
+    revdb.register_debug_command(revdb.CMD_BREAKPOINTS, lambda_breakpoints)
+    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)
@@ -48,6 +49,16 @@
 pycode.PyCode.co_revdb_linestarts = None   # or a string: an array of bits
 
 
+def enter_call(caller_frame, callee_frame):
+    if dbstate.breakpoint_stack_id != 0:
+        if dbstate.breakpoint_stack_id == revdb.get_unique_id(caller_frame):
+            revdb.breakpoint(-1)
+
+def leave_call(caller_frame, callee_frame):
+    if dbstate.breakpoint_stack_id != 0:
+        if dbstate.breakpoint_stack_id == revdb.get_unique_id(caller_frame):
+            revdb.breakpoint(-1)
+
 def potential_stop_point(frame):
     if not we_are_translated():
         return
@@ -369,6 +380,22 @@
 lambda_locals = lambda: command_locals
 
 
+def command_breakpoints(cmd, extra):
+    dbstate.breakpoint_stack_id = cmd.c_arg1
+lambda_breakpoints = lambda: command_breakpoints
+
+def command_stackid(cmd, extra):
+    frame = fetch_cur_frame()
+    if frame is not None and cmd.c_arg1 != 0:     # parent_flag
+        frame = dbstate.space.getexecutioncontext().getnextframe_nohidden(frame)
+    if frame is None:
+        uid = 0
+    else:
+        uid = revdb.get_unique_id(frame)
+    revdb.send_answer(revdb.ANSWER_STACKID, uid)
+lambda_stackid = lambda: command_stackid
+
+
 def command_allocating(uid, gcref):
     w_obj = cast_gcref_to_instance(W_Root, gcref)
     dbstate.printed_objects[uid] = w_obj
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
@@ -154,18 +154,20 @@
         self.remove_tainting()
         try:
             self.pgroup.go_forward(steps)
-            return True
+            return None
         except Breakpoint as b:
             self.hit_breakpoint(b)
-            return False
+            return b
 
-    def move_backward(self, steps):
+    def move_backward(self, steps, rel_stop_at=-1):
+        ignore_bkpt = steps == 1 and rel_stop_at == -1
         try:
-            self.pgroup.go_backward(steps, ignore_breakpoints=(steps==1))
-            return True
+            self.pgroup.go_backward(steps, ignore_breakpoints=ignore_bkpt,
+                                    rel_stop_at=rel_stop_at)
+            return None
         except Breakpoint as b:
             self.hit_breakpoint(b, backward=True)
-            return False
+            return b
 
     def hit_breakpoint(self, b, backward=False):
         if b.num != -1:
@@ -198,7 +200,7 @@
     @contextmanager
     def _stack_id_break(self, stack_id):
         # add temporarily a breakpoint that hits when we enter/leave
-        # the frame identified by 'stack_id'
+        # a frame from/to the frame identified by 'stack_id'
         b = self.pgroup.edit_breakpoints()
         b.stack_id = stack_id
         try:
@@ -208,50 +210,55 @@
 
     def command_next(self, argument):
         """Run forward for one step, skipping calls"""
-        depth1 = self.pgroup.get_stack_id()
-        with self._stack_id_break(depth1):
-            if not self.move_forward(1):
-                # we either hit a regular breakpoint, or we hit the
-                # temporary breakpoint
-                return
-            if depth1 == 0:   # we started outside any frame
-                return
-            if self.pgroup.get_stack_id() == depth1:
-                return        # we are still in the same frame
-        #
-        # If, after running one step, the stack id is different than
-        # before but we didn't leave that frame, then we must have
-        # entered a new one.  Continue until we leave that new frame.
-        # Can't do it more directly because the "breakpoint" of
-        # stack_id is only checked for on function enters and returns
-        # (which simplifies and speeds up things for the RPython
-        # code).
-        self.command_finish('')
+        stack_id = self.pgroup.get_stack_id(is_parent=False)
+        with self._stack_id_break(stack_id):
+            b = self.move_forward(1)
+        if b is None:
+            return    # no breakpoint hit, and no frame just entered: done
+        elif b.num != -1:
+            return    # a regular breakpoint was hit
+        else:
+            # we entered a frame.  Continue running until we leave that
+            # frame again
+            with self._stack_id_break(stack_id):
+                self.command_continue("")
     command_n = command_next
 
     def command_bnext(self, argument):
         """Run backward for one step, skipping calls"""
-        # similar to command_next()
-        depth1 = self.pgroup.get_stack_id()
-        with self._stack_id_break(depth1):
-            if not self.move_backward(1):
-                return
-            if depth1 == 0:
-                return
-            if self.pgroup.get_stack_id() == depth1:
-                return        # we are still in the same frame
-        self.command_bfinish('')
+        stack_id = self.pgroup.get_stack_id(is_parent=False)
+        with self._stack_id_break(stack_id):
+            b = self.move_backward(1, rel_stop_at=0)
+        if b is None:
+            return    # no breakpoint hit, and no frame just
+                      # reverse-entered: done
+        elif b.num != -1:
+            return    # a regular breakpoint was hit
+        else:
+            # we reverse-entered a frame.  Continue running backward
+            # until we go past the reverse-leave (i.e. the entering)
+            # of that frame.
+            with self._stack_id_break(stack_id):
+                self.command_bcontinue("")
     command_bn = command_bnext
 
     def command_finish(self, argument):
         """Run forward until the current function finishes"""
-        with self._stack_id_break(self.pgroup.get_stack_id()):
-            self.command_continue('')
+        stack_id = self.pgroup.get_stack_id(is_parent=True)
+        if stack_id == 0:
+            print 'No stack.'
+        else:
+            with self._stack_id_break(stack_id):
+                self.command_continue('')
 
     def command_bfinish(self, argument):
         """Run backward until the current function is called"""
-        with self._stack_id_break(self.pgroup.get_stack_id()):
-            self.command_bcontinue('')
+        stack_id = self.pgroup.get_stack_id(is_parent=True)
+        if stack_id == 0:
+            print 'No stack.'
+        else:
+            with self._stack_id_break(stack_id):
+                self.command_bcontinue('')
 
     def command_continue(self, argument):
         """Run forward"""
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
@@ -58,8 +58,8 @@
 # CMD_STACKID returns the id of the current or parent frame (depending
 # on the 'parent-flag' passed in), or 0 if not found.  The id can be just
 # the stack depth, or it can be the unique id of the frame object.  When
-# used in CMD_BREAKPOINTS, it means "break if we are entering/leaving that
-# frame".
+# used in CMD_BREAKPOINTS, it means "break if we are entering/leaving a
+# frame from/to the given frame".
 #    Message(ANSWER_STACKID, stack-id)
 ANSWER_STACKID    = 21
 
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
@@ -19,10 +19,11 @@
         self.num2name = {}     # {small number: break/watchpoint}
         self.watchvalues = {}  # {small number: resulting text}
         self.watchuids = {}    # {small number: [uid...]}
-        self.stack_id = 0      # breaks when leaving/entering this frame; 0=none
+        self.stack_id = 0      # breaks when leaving/entering a frame from/to
+                               # the frame identified by 'stack_id'
 
     def __repr__(self):
-        return 'AllBreakpoints(%r, %r, %r, %d)' % (
+        return 'AllBreakpoints(%r, %r, %r, %r)' % (
             self.num2name, self.watchvalues, self.watchuids,
             self.stack_id)
 
@@ -304,7 +305,7 @@
         if bkpt:
             raise bkpt
 
-    def go_backward(self, steps, ignore_breakpoints=False):
+    def go_backward(self, steps, ignore_breakpoints=False, rel_stop_at=-1):
         """Go backward, for the given number of 'steps' of time.
 
         Closes the active process.  Implemented as jump_in_time()
@@ -321,7 +322,7 @@
                 first_steps = 957
             self._backward_search_forward(
                 search_start_time       = initial_time - first_steps,
-                search_stop_time        = initial_time - 1,
+                search_stop_time        = initial_time + rel_stop_at,
                 search_go_on_until_time = initial_time - steps)
 
     def _backward_search_forward(self, search_start_time, search_stop_time,
@@ -536,8 +537,8 @@
     def edit_breakpoints(self):
         return self.all_breakpoints
 
-    def get_stack_id(self):
-        self.active.send(Message(CMD_STACKID))
+    def get_stack_id(self, is_parent):
+        self.active.send(Message(CMD_STACKID, is_parent))
         msg = self.active.expect(ANSWER_STACKID, Ellipsis)
         self.active.expect_ready()
         return msg.arg1
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
@@ -1097,9 +1097,8 @@
 void rpy_reverse_db_breakpoint(int64_t num)
 {
     if (flag_io_disabled != FID_REGULAR_MODE) {
-        fprintf(stderr, "revdb.breakpoint(): cannot be called from a "
-                        "debug command\n");
-        exit(1);
+        /* called from a debug command, ignore */
+        return;
     }
 
     switch (breakpoint_mode) {


More information about the pypy-commit mailing list