ci: Account for pre-existing children in process leak checks

pull/882/head
Alex Willmer 3 years ago
parent 552819e765
commit 8276b81b7d

@ -1,4 +1,5 @@
import errno
import logging
import os
import random
@ -11,6 +12,7 @@ import threading
import time
import traceback
import psutil
import unittest2
import mitogen.core
@ -67,7 +69,6 @@ def get_fd_count():
"""
Return the number of FDs open by this process.
"""
import psutil
return psutil.Process().num_fds()
@ -334,6 +335,10 @@ class TestCase(unittest2.TestCase):
# Broker() instantiations in setUp() etc.
mitogen.fork.on_fork()
cls._fd_count_before = get_fd_count()
# Ignore children started by external packages - in particular
# multiprocessing.resource_tracker.main()`, started when some Ansible
# versions instantiate a `multithreading.Lock()`.
cls._children_before = frozenset(psutil.Process().children())
super(TestCase, cls).setUpClass()
ALLOWED_THREADS = set([
@ -361,7 +366,10 @@ class TestCase(unittest2.TestCase):
def _teardown_check_fds(self):
mitogen.core.Latch._on_fork()
if get_fd_count() != self._fd_count_before:
import os; os.system('lsof +E -w -p %s | grep -vw mem' % (os.getpid(),))
if sys.platform == 'linux':
os.system('lsof +E -w -p %i | grep -vw mem' % (os.getpid(),))
else:
os.system('lsof -w -p %i | grep -vw mem' % (os.getpid(),))
assert 0, "%s leaked FDs. Count before: %s, after: %s" % (
self, self._fd_count_before, get_fd_count(),
)
@ -374,19 +382,33 @@ class TestCase(unittest2.TestCase):
if self.no_zombie_check:
return
# pid=0: Wait for any child process in the same process group as us.
# WNOHANG: Don't block if no processes ready to report status.
try:
pid, status = os.waitpid(0, os.WNOHANG)
except OSError:
return # ECHILD
except OSError as e:
# ECHILD: there are no child processes in our group.
if e.errno == errno.ECHILD:
return
raise
if pid:
assert 0, "%s failed to reap subprocess %d (status %d)." % (
self, pid, status
)
print('')
print('Children of unit test process:')
os.system('ps uww --ppid ' + str(os.getpid()))
children_after = frozenset(psutil.Process().children())
children_leaked = children_after.difference(self._children_before)
if not children_leaked:
return
print('Leaked children of unit test process:')
os.system('ps -o "user,pid,%%cpu,%%mem,vsz,rss,tty,stat,start,time,command" -ww -p %s'
% (','.join(str(p.pid) for p in children_leaked),))
if self._children_before:
print('Pre-existing children of unit test process:')
os.system('ps -o "user,pid,%%cpu,%%mem,vsz,rss,tty,stat,start,time,command" -ww -p %s'
% (','.join(str(p.pid) for p in self._children_before),))
assert 0, "%s leaked still-running subprocesses." % (self,)
def tearDown(self):

Loading…
Cancel
Save