Author: fijal Branch: release-pypy3.5-v5.9.x Changeset: r93535:42207cd2265c Date: 2017-12-21 15:53 +0200 http://bitbucket.org/pypy/pypy/changeset/42207cd2265c/
Log: merge py3.5 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/lib-python/3/ssl.py b/lib-python/3/ssl.py --- a/lib-python/3/ssl.py +++ b/lib-python/3/ssl.py @@ -140,23 +140,6 @@ except NameError: _SSLv2_IF_EXISTS = None - - - -import os -class DirEntry: - def __init__(self, path, name): - self.path = os.path.join(path, name) - self.name = name - def is_dir(self): - return os.path.isdir(self.path) -def myscandir(path='.'): - for name in os.listdir(path): - yield DirEntry(path, name) -os.scandir = myscandir - - - if sys.platform == "win32": from _ssl import enum_certificates, enum_crls 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 @@ -2,3 +2,41 @@ What's new in PyPy3 6.0 =========================== +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +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 + +.. 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/doc/whatsnew-pypy3-5.10.0.rst b/pypy/doc/whatsnew-pypy3-5.10.0.rst --- a/pypy/doc/whatsnew-pypy3-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy3-5.10.0.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 - +@pytest.mark.usefixtures('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/pypy/module/errno/__init__.py b/pypy/module/errno/__init__.py --- a/pypy/module/errno/__init__.py +++ b/pypy/module/errno/__init__.py @@ -1,6 +1,5 @@ -# Package initialisation from pypy.interpreter.mixedmodule import MixedModule -import errno +from pypy.module.errno.interp_errno import name2code class Module(MixedModule): """This module makes available standard errno system symbols. @@ -18,9 +17,7 @@ appleveldefs = {} interpleveldefs = {"errorcode": "interp_errno.get_errorcode(space)"} - -for name in dir(errno): - if name.startswith('__') or name in Module.interpleveldefs: - continue - Module.interpleveldefs[name] = ("space.newint(%s)" % - (getattr(errno, name), )) + +for name, code in name2code.iteritems(): + if code is not None: + Module.interpleveldefs[name] = ("space.newint(%s)" % code) diff --git a/pypy/module/errno/interp_errno.py b/pypy/module/errno/interp_errno.py --- a/pypy/module/errno/interp_errno.py +++ b/pypy/module/errno/interp_errno.py @@ -1,7 +1,95 @@ -import errno from rpython.rlib.objectmodel import not_rpython +from rpython.rtyper.tool.rffi_platform import DefinedConstantInteger, configure +from rpython.translator.tool.cbuild import ExternalCompilationInfo + +# from CPython 3.5 +errors = [ + "ENODEV", "ENOCSI", "EHOSTUNREACH", "ENOMSG", "EUCLEAN", "EL2NSYNC", + "EL2HLT", "ENODATA", "ENOTBLK", "ENOSYS", "EPIPE", "EINVAL", "EOVERFLOW", + "EADV", "EINTR", "EUSERS", "ENOTEMPTY", "ENOBUFS", "EPROTO", "EREMOTE", + "ENAVAIL", "ECHILD", "ELOOP", "EXDEV", "E2BIG", "ESRCH", "EMSGSIZE", + "EAFNOSUPPORT", "EBADR", "EHOSTDOWN", "EPFNOSUPPORT", "ENOPROTOOPT", + "EBUSY", "EWOULDBLOCK", "EBADFD", "EDOTDOT", "EISCONN", "ENOANO", + "ESHUTDOWN", "ECHRNG", "ELIBBAD", "ENONET", "EBADE", "EBADF", "EMULTIHOP", + "EIO", "EUNATCH", "EPROTOTYPE", "ENOSPC", "ENOEXEC", "EALREADY", + "ENETDOWN", "ENOTNAM", "EACCES", "ELNRNG", "EILSEQ", "ENOTDIR", "ENOTUNIQ", + "EPERM", "EDOM", "EXFULL", "ECONNREFUSED", "EISDIR", "EPROTONOSUPPORT", + "EROFS", "EADDRNOTAVAIL", "EIDRM", "ECOMM", "ESRMNT", "EREMOTEIO", + "EL3RST", "EBADMSG", "ENFILE", "ELIBMAX", "ESPIPE", "ENOLINK", "ENETRESET", + "ETIMEDOUT", "ENOENT", "EEXIST", "EDQUOT", "ENOSTR", "EBADSLT", "EBADRQC", + "ELIBACC", "EFAULT", "EFBIG", "EDEADLK", "ENOTCONN", "EDESTADDRREQ", + "ELIBSCN", "ENOLCK", "EISNAM", "ECONNABORTED", "ENETUNREACH", "ESTALE", + "ENOSR", "ENOMEM", "ENOTSOCK", "ESTRPIPE", "EMLINK", "ERANGE", "ELIBEXEC", + "EL3HLT", "ECONNRESET", "EADDRINUSE", "EOPNOTSUPP", "EREMCHG", "EAGAIN", + "ENAMETOOLONG", "ENOTTY", "ERESTART", "ESOCKTNOSUPPORT", "ETIME", "EBFONT", + "EDEADLOCK", "ETOOMANYREFS", "EMFILE", "ETXTBSY", "EINPROGRESS", "ENXIO", + "ENOPKG",] + +win_errors = [ + "WSASY", "WSAEHOSTDOWN", "WSAENETDOWN", "WSAENOTSOCK", "WSAEHOSTUNREACH", + "WSAELOOP", "WSAEMFILE", "WSAESTALE", "WSAVERNOTSUPPORTED", + "WSAENETUNREACH", "WSAEPROCLIM", "WSAEFAULT", "WSANOTINITIALISED", + "WSAEUSERS", "WSAMAKEASYNCREPL", "WSAENOPROTOOPT", "WSAECONNABORTED", + "WSAENAMETOOLONG", "WSAENOTEMPTY", "WSAESHUTDOWN", "WSAEAFNOSUPPORT", + "WSAETOOMANYREFS", "WSAEACCES", "WSATR", "WSABASEERR", "WSADESCRIPTIO", + "WSAEMSGSIZE", "WSAEBADF", "WSAECONNRESET", "WSAGETSELECTERRO", + "WSAETIMEDOUT", "WSAENOBUFS", "WSAEDISCON", "WSAEINTR", "WSAEPROTOTYPE", + "WSAHOS", "WSAEADDRINUSE", "WSAEADDRNOTAVAIL", "WSAEALREADY", + "WSAEPROTONOSUPPORT", "WSASYSNOTREADY", "WSAEWOULDBLOCK", + "WSAEPFNOSUPPORT", "WSAEOPNOTSUPP", "WSAEISCONN", "WSAENOTCONN", + "WSAEREMOTE", "WSAEINVAL", "WSAEINPROGRESS", "WSAGETSELECTEVEN", + "WSAESOCKTNOSUPPORT", "WSAGETASYNCERRO", "WSAMAKESELECTREPL", + "WSAGETASYNCBUFLE", "WSAEDESTADDRREQ", "WSAECONNREFUSED", "WSAENETRESET", + "WSAN",] + +more_errors = [ + "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY", "EKEYEXPIRED", + "EKEYREVOKED", "EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", + + # Solaris-specific errnos + "ECANCELED", "ENOTSUP", "EOWNERDEAD", "ENOTRECOVERABLE", "ELOCKUNMAPPED", + "ENOTACTIVE", + + # MacOSX specific errnos + "EAUTH", "EBADARCH", "EBADEXEC", "EBADMACHO", "EBADRPC", "EDEVERR", + "EFTYPE", "ENEEDAUTH", "ENOATTR", "ENOPOLICY", "EPROCLIM", "EPROCUNAVAIL", + "EPROGMISMATCH", "EPROGUNAVAIL", "EPWROFF", "ERPCMISMATCH", "ESHLIBVERS"] + + + +class CConfig: + _compilation_info_ = ExternalCompilationInfo(includes=['sys/errno.h']) + +for err_name in errors + win_errors + more_errors: + setattr(CConfig, err_name, DefinedConstantInteger(err_name)) +config = configure(CConfig) + +errorcode = {} +name2code = {} +for err_name in errors: + # Note: later names take precedence over earlier ones, if they have the + # same value + code = config[err_name] + if code is not None: + errorcode[code] = err_name + name2code[err_name] = code +for name in win_errors: + assert name.startswith('WSA') + code = config[name] + if code is not None: + if name[3:] in errors: + # errno.EFOO = <WSAEFOO> + name2code[name[3:]] = code + # errno.WSABAR = <WSABAR> + name2code[name] = code + errorcode[code] = name + +for err_name in more_errors: + code = config[err_name] + if code is not None: + errorcode[code] = err_name + name2code[err_name] = code @not_rpython def get_errorcode(space): - return space.wrap(errno.errorcode) # initializiation time - + return space.wrap(errorcode) # initialization time diff --git a/pypy/module/errno/test/test_errno.py b/pypy/module/errno/test/test_errno.py --- a/pypy/module/errno/test/test_errno.py +++ b/pypy/module/errno/test/test_errno.py @@ -3,7 +3,7 @@ class AppTestErrno: spaceconfig = dict(usemodules=['errno']) - def setup_class(cls): + def setup_class(cls): cls.w_errno = cls.space.appexec([], "(): import errno ; return errno") cls.w_errorcode = cls.space.wrap(errno.errorcode) @@ -11,28 +11,11 @@ assert not hasattr(self.errno, '__file__') def test_constants(self): - host_errorcode = self.errorcode.copy() - # On some systems, ENOTSUP is an alias to EOPNOTSUPP. Adjust the - # host_errorcode dictionary in case the host interpreter has slightly - # different errorcodes than the interpreter under test - if ('ENOTSUP' not in host_errorcode.values() and - 'ENOTSUP' in self.errno.errorcode.values()): - host_errorcode[self.errno.ENOTSUP] = 'ENOTSUP' - if ('EOPNOTSUPP' not in host_errorcode.values() and - 'EOPNOTSUPP' in self.errno.errorcode.values()): - host_errorcode[self.errno.EOPNOTSUPP] = 'EOPNOTSUPP' - for code, name in host_errorcode.items(): + # Assumes that our constants are a superset of the host's + for code, name in self.errorcode.items(): assert getattr(self.errno, name) == code def test_errorcode(self): - host_errorcode = self.errorcode.copy() - # On some systems, ENOTSUP is an alias to EOPNOTSUPP. Adjust the - # host_errorcode dictionary in case the host interpreter has slightly - # different errorcodes than the interpreter under test - if ('ENOTSUP' not in host_errorcode.values() and - 'ENOTSUP' in self.errno.errorcode.values()): - host_errorcode[self.errno.ENOTSUP] = 'ENOTSUP' - if ('EOPNOTSUPP' not in host_errorcode.values() and - 'EOPNOTSUPP' in self.errno.errorcode.values()): - host_errorcode[self.errno.EOPNOTSUPP] = 'EOPNOTSUPP' - assert host_errorcode == self.errno.errorcode + # Assumes that our codes are a superset of the host's + for value, name in self.errorcode.items(): + assert self.errno.errorcode[value] == name diff --git a/pypy/module/posix/test/test_scandir.py b/pypy/module/posix/test/test_scandir.py --- a/pypy/module/posix/test/test_scandir.py +++ b/pypy/module/posix/test/test_scandir.py @@ -98,8 +98,7 @@ assert d.stat().st_mode & 0o170000 == 0o100000 # S_IFREG assert d.stat().st_size == 0 - @py.test.mark.skipif(sys.platform == "win32", - reason="no symlink support so far") + @py.test.mark.skipif(sys.platform == "win32", reason="no symlink support so far") def test_stat4(self): posix = self.posix d = next(posix.scandir(self.dir4)) @@ -129,8 +128,7 @@ assert not d.is_file(follow_symlinks=False) assert d.is_dir(follow_symlinks=False) - @py.test.mark.skipif(sys.platform == "win32", - reason="no symlink support so far") + @py.test.mark.skipif(sys.platform == "win32", reason="no symlink support so far") def test_dir3(self): posix = self.posix d = next(posix.scandir(self.dir3)) @@ -141,8 +139,7 @@ assert d.is_file(follow_symlinks=True) assert not d.is_file(follow_symlinks=False) - @py.test.mark.skipif(sys.platform == "win32", - reason="no symlink support so far") + @py.test.mark.skipif(sys.platform == "win32", reason="no symlink support so far") def test_dir4(self): posix = self.posix d = next(posix.scandir(self.dir4)) @@ -153,8 +150,7 @@ assert d.is_dir(follow_symlinks=True) assert not d.is_dir(follow_symlinks=False) - @py.test.mark.skipif(sys.platform == "win32", - reason="no symlink support so far") + @py.test.mark.skipif(sys.platform == "win32", reason="no symlink support so far") def test_dir5(self): posix = self.posix d = next(posix.scandir(self.dir5)) @@ -164,8 +160,7 @@ assert d.is_symlink() raises(OSError, d.stat) - @py.test.mark.skipif(sys.platform == "win32", - reason="no symlink support so far") + @py.test.mark.skipif(sys.platform == "win32", reason="no symlink support so far") def test_dir6(self): posix = self.posix d = next(posix.scandir(self.dir6)) 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 + + +@pytest.fixture +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 pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit