17db96d56Sopenharmony_ciimport os 27db96d56Sopenharmony_ciimport sys 37db96d56Sopenharmony_ciimport warnings 47db96d56Sopenharmony_cifrom inspect import isabstract 57db96d56Sopenharmony_cifrom test import support 67db96d56Sopenharmony_cifrom test.support import os_helper 77db96d56Sopenharmony_cifrom test.libregrtest.utils import clear_caches 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_citry: 107db96d56Sopenharmony_ci from _abc import _get_dump 117db96d56Sopenharmony_ciexcept ImportError: 127db96d56Sopenharmony_ci import weakref 137db96d56Sopenharmony_ci 147db96d56Sopenharmony_ci def _get_dump(cls): 157db96d56Sopenharmony_ci # Reimplement _get_dump() for pure-Python implementation of 167db96d56Sopenharmony_ci # the abc module (Lib/_py_abc.py) 177db96d56Sopenharmony_ci registry_weakrefs = set(weakref.ref(obj) for obj in cls._abc_registry) 187db96d56Sopenharmony_ci return (registry_weakrefs, cls._abc_cache, 197db96d56Sopenharmony_ci cls._abc_negative_cache, cls._abc_negative_cache_version) 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_cidef dash_R(ns, test_name, test_func): 237db96d56Sopenharmony_ci """Run a test multiple times, looking for reference leaks. 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_ci Returns: 267db96d56Sopenharmony_ci False if the test didn't leak references; True if we detected refleaks. 277db96d56Sopenharmony_ci """ 287db96d56Sopenharmony_ci # This code is hackish and inelegant, but it seems to do the job. 297db96d56Sopenharmony_ci import copyreg 307db96d56Sopenharmony_ci import collections.abc 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci if not hasattr(sys, 'gettotalrefcount'): 337db96d56Sopenharmony_ci raise Exception("Tracking reference leaks requires a debug build " 347db96d56Sopenharmony_ci "of Python") 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_ci # Avoid false positives due to various caches 377db96d56Sopenharmony_ci # filling slowly with random data: 387db96d56Sopenharmony_ci warm_caches() 397db96d56Sopenharmony_ci 407db96d56Sopenharmony_ci # Save current values for dash_R_cleanup() to restore. 417db96d56Sopenharmony_ci fs = warnings.filters[:] 427db96d56Sopenharmony_ci ps = copyreg.dispatch_table.copy() 437db96d56Sopenharmony_ci pic = sys.path_importer_cache.copy() 447db96d56Sopenharmony_ci try: 457db96d56Sopenharmony_ci import zipimport 467db96d56Sopenharmony_ci except ImportError: 477db96d56Sopenharmony_ci zdc = None # Run unmodified on platforms without zipimport support 487db96d56Sopenharmony_ci else: 497db96d56Sopenharmony_ci zdc = zipimport._zip_directory_cache.copy() 507db96d56Sopenharmony_ci abcs = {} 517db96d56Sopenharmony_ci for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: 527db96d56Sopenharmony_ci if not isabstract(abc): 537db96d56Sopenharmony_ci continue 547db96d56Sopenharmony_ci for obj in abc.__subclasses__() + [abc]: 557db96d56Sopenharmony_ci abcs[obj] = _get_dump(obj)[0] 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci # bpo-31217: Integer pool to get a single integer object for the same 587db96d56Sopenharmony_ci # value. The pool is used to prevent false alarm when checking for memory 597db96d56Sopenharmony_ci # block leaks. Fill the pool with values in -1000..1000 which are the most 607db96d56Sopenharmony_ci # common (reference, memory block, file descriptor) differences. 617db96d56Sopenharmony_ci int_pool = {value: value for value in range(-1000, 1000)} 627db96d56Sopenharmony_ci def get_pooled_int(value): 637db96d56Sopenharmony_ci return int_pool.setdefault(value, value) 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci nwarmup, ntracked, fname = ns.huntrleaks 667db96d56Sopenharmony_ci fname = os.path.join(os_helper.SAVEDCWD, fname) 677db96d56Sopenharmony_ci repcount = nwarmup + ntracked 687db96d56Sopenharmony_ci 697db96d56Sopenharmony_ci # Pre-allocate to ensure that the loop doesn't allocate anything new 707db96d56Sopenharmony_ci rep_range = list(range(repcount)) 717db96d56Sopenharmony_ci rc_deltas = [0] * repcount 727db96d56Sopenharmony_ci alloc_deltas = [0] * repcount 737db96d56Sopenharmony_ci fd_deltas = [0] * repcount 747db96d56Sopenharmony_ci getallocatedblocks = sys.getallocatedblocks 757db96d56Sopenharmony_ci gettotalrefcount = sys.gettotalrefcount 767db96d56Sopenharmony_ci _getquickenedcount = sys._getquickenedcount 777db96d56Sopenharmony_ci fd_count = os_helper.fd_count 787db96d56Sopenharmony_ci # initialize variables to make pyflakes quiet 797db96d56Sopenharmony_ci rc_before = alloc_before = fd_before = 0 807db96d56Sopenharmony_ci 817db96d56Sopenharmony_ci if not ns.quiet: 827db96d56Sopenharmony_ci print("beginning", repcount, "repetitions", file=sys.stderr) 837db96d56Sopenharmony_ci print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, 847db96d56Sopenharmony_ci flush=True) 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_ci dash_R_cleanup(fs, ps, pic, zdc, abcs) 877db96d56Sopenharmony_ci support.gc_collect() 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci for i in rep_range: 907db96d56Sopenharmony_ci test_func() 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci dash_R_cleanup(fs, ps, pic, zdc, abcs) 937db96d56Sopenharmony_ci support.gc_collect() 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci # Read memory statistics immediately after the garbage collection 967db96d56Sopenharmony_ci alloc_after = getallocatedblocks() - _getquickenedcount() 977db96d56Sopenharmony_ci rc_after = gettotalrefcount() 987db96d56Sopenharmony_ci fd_after = fd_count() 997db96d56Sopenharmony_ci 1007db96d56Sopenharmony_ci if not ns.quiet: 1017db96d56Sopenharmony_ci print('.', end='', file=sys.stderr, flush=True) 1027db96d56Sopenharmony_ci 1037db96d56Sopenharmony_ci rc_deltas[i] = get_pooled_int(rc_after - rc_before) 1047db96d56Sopenharmony_ci alloc_deltas[i] = get_pooled_int(alloc_after - alloc_before) 1057db96d56Sopenharmony_ci fd_deltas[i] = get_pooled_int(fd_after - fd_before) 1067db96d56Sopenharmony_ci 1077db96d56Sopenharmony_ci alloc_before = alloc_after 1087db96d56Sopenharmony_ci rc_before = rc_after 1097db96d56Sopenharmony_ci fd_before = fd_after 1107db96d56Sopenharmony_ci 1117db96d56Sopenharmony_ci if not ns.quiet: 1127db96d56Sopenharmony_ci print(file=sys.stderr) 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci # These checkers return False on success, True on failure 1157db96d56Sopenharmony_ci def check_rc_deltas(deltas): 1167db96d56Sopenharmony_ci # Checker for reference counters and memory blocks. 1177db96d56Sopenharmony_ci # 1187db96d56Sopenharmony_ci # bpo-30776: Try to ignore false positives: 1197db96d56Sopenharmony_ci # 1207db96d56Sopenharmony_ci # [3, 0, 0] 1217db96d56Sopenharmony_ci # [0, 1, 0] 1227db96d56Sopenharmony_ci # [8, -8, 1] 1237db96d56Sopenharmony_ci # 1247db96d56Sopenharmony_ci # Expected leaks: 1257db96d56Sopenharmony_ci # 1267db96d56Sopenharmony_ci # [5, 5, 6] 1277db96d56Sopenharmony_ci # [10, 1, 1] 1287db96d56Sopenharmony_ci return all(delta >= 1 for delta in deltas) 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci def check_fd_deltas(deltas): 1317db96d56Sopenharmony_ci return any(deltas) 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci failed = False 1347db96d56Sopenharmony_ci for deltas, item_name, checker in [ 1357db96d56Sopenharmony_ci (rc_deltas, 'references', check_rc_deltas), 1367db96d56Sopenharmony_ci (alloc_deltas, 'memory blocks', check_rc_deltas), 1377db96d56Sopenharmony_ci (fd_deltas, 'file descriptors', check_fd_deltas) 1387db96d56Sopenharmony_ci ]: 1397db96d56Sopenharmony_ci # ignore warmup runs 1407db96d56Sopenharmony_ci deltas = deltas[nwarmup:] 1417db96d56Sopenharmony_ci if checker(deltas): 1427db96d56Sopenharmony_ci msg = '%s leaked %s %s, sum=%s' % ( 1437db96d56Sopenharmony_ci test_name, deltas, item_name, sum(deltas)) 1447db96d56Sopenharmony_ci print(msg, file=sys.stderr, flush=True) 1457db96d56Sopenharmony_ci with open(fname, "a", encoding="utf-8") as refrep: 1467db96d56Sopenharmony_ci print(msg, file=refrep) 1477db96d56Sopenharmony_ci refrep.flush() 1487db96d56Sopenharmony_ci failed = True 1497db96d56Sopenharmony_ci return failed 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_cidef dash_R_cleanup(fs, ps, pic, zdc, abcs): 1537db96d56Sopenharmony_ci import copyreg 1547db96d56Sopenharmony_ci import collections.abc 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci # Restore some original values. 1577db96d56Sopenharmony_ci warnings.filters[:] = fs 1587db96d56Sopenharmony_ci copyreg.dispatch_table.clear() 1597db96d56Sopenharmony_ci copyreg.dispatch_table.update(ps) 1607db96d56Sopenharmony_ci sys.path_importer_cache.clear() 1617db96d56Sopenharmony_ci sys.path_importer_cache.update(pic) 1627db96d56Sopenharmony_ci try: 1637db96d56Sopenharmony_ci import zipimport 1647db96d56Sopenharmony_ci except ImportError: 1657db96d56Sopenharmony_ci pass # Run unmodified on platforms without zipimport support 1667db96d56Sopenharmony_ci else: 1677db96d56Sopenharmony_ci zipimport._zip_directory_cache.clear() 1687db96d56Sopenharmony_ci zipimport._zip_directory_cache.update(zdc) 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci # Clear ABC registries, restoring previously saved ABC registries. 1717db96d56Sopenharmony_ci abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__] 1727db96d56Sopenharmony_ci abs_classes = filter(isabstract, abs_classes) 1737db96d56Sopenharmony_ci for abc in abs_classes: 1747db96d56Sopenharmony_ci for obj in abc.__subclasses__() + [abc]: 1757db96d56Sopenharmony_ci for ref in abcs.get(obj, set()): 1767db96d56Sopenharmony_ci if ref() is not None: 1777db96d56Sopenharmony_ci obj.register(ref()) 1787db96d56Sopenharmony_ci obj._abc_caches_clear() 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci # Clear caches 1817db96d56Sopenharmony_ci clear_caches() 1827db96d56Sopenharmony_ci 1837db96d56Sopenharmony_ci # Clear type cache at the end: previous function calls can modify types 1847db96d56Sopenharmony_ci sys._clear_type_cache() 1857db96d56Sopenharmony_ci 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_cidef warm_caches(): 1887db96d56Sopenharmony_ci # char cache 1897db96d56Sopenharmony_ci s = bytes(range(256)) 1907db96d56Sopenharmony_ci for i in range(256): 1917db96d56Sopenharmony_ci s[i:i+1] 1927db96d56Sopenharmony_ci # unicode cache 1937db96d56Sopenharmony_ci [chr(i) for i in range(256)] 1947db96d56Sopenharmony_ci # int cache 1957db96d56Sopenharmony_ci list(range(-5, 257)) 196