comparison planemo/lib/python3.7/site-packages/psutil/_psosx.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 """macOS platform implementation."""
6
7 import contextlib
8 import errno
9 import functools
10 import os
11 from collections import namedtuple
12
13 from . import _common
14 from . import _psposix
15 from . import _psutil_osx as cext
16 from . import _psutil_posix as cext_posix
17 from ._common import AccessDenied
18 from ._common import conn_tmap
19 from ._common import conn_to_ntuple
20 from ._common import isfile_strict
21 from ._common import memoize_when_activated
22 from ._common import NoSuchProcess
23 from ._common import parse_environ_block
24 from ._common import usage_percent
25 from ._common import ZombieProcess
26 from ._compat import PermissionError
27 from ._compat import ProcessLookupError
28
29
30 __extra__all__ = []
31
32
33 # =====================================================================
34 # --- globals
35 # =====================================================================
36
37
38 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
39 AF_LINK = cext_posix.AF_LINK
40
41 TCP_STATUSES = {
42 cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
43 cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
44 cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
45 cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
46 cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
47 cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
48 cext.TCPS_CLOSED: _common.CONN_CLOSE,
49 cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
50 cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
51 cext.TCPS_LISTEN: _common.CONN_LISTEN,
52 cext.TCPS_CLOSING: _common.CONN_CLOSING,
53 cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
54 }
55
56 PROC_STATUSES = {
57 cext.SIDL: _common.STATUS_IDLE,
58 cext.SRUN: _common.STATUS_RUNNING,
59 cext.SSLEEP: _common.STATUS_SLEEPING,
60 cext.SSTOP: _common.STATUS_STOPPED,
61 cext.SZOMB: _common.STATUS_ZOMBIE,
62 }
63
64 kinfo_proc_map = dict(
65 ppid=0,
66 ruid=1,
67 euid=2,
68 suid=3,
69 rgid=4,
70 egid=5,
71 sgid=6,
72 ttynr=7,
73 ctime=8,
74 status=9,
75 name=10,
76 )
77
78 pidtaskinfo_map = dict(
79 cpuutime=0,
80 cpustime=1,
81 rss=2,
82 vms=3,
83 pfaults=4,
84 pageins=5,
85 numthreads=6,
86 volctxsw=7,
87 )
88
89
90 # =====================================================================
91 # --- named tuples
92 # =====================================================================
93
94
95 # psutil.cpu_times()
96 scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
97 # psutil.virtual_memory()
98 svmem = namedtuple(
99 'svmem', ['total', 'available', 'percent', 'used', 'free',
100 'active', 'inactive', 'wired'])
101 # psutil.Process.memory_info()
102 pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
103 # psutil.Process.memory_full_info()
104 pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
105
106
107 # =====================================================================
108 # --- memory
109 # =====================================================================
110
111
112 def virtual_memory():
113 """System virtual memory as a namedtuple."""
114 total, active, inactive, wired, free, speculative = cext.virtual_mem()
115 # This is how Zabbix calculate avail and used mem:
116 # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
117 # osx/memory.c
118 # Also see: https://github.com/giampaolo/psutil/issues/1277
119 avail = inactive + free
120 used = active + wired
121 # This is NOT how Zabbix calculates free mem but it matches "free"
122 # cmdline utility.
123 free -= speculative
124 percent = usage_percent((total - avail), total, round_=1)
125 return svmem(total, avail, percent, used, free,
126 active, inactive, wired)
127
128
129 def swap_memory():
130 """Swap system memory as a (total, used, free, sin, sout) tuple."""
131 total, used, free, sin, sout = cext.swap_mem()
132 percent = usage_percent(used, total, round_=1)
133 return _common.sswap(total, used, free, percent, sin, sout)
134
135
136 # =====================================================================
137 # --- CPU
138 # =====================================================================
139
140
141 def cpu_times():
142 """Return system CPU times as a namedtuple."""
143 user, nice, system, idle = cext.cpu_times()
144 return scputimes(user, nice, system, idle)
145
146
147 def per_cpu_times():
148 """Return system CPU times as a named tuple"""
149 ret = []
150 for cpu_t in cext.per_cpu_times():
151 user, nice, system, idle = cpu_t
152 item = scputimes(user, nice, system, idle)
153 ret.append(item)
154 return ret
155
156
157 def cpu_count_logical():
158 """Return the number of logical CPUs in the system."""
159 return cext.cpu_count_logical()
160
161
162 def cpu_count_physical():
163 """Return the number of physical CPUs in the system."""
164 return cext.cpu_count_phys()
165
166
167 def cpu_stats():
168 ctx_switches, interrupts, soft_interrupts, syscalls, traps = \
169 cext.cpu_stats()
170 return _common.scpustats(
171 ctx_switches, interrupts, soft_interrupts, syscalls)
172
173
174 def cpu_freq():
175 """Return CPU frequency.
176 On macOS per-cpu frequency is not supported.
177 Also, the returned frequency never changes, see:
178 https://arstechnica.com/civis/viewtopic.php?f=19&t=465002
179 """
180 curr, min_, max_ = cext.cpu_freq()
181 return [_common.scpufreq(curr, min_, max_)]
182
183
184 # =====================================================================
185 # --- disks
186 # =====================================================================
187
188
189 disk_usage = _psposix.disk_usage
190 disk_io_counters = cext.disk_io_counters
191
192
193 def disk_partitions(all=False):
194 """Return mounted disk partitions as a list of namedtuples."""
195 retlist = []
196 partitions = cext.disk_partitions()
197 for partition in partitions:
198 device, mountpoint, fstype, opts = partition
199 if device == 'none':
200 device = ''
201 if not all:
202 if not os.path.isabs(device) or not os.path.exists(device):
203 continue
204 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
205 retlist.append(ntuple)
206 return retlist
207
208
209 # =====================================================================
210 # --- sensors
211 # =====================================================================
212
213
214 def sensors_battery():
215 """Return battery information."""
216 try:
217 percent, minsleft, power_plugged = cext.sensors_battery()
218 except NotImplementedError:
219 # no power source - return None according to interface
220 return None
221 power_plugged = power_plugged == 1
222 if power_plugged:
223 secsleft = _common.POWER_TIME_UNLIMITED
224 elif minsleft == -1:
225 secsleft = _common.POWER_TIME_UNKNOWN
226 else:
227 secsleft = minsleft * 60
228 return _common.sbattery(percent, secsleft, power_plugged)
229
230
231 # =====================================================================
232 # --- network
233 # =====================================================================
234
235
236 net_io_counters = cext.net_io_counters
237 net_if_addrs = cext_posix.net_if_addrs
238
239
240 def net_connections(kind='inet'):
241 """System-wide network connections."""
242 # Note: on macOS this will fail with AccessDenied unless
243 # the process is owned by root.
244 ret = []
245 for pid in pids():
246 try:
247 cons = Process(pid).connections(kind)
248 except NoSuchProcess:
249 continue
250 else:
251 if cons:
252 for c in cons:
253 c = list(c) + [pid]
254 ret.append(_common.sconn(*c))
255 return ret
256
257
258 def net_if_stats():
259 """Get NIC stats (isup, duplex, speed, mtu)."""
260 names = net_io_counters().keys()
261 ret = {}
262 for name in names:
263 try:
264 mtu = cext_posix.net_if_mtu(name)
265 isup = cext_posix.net_if_flags(name)
266 duplex, speed = cext_posix.net_if_duplex_speed(name)
267 except OSError as err:
268 # https://github.com/giampaolo/psutil/issues/1279
269 if err.errno != errno.ENODEV:
270 raise
271 else:
272 if hasattr(_common, 'NicDuplex'):
273 duplex = _common.NicDuplex(duplex)
274 ret[name] = _common.snicstats(isup, duplex, speed, mtu)
275 return ret
276
277
278 # =====================================================================
279 # --- other system functions
280 # =====================================================================
281
282
283 def boot_time():
284 """The system boot time expressed in seconds since the epoch."""
285 return cext.boot_time()
286
287
288 def users():
289 """Return currently connected users as a list of namedtuples."""
290 retlist = []
291 rawlist = cext.users()
292 for item in rawlist:
293 user, tty, hostname, tstamp, pid = item
294 if tty == '~':
295 continue # reboot or shutdown
296 if not tstamp:
297 continue
298 nt = _common.suser(user, tty or None, hostname or None, tstamp, pid)
299 retlist.append(nt)
300 return retlist
301
302
303 # =====================================================================
304 # --- processes
305 # =====================================================================
306
307
308 def pids():
309 ls = cext.pids()
310 if 0 not in ls:
311 # On certain macOS versions pids() C doesn't return PID 0 but
312 # "ps" does and the process is querable via sysctl():
313 # https://travis-ci.org/giampaolo/psutil/jobs/309619941
314 try:
315 Process(0).create_time()
316 ls.insert(0, 0)
317 except NoSuchProcess:
318 pass
319 except AccessDenied:
320 ls.insert(0, 0)
321 return ls
322
323
324 pid_exists = _psposix.pid_exists
325
326
327 def is_zombie(pid):
328 try:
329 st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']]
330 return st == cext.SZOMB
331 except Exception:
332 return False
333
334
335 def wrap_exceptions(fun):
336 """Decorator which translates bare OSError exceptions into
337 NoSuchProcess and AccessDenied.
338 """
339 @functools.wraps(fun)
340 def wrapper(self, *args, **kwargs):
341 try:
342 return fun(self, *args, **kwargs)
343 except ProcessLookupError:
344 if is_zombie(self.pid):
345 raise ZombieProcess(self.pid, self._name, self._ppid)
346 else:
347 raise NoSuchProcess(self.pid, self._name)
348 except PermissionError:
349 raise AccessDenied(self.pid, self._name)
350 except cext.ZombieProcessError:
351 raise ZombieProcess(self.pid, self._name, self._ppid)
352 return wrapper
353
354
355 @contextlib.contextmanager
356 def catch_zombie(proc):
357 """There are some poor C APIs which incorrectly raise ESRCH when
358 the process is still alive or it's a zombie, or even RuntimeError
359 (those who don't set errno). This is here in order to solve:
360 https://github.com/giampaolo/psutil/issues/1044
361 """
362 try:
363 yield
364 except (OSError, RuntimeError) as err:
365 if isinstance(err, RuntimeError) or err.errno == errno.ESRCH:
366 try:
367 # status() is not supposed to lie and correctly detect
368 # zombies so if it raises ESRCH it's true.
369 status = proc.status()
370 except NoSuchProcess:
371 raise err
372 else:
373 if status == _common.STATUS_ZOMBIE:
374 raise ZombieProcess(proc.pid, proc._name, proc._ppid)
375 else:
376 raise AccessDenied(proc.pid, proc._name)
377 else:
378 raise
379
380
381 class Process(object):
382 """Wrapper class around underlying C implementation."""
383
384 __slots__ = ["pid", "_name", "_ppid", "_cache"]
385
386 def __init__(self, pid):
387 self.pid = pid
388 self._name = None
389 self._ppid = None
390
391 @wrap_exceptions
392 @memoize_when_activated
393 def _get_kinfo_proc(self):
394 # Note: should work with all PIDs without permission issues.
395 ret = cext.proc_kinfo_oneshot(self.pid)
396 assert len(ret) == len(kinfo_proc_map)
397 return ret
398
399 @wrap_exceptions
400 @memoize_when_activated
401 def _get_pidtaskinfo(self):
402 # Note: should work for PIDs owned by user only.
403 with catch_zombie(self):
404 ret = cext.proc_pidtaskinfo_oneshot(self.pid)
405 assert len(ret) == len(pidtaskinfo_map)
406 return ret
407
408 def oneshot_enter(self):
409 self._get_kinfo_proc.cache_activate(self)
410 self._get_pidtaskinfo.cache_activate(self)
411
412 def oneshot_exit(self):
413 self._get_kinfo_proc.cache_deactivate(self)
414 self._get_pidtaskinfo.cache_deactivate(self)
415
416 @wrap_exceptions
417 def name(self):
418 name = self._get_kinfo_proc()[kinfo_proc_map['name']]
419 return name if name is not None else cext.proc_name(self.pid)
420
421 @wrap_exceptions
422 def exe(self):
423 with catch_zombie(self):
424 return cext.proc_exe(self.pid)
425
426 @wrap_exceptions
427 def cmdline(self):
428 with catch_zombie(self):
429 return cext.proc_cmdline(self.pid)
430
431 @wrap_exceptions
432 def environ(self):
433 with catch_zombie(self):
434 return parse_environ_block(cext.proc_environ(self.pid))
435
436 @wrap_exceptions
437 def ppid(self):
438 self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
439 return self._ppid
440
441 @wrap_exceptions
442 def cwd(self):
443 with catch_zombie(self):
444 return cext.proc_cwd(self.pid)
445
446 @wrap_exceptions
447 def uids(self):
448 rawtuple = self._get_kinfo_proc()
449 return _common.puids(
450 rawtuple[kinfo_proc_map['ruid']],
451 rawtuple[kinfo_proc_map['euid']],
452 rawtuple[kinfo_proc_map['suid']])
453
454 @wrap_exceptions
455 def gids(self):
456 rawtuple = self._get_kinfo_proc()
457 return _common.puids(
458 rawtuple[kinfo_proc_map['rgid']],
459 rawtuple[kinfo_proc_map['egid']],
460 rawtuple[kinfo_proc_map['sgid']])
461
462 @wrap_exceptions
463 def terminal(self):
464 tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']]
465 tmap = _psposix.get_terminal_map()
466 try:
467 return tmap[tty_nr]
468 except KeyError:
469 return None
470
471 @wrap_exceptions
472 def memory_info(self):
473 rawtuple = self._get_pidtaskinfo()
474 return pmem(
475 rawtuple[pidtaskinfo_map['rss']],
476 rawtuple[pidtaskinfo_map['vms']],
477 rawtuple[pidtaskinfo_map['pfaults']],
478 rawtuple[pidtaskinfo_map['pageins']],
479 )
480
481 @wrap_exceptions
482 def memory_full_info(self):
483 basic_mem = self.memory_info()
484 uss = cext.proc_memory_uss(self.pid)
485 return pfullmem(*basic_mem + (uss, ))
486
487 @wrap_exceptions
488 def cpu_times(self):
489 rawtuple = self._get_pidtaskinfo()
490 return _common.pcputimes(
491 rawtuple[pidtaskinfo_map['cpuutime']],
492 rawtuple[pidtaskinfo_map['cpustime']],
493 # children user / system times are not retrievable (set to 0)
494 0.0, 0.0)
495
496 @wrap_exceptions
497 def create_time(self):
498 return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
499
500 @wrap_exceptions
501 def num_ctx_switches(self):
502 # Unvoluntary value seems not to be available;
503 # getrusage() numbers seems to confirm this theory.
504 # We set it to 0.
505 vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']]
506 return _common.pctxsw(vol, 0)
507
508 @wrap_exceptions
509 def num_threads(self):
510 return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
511
512 @wrap_exceptions
513 def open_files(self):
514 if self.pid == 0:
515 return []
516 files = []
517 with catch_zombie(self):
518 rawlist = cext.proc_open_files(self.pid)
519 for path, fd in rawlist:
520 if isfile_strict(path):
521 ntuple = _common.popenfile(path, fd)
522 files.append(ntuple)
523 return files
524
525 @wrap_exceptions
526 def connections(self, kind='inet'):
527 if kind not in conn_tmap:
528 raise ValueError("invalid %r kind argument; choose between %s"
529 % (kind, ', '.join([repr(x) for x in conn_tmap])))
530 families, types = conn_tmap[kind]
531 with catch_zombie(self):
532 rawlist = cext.proc_connections(self.pid, families, types)
533 ret = []
534 for item in rawlist:
535 fd, fam, type, laddr, raddr, status = item
536 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
537 TCP_STATUSES)
538 ret.append(nt)
539 return ret
540
541 @wrap_exceptions
542 def num_fds(self):
543 if self.pid == 0:
544 return 0
545 with catch_zombie(self):
546 return cext.proc_num_fds(self.pid)
547
548 @wrap_exceptions
549 def wait(self, timeout=None):
550 return _psposix.wait_pid(self.pid, timeout, self._name)
551
552 @wrap_exceptions
553 def nice_get(self):
554 with catch_zombie(self):
555 return cext_posix.getpriority(self.pid)
556
557 @wrap_exceptions
558 def nice_set(self, value):
559 with catch_zombie(self):
560 return cext_posix.setpriority(self.pid, value)
561
562 @wrap_exceptions
563 def status(self):
564 code = self._get_kinfo_proc()[kinfo_proc_map['status']]
565 # XXX is '?' legit? (we're not supposed to return it anyway)
566 return PROC_STATUSES.get(code, '?')
567
568 @wrap_exceptions
569 def threads(self):
570 rawlist = cext.proc_threads(self.pid)
571 retlist = []
572 for thread_id, utime, stime in rawlist:
573 ntuple = _common.pthread(thread_id, utime, stime)
574 retlist.append(ntuple)
575 return retlist