diff options
-rw-r--r-- | docs/index.rst | 16 | ||||
-rw-r--r-- | psutil/__init__.py | 8 | ||||
-rw-r--r-- | psutil/_psbsd.py | 10 | ||||
-rw-r--r-- | psutil/_pslinux.py | 14 | ||||
-rw-r--r-- | psutil/_psosx.py | 5 | ||||
-rw-r--r-- | psutil/_psutil_bsd.c | 2 | ||||
-rw-r--r-- | psutil/_psutil_osx.c | 13 | ||||
-rw-r--r-- | psutil/_psutil_windows.c | 35 | ||||
-rw-r--r-- | psutil/_pswindows.py | 4 | ||||
-rw-r--r-- | psutil/arch/freebsd/specific.c | 35 | ||||
-rw-r--r-- | psutil/arch/freebsd/specific.h | 3 | ||||
-rw-r--r-- | psutil/tests/__init__.py | 5 | ||||
-rwxr-xr-x | psutil/tests/test_contracts.py | 3 | ||||
-rwxr-xr-x | psutil/tests/test_memory_leaks.py | 6 | ||||
-rwxr-xr-x | psutil/tests/test_process.py | 6 |
15 files changed, 156 insertions, 9 deletions
diff --git a/docs/index.rst b/docs/index.rst index d58e1c19..b35c89bf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1876,11 +1876,21 @@ Process class (OpenBSD) "laddr" and "raddr" fields for UNIX sockets are always set to "". This is a limitation of the OS. + .. note:: + (AIX) :class:`psutil.AccessDenied` is always raised unless running + as root (lsof does the same). + .. versionchanged:: 5.3.0 : "laddr" and "raddr" are named tuples. - .. note:: - (AIX) :class:`psutil.AccessDenied` is always raised unless running - as root (lsof does the same). + .. method:: is64bit() + + Return ``True`` if this is a 64-bit process, ``False`` if not, ``None`` if + undetermined. It can be assumed that ``False`` means 32-bit unless you have + a very old or exotic hardware. + + Availability: Linux, Windows, OSX, FreeBSD + + .. versionadded:: 5.4.0 .. method:: is_running() diff --git a/psutil/__init__.py b/psutil/__init__.py index 5e29a7fc..8f929846 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -844,6 +844,14 @@ class Process(object): """ return self._proc.num_handles() + if hasattr(_psplatform.Process, "is64bit"): + + def is64bit(self): + """Return True if this is a 64 bit process, False if not, + None if it cannot be determined. + """ + return self._proc.is64bit() + def num_ctx_switches(self): """Return the number of voluntary and involuntary context switches performed by this process. diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 0553401a..c2c926e2 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -871,3 +871,13 @@ class Process(object): @wrap_exceptions def memory_maps(self): return cext.proc_memory_maps(self.pid) + + @wrap_exceptions + def is64bit(self): + name = cext.proc_abi_vector(self.pid) + if name in ("FreeBSD ELF64", "Linux ELF64"): + return True + elif name in ("FreeBSD ELF32", "Linux ELF32"): + return False + else: + return None diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index b57adb34..06f1aa6b 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1971,3 +1971,17 @@ class Process(object): data = self._read_status_file() real, effective, saved = _gids_re.findall(data)[0] return _common.pgids(int(real), int(effective), int(saved)) + + @wrap_exceptions + def is64bit(self): + path = self.exe() + if not _common.path_exists_strict(path): + return None + with open_binary(path) as f: + header = f.read(5) + if header == b'\x7fELF\x02': + return True + elif header == b'\x7fELF\x01': + return False + else: + return None diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 308756a8..f149980f 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -74,6 +74,7 @@ kinfo_proc_map = dict( ctime=8, status=9, name=10, + is64bit=11, ) pidtaskinfo_map = dict( @@ -569,3 +570,7 @@ class Process(object): def memory_maps(self): with catch_zombie(self): return cext.proc_memory_maps(self.pid) + + @wrap_exceptions + def is64bit(self): + return self._get_kinfo_proc()[kinfo_proc_map['is64bit']] diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 9a2ed04b..09ae0c30 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -945,6 +945,8 @@ PsutilMethods[] = { "Set process CPU affinity."}, {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS, "Return an XML string to determine the number physical CPUs."}, + {"proc_abi_vector", psutil_proc_abi_vector, METH_VARARGS, + "Return process ABI vector name (to check process bitness)"}, #endif // --- system-related functions diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c index 55dd64ca..f43bb010 100644 --- a/psutil/_psutil_osx.c +++ b/psutil/_psutil_osx.c @@ -136,6 +136,7 @@ psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { struct kinfo_proc kp; PyObject *py_name; PyObject *py_retlist; + PyObject *py_is64bit; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; @@ -150,8 +151,14 @@ psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { py_name = Py_None; } + if (kp.kp_proc.p_flag & P_LP64) + py_is64bit = Py_True; + else + py_is64bit = Py_False; + Py_INCREF(py_is64bit); + py_retlist = Py_BuildValue( - "lllllllidiO", + "lllllllidiOO", (long)kp.kp_eproc.e_ppid, // (long) ppid (long)kp.kp_eproc.e_pcred.p_ruid, // (long) real uid (long)kp.kp_eproc.e_ucred.cr_uid, // (long) effective uid @@ -162,12 +169,14 @@ psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { kp.kp_eproc.e_tdev, // (int) tty nr PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime), // (double) create time (int)kp.kp_proc.p_stat, // (int) status - py_name // (pystr) name + py_name, // (str) name + py_is64bit // (bool) bitness ); if (py_retlist != NULL) { // XXX shall we decref() also in case of Py_BuildValue() error? Py_DECREF(py_name); + Py_DECREF(py_is64bit); } return py_retlist; } diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 81d1b4a0..21738e8c 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -3572,6 +3572,39 @@ psutil_sensors_battery(PyObject *self, PyObject *args) { } +/* + * Return True if process is 64 bit else False. + */ +static PyObject * +psutil_proc_is64bit(PyObject *self, PyObject *args) { + long pid; + SYSTEM_INFO sysinfo; + HANDLE hProcess; + BOOL isWow64; + + GetNativeSystemInfo(&sysinfo); + if (sysinfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64) + return Py_False; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + + hProcess = psutil_handle_from_pid(pid); + if (hProcess == NULL) + return NULL; + + if (! IsWow64Process(hProcess, &isWow64)) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + CloseHandle(hProcess); + if (isWow64) + return Py_False; + else + return Py_True; +} + + // ------------------------ Python init --------------------------- static PyMethodDef @@ -3633,6 +3666,8 @@ PsutilMethods[] = { "Return the number of handles opened by process."}, {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, "Return a list of process's memory mappings"}, + {"proc_is64bit", psutil_proc_is64bit, METH_VARARGS, + "Return True if process is 64 bit else False."}, // --- alternative pinfo interface {"proc_info", psutil_proc_info, METH_VARARGS, diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index b6c58c93..505846e7 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -991,3 +991,7 @@ class Process(object): ctx_switches = self.oneshot_info()[pinfo_map['ctx_switches']] # only voluntary ctx switches are supported return _common.pctxsw(ctx_switches, 0) + + @wrap_exceptions + def is64bit(self): + return cext.proc_is64bit(self.pid) diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c index 2c8944dd..52e2ae4a 100644 --- a/psutil/arch/freebsd/specific.c +++ b/psutil/arch/freebsd/specific.c @@ -1007,3 +1007,38 @@ error: PyErr_SetFromErrno(PyExc_OSError); return NULL; } + + +/* + * Return process ABI vector name (to check process bitness). + */ +PyObject * +psutil_proc_abi_vector(PyObject *self, PyObject *args) { + long pid; + size_t len; + int error; + int mib[4]; + char progt[32]; + size_t i; + len = sizeof(progt); + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_SV_NAME; + mib[3] = pid; + error = sysctl(mib, 4, progt, &len, NULL, 0); + + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (len == 0) { + PyErr_SetString(PyExc_RuntimeError, "size mismatch"); + return NULL; + } + + return Py_BuildValue("s", progt); +} diff --git a/psutil/arch/freebsd/specific.h b/psutil/arch/freebsd/specific.h index 0df66ecc..588abe8a 100644 --- a/psutil/arch/freebsd/specific.h +++ b/psutil/arch/freebsd/specific.h @@ -27,6 +27,5 @@ PyObject* psutil_proc_threads(PyObject* self, PyObject* args); PyObject* psutil_swap_mem(PyObject* self, PyObject* args); PyObject* psutil_virtual_mem(PyObject* self, PyObject* args); PyObject* psutil_cpu_stats(PyObject* self, PyObject* args); -#if defined(PSUTIL_FREEBSD) PyObject* psutil_sensors_battery(PyObject* self, PyObject* args); -#endif +PyObject* psutil_proc_abi_vector(PyObject* self, PyObject* args); diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 9e8d8596..0890b6f9 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -70,8 +70,8 @@ __all__ = [ 'VERBOSITY', "HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS", "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", - "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", - "HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO", + "HAS_SENSORS_BATTERY", "HAS_BATTERY""HAS_SENSORS_FANS", + "HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO", "HAS_IS64BIT", # subprocesses 'pyrun', 'reap_children', 'get_test_subprocess', 'create_zombie_proc', 'create_proc_children_pair', @@ -154,6 +154,7 @@ HAS_CONNECTIONS_UNIX = POSIX and not SUNOS HAS_ENVIRON = hasattr(psutil.Process, "environ") HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters") HAS_IONICE = hasattr(psutil.Process, "ionice") +HAS_IS64BIT = hasattr(psutil.Process, "is64bit") HAS_MEMORY_FULL_INFO = 'uss' in psutil.Process().memory_full_info()._fields HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps") HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num") diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 855b53bf..f9543e57 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -646,6 +646,9 @@ class TestFetchAllProcesses(unittest.TestCase): self.assertIsInstance(k, str) self.assertIsInstance(v, str) + def is64bit(self, ret, proc): + self.assertIn(ret, (True, False, None)) + if __name__ == '__main__': run_test_module_by_name(__file__) diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index 680fe780..bd23ff02 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -37,6 +37,7 @@ from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_CPU_FREQ from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE +from psutil.tests import HAS_IS64BIT from psutil.tests import HAS_MEMORY_MAPS from psutil.tests import HAS_PROC_CPU_NUM from psutil.tests import HAS_PROC_IO_COUNTERS @@ -383,6 +384,11 @@ class TestProcessObjectLeaks(TestMemLeak): def test_proc_info(self): self.execute(cext.proc_info, os.getpid()) + @skip_if_linux() + @unittest.skipIf(not HAS_IS64BIT, "not supported") + def test_is64bit(self): + self.execute(self.proc.is64bit) + class TestTerminatedProcessLeaks(TestProcessObjectLeaks): """Repeat the tests above looking for leaks occurring when dealing diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index a629cae5..3b60d38a 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -13,6 +13,7 @@ import itertools import os import signal import socket +import struct import subprocess import sys import tempfile @@ -45,6 +46,7 @@ from psutil.tests import get_winver from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE +from psutil.tests import HAS_IS64BIT from psutil.tests import HAS_MEMORY_MAPS from psutil.tests import HAS_PROC_CPU_NUM from psutil.tests import HAS_PROC_IO_COUNTERS @@ -677,6 +679,10 @@ class TestProcess(unittest.TestCase): assert 0 <= ret <= 100, ret assert 0 <= ret <= 100, ret + @unittest.skipIf(not HAS_IS64BIT, "not supported") + def test_is64bit(self): + self.assertEqual(psutil.Process().is64bit(), struct.calcsize("P") == 8) + def test_is_running(self): sproc = get_test_subprocess() p = psutil.Process(sproc.pid) |