Author: fijal
Branch: py3.5
Changeset: r93534:4f88f2f2d3f2
Date: 2017-12-21 15:51 +0200
http://bitbucket.org/pypy/pypy/changeset/4f88f2f2d3f2/

Log:    merge default

diff --git a/extra_tests/requirements.txt b/extra_tests/requirements.txt
--- a/extra_tests/requirements.txt
+++ b/extra_tests/requirements.txt
@@ -1,2 +1,3 @@
 pytest
 hypothesis
+vmprof
diff --git a/extra_tests/test_vmprof_greenlet.py 
b/extra_tests/test_vmprof_greenlet.py
new file mode 100644
--- /dev/null
+++ b/extra_tests/test_vmprof_greenlet.py
@@ -0,0 +1,28 @@
+import time
+import pytest
+import greenlet
+vmprof = pytest.importorskip('vmprof')
+
+def count_samples(filename):
+    stats = vmprof.read_profile(filename)
+    return len(stats.profiles)
+
+def cpuburn(duration):
+    end = time.time() + duration
+    while time.time() < end:
+        pass
+
+def test_sampling_inside_callback(tmpdir):
+    # see also test_sampling_inside_callback inside
+    # pypy/module/_continuation/test/test_stacklet.py
+    #
+    G = greenlet.greenlet(cpuburn)
+    fname = tmpdir.join('log.vmprof')
+    with fname.open('w+b') as f:
+        vmprof.enable(f.fileno(), 1/250.0)
+        G.switch(0.1)
+        vmprof.disable()
+    
+    samples = count_samples(str(fname))
+    # 0.1 seconds at 250Hz should be 25 samples
+    assert 23 < samples < 27
diff --git a/lib-python/2.7/subprocess.py b/lib-python/2.7/subprocess.py
--- a/lib-python/2.7/subprocess.py
+++ b/lib-python/2.7/subprocess.py
@@ -1296,7 +1296,7 @@
                   'copyfile' in caller.f_globals):
         dest_dir = sys.pypy_resolvedirof(target_executable)
         src_dir = sys.pypy_resolvedirof(sys.executable)
-        for libname in ['libpypy-c.so', 'libpypy-c.dylib']:
+        for libname in ['libpypy-c.so', 'libpypy-c.dylib', 'libpypy-c.dll']:
             dest_library = os.path.join(dest_dir, libname)
             src_library = os.path.join(src_dir, libname)
             if os.path.exists(src_library):
diff --git a/pypy/doc/index-of-release-notes.rst 
b/pypy/doc/index-of-release-notes.rst
--- a/pypy/doc/index-of-release-notes.rst
+++ b/pypy/doc/index-of-release-notes.rst
@@ -6,6 +6,7 @@
 
 .. toctree::
 
+   release-v5.10.0.rst
    release-v5.9.0.rst
    release-v5.8.0.rst
    release-v5.7.1.rst
diff --git a/pypy/doc/release-v5.10.0.rst b/pypy/doc/release-v5.10.0.rst
new file mode 100644
--- /dev/null
+++ b/pypy/doc/release-v5.10.0.rst
@@ -0,0 +1,94 @@
+======================================
+PyPy2.7 and PyPy3.5 v5.10 dual release
+======================================
+
+The PyPy team is proud to release both PyPy2.7 v5.10 (an interpreter supporting
+Python 2.7 syntax), and a final PyPy3.5 v5.10 (an interpreter for Python
+3.5 syntax). The two releases are both based on much the same codebase, thus
+the dual release.
+
+This release is an incremental release with very few new features, the main
+feature being the final PyPy3.5 release that works on linux and OS X with beta
+windows support. It also includes fixes for `vmprof`_ cooperation with 
greenlets.
+
+Compared to 5.9, the 5.10 release contains mostly bugfixes and small 
improvements.
+We have in the pipeline big new features coming for PyPy 6.0 that did not make
+the release cut and should be available within the next couple months.
+
+As always, this release is 100% compatible with the previous one and fixed
+several issues and bugs raised by the growing community of PyPy users.
+As always, we strongly recommend updating.
+
+This release concludes the Mozilla Open Source `grant`_ for having a compatible
+PyPy 3.5 release and we're very grateful for that.  Of course, we will continue
+to improve PyPy 3.5 and probably move to 3.6 during the course of 2018.
+
+You can download the v5.10 releases here:
+
+    http://pypy.org/download.html
+
+We would like to thank our donors for the continued support of the PyPy
+project.
+
+We would also like to thank our contributors and
+encourage new people to join the project. PyPy has many
+layers and we need help with all of them: `PyPy`_ and `RPython`_ documentation
+improvements, tweaking popular `modules`_ to run on pypy, or general `help`_
+with making RPython's JIT even better.
+
+.. _vmprof: http://vmprof.readthedocs.io
+.. _grant: 
https://morepypy.blogspot.com/2016/08/pypy-gets-funding-from-mozilla-for.html
+.. _`PyPy`: index.html
+.. _`RPython`: https://rpython.readthedocs.org
+.. _`modules`: project-ideas.html#make-more-python-modules-pypy-friendly
+.. _`help`: project-ideas.html
+
+What is PyPy?
+=============
+
+PyPy is a very compliant Python interpreter, almost a drop-in replacement for
+CPython 2.7 and CPython 3.5. It's fast (`PyPy and CPython 2.7.x`_ performance 
comparison)
+due to its integrated tracing JIT compiler.
+
+We also welcome developers of other `dynamic languages`_ to see what RPython
+can do for them.
+
+The PyPy 2.7 release supports: 
+
+  * **x86** machines on most common operating systems
+    (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD)
+  
+  * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux,
+  
+  * big- and little-endian variants of **PPC64** running Linux,
+
+  * **s390x** running Linux
+
+.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org
+.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html
+
+Changelog
+=========
+
+* improve ssl handling on windows for pypy3 (makes pip work)
+* improve unicode handling in various error reporters
+* fix vmprof cooperation with greenlets
+* fix some things in cpyext
+* test and document the cmp(nan, nan) == 0 behaviour
+* don't crash when calling sleep with inf or nan
+* fix bugs in _io module
+* inspect.isbuiltin() now returns True for functions implemented in C
+* allow the sequences future-import, docstring, future-import for CPython bug 
compatibility
+* Issue #2699: non-ascii messages in warnings
+* posix.lockf
+* fixes for FreeBSD platform
+* add .debug files, so builds contain debugging info, instead of being stripped
+* improvements to cppyy
+* issue #2677 copy pure c PyBuffer_{From,To}Contiguous from cpython
+* issue #2682, split firstword on any whitespace in sqlite3
+* ctypes: allow ptr[0] = foo when ptr is a pointer to struct
+* matplotlib will work with tkagg backend once `matplotlib pr #9356`_ is merged
+* improvements to utf32 surrogate handling
+* cffi version bump to 1.11.2
+
+.. _`matplotlib pr #9356`: https://github.com/matplotlib/matplotlib/pull/9356
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -31,7 +31,7 @@
 Upgrade the _vmprof backend to vmprof 0.4.10
 
 .. branch: fix-vmprof-stacklet-switch
-
+.. branch: fix-vmprof-stacklet-switch-2
 Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...)
 
 .. branch: win32-vcvars
@@ -39,3 +39,4 @@
 .. branch: rdict-fast-hash
 
 Make it possible to declare that the hash function of an r_dict is fast in 
RPython.
+
diff --git a/pypy/module/_continuation/interp_continuation.py 
b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -1,5 +1,6 @@
 from rpython.rlib.rstacklet import StackletThread
 from rpython.rlib import jit
+from rpython.rlib import rvmprof
 from pypy.interpreter.error import OperationError, get_cleared_operation_error
 from pypy.interpreter.error import oefmt
 from pypy.interpreter.executioncontext import ExecutionContext
@@ -241,12 +242,15 @@
     self.h = h
     global_state.clear()
     try:
+        rvmprof.start_sampling()
         frame = self.bottomframe
         w_result = frame.execute_frame()
     except Exception as e:
         global_state.propagate_exception = e
     else:
         global_state.w_value = w_result
+    finally:
+        rvmprof.stop_sampling()
     self.sthread.ec.topframeref = jit.vref_None
     global_state.origin = self
     global_state.destination = self
diff --git a/pypy/module/_continuation/test/test_stacklet.py 
b/pypy/module/_continuation/test/test_stacklet.py
--- a/pypy/module/_continuation/test/test_stacklet.py
+++ b/pypy/module/_continuation/test/test_stacklet.py
@@ -1,7 +1,10 @@
+import pytest
 import os
+from rpython.rlib.rvmprof.test.support import fakevmprof
+from pypy.interpreter.gateway import interp2app
 from pypy.module._continuation.test.support import BaseAppTest
 
-
[email protected]('app_fakevmprof')
 class AppTestStacklet(BaseAppTest):
     def setup_class(cls):
         BaseAppTest.setup_class.im_func(cls)
@@ -34,6 +37,33 @@
                 return res
             return stack
        """)
+        cls.w_appdirect = cls.space.wrap(cls.runappdirect)
+        if cls.runappdirect:
+            # make sure that "self.stack" does not pass the self
+            cls.w_stack = staticmethod(cls.w_stack.im_func)
+
+
+    @pytest.fixture
+    def app_fakevmprof(self, fakevmprof):
+        """
+        This is automaticaly re-initialized for every method: thanks to
+        fakevmprof's finalizer, it checks that we called {start,stop}_sampling
+        the in pairs
+        """
+        w = self.space.wrap
+        i2a = interp2app
+        def is_sampling_enabled(space):
+            return space.wrap(fakevmprof.is_sampling_enabled)
+        self.w_is_sampling_enabled = w(i2a(is_sampling_enabled))
+        #
+        def start_sampling(space):
+            fakevmprof.start_sampling()
+        self.w_start_sampling = w(i2a(start_sampling))
+        #
+        def stop_sampling(space):
+            fakevmprof.stop_sampling()
+        self.w_stop_sampling = w(i2a(stop_sampling))
+
 
     def test_new_empty(self):
         from _continuation import continulet
@@ -797,3 +827,25 @@
         bd50 = continulet(f)
         main.switch(to=bd50)
         print(999)
+
+    def test_sampling_inside_callback(self):
+        if self.appdirect:
+            # see also
+            # extra_tests.test_vmprof_greenlet.test_sampling_inside_callback
+            # for a "translated" version of this test
+            skip("we can't run this until we have _vmprof.is_sampling_enabled")
+        from _continuation import continulet
+        #
+        def my_callback(c1):
+            assert self.is_sampling_enabled()
+            return 42
+        #
+        try:
+            self.start_sampling()
+            assert self.is_sampling_enabled()
+            c = continulet(my_callback)
+            res = c.switch()
+            assert res == 42
+            assert self.is_sampling_enabled()
+        finally:
+            self.stop_sampling()
diff --git a/pypy/module/_continuation/test/test_translated.py 
b/pypy/module/_continuation/test/test_translated.py
--- a/pypy/module/_continuation/test/test_translated.py
+++ b/pypy/module/_continuation/test/test_translated.py
@@ -1,4 +1,5 @@
 import py
+import pytest
 try:
     import _continuation
 except ImportError:
@@ -101,11 +102,7 @@
         particular, we need to ensure that vmprof does not sample the stack in
         the middle of a switch, else we read nonsense.
         """
-        try:
-            import _vmprof
-        except ImportError:
-            py.test.skip("no _vmprof")
-        #
+        _vmprof = pytest.importorskip('_vmprof')
         def switch_forever(c):
             while True:
                 c.switch()
diff --git a/rpython/rlib/rstacklet.py b/rpython/rlib/rstacklet.py
--- a/rpython/rlib/rstacklet.py
+++ b/rpython/rlib/rstacklet.py
@@ -3,7 +3,7 @@
 from rpython.rlib import jit
 from rpython.rlib.objectmodel import fetch_translated_config
 from rpython.rtyper.lltypesystem import lltype, llmemory
-from rpython.rlib.rvmprof import cintf
+from rpython.rlib import rvmprof
 
 DEBUG = False
 
@@ -25,12 +25,12 @@
     def new(self, callback, arg=llmemory.NULL):
         if DEBUG:
             callback = _debug_wrapper(callback)
-        x = cintf.save_rvmprof_stack()
+        x = rvmprof.save_stack()
         try:
-            cintf.empty_rvmprof_stack()
+            rvmprof.empty_stack()
             h = self._gcrootfinder.new(self, callback, arg)
         finally:
-            cintf.restore_rvmprof_stack(x)
+            rvmprof.restore_stack(x)
         if DEBUG:
             debug.add(h)
         return h
@@ -40,11 +40,11 @@
     def switch(self, stacklet):
         if DEBUG:
             debug.remove(stacklet)
-        x = cintf.save_rvmprof_stack()
+        x = rvmprof.save_stack()
         try:
             h = self._gcrootfinder.switch(stacklet)
         finally:
-            cintf.restore_rvmprof_stack(x)
+            rvmprof.restore_stack(x)
         if DEBUG:
             debug.add(h)
         return h
diff --git a/rpython/rlib/rvmprof/__init__.py b/rpython/rlib/rvmprof/__init__.py
--- a/rpython/rlib/rvmprof/__init__.py
+++ b/rpython/rlib/rvmprof/__init__.py
@@ -56,10 +56,27 @@
     return None
 
 def stop_sampling():
-    from rpython.rlib.rvmprof.cintf import vmprof_stop_sampling
-    fd = vmprof_stop_sampling()
-    return rffi.cast(lltype.Signed, fd)
+    return _get_vmprof().stop_sampling()
 
 def start_sampling():
-    from rpython.rlib.rvmprof.cintf import vmprof_start_sampling
-    vmprof_start_sampling()
+    return _get_vmprof().start_sampling()
+
+# ----------------
+# stacklet support
+# ----------------
+#
+# Ideally, vmprof_tl_stack, VMPROFSTACK etc. should be part of "self.cintf":
+# not sure why they are a global. Eventually, we should probably fix all this
+# mess.
+from rpython.rlib.rvmprof.cintf import vmprof_tl_stack, VMPROFSTACK
+
+def save_stack():
+    stop_sampling()
+    return vmprof_tl_stack.get_or_make_raw()
+
+def empty_stack():
+    vmprof_tl_stack.setraw(lltype.nullptr(VMPROFSTACK))
+
+def restore_stack(x):
+    vmprof_tl_stack.setraw(x)
+    start_sampling()
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -10,71 +10,82 @@
 from rpython.rlib import rthread, jit
 from rpython.rlib.objectmodel import we_are_translated
 from rpython.config.translationoption import get_translation_config
+from rpython.jit.backend import detect_cpu
 
 class VMProfPlatformUnsupported(Exception):
     pass
 
+# vmprof works only on x86 for now
+IS_SUPPORTED = detect_cpu.autodetect().startswith('x86')
+if sys.platform == 'win32':
+    IS_SUPPORTED = False
+
 ROOT = py.path.local(rpythonroot).join('rpython', 'rlib', 'rvmprof')
 SRC = ROOT.join('src')
 SHARED = SRC.join('shared')
 BACKTRACE = SHARED.join('libbacktrace')
 
-compile_extra = ['-DRPYTHON_VMPROF']
-separate_module_files = [
-    SHARED.join('symboltable.c'),
-    SHARED.join('vmprof_unix.c')
-]
-if sys.platform.startswith('linux'):
-    separate_module_files += [
-       BACKTRACE.join('atomic.c'),
-       BACKTRACE.join('backtrace.c'),
-       BACKTRACE.join('state.c'),
-       BACKTRACE.join('elf.c'),
-       BACKTRACE.join('dwarf.c'),
-       BACKTRACE.join('fileline.c'),
-       BACKTRACE.join('mmap.c'),
-       BACKTRACE.join('mmapio.c'),
-       BACKTRACE.join('posix.c'),
-       BACKTRACE.join('sort.c'),
+def make_eci():
+    if make_eci.called:
+        raise ValueError("make_eci() should be called at most once")
+    #
+    compile_extra = ['-DRPYTHON_VMPROF']
+    separate_module_files = [
+        SHARED.join('symboltable.c'),
+        SHARED.join('vmprof_unix.c')
     ]
-    _libs = ['dl']
-    compile_extra += ['-DVMPROF_UNIX']
-    compile_extra += ['-DVMPROF_LINUX']
-elif sys.platform == 'win32':
-    compile_extra += ['-DVMPROF_WINDOWS']
-    separate_module_files = [SHARED.join('vmprof_win.c')]
-    _libs = []
-else:
-    # Guessing a BSD-like Unix platform
-    compile_extra += ['-DVMPROF_UNIX']
-    compile_extra += ['-DVMPROF_MAC']
-    if sys.platform.startswith('freebsd'):
-        _libs = ['unwind']
+    if sys.platform.startswith('linux'):
+        separate_module_files += [
+           BACKTRACE.join('atomic.c'),
+           BACKTRACE.join('backtrace.c'),
+           BACKTRACE.join('state.c'),
+           BACKTRACE.join('elf.c'),
+           BACKTRACE.join('dwarf.c'),
+           BACKTRACE.join('fileline.c'),
+           BACKTRACE.join('mmap.c'),
+           BACKTRACE.join('mmapio.c'),
+           BACKTRACE.join('posix.c'),
+           BACKTRACE.join('sort.c'),
+        ]
+        _libs = ['dl']
+        compile_extra += ['-DVMPROF_UNIX']
+        compile_extra += ['-DVMPROF_LINUX']
+    elif sys.platform == 'win32':
+        compile_extra += ['-DVMPROF_WINDOWS']
+        separate_module_files = [SHARED.join('vmprof_win.c')]
+        _libs = []
     else:
-        _libs = []
+        # Guessing a BSD-like Unix platform
+        compile_extra += ['-DVMPROF_UNIX']
+        compile_extra += ['-DVMPROF_MAC']
+        if sys.platform.startswith('freebsd'):
+            _libs = ['unwind']
+        else:
+            _libs = []
 
-
-eci_kwds = dict(
-    include_dirs = [SRC, SHARED, BACKTRACE],
-    includes = ['rvmprof.h','vmprof_stack.h'],
-    libraries = _libs,
-    separate_module_files = [
-        SRC.join('rvmprof.c'),
-        SHARED.join('compat.c'),
-        SHARED.join('machine.c'),
-        SHARED.join('vmp_stack.c'),
-        SHARED.join('vmprof_memory.c'),
-        SHARED.join('vmprof_common.c'),
-        # symbol table already in separate_module_files
-    ] + separate_module_files,
-    post_include_bits=[],
-    compile_extra=compile_extra
-    )
-if sys.platform != 'win32':
-    eci_kwds['separate_module_files'].append(
-        SHARED.join('vmprof_mt.c'),
-    )
-global_eci = ExternalCompilationInfo(**eci_kwds)
+    eci_kwds = dict(
+        include_dirs = [SRC, SHARED, BACKTRACE],
+        includes = ['rvmprof.h','vmprof_stack.h'],
+        libraries = _libs,
+        separate_module_files = [
+            SRC.join('rvmprof.c'),
+            SHARED.join('compat.c'),
+            SHARED.join('machine.c'),
+            SHARED.join('vmp_stack.c'),
+            SHARED.join('vmprof_memory.c'),
+            SHARED.join('vmprof_common.c'),
+            # symbol table already in separate_module_files
+        ] + separate_module_files,
+        post_include_bits=[],
+        compile_extra=compile_extra
+        )
+    if sys.platform != 'win32':
+        eci_kwds['separate_module_files'].append(
+            SHARED.join('vmprof_mt.c'),
+        )
+    make_eci.called = True
+    return ExternalCompilationInfo(**eci_kwds), eci_kwds
+make_eci.called = False
 
 def configure_libbacktrace_linux():
     bits = 32 if sys.maxsize == 2**31-1 else 64
@@ -85,14 +96,17 @@
     shutil.copy(str(BACKTRACE.join(specific_config)), str(config))
 
 def setup():
+    if not IS_SUPPORTED:
+        raise VMProfPlatformUnsupported
+    
     if sys.platform.startswith('linux'):
         configure_libbacktrace_linux()
 
+    eci, eci_kwds = make_eci()
     eci_kwds['compile_extra'].append('-DRPYTHON_LL2CTYPES')
     platform.verify_eci(ExternalCompilationInfo(
                         **eci_kwds))
 
-    eci = global_eci
     vmprof_init = rffi.llexternal("vmprof_init",
                                   [rffi.INT, rffi.DOUBLE, rffi.INT, rffi.INT,
                                    rffi.CCHARP, rffi.INT, rffi.INT],
@@ -122,32 +136,16 @@
                                               lltype.Signed, 
compilation_info=eci,
                                               _nowrapper=True)
 
+    vmprof_stop_sampling = rffi.llexternal("vmprof_stop_sampling", [],
+                                           rffi.INT, compilation_info=eci,
+                                           _nowrapper=True)
+    vmprof_start_sampling = rffi.llexternal("vmprof_start_sampling", [],
+                                            lltype.Void, compilation_info=eci,
+                                            _nowrapper=True)
+
     return CInterface(locals())
 
 
-# this is always present, but compiles to no-op if RPYTHON_VMPROF is not
-# defined (i.e. if we don't actually use vmprof in the generated C)
-auto_eci = ExternalCompilationInfo(post_include_bits=["""
-#ifndef RPYTHON_VMPROF
-#  define vmprof_stop_sampling()    (-1)
-#  define vmprof_start_sampling()   ((void)0)
-#endif
-"""])
-
-if get_translation_config() is None:
-    # tests need the full eci here
-    _eci = global_eci
-else:
-    _eci = auto_eci
-
-vmprof_stop_sampling = rffi.llexternal("vmprof_stop_sampling", [],
-                                       rffi.INT, compilation_info=_eci,
-                                       _nowrapper=True)
-vmprof_start_sampling = rffi.llexternal("vmprof_start_sampling", [],
-                                        lltype.Void, compilation_info=_eci,
-                                        _nowrapper=True)
-
-
 class CInterface(object):
     def __init__(self, namespace):
         for k, v in namespace.iteritems():
@@ -232,20 +230,6 @@
         leave_code(s)
 
 #
-# stacklet support
-
-def save_rvmprof_stack():
-    vmprof_stop_sampling()
-    return vmprof_tl_stack.get_or_make_raw()
-
-def empty_rvmprof_stack():
-    vmprof_tl_stack.setraw(lltype.nullptr(VMPROFSTACK))
-
-def restore_rvmprof_stack(x):
-    vmprof_tl_stack.setraw(x)
-    vmprof_start_sampling()
-
-#
 # traceback support
 
 def get_rvmprof_stack():
diff --git a/rpython/rlib/rvmprof/dummy.py b/rpython/rlib/rvmprof/dummy.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/dummy.py
@@ -0,0 +1,26 @@
+from rpython.rlib.objectmodel import specialize
+
+class DummyVMProf(object):
+
+    def __init__(self):
+        self._unique_id = 0
+
+    def register_code_object_class(self, CodeClass, full_name_func):
+        CodeClass._vmprof_unique_id = self._unique_id
+        self._unique_id += 1
+
+    @specialize.argtype(1)
+    def register_code(self, code, full_name_func):
+        pass
+
+    def enable(self, fileno, interval, memory=0, native=0, real_time=0):
+        pass
+
+    def disable(self):
+        pass
+
+    def start_sampling(self):
+        pass
+
+    def stop_sampling(self):
+        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
@@ -2,6 +2,7 @@
 from rpython.rlib.objectmodel import specialize, we_are_translated, not_rpython
 from rpython.rlib import jit, rposix, rgc
 from rpython.rlib.rvmprof import cintf
+from rpython.rlib.rvmprof.dummy import DummyVMProf
 from rpython.rtyper.annlowlevel import cast_instance_to_gcref
 from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
@@ -34,6 +35,9 @@
         return []
 
 class VMProf(object):
+    """
+    NOTE: the API of this class should be kept in sync with dummy.DummyVMProf
+    """
 
     _immutable_fields_ = ['is_enabled?']
 
@@ -168,6 +172,21 @@
         if self.cintf.vmprof_register_virtual_function(name, uid, 500000) < 0:
             raise VMProfError("vmprof buffers full!  disk full or too slow")
 
+    def stop_sampling(self):
+        """
+        Temporarily stop the sampling of stack frames. Signals are still
+        delivered, but are ignored.
+        """
+        fd = self.cintf.vmprof_stop_sampling()
+        return rffi.cast(lltype.Signed, fd)
+
+    def start_sampling(self):
+        """
+        Undo the effect of stop_sampling
+        """
+        self.cintf.vmprof_start_sampling()
+
+
 def vmprof_execute_code(name, get_code_fn, result_class=None,
                         _hack_update_stack_untranslated=False):
     """Decorator to be used on the function that interprets a code object.
@@ -240,5 +259,8 @@
 def _get_vmprof():
     global _vmprof_instance
     if _vmprof_instance is None:
-        _vmprof_instance = VMProf()
+        try:
+            _vmprof_instance = VMProf()
+        except cintf.VMProfPlatformUnsupported:
+            _vmprof_instance = DummyVMProf()
     return _vmprof_instance
diff --git a/rpython/rlib/rvmprof/test/support.py 
b/rpython/rlib/rvmprof/test/support.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/test/support.py
@@ -0,0 +1,45 @@
+import pytest
+from rpython.rlib import rvmprof
+
+class FakeVMProf(object):
+
+    def __init__(self):
+        self._enabled = False
+        self._ignore_signals = 1
+
+    # --- VMProf official API ---
+    # add fake methods as needed by the tests
+
+    def stop_sampling(self):
+        self._ignore_signals += 1
+
+    def start_sampling(self):
+        assert self._ignore_signals > 0, ('calling start_sampling() without '
+                                          'the corresponding stop_sampling()?')
+        self._ignore_signals -= 1
+
+    # --- FakeVMProf specific API ---
+    # this API is not part of rvmprof, but available only inside tests using
+    # fakevmprof
+
+    @property
+    def is_sampling_enabled(self):
+        return self._ignore_signals == 0
+
+    def check_status(self):
+        """
+        To be called during test teardown
+        """
+        if self._ignore_signals != 1:
+            msg = ('Invalid value for fakevmprof._ignore_signals: expected 1, '
+                   'got %d. This probably means that you called '
+                   '{start,stop}_sampling() a wrong number of times')
+            raise ValueError, msg % self._ignore_signals
+
+
[email protected]
+def fakevmprof(request, monkeypatch):
+    fake = FakeVMProf()
+    monkeypatch.setattr(rvmprof.rvmprof, '_vmprof_instance', fake)
+    request.addfinalizer(fake.check_status)
+    return fake
diff --git a/rpython/rlib/rvmprof/test/test_support.py 
b/rpython/rlib/rvmprof/test/test_support.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/test/test_support.py
@@ -0,0 +1,42 @@
+import pytest
+from rpython.rlib import rvmprof
+from rpython.rlib.rvmprof.test.support import FakeVMProf, fakevmprof
+
+class TestFakeVMProf(object):
+
+    def test_sampling(self):
+        fake = FakeVMProf()
+        assert not fake.is_sampling_enabled
+        #
+        fake.start_sampling()
+        assert fake.is_sampling_enabled
+        #
+        fake.stop_sampling()
+        fake.stop_sampling()
+        assert not fake.is_sampling_enabled
+        #
+        fake.start_sampling()
+        assert not fake.is_sampling_enabled
+        fake.start_sampling()
+        assert fake.is_sampling_enabled
+        #
+        pytest.raises(AssertionError, "fake.start_sampling()")
+    
+    def test_check_status(self):
+        fake = FakeVMProf()
+        fake.stop_sampling()
+        pytest.raises(ValueError, "fake.check_status()")
+
+
+class TestFixture(object):
+
+    def test_fixture(self, fakevmprof):
+        assert isinstance(fakevmprof, FakeVMProf)
+        assert rvmprof._get_vmprof() is fakevmprof
+        #
+        # tweak sampling using the "real" API, and check that we actually used
+        # the fake
+        rvmprof.start_sampling()
+        assert fakevmprof.is_sampling_enabled
+        rvmprof.stop_sampling()
+        assert not fakevmprof.is_sampling_enabled
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to