mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	* Add mimalloc v2.12 Modified src/alloc.c to remove include of alloc-override.c and not compile new handler. Did not include the following files: - include/mimalloc-new-delete.h - include/mimalloc-override.h - src/alloc-override-osx.c - src/alloc-override.c - src/static.c - src/region.c mimalloc is thread safe and shares a single heap across all runtimes, therefore finalization and getting global allocated blocks across all runtimes is different. * mimalloc: minimal changes for use in Python: - remove debug spam for freeing large allocations - use same bytes (0xDD) for freed allocations in CPython and mimalloc This is important for the test_capi debug memory tests * Don't export mimalloc symbol in libpython. * Enable mimalloc as Python allocator option. * Add mimalloc MIT license. * Log mimalloc in Lib/test/pythoninfo.py. * Document new mimalloc support. * Use macro defs for exports as done in: https://github.com/python/cpython/pull/31164/ Co-authored-by: Sam Gross <colesbury@gmail.com> Co-authored-by: Christian Heimes <christian@python.org> Co-authored-by: Victor Stinner <vstinner@python.org>
		
			
				
	
	
		
			185 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/env python
 | 
						|
# Script checking that all symbols exported by libpython start with Py or _Py
 | 
						|
 | 
						|
import os.path
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
import sysconfig
 | 
						|
 | 
						|
 | 
						|
ALLOWED_PREFIXES = ('Py', '_Py')
 | 
						|
if sys.platform == 'darwin':
 | 
						|
    ALLOWED_PREFIXES += ('__Py',)
 | 
						|
 | 
						|
# mimalloc doesn't use static, but it's symbols are not exported
 | 
						|
# from the shared library.  They do show up in the static library
 | 
						|
# before its linked into an executable.
 | 
						|
ALLOWED_STATIC_PREFIXES = ('mi_', '_mi_')
 | 
						|
 | 
						|
# "Legacy": some old symbols are prefixed by "PY_".
 | 
						|
EXCEPTIONS = frozenset({
 | 
						|
    'PY_TIMEOUT_MAX',
 | 
						|
})
 | 
						|
 | 
						|
IGNORED_EXTENSION = "_ctypes_test"
 | 
						|
# Ignore constructor and destructor functions
 | 
						|
IGNORED_SYMBOLS = {'_init', '_fini'}
 | 
						|
 | 
						|
 | 
						|
def is_local_symbol_type(symtype):
 | 
						|
    # Ignore local symbols.
 | 
						|
 | 
						|
    # If lowercase, the symbol is usually local; if uppercase, the symbol
 | 
						|
    # is global (external).  There are however a few lowercase symbols that
 | 
						|
    # are shown for special global symbols ("u", "v" and "w").
 | 
						|
    if symtype.islower() and symtype not in "uvw":
 | 
						|
        return True
 | 
						|
 | 
						|
    # Ignore the initialized data section (d and D) and the BSS data
 | 
						|
    # section. For example, ignore "__bss_start (type: B)"
 | 
						|
    # and "_edata (type: D)".
 | 
						|
    if symtype in "bBdD":
 | 
						|
        return True
 | 
						|
 | 
						|
    return False
 | 
						|
 | 
						|
 | 
						|
def get_exported_symbols(library, dynamic=False):
 | 
						|
    print(f"Check that {library} only exports symbols starting with Py or _Py")
 | 
						|
 | 
						|
    # Only look at dynamic symbols
 | 
						|
    args = ['nm', '--no-sort']
 | 
						|
    if dynamic:
 | 
						|
        args.append('--dynamic')
 | 
						|
    args.append(library)
 | 
						|
    print("+ %s" % ' '.join(args))
 | 
						|
    proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True)
 | 
						|
    if proc.returncode:
 | 
						|
        sys.stdout.write(proc.stdout)
 | 
						|
        sys.exit(proc.returncode)
 | 
						|
 | 
						|
    stdout = proc.stdout.rstrip()
 | 
						|
    if not stdout:
 | 
						|
        raise Exception("command output is empty")
 | 
						|
    return stdout
 | 
						|
 | 
						|
 | 
						|
def get_smelly_symbols(stdout, dynamic=False):
 | 
						|
    smelly_symbols = []
 | 
						|
    python_symbols = []
 | 
						|
    local_symbols = []
 | 
						|
 | 
						|
    for line in stdout.splitlines():
 | 
						|
        # Split line '0000000000001b80 D PyTextIOWrapper_Type'
 | 
						|
        if not line:
 | 
						|
            continue
 | 
						|
 | 
						|
        parts = line.split(maxsplit=2)
 | 
						|
        if len(parts) < 3:
 | 
						|
            continue
 | 
						|
 | 
						|
        symtype = parts[1].strip()
 | 
						|
        symbol = parts[-1]
 | 
						|
        result = '%s (type: %s)' % (symbol, symtype)
 | 
						|
 | 
						|
        if (symbol.startswith(ALLOWED_PREFIXES) or
 | 
						|
            symbol in EXCEPTIONS or
 | 
						|
            (not dynamic and symbol.startswith(ALLOWED_STATIC_PREFIXES))):
 | 
						|
            python_symbols.append(result)
 | 
						|
            continue
 | 
						|
 | 
						|
        if is_local_symbol_type(symtype):
 | 
						|
            local_symbols.append(result)
 | 
						|
        elif symbol in IGNORED_SYMBOLS:
 | 
						|
            local_symbols.append(result)
 | 
						|
        else:
 | 
						|
            smelly_symbols.append(result)
 | 
						|
 | 
						|
    if local_symbols:
 | 
						|
        print(f"Ignore {len(local_symbols)} local symbols")
 | 
						|
    return smelly_symbols, python_symbols
 | 
						|
 | 
						|
 | 
						|
def check_library(library, dynamic=False):
 | 
						|
    nm_output = get_exported_symbols(library, dynamic)
 | 
						|
    smelly_symbols, python_symbols = get_smelly_symbols(nm_output, dynamic)
 | 
						|
 | 
						|
    if not smelly_symbols:
 | 
						|
        print(f"OK: no smelly symbol found ({len(python_symbols)} Python symbols)")
 | 
						|
        return 0
 | 
						|
 | 
						|
    print()
 | 
						|
    smelly_symbols.sort()
 | 
						|
    for symbol in smelly_symbols:
 | 
						|
        print("Smelly symbol: %s" % symbol)
 | 
						|
 | 
						|
    print()
 | 
						|
    print("ERROR: Found %s smelly symbols!" % len(smelly_symbols))
 | 
						|
    return len(smelly_symbols)
 | 
						|
 | 
						|
 | 
						|
def check_extensions():
 | 
						|
    print(__file__)
 | 
						|
    # This assumes pybuilddir.txt is in same directory as pyconfig.h.
 | 
						|
    # In the case of out-of-tree builds, we can't assume pybuilddir.txt is
 | 
						|
    # in the source folder.
 | 
						|
    config_dir = os.path.dirname(sysconfig.get_config_h_filename())
 | 
						|
    filename = os.path.join(config_dir, "pybuilddir.txt")
 | 
						|
    try:
 | 
						|
        with open(filename, encoding="utf-8") as fp:
 | 
						|
            pybuilddir = fp.readline()
 | 
						|
    except FileNotFoundError:
 | 
						|
        print(f"Cannot check extensions because {filename} does not exist")
 | 
						|
        return True
 | 
						|
 | 
						|
    print(f"Check extension modules from {pybuilddir} directory")
 | 
						|
    builddir = os.path.join(config_dir, pybuilddir)
 | 
						|
    nsymbol = 0
 | 
						|
    for name in os.listdir(builddir):
 | 
						|
        if not name.endswith(".so"):
 | 
						|
            continue
 | 
						|
        if IGNORED_EXTENSION in name:
 | 
						|
            print()
 | 
						|
            print(f"Ignore extension: {name}")
 | 
						|
            continue
 | 
						|
 | 
						|
        print()
 | 
						|
        filename = os.path.join(builddir, name)
 | 
						|
        nsymbol += check_library(filename, dynamic=True)
 | 
						|
 | 
						|
    return nsymbol
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    nsymbol = 0
 | 
						|
 | 
						|
    # static library
 | 
						|
    LIBRARY = sysconfig.get_config_var('LIBRARY')
 | 
						|
    if not LIBRARY:
 | 
						|
        raise Exception("failed to get LIBRARY variable from sysconfig")
 | 
						|
    if os.path.exists(LIBRARY):
 | 
						|
        nsymbol += check_library(LIBRARY)
 | 
						|
 | 
						|
    # dynamic library
 | 
						|
    LDLIBRARY = sysconfig.get_config_var('LDLIBRARY')
 | 
						|
    if not LDLIBRARY:
 | 
						|
        raise Exception("failed to get LDLIBRARY variable from sysconfig")
 | 
						|
    if LDLIBRARY != LIBRARY:
 | 
						|
        print()
 | 
						|
        nsymbol += check_library(LDLIBRARY, dynamic=True)
 | 
						|
 | 
						|
    # Check extension modules like _ssl.cpython-310d-x86_64-linux-gnu.so
 | 
						|
    nsymbol += check_extensions()
 | 
						|
 | 
						|
    if nsymbol:
 | 
						|
        print()
 | 
						|
        print(f"ERROR: Found {nsymbol} smelly symbols in total!")
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
    print()
 | 
						|
    print(f"OK: all exported symbols of all libraries "
 | 
						|
          f"are prefixed with {' or '.join(map(repr, ALLOWED_PREFIXES))}")
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main()
 |