357 proc cpu num #954

Merged
merged 14 commits into from Jan 24, 2017
View
@@ -850,39 +850,41 @@ Process class
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
| Linux | Windows | OSX | BSD | SunOS |
+==============================+===============================+==============================+==============================+==========================+
- | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`name` |
+ | :meth:`cpu_num` | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`cpu_num` | :meth:`name` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`cmdline` |
+ | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_percent` | :meth:`cmdline` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`create_time` | :meth:`io_counters()` | :meth:`memory_info` | :meth:`create_time` | :meth:`create_time` |
+ | :meth:`~Process.cpu_times` | :meth:`io_counters()` | :meth:`memory_info` | :meth:`~Process.cpu_times` | :meth:`create_time` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`name` | :meth:`ionice` | :meth:`memory_percent` | :meth:`gids` | |
+ | :meth:`create_time` | :meth:`ionice` | :meth:`memory_percent` | :meth:`create_time` | |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`ppid` | :meth:`memory_info` | :meth:`num_ctx_switches` | :meth:`io_counters` | :meth:`memory_info` |
+ | :meth:`name` | :meth:`memory_info` | :meth:`num_ctx_switches` | :meth:`gids` | :meth:`memory_info` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`status` | :meth:`nice` | :meth:`num_threads` | :meth:`name` | :meth:`memory_percent` |
+ | :meth:`ppid` | :meth:`nice` | :meth:`num_threads` | :meth:`io_counters` | :meth:`memory_percent` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`terminal` | :meth:`memory_maps` | | :meth:`memory_info` | :meth:`nice` |
+ | :meth:`status` | :meth:`memory_maps` | | :meth:`name` | :meth:`nice` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | | :meth:`num_ctx_switches` | :meth:`create_time` | :meth:`memory_percent` | :meth:`num_threads` |
+ | :meth:`terminal` | :meth:`num_ctx_switches` | :meth:`create_time` | :meth:`memory_info` | :meth:`num_threads` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`gids` | :meth:`num_handles` | :meth:`gids` | :meth:`num_ctx_switches` | :meth:`ppid` |
+ | | :meth:`num_handles` | :meth:`gids` | :meth:`memory_percent` | :meth:`ppid` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`num_ctx_switches` | :meth:`num_threads` | :meth:`name` | :meth:`ppid` | :meth:`status` |
+ | :meth:`gids` | :meth:`num_threads` | :meth:`name` | :meth:`num_ctx_switches` | :meth:`status` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`num_threads` | :meth:`username` | :meth:`ppid` | :meth:`status` | :meth:`terminal` |
+ | :meth:`num_ctx_switches` | :meth:`username` | :meth:`ppid` | :meth:`ppid` | :meth:`terminal` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`uids` | | :meth:`status` | :meth:`terminal` | |
+ | :meth:`num_threads` | | :meth:`status` | :meth:`status` | |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`username` | | :meth:`terminal` | :meth:`uids` | :meth:`gids` |
+ | :meth:`uids` | | :meth:`terminal` | :meth:`terminal` | :meth:`gids` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | | | :meth:`uids` | :meth:`username` | :meth:`uids` |
+ | :meth:`username` | | :meth:`uids` | :meth:`uids` | :meth:`uids` |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`memory_full_info` | | :meth:`username` | | :meth:`username` |
+ | | | :meth:`username` | :meth:`username` | :meth:`username` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`memory_full_info` | | | | |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
| :meth:`memory_maps` | | | | |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | *speedup: +2.5x* | *speedup: +1.8x / +6.5x* | *speedup: +1.9x* | *speedup: +2.0x* | *speedup: +1.3x* |
+ | *speedup: +2.6x* | *speedup: +1.8x / +6.5x* | *speedup: +1.9x* | *speedup: +2.0x* | *speedup: +1.3x* |
+------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
.. versionadded:: 5.0.0
@@ -1239,6 +1241,18 @@ Process class
.. versionchanged:: 2.2.0 added support for FreeBSD
+ .. method:: cpu_num()
+
+ Return what CPU this process is currently running on.
+ The returned number should be ``<=`` :func:`psutil.cpu_count()`.
+ It may be used in conjunction with ``psutil.cpu_percent(percpu=True)`` to
+ observe the system workload distributed across multiple CPUs as shown by
+ `cpu_workload.py <https://github.com/giampaolo/psutil/blob/master/scripts/cpu_workload.py>`__ example script.
+
+ Availability: Linux, FreeBSD, SunOS
+
+ .. versionadded:: 5.1.0
+
.. method:: memory_info()
Return a namedtuple with variable fields depending on the platform
View
@@ -837,6 +837,19 @@ def cpu_affinity(self, cpus=None):
else:
self._proc.cpu_affinity_set(list(set(cpus)))
+ # Linux, FreeBSD, SunOS
+ if hasattr(_psplatform.Process, "cpu_num"):
+
+ def cpu_num(self):
+ """Return what CPU this process is currently running on.
+ The returned number should be <= psutil.cpu_count()
+ and <= len(psutil.cpu_percent(percpu=True)).
+ It may be used in conjunction with
+ psutil.cpu_percent(percpu=True) to observe the system
+ workload distributed across CPUs.
+ """
+ return self._proc.cpu_num()
+
# Linux, OSX and Windows only
if hasattr(_psplatform.Process, "environ"):
View
@@ -122,7 +122,8 @@
memtext=20,
memdata=21,
memstack=22,
- name=23,
+ cpunum=23,
+ name=24,
)
@@ -609,6 +610,11 @@ def cpu_times(self):
rawtuple[kinfo_proc_map['ch_user_time']],
rawtuple[kinfo_proc_map['ch_sys_time']])
+ if FREEBSD:
+ @wrap_exceptions
+ def cpu_num(self):
+ return self.oneshot()[kinfo_proc_map['cpunum']]
+
@wrap_exceptions
def memory_info(self):
rawtuple = self.oneshot()
View
@@ -1298,6 +1298,11 @@ def cpu_times(self):
children_stime = float(values[15]) / CLOCK_TICKS
return _common.pcputimes(utime, stime, children_utime, children_stime)
+ @wrap_exceptions
+ def cpu_num(self):
+ """What CPU the process is on."""
+ return int(self._parse_stat_file()[37])
+
@wrap_exceptions
def wait(self, timeout=None):
try:
View
@@ -468,6 +468,10 @@ def cpu_times(self):
raise
return _common.pcputimes(*times)
+ @wrap_exceptions
+ def cpu_num(self):
+ return cext.proc_cpu_num(self.pid, self._procfs_path)
+
@wrap_exceptions
def terminal(self):
procfs_path = self._procfs_path
View
@@ -198,6 +198,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
long memtext;
long memdata;
long memstack;
+ unsigned char oncpu;
kinfo_proc kp;
long pagesize = sysconf(_SC_PAGESIZE);
char str[1000];
@@ -252,9 +253,25 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
memstack = (long)kp.p_vm_ssize * pagesize;
#endif
+#ifdef PSUTIL_FREEBSD
+ // what CPU we're on; top was used as an example:
+ // https://svnweb.freebsd.org/base/head/usr.bin/top/machine.c?
+ // view=markup&pathrev=273835
+ if (kp.ki_stat == SRUN && kp.ki_oncpu != NOCPU)
+ oncpu = kp.ki_oncpu;
+ else
+ oncpu = kp.ki_lastcpu;
+#else
+ // On Net/OpenBSD we have kp.p_cpuid but it appears it's always
+ // set to KI_NOCPU. Even if it's not, ki_lastcpu does not exist
+ // so there's no way to determine where "sleeping" processes
+ // were. Not supported.
+ oncpu = -1;
+#endif
+
// Return a single big tuple with all process info.
py_retlist = Py_BuildValue(
- "(lillllllidllllddddlllllO)",
+ "(lillllllidllllddddlllllbO)",
#ifdef PSUTIL_FREEBSD
//
(long)kp.ki_ppid, // (long) ppid
@@ -287,6 +304,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
memtext, // (long) mem text
memdata, // (long) mem data
memstack, // (long) mem stack
+ // others
+ oncpu, // (unsigned char) the CPU we are on
#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
//
(long)kp.p_ppid, // (long) ppid
@@ -321,6 +340,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
memtext, // (long) mem text
memdata, // (long) mem data
memstack, // (long) mem stack
+ // others
+ oncpu, // (unsigned char) the CPU we are on
#endif
py_name // (pystr) name
);
View
@@ -168,6 +168,86 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) {
}
+/*
+ * Return what CPU the process is running on.
+ */
+static PyObject *
+psutil_proc_cpu_num(PyObject *self, PyObject *args) {
+ int fd = NULL;
+ int pid;
+ char path[1000];
+ struct prheader header;
+ struct lwpsinfo *lwp;
+ char *lpsinfo = NULL;
+ char *ptr = NULL;
+ int nent;
+ int size;
+ int proc_num;
+ size_t nbytes;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+
+ sprintf(path, "%s/%i/lpsinfo", procfs_path, pid);
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+ return NULL;
+ }
+
+ // read header
+ nbytes = pread(fd, &header, sizeof(header), 0);
+ if (nbytes == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ if (nbytes != sizeof(header)) {
+ PyErr_SetString(
+ PyExc_RuntimeError, "read() file structure size mismatch");
+ goto error;
+ }
+
+ // malloc
+ nent = header.pr_nent;
+ size = header.pr_entsize * nent;
+ ptr = lpsinfo = malloc(size);
+ if (lpsinfo == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ // read the rest
+ nbytes = pread(fd, lpsinfo, size, sizeof(header));
+ if (nbytes == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ if (nbytes != size) {
+ PyErr_SetString(
+ PyExc_RuntimeError, "read() file structure size mismatch");
+ goto error;
+ }
+
+ // done
+ lwp = (lwpsinfo_t *)ptr;
+ proc_num = lwp->pr_onpro;
+ close(fd);
+ free(ptr);
+ free(lpsinfo);
+ return Py_BuildValue("i", proc_num);
+
+error:
+ if (fd != NULL)
+ close(fd);
+ if (ptr != NULL)
+ free(ptr);
+ if (lpsinfo != NULL)
+ free(lpsinfo);
+ return NULL;
+}
+
+
/*
* Return process uids/gids as a Python tuple.
*/
@@ -1340,6 +1420,8 @@ PsutilMethods[] = {
"Return process memory mappings"},
{"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
"Return the number of context switches performed by process"},
+ {"proc_cpu_num", psutil_proc_cpu_num, METH_VARARGS,
+ "Return what CPU the process is on"},
// --- system-related functions
{"swap_mem", psutil_swap_mem, METH_VARARGS,
@@ -266,6 +266,12 @@ def test_threads(self):
def test_cpu_times(self):
self.execute(self.proc.cpu_times)
+ @skip_if_linux()
+ @unittest.skipUnless(hasattr(psutil.Process, "cpu_num"),
+ "platform not supported")
+ def test_cpu_num(self):
+ self.execute(self.proc.cpu_num)
+
@skip_if_linux()
def test_memory_info(self):
self.execute(self.proc.memory_info)
@@ -281,6 +281,16 @@ def test_cpu_times_2(self):
if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1:
self.fail("expected: %s, found: %s" % (ktime, kernel_time))
+ @unittest.skipUnless(hasattr(psutil.Process, "cpu_num"),
+ "platform not supported")
+ def test_cpu_num(self):
+ p = psutil.Process()
+ num = p.cpu_num()
+ self.assertGreaterEqual(num, 0)
+ if psutil.cpu_count() == 1:
+ self.assertEqual(num, 0)
+ self.assertIn(p.cpu_num(), range(psutil.cpu_count()))
+
def test_create_time(self):
sproc = get_test_subprocess()
now = time.time()
@@ -853,6 +863,10 @@ def test_cpu_affinity(self):
if hasattr(os, "sched_getaffinity"):
self.assertEqual(p.cpu_affinity(),
list(os.sched_getaffinity(p.pid)))
+ # also test num_cpu()
+ if hasattr(p, "num_cpu"):
+ self.assertEqual(p.cpu_affinity()[0], p.num_cpu())
+
#
p.cpu_affinity(all_cpus)
self.assertEqual(p.cpu_affinity(), all_cpus)
@@ -1723,6 +1737,12 @@ def cpu_times(self, ret, proc):
self.assertTrue(ret.user >= 0)
self.assertTrue(ret.system >= 0)
+ def cpu_num(self, ret, proc):
+ self.assertGreaterEqual(ret, 0)
+ if psutil.cpu_count() == 1:
+ self.assertEqual(ret, 0)
+ self.assertIn(ret, range(psutil.cpu_count()))
+
def memory_info(self, ret, proc):
for name in ret._fields:
self.assertGreaterEqual(getattr(ret, name), 0)
@@ -1793,6 +1813,9 @@ def is_running(self, ret, proc):
def cpu_affinity(self, ret, proc):
assert ret != [], ret
+ cpus = range(psutil.cpu_count())
+ for n in ret:
+ self.assertIn(n, cpus)
def terminal(self, ret, proc):
if ret is not None:
Oops, something went wrong.