Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OpenBSD, NetBSD] fix zombie process with no ctime #2289

Merged
merged 9 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ install-pip: ## Install pip (no-op if already installed).
setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them).
${MAKE} install-git-hooks
${MAKE} install-pip
$(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade --trusted-host files.pythonhosted.org pip
$(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade --trusted-host files.pythonhosted.org $(PY_DEPS)
$(PYTHON) -m pip install $(INSTALL_OPTS) --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade pip
$(PYTHON) -m pip install $(INSTALL_OPTS) --trusted-host files.pythonhosted.org --trusted-host pypi.org --upgrade $(PY_DEPS)

# ===================================================================
# Tests
Expand Down
16 changes: 15 additions & 1 deletion psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def __str__(self):
pass
if self._exitcode not in (_SENTINEL, None):
info["exitcode"] = self._exitcode
if self._create_time:
if self._create_time is not None:
info['started'] = _pprint_secs(self._create_time)
return "%s.%s(%s)" % (
self.__class__.__module__,
Expand All @@ -418,6 +418,20 @@ def __eq__(self, other):
# on PID and creation time.
if not isinstance(other, Process):
return NotImplemented
if OPENBSD or NETBSD: # pragma: no cover
# Zombie processes on Open/NetBSD have a creation time of
# 0.0. This covers the case when a process started normally
# (so it has a ctime), then it turned into a zombie. It's
# important to do this because is_running() depends on
# __eq__.
pid1, ctime1 = self._ident
pid2, ctime2 = other._ident
if pid1 == pid2:
if ctime1 and not ctime2:
try:
return self.status() == STATUS_ZOMBIE
except Error:
pass
return self._ident == other._ident

def __ne__(self, other):
Expand Down
9 changes: 8 additions & 1 deletion psutil/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
from psutil import AIX
from psutil import LINUX
from psutil import MACOS
from psutil import NETBSD
from psutil import OPENBSD
from psutil import POSIX
from psutil import SUNOS
from psutil import WINDOWS
Expand Down Expand Up @@ -959,7 +961,12 @@ def assertProcessGone(self, proc):

def assertProcessZombie(self, proc):
# A zombie process should always be instantiable.
psutil.Process(proc.pid)
clone = psutil.Process(proc.pid)
# Cloned zombie on Open/NetBSD has null creation time, see:
# https://github.com/giampaolo/psutil/issues/2287
self.assertEqual(proc, clone)
if not (OPENBSD or NETBSD):
self.assertEqual(hash(proc), hash(clone))
# Its status always be querable.
self.assertEqual(proc.status(), psutil.STATUS_ZOMBIE)
# It should be considered 'running'.
Expand Down
5 changes: 5 additions & 0 deletions psutil/tests/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ def test_process_iter(self):
p.wait()
self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()])

# assert there are no duplicates
ls = [x for x in psutil.process_iter()]
self.assertEqual(sorted(ls, key=lambda x: x.pid),
sorted(set(ls), key=lambda x: x.pid))

with mock.patch('psutil.Process',
side_effect=psutil.NoSuchProcess(os.getpid())):
self.assertEqual(list(psutil.process_iter()), [])
Expand Down