Author: Armin Rigo <[email protected]>
Branch: vmprof-review
Changeset: r78738:e34fc115c19d
Date: 2015-08-02 15:13 +0200
http://bitbucket.org/pypy/pypy/changeset/e34fc115c19d/
Log: in-progress
diff --git a/rpython/rlib/rvmprof/__init__.py b/rpython/rlib/rvmprof/__init__.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/__init__.py
@@ -0,0 +1,2 @@
+from rpython.rlib.rvmprof.rvmprof import get_vmprof
+from rpython.rlib.rvmprof.rvmprof import vmprof_execute_code
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -0,0 +1,5 @@
+
+
+def vmprof_init(): pass
+def vmprof_enable(fileno, interval_usec): return 0
+def vmprof_ignore_signals(ignore): pass
diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py
--- a/rpython/rlib/rvmprof/rvmprof.py
+++ b/rpython/rlib/rvmprof/rvmprof.py
@@ -1,13 +1,21 @@
-import sys
-from rpython.rlib.objectmodel import specialize
+import sys, os
+from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rlib.rstring import StringBuilder
-from rpython.rlib import rgc
+from rpython.rlib import jit, rgc
+from rpython.rlib.rvmprof import cintf
MAX_CODES = 8000
# ____________________________________________________________
+class VMProfError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+
class VMProf(object):
def __init__(self):
@@ -17,6 +25,9 @@
self._cleanup_()
def _cleanup_(self):
+ self.is_enabled = False
+ self.ever_enabled = False
+ self.fileno = -1
self._current_codes = None
if sys.maxint == 2147483647:
self._code_unique_id = 0 # XXX this is wrong, it won't work on
32bit
@@ -27,21 +38,12 @@
def register_code(self, code, name):
"""Register the code object. Call when a new code object is made.
"""
- if code._vmprof_unique_id != 0:
- return
- uid = self._code_unique_id + 4 # so we have two bits to mark stuff
- code._vmprof_unique_id = uid
- self._code_unique_id = uid
- #
- b = self._current_codes
- if b is None:
- b = self._current_codes = StringBuilder()
- b.append('\x02')
- write_long_to_string_builder(uid, b)
- write_long_to_string_builder(len(name), b)
- b.append(name)
- if b.getlength() >= MAX_CODES:
- self._flush_codes()
+ if code._vmprof_unique_id == 0:
+ uid = self._code_unique_id + 1
+ code._vmprof_unique_id = uid
+ self._code_unique_id = uid
+ if self.is_enabled:
+ self._write_code_registration(uid, name)
def register_code_object_class(self, CodeClass, full_name_func):
"""NOT_RPYTHON
@@ -61,21 +63,93 @@
CodeClass._vmprof_unique_id = 0 # default value: "unknown"
self._code_classes.add(CodeClass)
#
- def try_cast_to_pycode(gcref):
+ def try_cast_to_code(gcref):
return rgc.try_cast_gcref_to_instance(CodeClass, gcref)
#
def gather_all_code_objs():
- all_code_objs = rgc.do_get_objects(try_cast_to_pycode)
+ all_code_objs = rgc.do_get_objects(try_cast_to_code)
for code in all_code_objs:
- self.register_code(code, full_name_func(code))
+ uid = code._vmprof_unique_id
+ if uid != 0:
+ self._write_code_registration(uid, full_name_func(code))
prev()
# make a chained list of the gather() functions for all
# the types of code objects
prev = self._gather_all_code_objs
self._gather_all_code_objs = gather_all_code_objs
+ def enable(self, fileno, interval):
+ """Enable vmprof. Writes go to the given 'fileno'.
+ The sampling interval is given by 'interval' as a number of
+ seconds, as a float which must be not greater than 1.0.
+ Raises VMProfError if something goes wrong.
+ """
+ assert fileno >= 0
+ if self.is_enabled:
+ raise VMProfError("vmprof is already enabled")
+ if not (1e-6 <= interval <= 1.0):
+ raise VMProfError("bad value for 'interval'")
+ interval_usec = int(interval * 1000000.0)
+ #
+ self.fileno = fileno
+ self.is_enabled = True
+ self._write_header(interval_usec)
+ if not self.ever_enabled:
+ if we_are_translated():
+ p_error = cintf.vmprof_init()
+ if p_error:
+ raise VMProfError(rffi.charp2str(p_error))
+ self.ever_enabled = True
+ self._gather_all_code_objs()
+ if we_are_translated():
+ # does not work untranslated
+ res = cintf.vmprof_enable(fileno, interval_usec)
+ if res < 0:
+ raise VMProfError(os.strerror(rposix.get_saved_errno()))
-def write_long_to_string_builder(l, b):
+ def _write_code_registration(self, uid, name):
+ b = self._current_codes
+ if b is None:
+ b = self._current_codes = StringBuilder()
+ b.append('\x02')
+ _write_long_to_string_builder(uid, b)
+ _write_long_to_string_builder(len(name), b)
+ b.append(name)
+ if b.getlength() >= MAX_CODES:
+ self._flush_codes()
+
+ def _flush_codes(self):
+ buf = self._current_codes.build()
+ self._current_codes = None
+ self._carefully_write(buf)
+
+ def _carefully_write(self, buf):
+ fd = self._fileno
+ assert fd >= 0
+ if not buf:
+ return
+ cintf.vmprof_ignore_signals(True)
+ try:
+ while len(buf) > 0:
+ num = os.write(fd, buf)
+ buf = buf[num:]
+ finally:
+ cintf.vmprof_ignore_signals(False)
+
+ def _write_header(self, interval_usec):
+ b = StringBuilder()
+ _write_long_to_string_builder(0, b)
+ _write_long_to_string_builder(3, b)
+ _write_long_to_string_builder(0, b)
+ _write_long_to_string_builder(interval_usec, b)
+ _write_long_to_string_builder(0, b)
+ b.append('\x04') # interp name
+ b.append(chr(len('pypy')))
+ b.append('pypy')
+ self._carefully_write(b.build())
+
+
+def _write_long_to_string_builder(l, b):
b.append(chr(l & 0xff))
b.append(chr((l >> 8) & 0xff))
b.append(chr((l >> 16) & 0xff))
@@ -87,6 +161,68 @@
b.append(chr((l >> 56) & 0xff))
+def vmprof_execute_code(get_code_fn, result_class=None):
+ """Decorator to be used on the function that interprets a code object.
+
+ get_code_fn(*args) is called to extract the code object from the
+ arguments given to the decorated function.
+
+ The original function can return None, an integer, or an instance.
+ In the latter case (only), 'result_class' must be set.
+
+ NOTE: for now, this assumes that the decorated functions only takes
+ instances or plain integer arguments, and at most 5 of them
+ (including 'self' if applicable).
+ """
+ def lower(*args):
+ if len(args) == 0:
+ return (), ""
+ ll_args, token = lower(*args[1:])
+ ll_arg = args[0]
+ if isintance(ll_arg, int):
+ tok = "i"
+ else:
+ tok = "r"
+ ll_arg = cast_instance_to_gcref(ll_arg)
+ return (ll_arg,) + ll_args, tok + token
+
+ @specialize.memo()
+ def get_ll_trampoline(func, token):
+ xxx
+
+ def decorate(func):
+ def decorated_function(*args):
+ # go through the asm trampoline ONLY if we are translated but not
+ # being JITted.
+ #
+ # If we are not translated, we obviously don't want to go through
+ # the trampoline because there is no C function it can call.
+ #
+ # If we are being JITted, we want to skip the trampoline, else the
+ # JIT cannot see through it.
+ #
+ if we_are_translated() and not jit.we_are_jitted():
+ # if we are translated, call the trampoline
+ unique_id = get_code_fn(*args)._vmprof_unique_id
+ ll_args, token = lower(*args)
+ ll_trampoline = get_ll_trampoline(func, token)
+ ll_result = ll_trampoline(*ll_args + (unique_id,))
+ if result_class is not None:
+ return cast_base_ptr_to_instance(result_class, ll_result)
+ else:
+ return ll_result
+ else:
+ return func(*args)
+ decorated_function.__name__ = func.__name__ + '_rvmprof'
+ return decorated_function
+ return decorate
+
+
+_vmprof_instance = None
+
@specialize.memo()
def get_vmprof():
- return VMProf()
+ global _vmprof_instance
+ if _vmprof_instance is None:
+ _vmprof_instance = VMProf()
+ return _vmprof_instance
diff --git a/rpython/rlib/rvmprof/test/__init__.py
b/rpython/rlib/rvmprof/test/__init__.py
new file mode 100644
diff --git a/rpython/rlib/rvmprof/test/test_rvmprof.py
b/rpython/rlib/rvmprof/test/test_rvmprof.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/test/test_rvmprof.py
@@ -0,0 +1,14 @@
+from rpython.rlib.rvmprof import get_vmprof, vmprof_execute_code
+
+
+def test_vmprof_execute_code_1():
+
+ class MyCode:
+ pass
+ get_vmprof().register_code_object_class(MyCode, lambda code: 'some code')
+
+ @vmprof_execute_code(lambda code, num: code)
+ def main(code, num):
+ print num
+
+ main(MyCode(), 5)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit