Author: Manuel Jacob <m...@manueljacob.de> Branch: py3.3 Changeset: r79229:c9a245e856b7 Date: 2015-08-25 12:38 +0200 http://bitbucket.org/pypy/pypy/changeset/c9a245e856b7/
Log: hg merge py3k diff too long, truncating to 2000 out of 3074 lines diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.2.0 +Version: 1.2.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.2.0" -__version_info__ = (1, 2, 0) +__version__ = "1.2.1" +__version_info__ = (1, 2, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -46,7 +46,7 @@ # endif #else # include <stdint.h> -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) # include <alloca.h> # endif #endif diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py --- a/lib_pypy/cffi/cparser.py +++ b/lib_pypy/cffi/cparser.py @@ -15,9 +15,11 @@ except ImportError: lock = None -_r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)\s+(.*?)$", - re.MULTILINE) +_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", + re.DOTALL | re.MULTILINE) +_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" + r"\b((?:[^\n\\]|\\.)*?)$", + re.DOTALL | re.MULTILINE) _r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") _r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") _r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") @@ -39,6 +41,7 @@ macros = {} for match in _r_define.finditer(csource): macroname, macrovalue = match.groups() + macrovalue = macrovalue.replace('\\\n', '').strip() macros[macroname] = macrovalue csource = _r_define.sub('', csource) # Replace "[...]" with "[__dotdotdotarray__]" @@ -423,13 +426,10 @@ raise api.CDefError( "%s: a function with only '(...)' as argument" " is not correct C" % (funcname or 'in expression')) - elif (len(params) == 1 and - isinstance(params[0].type, pycparser.c_ast.TypeDecl) and - isinstance(params[0].type.type, pycparser.c_ast.IdentifierType) - and list(params[0].type.type.names) == ['void']): - del params[0] args = [self._as_func_arg(self._get_type(argdeclnode.type)) for argdeclnode in params] + if not ellipsis and args == [model.void_type]: + args = [] result = self._get_type(typenode.type) return model.RawFunctionType(tuple(args), result, ellipsis) diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py --- a/lib_pypy/cffi/recompiler.py +++ b/lib_pypy/cffi/recompiler.py @@ -4,11 +4,6 @@ VERSION = "0x2601" -try: - int_type = (int, long) -except NameError: # Python 3 - int_type = int - class GlobalExpr: def __init__(self, name, address, type_op, size=0, check_value=0): diff --git a/lib_pypy/cffi/setuptools_ext.py b/lib_pypy/cffi/setuptools_ext.py --- a/lib_pypy/cffi/setuptools_ext.py +++ b/lib_pypy/cffi/setuptools_ext.py @@ -81,10 +81,16 @@ allsources.extend(kwds.pop('sources', [])) ext = Extension(name=module_name, sources=allsources, **kwds) - def make_mod(tmpdir): + def make_mod(tmpdir, pre_run=None): c_file = os.path.join(tmpdir, module_name + source_extension) log.info("generating cffi module %r" % c_file) mkpath(tmpdir) + # a setuptools-only, API-only hook: called with the "ext" and "ffi" + # arguments just before we turn the ffi into C code. To use it, + # subclass the 'distutils.command.build_ext.build_ext' class and + # add a method 'def pre_run(self, ext, ffi)'. + if pre_run is not None: + pre_run(ext, ffi) updated = recompiler.make_c_source(ffi, module_name, source, c_file) if not updated: log.info("already up-to-date") @@ -98,7 +104,8 @@ class build_ext_make_mod(base_class): def run(self): if ext.sources[0] == '$PLACEHOLDER': - ext.sources[0] = make_mod(self.build_temp) + pre_run = getattr(self, 'pre_run', None) + ext.sources[0] = make_mod(self.build_temp, pre_run) base_class.run(self) dist.cmdclass['build_ext'] = build_ext_make_mod # NB. multiple runs here will create multiple 'build_ext_make_mod' diff --git a/pypy/bin/pyinteractive.py b/pypy/bin/pyinteractive.py --- a/pypy/bin/pyinteractive.py +++ b/pypy/bin/pyinteractive.py @@ -143,7 +143,8 @@ if interactiveconfig.runmodule: command = args.pop(0) for arg in args: - space.call_method(space.sys.get('argv'), 'append', space.wrap(arg)) + space.call_method(space.sys.get('argv'), 'append', + space.wrap_fsdecoded(arg)) # load the source of the program given as command-line argument if interactiveconfig.runcommand: diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -7,6 +7,7 @@ .. toctree:: whatsnew-head.rst + whatsnew-2.6.1.rst whatsnew-2.6.0.rst whatsnew-2.5.1.rst whatsnew-2.5.0.rst diff --git a/pypy/doc/whatsnew-2.6.1.rst b/pypy/doc/whatsnew-2.6.1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-2.6.1.rst @@ -0,0 +1,72 @@ +======================== +What's new in PyPy 2.6.1 +======================== + +.. this is a revision shortly after release-2.6.0 +.. startrev: 91904d5c5188 + +.. branch: use_min_scalar +Correctly resolve the output dtype of ufunc(array, scalar) calls. + +.. branch: stdlib-2.7.10 + +Update stdlib to version 2.7.10 + +.. branch: issue2062 + +.. branch: disable-unroll-for-short-loops +The JIT no longer performs loop unrolling if the loop compiles to too much code. + +.. branch: run-create_cffi_imports + +Build cffi import libraries as part of translation by monkey-patching an +additional task into translation + +.. branch: int-float-list-strategy + +Use a compact strategy for Python lists that mix integers and floats, +at least if the integers fit inside 32 bits. These lists are now +stored as an array of floats, like lists that contain only floats; the +difference is that integers are stored as tagged NaNs. (This should +have no visible effect! After ``lst = [42, 42.5]``, the value of +``lst[0]`` is still *not* the float ``42.0`` but the integer ``42``.) + +.. branch: cffi-callback-onerror +Part of cffi 1.2. + +.. branch: cffi-new-allocator +Part of cffi 1.2. + +.. branch: unicode-dtype + +Partial implementation of unicode dtype and unicode scalars. + +.. branch: dtypes-compatability + +Improve compatibility with numpy dtypes; handle offsets to create unions, +fix str() and repr(), allow specifying itemsize, metadata and titles, add flags, +allow subclassing dtype + +.. branch: indexing + +Refactor array indexing to support ellipses. + +.. branch: numpy-docstrings + +Allow the docstrings of built-in numpy objects to be set at run-time. + +.. branch: nditer-revisited + +Implement nditer 'buffered' flag and fix some edge cases + +.. branch: ufunc-reduce + +Allow multiple axes in ufunc.reduce() + +.. branch: fix-tinylang-goals + +Update tinylang goals to match current rpython + +.. branch: vmprof-review + +Clean up of vmprof, notably to handle correctly multiple threads 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,68 +2,6 @@ What's new in PyPy 2.6+ ======================= -.. this is a revision shortly after release-2.6.0 -.. startrev: 91904d5c5188 +.. this is a revision shortly after release-2.6.1 +.. startrev: 83ebc73d4fcb -.. branch: use_min_scalar -Correctly resolve the output dtype of ufunc(array, scalar) calls. - -.. branch: stdlib-2.7.10 - -Update stdlib to version 2.7.10 - -.. branch: issue2062 - -.. branch: disable-unroll-for-short-loops -The JIT no longer performs loop unrolling if the loop compiles to too much code. - -.. branch: run-create_cffi_imports - -Build cffi import libraries as part of translation by monkey-patching an -additional task into translation - -.. branch: int-float-list-strategy - -Use a compact strategy for Python lists that mix integers and floats, -at least if the integers fit inside 32 bits. These lists are now -stored as an array of floats, like lists that contain only floats; the -difference is that integers are stored as tagged NaNs. (This should -have no visible effect! After ``lst = [42, 42.5]``, the value of -``lst[0]`` is still *not* the float ``42.0`` but the integer ``42``.) - -.. branch: cffi-callback-onerror -.. branch: cffi-new-allocator - -.. branch: unicode-dtype - -Partial implementation of unicode dtype and unicode scalars. - -.. branch: dtypes-compatability - -Improve compatibility with numpy dtypes; handle offsets to create unions, -fix str() and repr(), allow specifying itemsize, metadata and titles, add flags, -allow subclassing dtype - -.. branch: indexing - -Refactor array indexing to support ellipses. - -.. branch: numpy-docstrings - -Allow the docstrings of built-in numpy objects to be set at run-time. - -.. branch: nditer-revisited - -Implement nditer 'buffered' flag and fix some edge cases - -.. branch: ufunc-reduce - -Allow multiple axes in ufunc.reduce() - -.. branch: fix-tinylang-goals - -Update tinylang goals to match current rpython - -.. branch: vmprof-review - -Clean up of vmprof, notably to handle correctly multiple threads diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -61,8 +61,8 @@ rlocale.setlocale(rlocale.LC_CTYPE, '') except rlocale.LocaleError: pass - w_executable = space.fsdecode(space.wrapbytes(argv[0])) - w_argv = space.newlist([space.fsdecode(space.wrapbytes(s)) + w_executable = space.wrap_fsdecoded(argv[0]) + w_argv = space.newlist([space.wrap_fsdecoded(s) for s in argv[1:]]) w_exitcode = space.call_function(w_entry_point, w_executable, w_argv) exitcode = space.int_w(w_exitcode) diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1052,6 +1052,9 @@ from pypy.objspace.std.listobject import make_empty_list_with_size return make_empty_list_with_size(self, sizehint) + def wrap_fsdecoded(self, x): + return self.fsdecode(self.wrapbytes(x)) + @jit.unroll_safe def exception_match(self, w_exc_type, w_check_class): """Checks if the given exception type matches 'w_check_class'.""" diff --git a/pypy/interpreter/main.py b/pypy/interpreter/main.py --- a/pypy/interpreter/main.py +++ b/pypy/interpreter/main.py @@ -19,7 +19,8 @@ def compilecode(space, source, filename, cmd='exec'): w = space.wrap w_code = space.builtin.call( - 'compile', space.wrapbytes(source), w(filename), w(cmd), w(0), w(0)) + 'compile', space.wrapbytes(source), space.wrap_fsdecoded(filename), + w(cmd), w(0), w(0)) pycode = space.interp_w(eval.Code, w_code) return pycode @@ -44,7 +45,8 @@ space.setitem(w_globals, w('__builtins__'), space.builtin) if filename is not None: - space.setitem(w_globals, w('__file__'), w(filename)) + space.setitem(w_globals, w('__file__'), + space.wrap_fsdecoded(filename)) retval = pycode.exec_code(space, w_globals, w_globals) if eval: diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -2,7 +2,7 @@ from pypy.interpreter.mixedmodule import MixedModule from rpython.rlib import rdynload -VERSION = "1.2.0" +VERSION = "1.2.1" class Module(MixedModule): diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -3427,4 +3427,4 @@ def test_version(): # this test is here mostly for PyPy - assert __version__ == "1.2.0" + assert __version__ == "1.2.1" diff --git a/pypy/module/_vmprof/test/test__vmprof.py b/pypy/module/_vmprof/test/test__vmprof.py --- a/pypy/module/_vmprof/test/test__vmprof.py +++ b/pypy/module/_vmprof/test/test__vmprof.py @@ -21,11 +21,12 @@ i = 0 count = 0 i += 5 * WORD # header - assert s[i] == 4 - i += 1 # marker - assert s[i] == 4 - i += 1 # length - i += len('pypy') + assert s[i ] == 5 # MARKER_HEADER + assert s[i + 1] == 0 # 0 + assert s[i + 2] == 1 # VERSION_THREAD_ID + assert s[i + 3] == 4 # len('pypy') + assert s[i + 4: i + 8] == b'pypy' + i += 8 while i < len(s): if s[i] == 3: break diff --git a/pypy/module/_vmprof/test/test_direct.py b/pypy/module/_vmprof/test/test_direct.py --- a/pypy/module/_vmprof/test/test_direct.py +++ b/pypy/module/_vmprof/test/test_direct.py @@ -42,7 +42,7 @@ } -""" + open(str(srcdir.join("rvmprof_get_custom_offset.h"))).read()) +""" + open(str(srcdir.join("vmprof_get_custom_offset.h"))).read()) class TestDirect(object): def test_infrastructure(self): diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h --- a/pypy/module/cpyext/include/object.h +++ b/pypy/module/cpyext/include/object.h @@ -364,6 +364,8 @@ PyObject *ht_name, *ht_slots; } PyHeapTypeObject; +#define PyObject_Bytes PyObject_Str + /* Flag bits for printing: */ #define Py_PRINT_RAW 1 /* No string quotes etc. */ diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py @@ -160,6 +160,35 @@ assert func.name == 'sin' assert func.BType == '<func (<double>, <double>), <double>, False>' +def test_remove_line_continuation_comments(): + ffi = FFI(backend=FakeBackend()) + ffi.cdef(""" + double // blah \\ + more comments + x(void); + double // blah\\\\ + y(void); + double // blah\\ \ + etc + z(void); + """) + m = ffi.dlopen(lib_m) + m.x + m.y + m.z + +def test_line_continuation_in_defines(): + ffi = FFI(backend=FakeBackend()) + ffi.cdef(""" + #define ABC\\ + 42 + #define BCD \\ + 43 + """) + m = ffi.dlopen(lib_m) + assert m.ABC == 42 + assert m.BCD == 43 + def test_define_not_supported_for_now(): ffi = FFI(backend=FakeBackend()) e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"') @@ -238,6 +267,13 @@ ffi = FFI() ffi.cdef("typedef _Bool bool; void f(bool);") +def test_void_renamed_as_only_arg(): + ffi = FFI() + ffi.cdef("typedef void void_t1;" + "typedef void_t1 void_t;" + "typedef int (*func_t)(void_t);") + assert ffi.typeof("func_t").args == () + def test_win_common_types(): from cffi.commontypes import COMMON_TYPES, _CACHE from cffi.commontypes import win_common_types, resolve_common_type diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py @@ -25,6 +25,9 @@ if 1: # test the .cpp mode too kwds.setdefault('source_extension', '.cpp') source = 'extern "C" {\n%s\n}' % (source,) + else: + kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + + ['-Werror']) return recompiler._verify(ffi, module_name, source, *args, **kwds) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_zdist.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_zdist.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_zdist.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_zdist.py @@ -318,15 +318,32 @@ import cffi ffi = cffi.FFI() ffi.set_source("pack3.mymod", "/*code would be here*/") + ffi._hi_there = 42 """) with open("setup.py", "w") as f: - f.write("""if 1: + f.write("from __future__ import print_function\n" + """if 1: from setuptools import setup + from distutils.command.build_ext import build_ext + import os + + class TestBuildExt(build_ext): + def pre_run(self, ext, ffi): + print('_make_setuptools_api: in pre_run:', end=" ") + assert ffi._hi_there == 42 + assert ext.name == "pack3.mymod" + fn = os.path.join(os.path.dirname(self.build_lib), + '..', 'see_me') + print('creating %r' % (fn,)) + open(fn, 'w').close() + setup(name='example1', version='0.1', packages=['pack3'], package_dir={'': 'src1'}, - cffi_modules=["src1/pack3/_build.py:ffi"]) + cffi_modules=["src1/pack3/_build.py:ffi"], + cmdclass={'build_ext': TestBuildExt}, + ) """) @chdir_to_tmp @@ -335,6 +352,7 @@ self.run(["setup.py", "build"]) self.check_produced_files({'setup.py': None, 'build': '?', + 'see_me': None, 'src1': {'pack3': {'__init__.py': None, '_build.py': None}}}) @@ -344,6 +362,7 @@ self.run(["setup.py", "build_ext", "-i"]) self.check_produced_files({'setup.py': None, 'build': '?', + 'see_me': None, 'src1': {'pack3': {'__init__.py': None, '_build.py': None, 'mymod.SO': None}}}) diff --git a/rpython/flowspace/objspace.py b/rpython/flowspace/objspace.py --- a/rpython/flowspace/objspace.py +++ b/rpython/flowspace/objspace.py @@ -13,6 +13,11 @@ def _assert_rpythonic(func): """Raise ValueError if ``func`` is obviously not RPython""" + try: + func.func_code.co_cellvars + except AttributeError: + raise ValueError("%r is not RPython: it is likely an unexpected " + "built-in function or type" % (func,)) if func.func_doc and func.func_doc.lstrip().startswith('NOT_RPYTHON'): raise ValueError("%r is tagged as NOT_RPYTHON" % (func,)) if func.func_code.co_cellvars: diff --git a/rpython/flowspace/test/test_objspace.py b/rpython/flowspace/test/test_objspace.py --- a/rpython/flowspace/test/test_objspace.py +++ b/rpython/flowspace/test/test_objspace.py @@ -1363,6 +1363,15 @@ simplify_graph(graph) assert self.all_operations(graph) == {'bool': 1, 'inplace_add': 1} + def test_unexpected_builtin_function(self): + import itertools + e = py.test.raises(ValueError, build_flow, itertools.permutations) + assert ' is not RPython:' in str(e.value) + e = py.test.raises(ValueError, build_flow, itertools.tee) + assert ' is not RPython:' in str(e.value) + e = py.test.raises(ValueError, build_flow, Exception.__init__) + assert ' is not RPython:' in str(e.value) + DATA = {'x': 5, 'y': 6} diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py --- a/rpython/jit/backend/arm/runner.py +++ b/rpython/jit/backend/arm/runner.py @@ -64,12 +64,6 @@ operations, original_loop_token, log=log) - def clear_latest_values(self, count): - setitem = self.assembler.fail_boxes_ptr.setitem - null = lltype.nullptr(llmemory.GCREF.TO) - for index in range(count): - setitem(index, null) - def cast_ptr_to_int(x): adr = llmemory.cast_ptr_to_adr(x) return CPU_ARM.cast_adr_to_int(adr) diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -636,8 +636,7 @@ assert isinstance(box, Box) loc = self.fm.get_new_loc(box) locs.append(loc.value - base_ofs) - if looptoken.compiled_loop_token is not None: - # for tests + if looptoken.compiled_loop_token is not None: # <- for tests looptoken.compiled_loop_token._ll_initial_locs = locs def can_merge_with_next_guard(self, op, i, operations): diff --git a/rpython/jit/backend/llsupport/src/codemap.c b/rpython/jit/backend/llsupport/src/codemap.c --- a/rpython/jit/backend/llsupport/src/codemap.c +++ b/rpython/jit/backend/llsupport/src/codemap.c @@ -6,9 +6,9 @@ #endif #ifdef RPYTHON_VMPROF -RPY_EXTERN void rpython_vmprof_ignore_signals(int ignored); +RPY_EXTERN void vmprof_ignore_signals(int ignored); static void pypy_codemap_invalid_set(int ignored) { - rpython_vmprof_ignore_signals(ignored); + vmprof_ignore_signals(ignored); } #else static void pypy_codemap_invalid_set(int ignored) { diff --git a/rpython/jit/backend/x86/runner.py b/rpython/jit/backend/x86/runner.py --- a/rpython/jit/backend/x86/runner.py +++ b/rpython/jit/backend/x86/runner.py @@ -100,12 +100,6 @@ return self.assembler.assemble_bridge(faildescr, inputargs, operations, original_loop_token, log, logger) - def clear_latest_values(self, count): - setitem = self.assembler.fail_boxes_ptr.setitem - null = lltype.nullptr(llmemory.GCREF.TO) - for index in range(count): - setitem(index, null) - def cast_ptr_to_int(x): adr = llmemory.cast_ptr_to_adr(x) return CPU386.cast_adr_to_int(adr) diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py --- a/rpython/jit/metainterp/test/test_ajit.py +++ b/rpython/jit/metainterp/test/test_ajit.py @@ -268,6 +268,7 @@ y -= 1 return res res = self.meta_interp(f, [6, sys.maxint, 48]) + self.check_trace_count(6) assert res == f(6, sys.maxint, 48) def test_loop_invariant_mul_bridge_ovf2(self): diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -1087,6 +1087,16 @@ """ assert type(value) is cls +def ll_record_exact_class(ll_value, ll_cls): + from rpython.rlib.debug import ll_assert + from rpython.rtyper.lltypesystem.lloperation import llop + from rpython.rtyper.lltypesystem import lltype + from rpython.rtyper.rclass import ll_type + ll_assert(ll_value == lltype.nullptr(lltype.typeOf(ll_value).TO), "record_exact_class called with None argument") + ll_assert(ll_type(ll_value) is ll_cls, "record_exact_class called with invalid arguments") + llop.jit_record_exact_class(lltype.Void, ll_value, ll_cls) + + class Entry(ExtRegistryEntry): _about_ = record_exact_class @@ -1100,12 +1110,10 @@ from rpython.rtyper import rclass classrepr = rclass.get_type_repr(hop.rtyper) - - hop.exception_cannot_occur() v_inst = hop.inputarg(hop.args_r[0], arg=0) v_cls = hop.inputarg(classrepr, arg=1) - return hop.genop('jit_record_exact_class', [v_inst, v_cls], - resulttype=lltype.Void) + hop.exception_is_here() + return hop.gendirectcall(ll_record_exact_class, v_inst, v_cls) def _jit_conditional_call(condition, function, *args): pass 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 @@ -40,24 +40,20 @@ **eci_kwds)) - vmprof_init = rffi.llexternal("rpython_vmprof_init", [rffi.INT], rffi.CCHARP, - compilation_info=eci) - vmprof_enable = rffi.llexternal("rpython_vmprof_enable", [rffi.LONG], rffi.INT, + vmprof_init = rffi.llexternal("vmprof_init", + [rffi.INT, rffi.DOUBLE, rffi.CCHARP], + rffi.CCHARP, compilation_info=eci) + vmprof_enable = rffi.llexternal("vmprof_enable", [], rffi.INT, compilation_info=eci, save_err=rffi.RFFI_SAVE_ERRNO) - vmprof_disable = rffi.llexternal("rpython_vmprof_disable", [], rffi.INT, + vmprof_disable = rffi.llexternal("vmprof_disable", [], rffi.INT, compilation_info=eci, save_err=rffi.RFFI_SAVE_ERRNO) - vmprof_write_buf = rffi.llexternal("rpython_vmprof_write_buf", - [rffi.CCHARP, rffi.LONG], - lltype.Void, compilation_info=eci) - - ## vmprof_register_virtual_function = rffi.llexternal( - ## "vmprof_register_virtual_function", - ## [rffi.CCHARP, rffi.VOIDP, rffi.VOIDP], lltype.Void, - ## compilation_info=eci, _nowrapper=True) - - vmprof_ignore_signals = rffi.llexternal("rpython_vmprof_ignore_signals", + vmprof_register_virtual_function = rffi.llexternal( + "vmprof_register_virtual_function", + [rffi.CCHARP, rffi.LONG, rffi.INT], + rffi.INT, compilation_info=eci) + vmprof_ignore_signals = rffi.llexternal("vmprof_ignore_signals", [rffi.INT], lltype.Void, compilation_info=eci) return CInterface(locals()) @@ -83,10 +79,20 @@ cont_name = 'rpyvmprof_f_%s_%s' % (name, token) tramp_name = 'rpyvmprof_t_%s_%s' % (name, token) + orig_tramp_name = tramp_name func.c_name = cont_name func._dont_inline_ = True + if sys.platform == 'darwin': + # according to internet "At the time UNIX was written in 1974...." + # "... all C functions are prefixed with _" + cont_name = '_' + cont_name + tramp_name = '_' + tramp_name + PLT = "" + else: + PLT = "@PLT" + assert detect_cpu.autodetect().startswith(detect_cpu.MODEL_X86_64), ( "rvmprof only supports x86-64 CPUs for now") @@ -111,17 +117,15 @@ target.write("""\ \t.text \t.globl\t%(tramp_name)s -\t.type\t%(tramp_name)s, @function %(tramp_name)s: \t.cfi_startproc \tpushq\t%(reg)s \t.cfi_def_cfa_offset 16 -\tcall %(cont_name)s@PLT +\tcall %(cont_name)s%(PLT)s \taddq\t$8, %%rsp \t.cfi_def_cfa_offset 8 \tret \t.cfi_endproc -\t.size\t%(tramp_name)s, .-%(tramp_name)s """ % locals()) def tok2cname(tok): @@ -133,7 +137,7 @@ header = 'RPY_EXTERN %s %s(%s);\n' % ( tok2cname(restok), - tramp_name, + orig_tramp_name, ', '.join([tok2cname(tok) for tok in token] + ['long'])) header += """\ @@ -147,7 +151,7 @@ #endif #define VMPROF_ADDR_OF_TRAMPOLINE cmp_%s } -""" % (tramp_name, tramp_name, tramp_name) +""" % (tramp_name, orig_tramp_name, tramp_name) eci = ExternalCompilationInfo( post_include_bits = [header], @@ -155,7 +159,7 @@ ) return rffi.llexternal( - tramp_name, + orig_tramp_name, [token2lltype(tok) for tok in token] + [lltype.Signed], token2lltype(restok), compilation_info=eci, 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,14 +1,12 @@ import sys, os from rpython.rlib.objectmodel import specialize, we_are_translated -from rpython.rlib.rstring import StringBuilder from rpython.rlib import jit, rgc, rposix from rpython.rlib.rvmprof import cintf from rpython.rtyper.annlowlevel import cast_instance_to_gcref from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance from rpython.rtyper.lltypesystem import rffi -MAX_CODES = 8000 - 255 -MAX_FUNC_NAME = 255 +MAX_FUNC_NAME = 1023 # ____________________________________________________________ @@ -34,8 +32,6 @@ def _cleanup_(self): self.is_enabled = False - self.fileno = -1 - self._current_codes = None @specialize.argtype(1) def register_code(self, code, full_name_func): @@ -102,18 +98,13 @@ 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) - p_error = self.cintf.vmprof_init(fileno) + p_error = self.cintf.vmprof_init(fileno, interval, "pypy") if p_error: raise VMProfError(rffi.charp2str(p_error)) - self.fileno = fileno - self._write_header(interval_usec) self._gather_all_code_objs() - res = self.cintf.vmprof_enable(interval_usec) + res = self.cintf.vmprof_enable() if res < 0: raise VMProfError(os.strerror(rposix.get_saved_errno())) self.is_enabled = True @@ -125,9 +116,6 @@ if not self.is_enabled: raise VMProfError("vmprof is not enabled") self.is_enabled = False - if self._current_codes is not None: - self._flush_codes() - self.fileno = -1 res = self.cintf.vmprof_disable() if res < 0: raise VMProfError(os.strerror(rposix.get_saved_errno())) @@ -136,48 +124,8 @@ assert name.count(':') == 3 and len(name) <= MAX_FUNC_NAME, ( "the name must be 'class:func_name:func_line:filename' " "and at most %d characters; got '%s'" % (MAX_FUNC_NAME, 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.cintf.vmprof_write_buf(buf, len(buf)) - # NOTE: keep in mind that vmprof_write_buf() can only write - # a maximum of 8184 bytes. This should be guaranteed here because: - assert MAX_CODES + 17 + MAX_FUNC_NAME <= 8184 - - 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') - buf = b.build() - self.cintf.vmprof_write_buf(buf, len(buf)) - - -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)) - b.append(chr((l >> 24) & 0xff)) - if sys.maxint > 2147483647: - b.append(chr((l >> 32) & 0xff)) - b.append(chr((l >> 40) & 0xff)) - b.append(chr((l >> 48) & 0xff)) - b.append(chr((l >> 56) & 0xff)) + if self.cintf.vmprof_register_virtual_function(name, uid, 500000) < 0: + raise VMProfError("vmprof buffers full! disk full or too slow") def vmprof_execute_code(name, get_code_fn, result_class=None): diff --git a/rpython/rlib/rvmprof/src/rvmprof.c b/rpython/rlib/rvmprof/src/rvmprof.c --- a/rpython/rlib/rvmprof/src/rvmprof.c +++ b/rpython/rlib/rvmprof/src/rvmprof.c @@ -1,22 +1,3 @@ -/* VMPROF - * - * statistical sampling profiler specifically designed to profile programs - * which run on a Virtual Machine and/or bytecode interpreter, such as Python, - * etc. - * - * The logic to dump the C stack traces is partly stolen from the code in - * gperftools. - * The file "getpc.h" has been entirely copied from gperftools. - * - * Tested only on gcc, linux, x86_64. - * - * Copyright (C) 2014-2015 - * Antonio Cuni - anto.c...@gmail.com - * Maciej Fijalkowski - fij...@gmail.com - * Armin Rigo - ar...@tunes.org - * - */ - #define _GNU_SOURCE 1 @@ -39,431 +20,4 @@ #endif -#include <dlfcn.h> -#include <assert.h> -#include <pthread.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include "rvmprof_getpc.h" -#include "rvmprof_unwind.h" -#include "rvmprof_mt.h" - - -/************************************************************/ - -// functions copied from libunwind using dlopen - -static int (*unw_get_reg)(unw_cursor_t*, int, unw_word_t*) = NULL; -static int (*unw_step)(unw_cursor_t*) = NULL; -static int (*unw_init_local)(unw_cursor_t *, unw_context_t *) = NULL; -static int (*unw_get_proc_info)(unw_cursor_t *, unw_proc_info_t *) = NULL; - -static int profile_file = -1; - - -RPY_EXTERN -char *rpython_vmprof_init(int fd) -{ - if (!unw_get_reg) { - void *libhandle; - - if (!(libhandle = dlopen("libunwind.so", RTLD_LAZY | RTLD_LOCAL))) - goto error; - if (!(unw_get_reg = dlsym(libhandle, "_ULx86_64_get_reg"))) - goto error; - if (!(unw_get_proc_info = dlsym(libhandle, "_ULx86_64_get_proc_info"))) - goto error; - if (!(unw_init_local = dlsym(libhandle, "_ULx86_64_init_local"))) - goto error; - if (!(unw_step = dlsym(libhandle, "_ULx86_64_step"))) - goto error; - } - if (prepare_concurrent_bufs() < 0) - return "out of memory"; - - assert(fd >= 0); - profile_file = fd; - return NULL; - - error: - return dlerror(); -} - -/************************************************************/ - -/* value: last bit is 1 if signals must be ignored; all other bits - are a counter for how many threads are currently in a signal handler */ -static long volatile signal_handler_value = 1; - -RPY_EXTERN -void rpython_vmprof_ignore_signals(int ignored) -{ - if (!ignored) { - __sync_fetch_and_and(&signal_handler_value, ~1L); - } - else { - /* set the last bit, and wait until concurrently-running signal - handlers finish */ - while (__sync_or_and_fetch(&signal_handler_value, 1L) != 1L) { - usleep(1); - } - } -} - - -/* ************************************************************* - * functions to write a profile file compatible with gperftools - * ************************************************************* - */ - -#define MAX_FUNC_NAME 128 -#define MAX_STACK_DEPTH \ - ((SINGLE_BUF_SIZE - sizeof(struct prof_stacktrace_s)) / sizeof(void *)) - -#define MARKER_STACKTRACE '\x01' -#define MARKER_VIRTUAL_IP '\x02' -#define MARKER_TRAILER '\x03' - -struct prof_stacktrace_s { - char padding[sizeof(long) - 1]; - char marker; - long count, depth; - void *stack[]; -}; - -static long profile_interval_usec = 0; -static char atfork_hook_installed = 0; - - -/* ****************************************************** - * libunwind workaround for process JIT frames correctly - * ****************************************************** - */ - -#include "rvmprof_get_custom_offset.h" - -typedef struct { - void* _unused1; - void* _unused2; - void* sp; - void* ip; - void* _unused3[sizeof(unw_cursor_t)/sizeof(void*) - 4]; -} vmprof_hacked_unw_cursor_t; - -static int vmprof_unw_step(unw_cursor_t *cp, int first_run) -{ - void* ip; - void* sp; - ptrdiff_t sp_offset; - unw_get_reg (cp, UNW_REG_IP, (unw_word_t*)&ip); - unw_get_reg (cp, UNW_REG_SP, (unw_word_t*)&sp); - if (!first_run) { - // make sure we're pointing to the CALL and not to the first - // instruction after. If the callee adjusts the stack for us - // it's not safe to be at the instruction after - ip -= 1; - } - sp_offset = vmprof_unw_get_custom_offset(ip, cp); - - if (sp_offset == -1) { - // it means that the ip is NOT in JITted code, so we can use the - // stardard unw_step - return unw_step(cp); - } - else { - // this is a horrible hack to manually walk the stack frame, by - // setting the IP and SP in the cursor - vmprof_hacked_unw_cursor_t *cp2 = (vmprof_hacked_unw_cursor_t*)cp; - void* bp = (void*)sp + sp_offset; - cp2->sp = bp; - bp -= sizeof(void*); - cp2->ip = ((void**)bp)[0]; - // the ret is on the top of the stack minus WORD - return 1; - } -} - - -/* ************************************************************* - * functions to dump the stack trace - * ************************************************************* - */ - -static int get_stack_trace(void** result, int max_depth, ucontext_t *ucontext) -{ - void *ip; - int n = 0; - unw_cursor_t cursor; - unw_context_t uc = *ucontext; - - int ret = unw_init_local(&cursor, &uc); - assert(ret >= 0); - (void)ret; - - while (n < max_depth) { - if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *) &ip) < 0) { - break; - } - - unw_proc_info_t pip; - unw_get_proc_info(&cursor, &pip); - - /* if n==0, it means that the signal handler interrupted us while we - were in the trampoline, so we are not executing (yet) the real main - loop function; just skip it */ - if (VMPROF_ADDR_OF_TRAMPOLINE((void*)pip.start_ip) && n > 0) { - // found main loop stack frame - void* sp; - unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t *) &sp); - void *arg_addr = (char*)sp /* + mainloop_sp_offset */; - void **arg_ptr = (void**)arg_addr; - /* if (mainloop_get_virtual_ip) { - ip = mainloop_get_virtual_ip(*arg_ptr); - } else { */ - ip = *arg_ptr; - } - - int first_run = (n == 0); - result[n++] = ip; - n = vmprof_write_header_for_jit_addr(result, n, ip, max_depth); - if (vmprof_unw_step(&cursor, first_run) <= 0) - break; - } - return n; -} - - -/* ************************************************************* - * the signal handler - * ************************************************************* - */ - -static void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext) -{ - long val = __sync_fetch_and_add(&signal_handler_value, 2L); - - if ((val & 1) == 0) { - int saved_errno = errno; - int fd = profile_file; - assert(fd >= 0); - - struct profbuf_s *p = reserve_buffer(fd); - if (p == NULL) { - /* ignore this signal: there are no free buffers right now */ - } - else { - int depth; - struct prof_stacktrace_s *st = (struct prof_stacktrace_s *)p->data; - st->marker = MARKER_STACKTRACE; - st->count = 1; - st->stack[0] = GetPC((ucontext_t*)ucontext); - depth = get_stack_trace(st->stack+1, MAX_STACK_DEPTH-1, ucontext); - depth++; // To account for pc value in stack[0]; - st->depth = depth; - p->data_offset = offsetof(struct prof_stacktrace_s, marker); - p->data_size = (depth * sizeof(void *) + - sizeof(struct prof_stacktrace_s) - - offsetof(struct prof_stacktrace_s, marker)); - commit_buffer(fd, p); - } - - errno = saved_errno; - } - - __sync_sub_and_fetch(&signal_handler_value, 2L); -} - - -/* ************************************************************* - * the setup and teardown functions - * ************************************************************* - */ - -static int install_sigprof_handler(void) -{ - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = sigprof_handler; - sa.sa_flags = SA_RESTART | SA_SIGINFO; - if (sigemptyset(&sa.sa_mask) == -1 || - sigaction(SIGPROF, &sa, NULL) == -1) - return -1; - return 0; -} - -static int remove_sigprof_handler(void) -{ - if (signal(SIGPROF, SIG_DFL) == SIG_ERR) - return -1; - return 0; -} - -static int install_sigprof_timer(void) -{ - static struct itimerval timer; - timer.it_interval.tv_sec = 0; - timer.it_interval.tv_usec = profile_interval_usec; - timer.it_value = timer.it_interval; - if (setitimer(ITIMER_PROF, &timer, NULL) != 0) - return -1; - return 0; -} - -static int remove_sigprof_timer(void) { - static struct itimerval timer; - timer.it_interval.tv_sec = 0; - timer.it_interval.tv_usec = 0; - timer.it_value.tv_sec = 0; - timer.it_value.tv_usec = 0; - if (setitimer(ITIMER_PROF, &timer, NULL) != 0) - return -1; - return 0; -} - -static void atfork_disable_timer(void) { - if (profile_interval_usec > 0) { - remove_sigprof_timer(); - } -} - -static void atfork_enable_timer(void) { - if (profile_interval_usec > 0) { - install_sigprof_timer(); - } -} - -static int install_pthread_atfork_hooks(void) { - /* this is needed to prevent the problems described there: - - http://code.google.com/p/gperftools/issues/detail?id=278 - - http://lists.debian.org/debian-glibc/2010/03/msg00161.html - - TL;DR: if the RSS of the process is large enough, the clone() syscall - will be interrupted by the SIGPROF before it can complete, then - retried, interrupted again and so on, in an endless loop. The - solution is to disable the timer around the fork, and re-enable it - only inside the parent. - */ - if (atfork_hook_installed) - return 0; - int ret = pthread_atfork(atfork_disable_timer, atfork_enable_timer, NULL); - if (ret != 0) - return -1; - atfork_hook_installed = 1; - return 0; -} - -RPY_EXTERN -int rpython_vmprof_enable(long interval_usec) -{ - assert(profile_file >= 0); - assert(interval_usec > 0); - profile_interval_usec = interval_usec; - - if (install_pthread_atfork_hooks() == -1) - goto error; - if (install_sigprof_handler() == -1) - goto error; - if (install_sigprof_timer() == -1) - goto error; - rpython_vmprof_ignore_signals(0); - return 0; - - error: - profile_file = -1; - profile_interval_usec = 0; - return -1; -} - -static int _write_all(const void *buf, size_t bufsize) -{ - while (bufsize > 0) { - ssize_t count = write(profile_file, buf, bufsize); - if (count <= 0) - return -1; /* failed */ - buf += count; - bufsize -= count; - } - return 0; -} - -static int close_profile(void) -{ - char buf[4096]; - ssize_t size; - unsigned char marker = MARKER_TRAILER; - - if (_write_all(&marker, 1) < 0) - return -1; - -#ifdef __linux__ - // copy /proc/self/maps to the end of the profile file - int srcfd = open("/proc/self/maps", O_RDONLY); - if (srcfd < 0) - return -1; - - while ((size = read(srcfd, buf, sizeof buf)) > 0) { - if (_write_all(buf, size) < 0) { - close(srcfd); - return -1; - } - } - close(srcfd); -#else - // freebsd and mac - sprintf(buf, "procstat -v %d", getpid()); - FILE *srcf = popen(buf, "r"); - if (!srcf) - return -1; - - while ((size = fread(buf, 1, sizeof buf, src))) { - if (_write_all(buf, size) < 0) { - pclose(srcf); - return -1; - } - } - pclose(srcf); -#endif - - /* don't close() the file descriptor from here */ - profile_file = -1; - return 0; -} - -RPY_EXTERN -int rpython_vmprof_disable(void) -{ - rpython_vmprof_ignore_signals(1); - profile_interval_usec = 0; - - if (remove_sigprof_timer() == -1) - return -1; - if (remove_sigprof_handler() == -1) - return -1; - if (shutdown_concurrent_bufs(profile_file) < 0) - return -1; - return close_profile(); -} - -RPY_EXTERN -void rpython_vmprof_write_buf(char *buf, long size) -{ - struct profbuf_s *p; - - while ((p = reserve_buffer(profile_file)) == NULL) { - /* spin loop waiting for a buffer to be ready; should almost never - be the case */ - usleep(1); - } - - if (size > SINGLE_BUF_SIZE) - size = SINGLE_BUF_SIZE; - memcpy(p->data, buf, size); - p->data_size = size; - - commit_buffer(profile_file, p); -} +#include "vmprof_main.h" diff --git a/rpython/rlib/rvmprof/src/rvmprof.h b/rpython/rlib/rvmprof/src/rvmprof.h --- a/rpython/rlib/rvmprof/src/rvmprof.h +++ b/rpython/rlib/rvmprof/src/rvmprof.h @@ -1,6 +1,6 @@ -RPY_EXTERN char *rpython_vmprof_init(int); -RPY_EXTERN void rpython_vmprof_ignore_signals(int); -RPY_EXTERN int rpython_vmprof_enable(long); -RPY_EXTERN int rpython_vmprof_disable(void); -RPY_EXTERN void rpython_vmprof_write_buf(char *, long); +RPY_EXTERN char *vmprof_init(int, double, char *); +RPY_EXTERN void vmprof_ignore_signals(int); +RPY_EXTERN int vmprof_enable(void); +RPY_EXTERN int vmprof_disable(void); +RPY_EXTERN int vmprof_register_virtual_function(char *, long, int); diff --git a/rpython/rlib/rvmprof/src/rvmprof_get_custom_offset.h b/rpython/rlib/rvmprof/src/rvmprof_get_custom_offset.h deleted file mode 100644 --- a/rpython/rlib/rvmprof/src/rvmprof_get_custom_offset.h +++ /dev/null @@ -1,63 +0,0 @@ - -#ifdef PYPY_JIT_CODEMAP -void *pypy_find_codemap_at_addr(long addr, long *start_addr); -long pypy_yield_codemap_at_addr(void *codemap_raw, long addr, - long *current_pos_addr); -long pypy_jit_stack_depth_at_loc(long loc); -#endif - - -static ptrdiff_t vmprof_unw_get_custom_offset(void* ip, void *cp) { -#ifdef PYPY_JIT_CODEMAP - intptr_t ip_l = (intptr_t)ip; - return pypy_jit_stack_depth_at_loc(ip_l); -#else - return -1; -#endif -} - -static long vmprof_write_header_for_jit_addr(void **result, long n, - void *ip, int max_depth) -{ -#ifdef PYPY_JIT_CODEMAP - void *codemap; - long current_pos = 0; - intptr_t id; - long start_addr = 0; - intptr_t addr = (intptr_t)ip; - int start, k; - void *tmp; - - codemap = pypy_find_codemap_at_addr(addr, &start_addr); - if (codemap == NULL) - // not a jit code at all - return n; - - // modify the last entry to point to start address and not the random one - // in the middle - result[n - 1] = (void*)start_addr; - result[n] = (void*)2; - n++; - start = n; - while (n < max_depth) { - id = pypy_yield_codemap_at_addr(codemap, addr, ¤t_pos); - if (id == -1) - // finish - break; - if (id == 0) - continue; // not main codemap - result[n++] = (void *)id; - } - k = 0; - while (k < (n - start) / 2) { - tmp = result[start + k]; - result[start + k] = result[n - k - 1]; - result[n - k - 1] = tmp; - k++; - } - if (n < max_depth) { - result[n++] = (void*)3; - } -#endif - return n; -} diff --git a/rpython/rlib/rvmprof/src/rvmprof_getpc.h b/rpython/rlib/rvmprof/src/rvmprof_getpc.h deleted file mode 100644 --- a/rpython/rlib/rvmprof/src/rvmprof_getpc.h +++ /dev/null @@ -1,187 +0,0 @@ -// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// --- -// Author: Craig Silverstein -// -// This is an internal header file used by profiler.cc. It defines -// the single (inline) function GetPC. GetPC is used in a signal -// handler to figure out the instruction that was being executed when -// the signal-handler was triggered. -// -// To get this, we use the ucontext_t argument to the signal-handler -// callback, which holds the full context of what was going on when -// the signal triggered. How to get from a ucontext_t to a Program -// Counter is OS-dependent. - -#ifndef BASE_GETPC_H_ -#define BASE_GETPC_H_ - -#include "rvmprof_config.h" - -// On many linux systems, we may need _GNU_SOURCE to get access to -// the defined constants that define the register we want to see (eg -// REG_EIP). Note this #define must come first! -#define _GNU_SOURCE 1 -// If #define _GNU_SOURCE causes problems, this might work instead. -// It will cause problems for FreeBSD though!, because it turns off -// the needed __BSD_VISIBLE. -//#define _XOPEN_SOURCE 500 - -#include <string.h> // for memcmp -#if defined(HAVE_SYS_UCONTEXT_H) -#include <sys/ucontext.h> -#elif defined(HAVE_UCONTEXT_H) -#include <ucontext.h> // for ucontext_t (and also mcontext_t) -#elif defined(HAVE_CYGWIN_SIGNAL_H) -#include <cygwin/signal.h> -typedef ucontext ucontext_t; -#endif - - -// Take the example where function Foo() calls function Bar(). For -// many architectures, Bar() is responsible for setting up and tearing -// down its own stack frame. In that case, it's possible for the -// interrupt to happen when execution is in Bar(), but the stack frame -// is not properly set up (either before it's done being set up, or -// after it's been torn down but before Bar() returns). In those -// cases, the stack trace cannot see the caller function anymore. -// -// GetPC can try to identify this situation, on architectures where it -// might occur, and unwind the current function call in that case to -// avoid false edges in the profile graph (that is, edges that appear -// to show a call skipping over a function). To do this, we hard-code -// in the asm instructions we might see when setting up or tearing -// down a stack frame. -// -// This is difficult to get right: the instructions depend on the -// processor, the compiler ABI, and even the optimization level. This -// is a best effort patch -- if we fail to detect such a situation, or -// mess up the PC, nothing happens; the returned PC is not used for -// any further processing. -struct CallUnrollInfo { - // Offset from (e)ip register where this instruction sequence - // should be matched. Interpreted as bytes. Offset 0 is the next - // instruction to execute. Be extra careful with negative offsets in - // architectures of variable instruction length (like x86) - it is - // not that easy as taking an offset to step one instruction back! - int pc_offset; - // The actual instruction bytes. Feel free to make it larger if you - // need a longer sequence. - unsigned char ins[16]; - // How many bytes to match from ins array? - int ins_size; - // The offset from the stack pointer (e)sp where to look for the - // call return address. Interpreted as bytes. - int return_sp_offset; -}; - - -// The dereferences needed to get the PC from a struct ucontext were -// determined at configure time, and stored in the macro -// PC_FROM_UCONTEXT in config.h. The only thing we need to do here, -// then, is to do the magic call-unrolling for systems that support it. - -// -- Special case 1: linux x86, for which we have CallUnrollInfo -#if defined(__linux) && defined(__i386) && defined(__GNUC__) -static const CallUnrollInfo callunrollinfo[] = { - // Entry to a function: push %ebp; mov %esp,%ebp - // Top-of-stack contains the caller IP. - { 0, - {0x55, 0x89, 0xe5}, 3, - 0 - }, - // Entry to a function, second instruction: push %ebp; mov %esp,%ebp - // Top-of-stack contains the old frame, caller IP is +4. - { -1, - {0x55, 0x89, 0xe5}, 3, - 4 - }, - // Return from a function: RET. - // Top-of-stack contains the caller IP. - { 0, - {0xc3}, 1, - 0 - } -}; - -void* GetPC(ucontext_t *signal_ucontext) { - // See comment above struct CallUnrollInfo. Only try instruction - // flow matching if both eip and esp looks reasonable. - const int eip = signal_ucontext->uc_mcontext.gregs[REG_EIP]; - const int esp = signal_ucontext->uc_mcontext.gregs[REG_ESP]; - if ((eip & 0xffff0000) != 0 && (~eip & 0xffff0000) != 0 && - (esp & 0xffff0000) != 0) { - char* eip_char = reinterpret_cast<char*>(eip); - for (int i = 0; i < sizeof(callunrollinfo)/sizeof(*callunrollinfo); ++i) { - if (!memcmp(eip_char + callunrollinfo[i].pc_offset, - callunrollinfo[i].ins, callunrollinfo[i].ins_size)) { - // We have a match. - void **retaddr = (void**)(esp + callunrollinfo[i].return_sp_offset); - return *retaddr; - } - } - } - return (void*)eip; -} - -// Special case #2: Windows, which has to do something totally different. -#elif defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__MINGW32__) -// If this is ever implemented, probably the way to do it is to have -// profiler.cc use a high-precision timer via timeSetEvent: -// http://msdn2.microsoft.com/en-us/library/ms712713.aspx -// We'd use it in mode TIME_CALLBACK_FUNCTION/TIME_PERIODIC. -// The callback function would be something like prof_handler, but -// alas the arguments are different: no ucontext_t! I don't know -// how we'd get the PC (using StackWalk64?) -// http://msdn2.microsoft.com/en-us/library/ms680650.aspx - -#include "base/logging.h" // for RAW_LOG -#ifndef HAVE_CYGWIN_SIGNAL_H -typedef int ucontext_t; -#endif - -void* GetPC(ucontext_t *signal_ucontext) { - RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n"); - return NULL; -} - -// Normal cases. If this doesn't compile, it's probably because -// PC_FROM_UCONTEXT is the empty string. You need to figure out -// the right value for your system, and add it to the list in -// configure.ac (or set it manually in your config.h). -#else -void* GetPC(ucontext_t *signal_ucontext) { - return (void*)signal_ucontext->PC_FROM_UCONTEXT; // defined in config.h -} - -#endif - -#endif // BASE_GETPC_H_ diff --git a/rpython/rlib/rvmprof/src/rvmprof_mt.h b/rpython/rlib/rvmprof/src/rvmprof_mt.h deleted file mode 100644 --- a/rpython/rlib/rvmprof/src/rvmprof_mt.h +++ /dev/null @@ -1,210 +0,0 @@ -/* Support for multithreaded write() operations */ - -#include <sys/mman.h> -#include <string.h> - -/* The idea is that we have MAX_NUM_BUFFERS available, all of size - SINGLE_BUF_SIZE. Threads and signal handlers can ask to reserve a - buffer, fill it, and finally "commit" it, at which point its - content is written into the profile file. There is no hard - guarantee about the order in which the committed blocks are - actually written. We do this with two constrains: - - - write() calls should not overlap; only one thread can be - currently calling it. - - - the code needs to be multithread-safe *and* signal-handler-safe, - which means it must be written in a wait-free style: never have - spin loops waiting for some lock to be released, from any of - the functions that can be called from the signal handler! The - code holding the lock could be running in the same thread, - currently interrupted by the signal handler. - - The value of MAX_NUM_BUFFERS is a trade-off between too high - (lots of unnecessary memory, lots of checking all of them) - and too low (risk that there is none left). -*/ -#define MAX_NUM_BUFFERS 20 -#define SINGLE_BUF_SIZE (8192 - 2 * sizeof(unsigned int)) - -#if defined(__i386__) || defined(__amd64__) - static inline void write_fence(void) { asm("" : : : "memory"); } -#else - static inline void write_fence(void) { __sync_synchronize(); } -#endif - - -#define PROFBUF_UNUSED 0 -#define PROFBUF_FILLING 1 -#define PROFBUF_READY 2 - - -struct profbuf_s { - unsigned int data_size; - unsigned int data_offset; - char data[SINGLE_BUF_SIZE]; -}; - -static char volatile profbuf_state[MAX_NUM_BUFFERS]; -static struct profbuf_s *profbuf_all_buffers = NULL; -static int volatile profbuf_write_lock = 2; -static long profbuf_pending_write; - - -static void unprepare_concurrent_bufs(void) -{ - if (profbuf_all_buffers != NULL) { - munmap(profbuf_all_buffers, sizeof(struct profbuf_s) * MAX_NUM_BUFFERS); - profbuf_all_buffers = NULL; - } -} - -static int prepare_concurrent_bufs(void) -{ - assert(sizeof(struct profbuf_s) == 8192); - - unprepare_concurrent_bufs(); - profbuf_all_buffers = mmap(NULL, sizeof(struct profbuf_s) * MAX_NUM_BUFFERS, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (profbuf_all_buffers == MAP_FAILED) { - profbuf_all_buffers = NULL; - return -1; - } - memset((char *)profbuf_state, PROFBUF_UNUSED, sizeof(profbuf_state)); - profbuf_write_lock = 0; - profbuf_pending_write = -1; - return 0; -} - -static int _write_single_ready_buffer(int fd, long i) -{ - /* Try to write to disk the buffer number 'i'. This function must - only be called while we hold the write lock. */ - assert(profbuf_write_lock != 0); - - if (profbuf_pending_write >= 0) { - /* A partially written buffer is waiting. We'll write the - rest of this buffer now, instead of 'i'. */ - i = profbuf_pending_write; - assert(profbuf_state[i] == PROFBUF_READY); - } - - if (profbuf_state[i] != PROFBUF_READY) { - /* this used to be a race condition: the buffer was written by a - different thread already, nothing to do now */ - return 0; - } - - int err; - struct profbuf_s *p = &profbuf_all_buffers[i]; - ssize_t count = write(fd, p->data + p->data_offset, p->data_size); - if (count == p->data_size) { - profbuf_state[i] = PROFBUF_UNUSED; - profbuf_pending_write = -1; - } - else { - if (count > 0) { - p->data_offset += count; - p->data_size -= count; - } - profbuf_pending_write = i; - if (count < 0) - return -1; - } - return 0; -} - -static void _write_ready_buffers(int fd) -{ - long i; - int has_write_lock = 0; - - for (i = 0; i < MAX_NUM_BUFFERS; i++) { - if (profbuf_state[i] == PROFBUF_READY) { - if (!has_write_lock) { - if (!__sync_bool_compare_and_swap(&profbuf_write_lock, 0, 1)) - return; /* can't acquire the write lock, give up */ - has_write_lock = 1; - } - if (_write_single_ready_buffer(fd, i) < 0) - break; - } - } - if (has_write_lock) - profbuf_write_lock = 0; -} - -static struct profbuf_s *reserve_buffer(int fd) -{ - /* Tries to enter a region of code that fills one buffer. If - successful, returns the profbuf_s. It fails only if the - concurrent buffers are all busy (extreme multithreaded usage). - - This might call write() to emit the data sitting in - previously-prepared buffers. In case of write() error, the - error is ignored but unwritten data stays in the buffers. - */ - long i; - - _write_ready_buffers(fd); - - for (i = 0; i < MAX_NUM_BUFFERS; i++) { - if (profbuf_state[i] == PROFBUF_UNUSED && - __sync_bool_compare_and_swap(&profbuf_state[i], PROFBUF_UNUSED, - PROFBUF_FILLING)) { - struct profbuf_s *p = &profbuf_all_buffers[i]; - p->data_size = 0; - p->data_offset = 0; - return p; - } - } - /* no unused buffer found */ - return NULL; -} - -static void commit_buffer(int fd, struct profbuf_s *buf) -{ - /* Leaves a region of code that filled 'buf'. - - This might call write() to emit the data now ready. In case of - write() error, the error is ignored but unwritten data stays in - the buffers. - */ - - /* Make sure every thread sees the full content of 'buf' */ - write_fence(); - - /* Then set the 'ready' flag */ - long i = buf - profbuf_all_buffers; - assert(profbuf_state[i] == PROFBUF_FILLING); - profbuf_state[i] = PROFBUF_READY; - - if (!__sync_bool_compare_and_swap(&profbuf_write_lock, 0, 1)) { - /* can't acquire the write lock, ignore */ - } - else { - _write_single_ready_buffer(fd, i); - profbuf_write_lock = 0; - } -} - -static int shutdown_concurrent_bufs(int fd) -{ - /* no signal handler can be running concurrently here, because we - already did rpython_vmprof_ignore_signals(1) */ - assert(profbuf_write_lock == 0); - profbuf_write_lock = 2; - - /* last attempt to flush buffers */ - int i; - for (i = 0; i < MAX_NUM_BUFFERS; i++) { - while (profbuf_state[i] == PROFBUF_READY) { - if (_write_single_ready_buffer(fd, i) < 0) - return -1; - } - } - unprepare_concurrent_bufs(); - return 0; -} diff --git a/rpython/rlib/rvmprof/src/rvmprof_config.h b/rpython/rlib/rvmprof/src/vmprof_config.h rename from rpython/rlib/rvmprof/src/rvmprof_config.h rename to rpython/rlib/rvmprof/src/vmprof_config.h diff --git a/rpython/rlib/rvmprof/src/vmprof_get_custom_offset.h b/rpython/rlib/rvmprof/src/vmprof_get_custom_offset.h new file mode 100644 --- /dev/null +++ b/rpython/rlib/rvmprof/src/vmprof_get_custom_offset.h @@ -0,0 +1,120 @@ + +#ifdef PYPY_JIT_CODEMAP +void *pypy_find_codemap_at_addr(long addr, long *start_addr); +long pypy_yield_codemap_at_addr(void *codemap_raw, long addr, + long *current_pos_addr); +long pypy_jit_stack_depth_at_loc(long loc); +#endif + + +#ifdef CPYTHON_GET_CUSTOM_OFFSET +static void *tramp_start, *tramp_end; +#endif + + +static ptrdiff_t vmprof_unw_get_custom_offset(void* ip, void *cp) { + +#if defined(PYPY_JIT_CODEMAP) + + intptr_t ip_l = (intptr_t)ip; + return pypy_jit_stack_depth_at_loc(ip_l); + +#elif defined(CPYTHON_GET_CUSTOM_OFFSET) + + if (ip >= tramp_start && ip <= tramp_end) { + // XXX the return value is wrong for all the places before push and + // after pop, fix + void *bp; + void *sp; + + /* This is a stage2 trampoline created by hotpatch: + + push %rbx + push %rbp + mov %rsp,%rbp + and $0xfffffffffffffff0,%rsp // make sure the stack is aligned + movabs $0x7ffff687bb10,%rbx + callq *%rbx + leaveq + pop %rbx + retq + + the stack layout is like this: + + +-----------+ high addresses + | ret addr | + +-----------+ + | saved rbx | start of the function frame + +-----------+ + | saved rbp | + +-----------+ + | ........ | <-- rbp + +-----------+ low addresses + + So, the trampoline frame starts at rbp+16, and the return address, + is at rbp+24. The vmprof API requires us to return the offset of + the frame relative to sp, hence we have this weird computation. + + XXX (antocuni): I think we could change the API to return directly + the frame address instead of the offset; however, this require a + change in the PyPy code too + */ + + unw_get_reg (cp, UNW_REG_SP, (unw_word_t*)&sp); + unw_get_reg (cp, UNW_X86_64_RBP, (unw_word_t*)&bp); + return bp+16+8-sp; + } + return -1; + +#else + + return -1; + +#endif +} + +static long vmprof_write_header_for_jit_addr(void **result, long n, + void *ip, int max_depth) +{ +#ifdef PYPY_JIT_CODEMAP + void *codemap; + long current_pos = 0; + intptr_t id; + long start_addr = 0; + intptr_t addr = (intptr_t)ip; + int start, k; + void *tmp; + + codemap = pypy_find_codemap_at_addr(addr, &start_addr); + if (codemap == NULL) + // not a jit code at all + return n; + + // modify the last entry to point to start address and not the random one + // in the middle + result[n - 1] = (void*)start_addr; + result[n] = (void*)2; + n++; + start = n; + while (n < max_depth) { + id = pypy_yield_codemap_at_addr(codemap, addr, ¤t_pos); + if (id == -1) + // finish + break; + if (id == 0) + continue; // not main codemap + result[n++] = (void *)id; + } + k = 0; + while (k < (n - start) / 2) { + tmp = result[start + k]; + result[start + k] = result[n - k - 1]; + result[n - k - 1] = tmp; + k++; + } + if (n < max_depth) { + result[n++] = (void*)3; + } +#endif + return n; +} diff --git a/rpython/rlib/rvmprof/src/vmprof_getpc.h b/rpython/rlib/rvmprof/src/vmprof_getpc.h new file mode 100644 --- /dev/null +++ b/rpython/rlib/rvmprof/src/vmprof_getpc.h @@ -0,0 +1,193 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit