[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