[AIX][lit] Don't depend on psutil on AIX

Summary:
On AIX psutil can run into problems with permissions to read the process
tree, which causes problems for python timeout tests which need to kill off
a test and it's children.

This patch adds a workaround by invoking shell via subprocess and using a
platform specific option to ps to list all the descendant processes so we can
kill them. We add some checks so lit can tell whether timeout tests are
supported with out exposing whether we are utilizing the psutil
implementation or the alternative.

Reviewers: hubert.reinterpretcast, andusy, davide, delcypher

Reviewed By: delcypher

Subscribers: davide, delcypher, christof, lldb-commits, libcxx-commits, llvm-commits

Tags: #lldb, #libc, #llvm

Differential Revision: https://reviews.llvm.org/D64251

llvm-svn: 366912
This commit is contained in:
David Tenty 2019-07-24 15:04:27 +00:00
parent 4fdcabf259
commit b9179ff857
7 changed files with 98 additions and 59 deletions

View File

@ -253,24 +253,27 @@ def killProcessAndChildren(pid):
TODO: Reimplement this without using psutil so we can
remove our dependency on it.
"""
import psutil
try:
psutilProc = psutil.Process(pid)
# Handle the different psutil API versions
if platform.system() == 'AIX':
subprocess.call('kill -kill $(ps -o pid= -L{})'.format(pid), shell=True)
else:
import psutil
try:
# psutil >= 2.x
children_iterator = psutilProc.children(recursive=True)
except AttributeError:
# psutil 1.x
children_iterator = psutilProc.get_children(recursive=True)
for child in children_iterator:
psutilProc = psutil.Process(pid)
# Handle the different psutil API versions
try:
child.kill()
except psutil.NoSuchProcess:
pass
psutilProc.kill()
except psutil.NoSuchProcess:
pass
# psutil >= 2.x
children_iterator = psutilProc.children(recursive=True)
except AttributeError:
# psutil 1.x
children_iterator = psutilProc.get_children(recursive=True)
for child in children_iterator:
try:
child.kill()
except psutil.NoSuchProcess:
pass
psutilProc.kill()
except psutil.NoSuchProcess:
pass
def executeCommandVerbose(cmd, *args, **kwargs):

View File

@ -1,6 +1,7 @@
# -*- Python -*-
import os
import platform
import re
import shutil
import site
@ -75,13 +76,14 @@ for i in ['module-cache-clang', 'module-cache-lldb']:
shutil.rmtree(cachedir)
# Set a default per-test timeout of 10 minutes. Setting a timeout per test
# requires the psutil module and lit complains if the value is set but the
# module can't be found.
try:
import psutil # noqa: F401
# requires that killProcessAndChildren() is supported on the platform and
# lit complains if the value is set but it is not supported.
supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
if supported:
lit_config.maxIndividualTestTime = 600
except ImportError:
pass
else:
lit_config.warning("Could not set a default per-test timeout. " + errormsg)
# If running tests natively, check for CPU features needed for some tests.

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import
import inspect
import os
import platform
import sys
import lit.Test
@ -76,6 +77,19 @@ class LitConfig(object):
"""
return self._maxIndividualTestTime
@property
def maxIndividualTestTimeIsSupported(self):
"""
Returns a tuple (<supported> , <error message>)
where
`<supported>` is True if setting maxIndividualTestTime is supported
on the current host, returns False otherwise.
`<error message>` is an empty string if `<supported>` is True,
otherwise is contains a string describing why setting
maxIndividualTestTime is not supported.
"""
return lit.util.killProcessAndChildrenIsSupported()
@maxIndividualTestTime.setter
def maxIndividualTestTime(self, value):
"""
@ -86,16 +100,13 @@ class LitConfig(object):
self.fatal('maxIndividualTestTime must set to a value of type int.')
self._maxIndividualTestTime = value
if self.maxIndividualTestTime > 0:
# The current implementation needs psutil to set
# The current implementation needs psutil on some platforms to set
# a timeout per test. Check it's available.
# See lit.util.killProcessAndChildren()
try:
import psutil # noqa: F401
except ImportError:
self.fatal("Setting a timeout per test requires the"
" Python psutil module but it could not be"
" found. Try installing it via pip or via"
" your operating system's package manager.")
supported, errormsg = self.maxIndividualTestTimeIsSupported
if not supported:
self.fatal('Setting a timeout per test not supported. ' +
errormsg)
elif self.maxIndividualTestTime < 0:
self.fatal('The timeout per test must be >= 0 seconds')

View File

@ -423,34 +423,56 @@ def findPlatformSdkVersionOnMacOS(config, lit_config):
return out.decode()
return None
def killProcessAndChildrenIsSupported():
"""
Returns a tuple (<supported> , <error message>)
where
`<supported>` is True if `killProcessAndChildren()` is supported on
the current host, returns False otherwise.
`<error message>` is an empty string if `<supported>` is True,
otherwise is contains a string describing why the function is
not supported.
"""
if platform.system() == 'AIX':
return (True, "")
try:
import psutil # noqa: F401
return (True, "")
except ImportError:
return (False, "Requires the Python psutil module but it could"
" not be found. Try installing it via pip or via"
" your operating system's package manager.")
def killProcessAndChildren(pid):
"""This function kills a process with ``pid`` and all its running children
(recursively). It is currently implemented using the psutil module which
provides a simple platform neutral implementation.
(recursively). It is currently implemented using the psutil module on some
platforms which provides a simple platform neutral implementation.
TODO: Reimplement this without using psutil so we can remove
our dependency on it.
TODO: Reimplement this without using psutil on all platforms so we can
remove our dependency on it.
"""
import psutil
try:
psutilProc = psutil.Process(pid)
# Handle the different psutil API versions
if platform.system() == 'AIX':
subprocess.call('kill -kill $(ps -o pid= -L{})'.format(pid), shell=True)
else:
import psutil
try:
# psutil >= 2.x
children_iterator = psutilProc.children(recursive=True)
except AttributeError:
# psutil 1.x
children_iterator = psutilProc.get_children(recursive=True)
for child in children_iterator:
psutilProc = psutil.Process(pid)
# Handle the different psutil API versions
try:
child.kill()
except psutil.NoSuchProcess:
pass
psutilProc.kill()
except psutil.NoSuchProcess:
pass
# psutil >= 2.x
children_iterator = psutilProc.children(recursive=True)
except AttributeError:
# psutil 1.x
children_iterator = psutilProc.get_children(recursive=True)
for child in children_iterator:
try:
child.kill()
except psutil.NoSuchProcess:
pass
psutilProc.kill()
except psutil.NoSuchProcess:
pass
try:

View File

@ -1,4 +1,4 @@
# REQUIRES: python-psutil
# REQUIRES: lit-max-individual-test-time
# Check that the per test timeout is enforced when running GTest tests.
#

View File

@ -1,6 +1,7 @@
# -*- Python -*-
import os
import platform
import sys
import lit.formats
@ -56,10 +57,10 @@ if lit_config.params.get('check-coverage', None):
os.path.dirname(__file__), ".coveragerc")
# Add a feature to detect if psutil is available
try:
import psutil
lit_config.note('Found python psutil module')
config.available_features.add("python-psutil")
except ImportError:
lit_config.warning('Could not import psutil. Some tests will be skipped and'
' the --timeout command line argument will not work.')
supported, errormsg = lit_config.maxIndividualTestTimeIsSupported
if supported:
config.available_features.add("lit-max-individual-test-time")
else:
lit_config.warning('Setting a timeout per test not supported. ' + errormsg
+ ' Some tests will be skipped and the --timeout'
' command line argument will not work.')

View File

@ -1,4 +1,4 @@
# REQUIRES: python-psutil
# REQUIRES: lit-max-individual-test-time
# llvm.org/PR33944
# UNSUPPORTED: system-windows