comparison planemo/lib/python3.7/site-packages/psutil/_psbsd.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """FreeBSD, OpenBSD and NetBSD platforms implementation."""
6
7 import contextlib
8 import errno
9 import functools
10 import os
11 import xml.etree.ElementTree as ET
12 from collections import namedtuple
13 from collections import defaultdict
14
15 from . import _common
16 from . import _psposix
17 from . import _psutil_bsd as cext
18 from . import _psutil_posix as cext_posix
19 from ._common import AccessDenied
20 from ._common import conn_tmap
21 from ._common import conn_to_ntuple
22 from ._common import FREEBSD
23 from ._common import memoize
24 from ._common import memoize_when_activated
25 from ._common import NETBSD
26 from ._common import NoSuchProcess
27 from ._common import OPENBSD
28 from ._common import usage_percent
29 from ._common import ZombieProcess
30 from ._compat import FileNotFoundError
31 from ._compat import PermissionError
32 from ._compat import ProcessLookupError
33 from ._compat import which
34
35
36 __extra__all__ = []
37
38
39 # =====================================================================
40 # --- globals
41 # =====================================================================
42
43
44 if FREEBSD:
45 PROC_STATUSES = {
46 cext.SIDL: _common.STATUS_IDLE,
47 cext.SRUN: _common.STATUS_RUNNING,
48 cext.SSLEEP: _common.STATUS_SLEEPING,
49 cext.SSTOP: _common.STATUS_STOPPED,
50 cext.SZOMB: _common.STATUS_ZOMBIE,
51 cext.SWAIT: _common.STATUS_WAITING,
52 cext.SLOCK: _common.STATUS_LOCKED,
53 }
54 elif OPENBSD or NETBSD:
55 PROC_STATUSES = {
56 cext.SIDL: _common.STATUS_IDLE,
57 cext.SSLEEP: _common.STATUS_SLEEPING,
58 cext.SSTOP: _common.STATUS_STOPPED,
59 # According to /usr/include/sys/proc.h SZOMB is unused.
60 # test_zombie_process() shows that SDEAD is the right
61 # equivalent. Also it appears there's no equivalent of
62 # psutil.STATUS_DEAD. SDEAD really means STATUS_ZOMBIE.
63 # cext.SZOMB: _common.STATUS_ZOMBIE,
64 cext.SDEAD: _common.STATUS_ZOMBIE,
65 cext.SZOMB: _common.STATUS_ZOMBIE,
66 # From http://www.eecs.harvard.edu/~margo/cs161/videos/proc.h.txt
67 # OpenBSD has SRUN and SONPROC: SRUN indicates that a process
68 # is runnable but *not* yet running, i.e. is on a run queue.
69 # SONPROC indicates that the process is actually executing on
70 # a CPU, i.e. it is no longer on a run queue.
71 # As such we'll map SRUN to STATUS_WAKING and SONPROC to
72 # STATUS_RUNNING
73 cext.SRUN: _common.STATUS_WAKING,
74 cext.SONPROC: _common.STATUS_RUNNING,
75 }
76 elif NETBSD:
77 PROC_STATUSES = {
78 cext.SIDL: _common.STATUS_IDLE,
79 cext.SACTIVE: _common.STATUS_RUNNING,
80 cext.SDYING: _common.STATUS_ZOMBIE,
81 cext.SSTOP: _common.STATUS_STOPPED,
82 cext.SZOMB: _common.STATUS_ZOMBIE,
83 cext.SDEAD: _common.STATUS_DEAD,
84 cext.SSUSPENDED: _common.STATUS_SUSPENDED, # unique to NetBSD
85 }
86
87 TCP_STATUSES = {
88 cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
89 cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
90 cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
91 cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
92 cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
93 cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
94 cext.TCPS_CLOSED: _common.CONN_CLOSE,
95 cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
96 cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
97 cext.TCPS_LISTEN: _common.CONN_LISTEN,
98 cext.TCPS_CLOSING: _common.CONN_CLOSING,
99 cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
100 }
101
102 if NETBSD:
103 PAGESIZE = os.sysconf("SC_PAGESIZE")
104 else:
105 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
106 AF_LINK = cext_posix.AF_LINK
107
108 HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
109 HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads")
110 HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files')
111 HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds')
112
113 kinfo_proc_map = dict(
114 ppid=0,
115 status=1,
116 real_uid=2,
117 effective_uid=3,
118 saved_uid=4,
119 real_gid=5,
120 effective_gid=6,
121 saved_gid=7,
122 ttynr=8,
123 create_time=9,
124 ctx_switches_vol=10,
125 ctx_switches_unvol=11,
126 read_io_count=12,
127 write_io_count=13,
128 user_time=14,
129 sys_time=15,
130 ch_user_time=16,
131 ch_sys_time=17,
132 rss=18,
133 vms=19,
134 memtext=20,
135 memdata=21,
136 memstack=22,
137 cpunum=23,
138 name=24,
139 )
140
141
142 # =====================================================================
143 # --- named tuples
144 # =====================================================================
145
146
147 # psutil.virtual_memory()
148 svmem = namedtuple(
149 'svmem', ['total', 'available', 'percent', 'used', 'free',
150 'active', 'inactive', 'buffers', 'cached', 'shared', 'wired'])
151 # psutil.cpu_times()
152 scputimes = namedtuple(
153 'scputimes', ['user', 'nice', 'system', 'idle', 'irq'])
154 # psutil.Process.memory_info()
155 pmem = namedtuple('pmem', ['rss', 'vms', 'text', 'data', 'stack'])
156 # psutil.Process.memory_full_info()
157 pfullmem = pmem
158 # psutil.Process.cpu_times()
159 pcputimes = namedtuple('pcputimes',
160 ['user', 'system', 'children_user', 'children_system'])
161 # psutil.Process.memory_maps(grouped=True)
162 pmmap_grouped = namedtuple(
163 'pmmap_grouped', 'path rss, private, ref_count, shadow_count')
164 # psutil.Process.memory_maps(grouped=False)
165 pmmap_ext = namedtuple(
166 'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count')
167 # psutil.disk_io_counters()
168 if FREEBSD:
169 sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
170 'read_bytes', 'write_bytes',
171 'read_time', 'write_time',
172 'busy_time'])
173 else:
174 sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
175 'read_bytes', 'write_bytes'])
176
177
178 # =====================================================================
179 # --- memory
180 # =====================================================================
181
182
183 def virtual_memory():
184 """System virtual memory as a namedtuple."""
185 mem = cext.virtual_mem()
186 total, free, active, inactive, wired, cached, buffers, shared = mem
187 if NETBSD:
188 # On NetBSD buffers and shared mem is determined via /proc.
189 # The C ext set them to 0.
190 with open('/proc/meminfo', 'rb') as f:
191 for line in f:
192 if line.startswith(b'Buffers:'):
193 buffers = int(line.split()[1]) * 1024
194 elif line.startswith(b'MemShared:'):
195 shared = int(line.split()[1]) * 1024
196 avail = inactive + cached + free
197 used = active + wired + cached
198 percent = usage_percent((total - avail), total, round_=1)
199 return svmem(total, avail, percent, used, free,
200 active, inactive, buffers, cached, shared, wired)
201
202
203 def swap_memory():
204 """System swap memory as (total, used, free, sin, sout) namedtuple."""
205 total, used, free, sin, sout = cext.swap_mem()
206 percent = usage_percent(used, total, round_=1)
207 return _common.sswap(total, used, free, percent, sin, sout)
208
209
210 # =====================================================================
211 # --- CPU
212 # =====================================================================
213
214
215 def cpu_times():
216 """Return system per-CPU times as a namedtuple"""
217 user, nice, system, idle, irq = cext.cpu_times()
218 return scputimes(user, nice, system, idle, irq)
219
220
221 if HAS_PER_CPU_TIMES:
222 def per_cpu_times():
223 """Return system CPU times as a namedtuple"""
224 ret = []
225 for cpu_t in cext.per_cpu_times():
226 user, nice, system, idle, irq = cpu_t
227 item = scputimes(user, nice, system, idle, irq)
228 ret.append(item)
229 return ret
230 else:
231 # XXX
232 # Ok, this is very dirty.
233 # On FreeBSD < 8 we cannot gather per-cpu information, see:
234 # https://github.com/giampaolo/psutil/issues/226
235 # If num cpus > 1, on first call we return single cpu times to avoid a
236 # crash at psutil import time.
237 # Next calls will fail with NotImplementedError
238 def per_cpu_times():
239 """Return system CPU times as a namedtuple"""
240 if cpu_count_logical() == 1:
241 return [cpu_times()]
242 if per_cpu_times.__called__:
243 raise NotImplementedError("supported only starting from FreeBSD 8")
244 per_cpu_times.__called__ = True
245 return [cpu_times()]
246
247 per_cpu_times.__called__ = False
248
249
250 def cpu_count_logical():
251 """Return the number of logical CPUs in the system."""
252 return cext.cpu_count_logical()
253
254
255 if OPENBSD or NETBSD:
256 def cpu_count_physical():
257 # OpenBSD and NetBSD do not implement this.
258 return 1 if cpu_count_logical() == 1 else None
259 else:
260 def cpu_count_physical():
261 """Return the number of physical CPUs in the system."""
262 # From the C module we'll get an XML string similar to this:
263 # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
264 # We may get None in case "sysctl kern.sched.topology_spec"
265 # is not supported on this BSD version, in which case we'll mimic
266 # os.cpu_count() and return None.
267 ret = None
268 s = cext.cpu_count_phys()
269 if s is not None:
270 # get rid of padding chars appended at the end of the string
271 index = s.rfind("</groups>")
272 if index != -1:
273 s = s[:index + 9]
274 root = ET.fromstring(s)
275 try:
276 ret = len(root.findall('group/children/group/cpu')) or None
277 finally:
278 # needed otherwise it will memleak
279 root.clear()
280 if not ret:
281 # If logical CPUs are 1 it's obvious we'll have only 1
282 # physical CPU.
283 if cpu_count_logical() == 1:
284 return 1
285 return ret
286
287
288 def cpu_stats():
289 """Return various CPU stats as a named tuple."""
290 if FREEBSD:
291 # Note: the C ext is returning some metrics we are not exposing:
292 # traps.
293 ctxsw, intrs, soft_intrs, syscalls, traps = cext.cpu_stats()
294 elif NETBSD:
295 # XXX
296 # Note about intrs: the C extension returns 0. intrs
297 # can be determined via /proc/stat; it has the same value as
298 # soft_intrs thought so the kernel is faking it (?).
299 #
300 # Note about syscalls: the C extension always sets it to 0 (?).
301 #
302 # Note: the C ext is returning some metrics we are not exposing:
303 # traps, faults and forks.
304 ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
305 cext.cpu_stats()
306 with open('/proc/stat', 'rb') as f:
307 for line in f:
308 if line.startswith(b'intr'):
309 intrs = int(line.split()[1])
310 elif OPENBSD:
311 # Note: the C ext is returning some metrics we are not exposing:
312 # traps, faults and forks.
313 ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
314 cext.cpu_stats()
315 return _common.scpustats(ctxsw, intrs, soft_intrs, syscalls)
316
317
318 # =====================================================================
319 # --- disks
320 # =====================================================================
321
322
323 def disk_partitions(all=False):
324 """Return mounted disk partitions as a list of namedtuples.
325 'all' argument is ignored, see:
326 https://github.com/giampaolo/psutil/issues/906
327 """
328 retlist = []
329 partitions = cext.disk_partitions()
330 for partition in partitions:
331 device, mountpoint, fstype, opts = partition
332 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
333 retlist.append(ntuple)
334 return retlist
335
336
337 disk_usage = _psposix.disk_usage
338 disk_io_counters = cext.disk_io_counters
339
340
341 # =====================================================================
342 # --- network
343 # =====================================================================
344
345
346 net_io_counters = cext.net_io_counters
347 net_if_addrs = cext_posix.net_if_addrs
348
349
350 def net_if_stats():
351 """Get NIC stats (isup, duplex, speed, mtu)."""
352 names = net_io_counters().keys()
353 ret = {}
354 for name in names:
355 try:
356 mtu = cext_posix.net_if_mtu(name)
357 isup = cext_posix.net_if_flags(name)
358 duplex, speed = cext_posix.net_if_duplex_speed(name)
359 except OSError as err:
360 # https://github.com/giampaolo/psutil/issues/1279
361 if err.errno != errno.ENODEV:
362 raise
363 else:
364 if hasattr(_common, 'NicDuplex'):
365 duplex = _common.NicDuplex(duplex)
366 ret[name] = _common.snicstats(isup, duplex, speed, mtu)
367 return ret
368
369
370 def net_connections(kind):
371 """System-wide network connections."""
372 if OPENBSD:
373 ret = []
374 for pid in pids():
375 try:
376 cons = Process(pid).connections(kind)
377 except (NoSuchProcess, ZombieProcess):
378 continue
379 else:
380 for conn in cons:
381 conn = list(conn)
382 conn.append(pid)
383 ret.append(_common.sconn(*conn))
384 return ret
385
386 if kind not in _common.conn_tmap:
387 raise ValueError("invalid %r kind argument; choose between %s"
388 % (kind, ', '.join([repr(x) for x in conn_tmap])))
389 families, types = conn_tmap[kind]
390 ret = set()
391 if NETBSD:
392 rawlist = cext.net_connections(-1)
393 else:
394 rawlist = cext.net_connections()
395 for item in rawlist:
396 fd, fam, type, laddr, raddr, status, pid = item
397 # TODO: apply filter at C level
398 if fam in families and type in types:
399 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
400 TCP_STATUSES, pid)
401 ret.add(nt)
402 return list(ret)
403
404
405 # =====================================================================
406 # --- sensors
407 # =====================================================================
408
409
410 if FREEBSD:
411
412 def sensors_battery():
413 """Return battery info."""
414 try:
415 percent, minsleft, power_plugged = cext.sensors_battery()
416 except NotImplementedError:
417 # See: https://github.com/giampaolo/psutil/issues/1074
418 return None
419 power_plugged = power_plugged == 1
420 if power_plugged:
421 secsleft = _common.POWER_TIME_UNLIMITED
422 elif minsleft == -1:
423 secsleft = _common.POWER_TIME_UNKNOWN
424 else:
425 secsleft = minsleft * 60
426 return _common.sbattery(percent, secsleft, power_plugged)
427
428 def sensors_temperatures():
429 "Return CPU cores temperatures if available, else an empty dict."
430 ret = defaultdict(list)
431 num_cpus = cpu_count_logical()
432 for cpu in range(num_cpus):
433 try:
434 current, high = cext.sensors_cpu_temperature(cpu)
435 if high <= 0:
436 high = None
437 name = "Core %s" % cpu
438 ret["coretemp"].append(
439 _common.shwtemp(name, current, high, high))
440 except NotImplementedError:
441 pass
442
443 return ret
444
445 def cpu_freq():
446 """Return frequency metrics for CPUs. As of Dec 2018 only
447 CPU 0 appears to be supported by FreeBSD and all other cores
448 match the frequency of CPU 0.
449 """
450 ret = []
451 num_cpus = cpu_count_logical()
452 for cpu in range(num_cpus):
453 try:
454 current, available_freq = cext.cpu_frequency(cpu)
455 except NotImplementedError:
456 continue
457 if available_freq:
458 try:
459 min_freq = int(available_freq.split(" ")[-1].split("/")[0])
460 except(IndexError, ValueError):
461 min_freq = None
462 try:
463 max_freq = int(available_freq.split(" ")[0].split("/")[0])
464 except(IndexError, ValueError):
465 max_freq = None
466 ret.append(_common.scpufreq(current, min_freq, max_freq))
467 return ret
468
469
470 # =====================================================================
471 # --- other system functions
472 # =====================================================================
473
474
475 def boot_time():
476 """The system boot time expressed in seconds since the epoch."""
477 return cext.boot_time()
478
479
480 def users():
481 """Return currently connected users as a list of namedtuples."""
482 retlist = []
483 rawlist = cext.users()
484 for item in rawlist:
485 user, tty, hostname, tstamp, pid = item
486 if pid == -1:
487 assert OPENBSD
488 pid = None
489 if tty == '~':
490 continue # reboot or shutdown
491 nt = _common.suser(user, tty or None, hostname, tstamp, pid)
492 retlist.append(nt)
493 return retlist
494
495
496 # =====================================================================
497 # --- processes
498 # =====================================================================
499
500
501 @memoize
502 def _pid_0_exists():
503 try:
504 Process(0).name()
505 except NoSuchProcess:
506 return False
507 except AccessDenied:
508 return True
509 else:
510 return True
511
512
513 def pids():
514 """Returns a list of PIDs currently running on the system."""
515 ret = cext.pids()
516 if OPENBSD and (0 not in ret) and _pid_0_exists():
517 # On OpenBSD the kernel does not return PID 0 (neither does
518 # ps) but it's actually querable (Process(0) will succeed).
519 ret.insert(0, 0)
520 return ret
521
522
523 if OPENBSD or NETBSD:
524 def pid_exists(pid):
525 """Return True if pid exists."""
526 exists = _psposix.pid_exists(pid)
527 if not exists:
528 # We do this because _psposix.pid_exists() lies in case of
529 # zombie processes.
530 return pid in pids()
531 else:
532 return True
533 else:
534 pid_exists = _psposix.pid_exists
535
536
537 def is_zombie(pid):
538 try:
539 st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']]
540 return st == cext.SZOMB
541 except Exception:
542 return False
543
544
545 def wrap_exceptions(fun):
546 """Decorator which translates bare OSError exceptions into
547 NoSuchProcess and AccessDenied.
548 """
549 @functools.wraps(fun)
550 def wrapper(self, *args, **kwargs):
551 try:
552 return fun(self, *args, **kwargs)
553 except ProcessLookupError:
554 if is_zombie(self.pid):
555 raise ZombieProcess(self.pid, self._name, self._ppid)
556 else:
557 raise NoSuchProcess(self.pid, self._name)
558 except PermissionError:
559 raise AccessDenied(self.pid, self._name)
560 except OSError:
561 if self.pid == 0:
562 if 0 in pids():
563 raise AccessDenied(self.pid, self._name)
564 else:
565 raise
566 raise
567 return wrapper
568
569
570 @contextlib.contextmanager
571 def wrap_exceptions_procfs(inst):
572 """Same as above, for routines relying on reading /proc fs."""
573 try:
574 yield
575 except (ProcessLookupError, FileNotFoundError):
576 # ENOENT (no such file or directory) gets raised on open().
577 # ESRCH (no such process) can get raised on read() if
578 # process is gone in meantime.
579 if is_zombie(inst.pid):
580 raise ZombieProcess(inst.pid, inst._name, inst._ppid)
581 else:
582 raise NoSuchProcess(inst.pid, inst._name)
583 except PermissionError:
584 raise AccessDenied(inst.pid, inst._name)
585
586
587 class Process(object):
588 """Wrapper class around underlying C implementation."""
589
590 __slots__ = ["pid", "_name", "_ppid", "_cache"]
591
592 def __init__(self, pid):
593 self.pid = pid
594 self._name = None
595 self._ppid = None
596
597 def _assert_alive(self):
598 """Raise NSP if the process disappeared on us."""
599 # For those C function who do not raise NSP, possibly returning
600 # incorrect or incomplete result.
601 cext.proc_name(self.pid)
602
603 @wrap_exceptions
604 @memoize_when_activated
605 def oneshot(self):
606 """Retrieves multiple process info in one shot as a raw tuple."""
607 ret = cext.proc_oneshot_info(self.pid)
608 assert len(ret) == len(kinfo_proc_map)
609 return ret
610
611 def oneshot_enter(self):
612 self.oneshot.cache_activate(self)
613
614 def oneshot_exit(self):
615 self.oneshot.cache_deactivate(self)
616
617 @wrap_exceptions
618 def name(self):
619 name = self.oneshot()[kinfo_proc_map['name']]
620 return name if name is not None else cext.proc_name(self.pid)
621
622 @wrap_exceptions
623 def exe(self):
624 if FREEBSD:
625 if self.pid == 0:
626 return '' # else NSP
627 return cext.proc_exe(self.pid)
628 elif NETBSD:
629 if self.pid == 0:
630 # /proc/0 dir exists but /proc/0/exe doesn't
631 return ""
632 with wrap_exceptions_procfs(self):
633 return os.readlink("/proc/%s/exe" % self.pid)
634 else:
635 # OpenBSD: exe cannot be determined; references:
636 # https://chromium.googlesource.com/chromium/src/base/+/
637 # master/base_paths_posix.cc
638 # We try our best guess by using which against the first
639 # cmdline arg (may return None).
640 cmdline = self.cmdline()
641 if cmdline:
642 return which(cmdline[0]) or ""
643 else:
644 return ""
645
646 @wrap_exceptions
647 def cmdline(self):
648 if OPENBSD and self.pid == 0:
649 return [] # ...else it crashes
650 elif NETBSD:
651 # XXX - most of the times the underlying sysctl() call on Net
652 # and Open BSD returns a truncated string.
653 # Also /proc/pid/cmdline behaves the same so it looks
654 # like this is a kernel bug.
655 try:
656 return cext.proc_cmdline(self.pid)
657 except OSError as err:
658 if err.errno == errno.EINVAL:
659 if is_zombie(self.pid):
660 raise ZombieProcess(self.pid, self._name, self._ppid)
661 elif not pid_exists(self.pid):
662 raise NoSuchProcess(self.pid, self._name, self._ppid)
663 else:
664 # XXX: this happens with unicode tests. It means the C
665 # routine is unable to decode invalid unicode chars.
666 return []
667 else:
668 raise
669 else:
670 return cext.proc_cmdline(self.pid)
671
672 @wrap_exceptions
673 def terminal(self):
674 tty_nr = self.oneshot()[kinfo_proc_map['ttynr']]
675 tmap = _psposix.get_terminal_map()
676 try:
677 return tmap[tty_nr]
678 except KeyError:
679 return None
680
681 @wrap_exceptions
682 def ppid(self):
683 self._ppid = self.oneshot()[kinfo_proc_map['ppid']]
684 return self._ppid
685
686 @wrap_exceptions
687 def uids(self):
688 rawtuple = self.oneshot()
689 return _common.puids(
690 rawtuple[kinfo_proc_map['real_uid']],
691 rawtuple[kinfo_proc_map['effective_uid']],
692 rawtuple[kinfo_proc_map['saved_uid']])
693
694 @wrap_exceptions
695 def gids(self):
696 rawtuple = self.oneshot()
697 return _common.pgids(
698 rawtuple[kinfo_proc_map['real_gid']],
699 rawtuple[kinfo_proc_map['effective_gid']],
700 rawtuple[kinfo_proc_map['saved_gid']])
701
702 @wrap_exceptions
703 def cpu_times(self):
704 rawtuple = self.oneshot()
705 return _common.pcputimes(
706 rawtuple[kinfo_proc_map['user_time']],
707 rawtuple[kinfo_proc_map['sys_time']],
708 rawtuple[kinfo_proc_map['ch_user_time']],
709 rawtuple[kinfo_proc_map['ch_sys_time']])
710
711 if FREEBSD:
712 @wrap_exceptions
713 def cpu_num(self):
714 return self.oneshot()[kinfo_proc_map['cpunum']]
715
716 @wrap_exceptions
717 def memory_info(self):
718 rawtuple = self.oneshot()
719 return pmem(
720 rawtuple[kinfo_proc_map['rss']],
721 rawtuple[kinfo_proc_map['vms']],
722 rawtuple[kinfo_proc_map['memtext']],
723 rawtuple[kinfo_proc_map['memdata']],
724 rawtuple[kinfo_proc_map['memstack']])
725
726 memory_full_info = memory_info
727
728 @wrap_exceptions
729 def create_time(self):
730 return self.oneshot()[kinfo_proc_map['create_time']]
731
732 @wrap_exceptions
733 def num_threads(self):
734 if HAS_PROC_NUM_THREADS:
735 # FreeBSD
736 return cext.proc_num_threads(self.pid)
737 else:
738 return len(self.threads())
739
740 @wrap_exceptions
741 def num_ctx_switches(self):
742 rawtuple = self.oneshot()
743 return _common.pctxsw(
744 rawtuple[kinfo_proc_map['ctx_switches_vol']],
745 rawtuple[kinfo_proc_map['ctx_switches_unvol']])
746
747 @wrap_exceptions
748 def threads(self):
749 # Note: on OpenSBD this (/dev/mem) requires root access.
750 rawlist = cext.proc_threads(self.pid)
751 retlist = []
752 for thread_id, utime, stime in rawlist:
753 ntuple = _common.pthread(thread_id, utime, stime)
754 retlist.append(ntuple)
755 if OPENBSD:
756 self._assert_alive()
757 return retlist
758
759 @wrap_exceptions
760 def connections(self, kind='inet'):
761 if kind not in conn_tmap:
762 raise ValueError("invalid %r kind argument; choose between %s"
763 % (kind, ', '.join([repr(x) for x in conn_tmap])))
764
765 if NETBSD:
766 families, types = conn_tmap[kind]
767 ret = []
768 rawlist = cext.net_connections(self.pid)
769 for item in rawlist:
770 fd, fam, type, laddr, raddr, status, pid = item
771 assert pid == self.pid
772 if fam in families and type in types:
773 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
774 TCP_STATUSES)
775 ret.append(nt)
776 self._assert_alive()
777 return list(ret)
778
779 families, types = conn_tmap[kind]
780 rawlist = cext.proc_connections(self.pid, families, types)
781 ret = []
782 for item in rawlist:
783 fd, fam, type, laddr, raddr, status = item
784 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
785 TCP_STATUSES)
786 ret.append(nt)
787
788 if OPENBSD:
789 self._assert_alive()
790
791 return ret
792
793 @wrap_exceptions
794 def wait(self, timeout=None):
795 return _psposix.wait_pid(self.pid, timeout, self._name)
796
797 @wrap_exceptions
798 def nice_get(self):
799 return cext_posix.getpriority(self.pid)
800
801 @wrap_exceptions
802 def nice_set(self, value):
803 return cext_posix.setpriority(self.pid, value)
804
805 @wrap_exceptions
806 def status(self):
807 code = self.oneshot()[kinfo_proc_map['status']]
808 # XXX is '?' legit? (we're not supposed to return it anyway)
809 return PROC_STATUSES.get(code, '?')
810
811 @wrap_exceptions
812 def io_counters(self):
813 rawtuple = self.oneshot()
814 return _common.pio(
815 rawtuple[kinfo_proc_map['read_io_count']],
816 rawtuple[kinfo_proc_map['write_io_count']],
817 -1,
818 -1)
819
820 @wrap_exceptions
821 def cwd(self):
822 """Return process current working directory."""
823 # sometimes we get an empty string, in which case we turn
824 # it into None
825 if OPENBSD and self.pid == 0:
826 return None # ...else it would raise EINVAL
827 elif NETBSD or HAS_PROC_OPEN_FILES:
828 # FreeBSD < 8 does not support functions based on
829 # kinfo_getfile() and kinfo_getvmmap()
830 return cext.proc_cwd(self.pid) or None
831 else:
832 raise NotImplementedError(
833 "supported only starting from FreeBSD 8" if
834 FREEBSD else "")
835
836 nt_mmap_grouped = namedtuple(
837 'mmap', 'path rss, private, ref_count, shadow_count')
838 nt_mmap_ext = namedtuple(
839 'mmap', 'addr, perms path rss, private, ref_count, shadow_count')
840
841 def _not_implemented(self):
842 raise NotImplementedError
843
844 # FreeBSD < 8 does not support functions based on kinfo_getfile()
845 # and kinfo_getvmmap()
846 if HAS_PROC_OPEN_FILES:
847 @wrap_exceptions
848 def open_files(self):
849 """Return files opened by process as a list of namedtuples."""
850 rawlist = cext.proc_open_files(self.pid)
851 return [_common.popenfile(path, fd) for path, fd in rawlist]
852 else:
853 open_files = _not_implemented
854
855 # FreeBSD < 8 does not support functions based on kinfo_getfile()
856 # and kinfo_getvmmap()
857 if HAS_PROC_NUM_FDS:
858 @wrap_exceptions
859 def num_fds(self):
860 """Return the number of file descriptors opened by this process."""
861 ret = cext.proc_num_fds(self.pid)
862 if NETBSD:
863 self._assert_alive()
864 return ret
865 else:
866 num_fds = _not_implemented
867
868 # --- FreeBSD only APIs
869
870 if FREEBSD:
871
872 @wrap_exceptions
873 def cpu_affinity_get(self):
874 return cext.proc_cpu_affinity_get(self.pid)
875
876 @wrap_exceptions
877 def cpu_affinity_set(self, cpus):
878 # Pre-emptively check if CPUs are valid because the C
879 # function has a weird behavior in case of invalid CPUs,
880 # see: https://github.com/giampaolo/psutil/issues/586
881 allcpus = tuple(range(len(per_cpu_times())))
882 for cpu in cpus:
883 if cpu not in allcpus:
884 raise ValueError("invalid CPU #%i (choose between %s)"
885 % (cpu, allcpus))
886 try:
887 cext.proc_cpu_affinity_set(self.pid, cpus)
888 except OSError as err:
889 # 'man cpuset_setaffinity' about EDEADLK:
890 # <<the call would leave a thread without a valid CPU to run
891 # on because the set does not overlap with the thread's
892 # anonymous mask>>
893 if err.errno in (errno.EINVAL, errno.EDEADLK):
894 for cpu in cpus:
895 if cpu not in allcpus:
896 raise ValueError(
897 "invalid CPU #%i (choose between %s)" % (
898 cpu, allcpus))
899 raise
900
901 @wrap_exceptions
902 def memory_maps(self):
903 return cext.proc_memory_maps(self.pid)