mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	 05f2f0ac92
			
		
	
	
		05f2f0ac92
		
			
		
	
	
	
	
		
			
			* 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()
 |