Skip to content

Commit 9c14061

Browse files
authored
bpo-36560: Fix reference leak hunting in regrtest (GH-12744) (GH-12745)
Fix reference leak hunting in regrtest: compute also deltas (of reference count and file descriptor count) during warmup, to ensure that everything is initialized before starting to hunt reference leaks. Other changes: * Replace gc.collect() with support.gc_collect() in clear_caches() * dash_R() is now more quiet with --quiet option (don't display progress). * Precompute the full range for "for it in range(repcount):" to ensure that the iteration doesn't allocate anything new. * dash_R() now is responsible to call warm_caches(). (cherry picked from commit 5aaac94)
1 parent 4e8e8aa commit 9c14061

File tree

2 files changed

+41
-18
lines changed

2 files changed

+41
-18
lines changed

Lib/test/regrtest.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,6 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
529529

530530
if slaveargs is not None:
531531
args, kwargs = json.loads(slaveargs)
532-
if kwargs['huntrleaks']:
533-
warm_caches()
534532
if testdir:
535533
kwargs['testdir'] = testdir
536534
try:
@@ -541,9 +539,6 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
541539
print json.dumps(result)
542540
sys.exit(0)
543541

544-
if huntrleaks:
545-
warm_caches()
546-
547542
good = []
548543
bad = []
549544
skipped = []
@@ -1332,7 +1327,7 @@ def runtest_inner(test, verbose, quiet, huntrleaks=False, pgo=False, testdir=Non
13321327
indirect_test = getattr(the_module, "test_main", None)
13331328
if huntrleaks:
13341329
refleak = dash_R(the_module, test, indirect_test,
1335-
huntrleaks)
1330+
huntrleaks, quiet)
13361331
else:
13371332
if indirect_test is not None:
13381333
indirect_test()
@@ -1425,7 +1420,7 @@ def cleanup_test_droppings(testname, verbose):
14251420
print >> sys.stderr, ("%r left behind %s %r and it couldn't be "
14261421
"removed: %s" % (testname, kind, name, msg))
14271422

1428-
def dash_R(the_module, test, indirect_test, huntrleaks):
1423+
def dash_R(the_module, test, indirect_test, huntrleaks, quiet):
14291424
"""Run a test multiple times, looking for reference leaks.
14301425
14311426
Returns:
@@ -1438,6 +1433,10 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
14381433
raise Exception("Tracking reference leaks requires a debug build "
14391434
"of Python")
14401435

1436+
# Avoid false positives due to various caches
1437+
# filling slowly with random data:
1438+
warm_caches()
1439+
14411440
# Save current values for dash_R_cleanup() to restore.
14421441
fs = warnings.filters[:]
14431442
ps = copy_reg.dispatch_table.copy()
@@ -1457,6 +1456,14 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
14571456
for obj in abc.__subclasses__() + [abc]:
14581457
abcs[obj] = obj._abc_registry.copy()
14591458

1459+
# bpo-31217: Integer pool to get a single integer object for the same
1460+
# value. The pool is used to prevent false alarm when checking for memory
1461+
# block leaks. Fill the pool with values in -1000..1000 which are the most
1462+
# common (reference, memory block, file descriptor) differences.
1463+
int_pool = {value: value for value in range(-1000, 1000)}
1464+
def get_pooled_int(value):
1465+
return int_pool.setdefault(value, value)
1466+
14601467
if indirect_test:
14611468
def run_the_test():
14621469
indirect_test()
@@ -1467,27 +1474,39 @@ def run_the_test():
14671474
deltas = []
14681475
nwarmup, ntracked, fname = huntrleaks
14691476
fname = os.path.join(support.SAVEDCWD, fname)
1477+
1478+
# Pre-allocate to ensure that the loop doesn't allocate anything new
14701479
repcount = nwarmup + ntracked
1471-
rc_deltas = [0] * ntracked
1472-
fd_deltas = [0] * ntracked
1480+
rc_deltas = [0] * repcount
1481+
fd_deltas = [0] * repcount
1482+
rep_range = list(range(repcount))
1483+
1484+
if not quiet:
1485+
print >> sys.stderr, "beginning", repcount, "repetitions"
1486+
print >> sys.stderr, ("1234567890"*(repcount//10 + 1))[:repcount]
14731487

1474-
print >> sys.stderr, "beginning", repcount, "repetitions"
1475-
print >> sys.stderr, ("1234567890"*(repcount//10 + 1))[:repcount]
14761488
dash_R_cleanup(fs, ps, pic, zdc, abcs)
1489+
14771490
# initialize variables to make pyflakes quiet
14781491
rc_before = fd_before = 0
1479-
for i in range(repcount):
1492+
1493+
for i in rep_range:
14801494
run_the_test()
1481-
sys.stderr.write('.')
1495+
1496+
if not quiet:
1497+
sys.stderr.write('.')
1498+
14821499
dash_R_cleanup(fs, ps, pic, zdc, abcs)
1500+
14831501
rc_after = sys.gettotalrefcount()
14841502
fd_after = support.fd_count()
1485-
if i >= nwarmup:
1486-
rc_deltas[i - nwarmup] = rc_after - rc_before
1487-
fd_deltas[i - nwarmup] = fd_after - fd_before
1503+
rc_deltas[i] = get_pooled_int(rc_after - rc_before)
1504+
fd_deltas[i] = get_pooled_int(fd_after - fd_before)
14881505
rc_before = rc_after
14891506
fd_before = fd_after
1490-
print >> sys.stderr
1507+
1508+
if not quiet:
1509+
print >> sys.stderr
14911510

14921511
# These checkers return False on success, True on failure
14931512
def check_rc_deltas(deltas):
@@ -1513,6 +1532,7 @@ def check_fd_deltas(deltas):
15131532
(rc_deltas, 'references', check_rc_deltas),
15141533
(fd_deltas, 'file descriptors', check_fd_deltas)
15151534
]:
1535+
deltas = deltas[nwarmup:]
15161536
if checker(deltas):
15171537
msg = '%s leaked %s %s, sum=%s' % (test, deltas, item_name, sum(deltas))
15181538
print >> sys.stderr, msg
@@ -1647,7 +1667,7 @@ def clear_caches():
16471667
ctypes._reset_cache()
16481668

16491669
# Collect cyclic trash.
1650-
gc.collect()
1670+
support.gc_collect()
16511671

16521672
def warm_caches():
16531673
"""Create explicitly internal singletons which are created on demand
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix reference leak hunting in regrtest: compute also deltas (of reference count
2+
and file descriptor count) during warmup, to ensure that everything is
3+
initialized before starting to hunt reference leaks.

0 commit comments

Comments
 (0)