[pypy-commit] pypy default: Fixed sys.settrace() of the pypy interpreter when under the JIT.

alex_gaynor noreply at buildbot.pypy.org
Wed Oct 30 23:08:49 CET 2013


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: 
Changeset: r67754:2a993c608319
Date: 2013-10-30 15:08 -0700
http://bitbucket.org/pypy/pypy/changeset/2a993c608319/

Log:	Fixed sys.settrace() of the pypy interpreter when under the JIT.
	Properly run the tracefunction at every bytecode.

diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,5 @@
 =====================================
-PyPy: Python in Python Implementation 
+PyPy: Python in Python Implementation
 =====================================
 
 Welcome to PyPy!
@@ -26,9 +26,11 @@
 Building
 ========
 
-build with::
+build with:
 
-  rpython/bin/rpython -Ojit pypy/goal/targetpypystandalone.py
+.. code-block:: console
+
+    $ rpython/bin/rpython -Ojit pypy/goal/targetpypystandalone.py
 
 This ends up with ``pypy-c`` binary in the main pypy directory. We suggest
 to use virtualenv with the resulting pypy-c as the interpreter, you can
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -10,7 +10,7 @@
 from rpython.rlib.rarithmetic import r_uint
 
 from pypy.interpreter.executioncontext import (ExecutionContext, ActionFlag,
-    UserDelAction, FrameTraceAction)
+    UserDelAction)
 from pypy.interpreter.error import (OperationError, operationerrfmt,
     new_exception_class)
 from pypy.interpreter.argument import Arguments
@@ -330,7 +330,6 @@
         self.actionflag = ActionFlag()    # changed by the signal module
         self.check_signal_action = None   # changed by the signal module
         self.user_del_action = UserDelAction(self)
-        self.frame_trace_action = FrameTraceAction(self)
         self._code_of_sys_exc_info = None
 
         from pypy.interpreter.pycode import cpython_magic, default_magic
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -27,13 +27,10 @@
     def __init__(self, space):
         self.space = space
         self.topframeref = jit.vref_None
-        # tracing: space.frame_trace_action.fire() must be called to ensure
-        # that tracing occurs whenever self.w_tracefunc or self.is_tracing
-        # is modified.
-        self.w_tracefunc = None        # if not None, no JIT
+        self.w_tracefunc = None
         self.is_tracing = 0
         self.compiler = space.createcompiler()
-        self.profilefunc = None        # if not None, no JIT
+        self.profilefunc = None
         self.w_profilefuncarg = None
 
     def gettopframe(self):
@@ -76,9 +73,6 @@
                 frame_vref()
             jit.virtual_ref_finish(frame_vref, frame)
 
-        if self.gettrace() is not None and not frame.hide():
-            self.space.frame_trace_action.fire()
-
     # ________________________________________________________________
 
     def c_call_trace(self, frame, w_func, args=None):
@@ -123,25 +117,77 @@
     def return_trace(self, frame, w_retval):
         "Trace the return from a function"
         if self.gettrace() is not None:
-            return_from_hidden = self._trace(frame, 'return', w_retval)
-            # special case: if we are returning from a hidden function,
-            # then maybe we have to fire() the action again; otherwise
-            # it will not be called.  See test_trace_hidden_prints.
-            if return_from_hidden:
-                self.space.frame_trace_action.fire()
+            self._trace(frame, 'return', w_retval)
 
     def bytecode_trace(self, frame, decr_by=TICK_COUNTER_STEP):
         "Trace function called before each bytecode."
         # this is split into a fast path and a slower path that is
         # not invoked every time bytecode_trace() is.
+        self.bytecode_only_trace(frame)
         actionflag = self.space.actionflag
         if actionflag.decrement_ticker(decr_by) < 0:
             actionflag.action_dispatcher(self, frame)     # slow path
     bytecode_trace._always_inline_ = True
 
+    def bytecode_only_trace(self, frame):
+        """
+        Like bytecode_trace() but doesn't invoke any other events besides the
+        trace function.
+        """
+        if (frame.w_f_trace is None or self.is_tracing or
+            self.gettrace() is None):
+            return
+        self.run_trace_func(frame)
+    bytecode_only_trace._always_inline_ = True
+
+    @jit.unroll_safe
+    def run_trace_func(self, frame):
+        code = frame.pycode
+        if frame.instr_lb <= frame.last_instr < frame.instr_ub:
+            if frame.last_instr < frame.instr_prev_plus_one:
+                # We jumped backwards in the same line.
+                self._trace(frame, 'line', self.space.w_None)
+        else:
+            size = len(code.co_lnotab) / 2
+            addr = 0
+            line = code.co_firstlineno
+            p = 0
+            lineno = code.co_lnotab
+            while size > 0:
+                c = ord(lineno[p])
+                if (addr + c) > frame.last_instr:
+                    break
+                addr += c
+                if c:
+                    frame.instr_lb = addr
+
+                line += ord(lineno[p + 1])
+                p += 2
+                size -= 1
+
+            if size > 0:
+                while True:
+                    size -= 1
+                    if size < 0:
+                        break
+                    addr += ord(lineno[p])
+                    if ord(lineno[p + 1]):
+                        break
+                    p += 2
+                frame.instr_ub = addr
+            else:
+                frame.instr_ub = sys.maxint
+
+            if frame.instr_lb == frame.last_instr: # At start of line!
+                frame.f_lineno = line
+                self._trace(frame, 'line', self.space.w_None)
+
+        frame.instr_prev_plus_one = frame.last_instr + 1
+
     def bytecode_trace_after_exception(self, frame):
         "Like bytecode_trace(), but without increasing the ticker."
         actionflag = self.space.actionflag
+        self.bytecode_only_trace(frame)
         if actionflag.get_ticker() < 0:
             actionflag.action_dispatcher(self, frame)     # slow path
     bytecode_trace_after_exception._always_inline_ = 'try'
@@ -178,7 +224,9 @@
         else:
             self.force_all_frames()
             self.w_tracefunc = w_func
-            self.space.frame_trace_action.fire()
+            # Increase the JIT's trace_limit when we have a tracefunc, it
+            # generates a ton of extra ops.
+            jit.set_param(None, 'trace_limit', 10000)
 
     def gettrace(self):
         return jit.promote(self.w_tracefunc)
@@ -221,14 +269,13 @@
         is_tracing = self.is_tracing
         self.is_tracing = 0
         try:
-            self.space.frame_trace_action.fire()
             return self.space.call(w_func, w_args)
         finally:
             self.is_tracing = is_tracing
 
     def _trace(self, frame, event, w_arg, operr=None):
         if self.is_tracing or frame.hide():
-            return True
+            return
 
         space = self.space
 
@@ -260,7 +307,6 @@
             finally:
                 self.is_tracing -= 1
                 frame.locals2fast()
-                space.frame_trace_action.fire()
 
         # Profile cases
         if self.profilefunc is not None:
@@ -269,7 +315,7 @@
                     event == 'c_call' or
                     event == 'c_return' or
                     event == 'c_exception'):
-                return False
+                return
 
             last_exception = frame.last_exception
             if event == 'leaveframe':
@@ -289,7 +335,6 @@
             finally:
                 frame.last_exception = last_exception
                 self.is_tracing -= 1
-        return False
 
     def checksignals(self):
         """Similar to PyErr_CheckSignals().  If called in the main thread,
@@ -475,54 +520,3 @@
             except OperationError, e:
                 e.write_unraisable(space, descrname, w_obj)
                 e.clear(space)   # break up reference cycles
-
-class FrameTraceAction(AsyncAction):
-    """An action that calls the local trace functions (w_f_trace)."""
-
-    @jit.unroll_safe
-    def perform(self, executioncontext, frame):
-        if (frame.w_f_trace is None or executioncontext.is_tracing or
-            executioncontext.gettrace() is None):
-            return
-        code = frame.pycode
-        if frame.instr_lb <= frame.last_instr < frame.instr_ub:
-            if frame.last_instr < frame.instr_prev_plus_one:
-                # We jumped backwards in the same line.
-                executioncontext._trace(frame, 'line', self.space.w_None)
-        else:
-            size = len(code.co_lnotab) / 2
-            addr = 0
-            line = code.co_firstlineno
-            p = 0
-            lineno = code.co_lnotab
-            while size > 0:
-                c = ord(lineno[p])
-                if (addr + c) > frame.last_instr:
-                    break
-                addr += c
-                if c:
-                    frame.instr_lb = addr
-
-                line += ord(lineno[p + 1])
-                p += 2
-                size -= 1
-
-            if size > 0:
-                while True:
-                    size -= 1
-                    if size < 0:
-                        break
-                    addr += ord(lineno[p])
-                    if ord(lineno[p + 1]):
-                        break
-                    p += 2
-                frame.instr_ub = addr
-            else:
-                frame.instr_ub = sys.maxint
-
-            if frame.instr_lb == frame.last_instr: # At start of line!
-                frame.f_lineno = line
-                executioncontext._trace(frame, 'line', self.space.w_None)
-
-        frame.instr_prev_plus_one = frame.last_instr + 1
-        self.space.frame_trace_action.fire()     # continue tracing
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -464,8 +464,6 @@
         new_frame.instr_prev_plus_one = space.int_w(w_instr_prev_plus_one)
 
         self._setcellvars(cellvars)
-        # XXX what if the frame is in another thread??
-        space.frame_trace_action.fire()
 
     def hide(self):
         return self.pycode.hidden_applevel
@@ -759,7 +757,6 @@
         else:
             self.w_f_trace = w_trace
             self.f_lineno = self.get_last_lineno()
-            space.frame_trace_action.fire()
 
     def fdel_f_trace(self, space):
         self.w_f_trace = None
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -144,9 +144,11 @@
     def dispatch_bytecode(self, co_code, next_instr, ec):
         while True:
             self.last_instr = intmask(next_instr)
-            if not jit.we_are_jitted():
+            if jit.we_are_jitted():
+                ec.bytecode_only_trace(self)
+            else:
                 ec.bytecode_trace(self)
-                next_instr = r_uint(self.last_instr)
+            next_instr = r_uint(self.last_instr)
             opcode = ord(co_code[next_instr])
             next_instr += 1
 
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -425,6 +425,7 @@
         loop, = log.loops_by_id('call', is_entry_bridge=True)
         assert loop.match("""
             guard_value(i4, 1, descr=...)
+            guard_isnull(p5, descr=...)
             guard_nonnull_class(p12, ConstClass(W_IntObject), descr=...)
             guard_value(i8, 0, descr=...)
             guard_value(p2, ConstPtr(ptr21), descr=...)


More information about the pypy-commit mailing list