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