[pypy-svn] r58916 - in pypy/branch/2.5-merge/pypy/interpreter: . test

arigo at codespeak.net arigo at codespeak.net
Fri Oct 10 18:00:56 CEST 2008


Author: arigo
Date: Fri Oct 10 18:00:52 2008
New Revision: 58916

Modified:
   pypy/branch/2.5-merge/pypy/interpreter/baseobjspace.py
   pypy/branch/2.5-merge/pypy/interpreter/executioncontext.py
   pypy/branch/2.5-merge/pypy/interpreter/function.py
   pypy/branch/2.5-merge/pypy/interpreter/pyframe.py
   pypy/branch/2.5-merge/pypy/interpreter/pyopcode.py
   pypy/branch/2.5-merge/pypy/interpreter/test/test_executioncontext.py
Log:
(antocuni, arigo)
Merge the builtin-profiling branch, which adds support for
sending the c_call, c_return and c_exception events to
the app-level profilers set by sys.setprofile().


Modified: pypy/branch/2.5-merge/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/baseobjspace.py	Fri Oct 10 18:00:52 2008
@@ -702,9 +702,19 @@
         return self.call_args(w_func, args)
 
     def call_valuestack(self, w_func, nargs, frame):
+        from pypy.interpreter.function import Function, Method, is_builtin_code
+        if frame.is_being_profiled and is_builtin_code(w_func):
+            # XXX: this code is copied&pasted :-( from the slow path below
+            # call_valuestack().
+            args = frame.make_arguments(nargs)
+            try:
+                return self.call_args_and_c_profile(frame, w_func, args)
+            finally:
+                if isinstance(args, ArgumentsFromValuestack):
+                    args.frame = None
+
         if not self.config.objspace.disable_call_speedhacks:
             # XXX start of hack for performance
-            from pypy.interpreter.function import Function, Method
             hint(w_func.__class__, promote=True)
             if isinstance(w_func, Method):
                 w_inst = w_func.w_instance
@@ -729,6 +739,17 @@
             if isinstance(args, ArgumentsFromValuestack):
                 args.frame = None
 
+    def call_args_and_c_profile(self, frame, w_func, args):
+        ec = self.getexecutioncontext()
+        ec.c_call_trace(frame, w_func)
+        try:
+            w_res = self.call_args(w_func, args)
+        except OperationError, e:
+            ec.c_exception_trace(frame, e.w_value)
+            raise
+        ec.c_return_trace(frame, w_res)
+        return w_res
+
     def call_method(self, w_obj, methname, *arg_w):
         w_meth = self.getattr(w_obj, self.wrap(methname))
         return self.call_function(w_meth, *arg_w)

Modified: pypy/branch/2.5-merge/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/executioncontext.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/executioncontext.py	Fri Oct 10 18:00:52 2008
@@ -71,8 +71,7 @@
         def leave(self, ec):
             self.framestack = ec.framestack
             self.w_tracefunc = ec.w_tracefunc
-            self.profilefunc = ec.profilefunc
-            self.w_profilefuncarg = ec.w_profilefuncarg
+            self.setllprofile(ec.profilefunc, ec.w_profilefuncarg)
             self.is_tracing = ec.is_tracing
 
         # the following interface is for pickling and unpickling
@@ -104,10 +103,43 @@
         space.setitem(w_globals, w_key, w_value)
         return w_globals
 
+    def c_call_trace(self, frame, w_func):
+        "Profile the call of a builtin function"
+        if self.profilefunc is None:
+            frame.is_being_profiled = False
+        else:
+            self._trace(frame, 'c_call', w_func)
+
+    def c_return_trace(self, frame, w_retval):
+        "Profile the return from a builtin function"
+        if self.profilefunc is None:
+            frame.is_being_profiled = False
+        else:
+            self._trace(frame, 'c_return', w_retval)
+
+    def c_exception_trace(self, frame, w_exc):
+        "Profile function called upon OperationError."
+        if self.profilefunc is None:
+            frame.is_being_profiled = False
+        else:
+            self._trace(frame, 'c_exception', w_exc)
+
+    def _llprofile(self, event, w_arg):
+        fr = self.framestack.items
+        space = self.space
+        w_callback = self.profilefunc
+        if w_callback is not None:
+            frame = None
+            if fr:
+                frame = fr[0]
+            self.profilefunc(space, self.w_profilefuncarg, frame, event, w_arg)
+
     def call_trace(self, frame):
         "Trace the call of a function"
         if self.w_tracefunc is not None or self.profilefunc is not None:
             self._trace(frame, 'call', self.space.w_None)
+            if self.profilefunc:
+                frame.is_being_profiled = True
 
     def return_trace(self, frame, w_retval):
         "Trace the return from a function"
@@ -157,13 +189,15 @@
             self.profilefunc = None
             self.w_profilefuncarg = None
         else:
-            self.w_profilefuncarg = w_func
-            self.profilefunc = app_profile_call
+            self.setllprofile(app_profile_call, w_func)
 
     def setllprofile(self, func, w_arg):
         self.profilefunc = func
-        if func is not None and w_arg is None:
-            raise ValueError("Cannot call setllprofile with real None")
+        if func is not None:
+            if w_arg is None:
+                raise ValueError("Cannot call setllprofile with real None")
+            for frame in self.framestack.items:
+                frame.is_being_profiled = True
         self.w_profilefuncarg = w_arg
 
     def call_tracing(self, w_func, w_args):
@@ -176,7 +210,7 @@
             self.is_tracing = is_tracing
 
     def _trace(self, frame, event, w_arg, operr=None):
-        if self.is_tracing or frame.hide():
+        if self.is_tracing or frame.hide() or frame is None:
             return
 
         space = self.space
@@ -212,7 +246,8 @@
 
         # Profile cases
         if self.profilefunc is not None:
-            if event not in ['leaveframe', 'call']:
+            if event not in ['leaveframe', 'call', 'c_call',
+                             'c_return', 'c_exception']:
                 return
 
             last_exception = None

Modified: pypy/branch/2.5-merge/pypy/interpreter/function.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/function.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/function.py	Fri Oct 10 18:00:52 2008
@@ -33,7 +33,8 @@
     def __repr__(self):
         # return "function %s.%s" % (self.space, self.name)
         # maybe we want this shorter:
-        return "<Function %s>" % getattr(self, 'name', '?')
+        name = getattr(self, 'name', '?')
+        return "<%s %s>" % (self.__class__.__name__, name)
 
     def call_args(self, args):
         # delegate activation to code        
@@ -519,3 +520,13 @@
 
     def descr_function_repr(self):
         return self.space.wrap('<built-in function %s>' % (self.name,))
+
+def is_builtin_code(w_func):
+    from pypy.interpreter.gateway import BuiltinCode
+    if isinstance(w_func, Method):
+        w_func = w_func.w_function
+    if isinstance(w_func, Function):
+        code = w_func.getcode()
+    else:
+        code = None
+    return isinstance(code, BuiltinCode)

Modified: pypy/branch/2.5-merge/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/pyframe.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/pyframe.py	Fri Oct 10 18:00:52 2008
@@ -43,6 +43,7 @@
     instr_lb                 = 0
     instr_ub                 = -1
     instr_prev               = -1
+    is_being_profiled        = False
 
     def __init__(self, space, code, w_globals, closure):
         self = hint(self, access_directly=True)

Modified: pypy/branch/2.5-merge/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/pyopcode.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/pyopcode.py	Fri Oct 10 18:00:52 2008
@@ -848,6 +848,7 @@
                       
     def call_function(f, oparg, w_star=None, w_starstar=None):
         from pypy.rlib import rstack # for resume points
+        from pypy.interpreter.function import is_builtin_code
     
         n_arguments = oparg & 0xff
         n_keywords = (oparg>>8) & 0xff
@@ -857,7 +858,10 @@
         arguments = f.popvalues(n_arguments)
         args = Arguments(f.space, arguments, keywords, w_star, w_starstar)
         w_function  = f.popvalue()
-        w_result = f.space.call_args(w_function, args)
+        if f.is_being_profiled and is_builtin_code(w_function):
+            w_result = f.space.call_args_and_c_profile(f, w_function, args)
+        else:
+            w_result = f.space.call_args(w_function, args)
         rstack.resume_point("call_function", f, returns=w_result)
         f.pushvalue(w_result)
         

Modified: pypy/branch/2.5-merge/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- pypy/branch/2.5-merge/pypy/interpreter/test/test_executioncontext.py	(original)
+++ pypy/branch/2.5-merge/pypy/interpreter/test/test_executioncontext.py	Fri Oct 10 18:00:52 2008
@@ -62,7 +62,6 @@
         assert space.sys.checkinterval / 10 < i < space.sys.checkinterval * 3
 
     def test_llprofile(self):
-        py.test.skip("not working yet")
         l = []
         
         def profile_func(space, w_arg, frame, event, w_aarg):
@@ -72,9 +71,125 @@
         space = self.space
         space.getexecutioncontext().setllprofile(profile_func, space.w_None)
         space.appexec([], """():
-        l = []
-        l.append(3)
+        pass
         """)
         space.getexecutioncontext().setllprofile(None, None)
-        assert l == ['call', 'return', 'call', 'c_call', 'c_return', 'return']
+        assert l == ['call', 'return', 'call', 'return']
+
+    def test_llprofile_c_call(self):
+        l = []
+        
+        def profile_func(space, w_arg, frame, event, w_aarg):
+            assert w_arg is space.w_None
+            l.append(event)
+
+        space = self.space
+        space.getexecutioncontext().setllprofile(profile_func, space.w_None)
+
+        def check_snippet(snippet):
+            space.appexec([], """():
+            %s
+            return
+            """ % snippet)
+            space.getexecutioncontext().setllprofile(None, None)
+            assert l == ['call', 'return', 'call', 'c_call', 'c_return', 'return']
+
+        check_snippet('l = []; l.append(42)')
+        check_snippet('max(1, 2)')
+        check_snippet('args = (1, 2); max(*args)')
+        check_snippet('max(1, 2, **{})')
+        check_snippet('args = (1, 2); max(*args, **{})')
+        check_snippet('abs(val=0)')
+        
+    def test_llprofile_c_exception(self):
+        l = []
+        
+        def profile_func(space, w_arg, frame, event, w_aarg):
+            assert w_arg is space.w_None
+            l.append(event)
+
+        space = self.space
+        space.getexecutioncontext().setllprofile(profile_func, space.w_None)
+
+        def check_snippet(snippet):
+            space.appexec([], """():
+            try:
+                %s
+            except:
+                pass
+            return
+            """ % snippet)
+            space.getexecutioncontext().setllprofile(None, None)
+            assert l == ['call', 'return', 'call', 'c_call', 'c_exception', 'return']
+
+        check_snippet('d = {}; d.__getitem__(42)')
 
+    def test_c_call_setprofile_outer_frame(self):
+        space = self.space
+        w_events = space.appexec([], """():
+        import sys
+        l = []
+        def profile(frame, event, arg):
+            l.append(event)
+
+        def foo():
+            sys.setprofile(profile)
+
+        def bar():
+            foo()
+            max(1, 2)
+
+        bar()
+        sys.setprofile(None)
+        return l
+        """)
+        events = space.unwrap(w_events)
+        assert events == ['return', 'c_call', 'c_return', 'return', 'c_call']
+
+    def test_c_call_setprofile_strange_method(self):
+        space = self.space
+        w_events = space.appexec([], """():
+        import sys
+        class A(object):
+            def __init__(self, value):
+                self.value = value
+            def meth(self):
+                pass
+        MethodType = type(A.meth)
+        strangemeth = MethodType(A, 42, int)
+        l = []
+        def profile(frame, event, arg):
+            l.append(event)
+
+        def foo():
+            sys.setprofile(profile)
+
+        def bar():
+            foo()
+            strangemeth()
+
+        bar()
+        sys.setprofile(None)
+        return l
+        """)
+        events = space.unwrap(w_events)
+        assert events == ['return', 'call', 'return', 'return', 'c_call']
+
+    def test_c_call_profiles_immediately(self):
+        space = self.space
+        w_events = space.appexec([], """():
+        import sys
+        l = []
+        def profile(frame, event, arg):
+            l.append(event)
+
+        def bar():
+            sys.setprofile(profile)
+            max(3, 4)
+
+        bar()
+        sys.setprofile(None)
+        return l
+        """)
+        events = space.unwrap(w_events)
+        assert events == ['c_call', 'c_return', 'return', 'c_call']



More information about the Pypy-commit mailing list