comparison env/lib/python3.7/site-packages/psutil/_psosx.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
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 wrap_exceptions(fun):
328 """Decorator which translates bare OSError exceptions into
329 NoSuchProcess and AccessDenied.
330 """
331 @functools.wraps(fun)
332 def wrapper(self, *args, **kwargs):
333 try:
334 return fun(self, *args, **kwargs)
335 except ProcessLookupError:
336 raise NoSuchProcess(self.pid, self._name)
337 except PermissionError:
338 raise AccessDenied(self.pid, self._name)
339 except cext.ZombieProcessError:
340 raise ZombieProcess(self.pid, self._name, self._ppid)
341 return wrapper
342
343
344 @contextlib.contextmanager
345 def catch_zombie(proc):
346 """There are some poor C APIs which incorrectly raise ESRCH when
347 the process is still alive or it's a zombie, or even RuntimeError
348 (those who don't set errno). This is here in order to solve:
349 https://github.com/giampaolo/psutil/issues/1044
350 """
351 try:
352 yield
353 except (OSError, RuntimeError) as err:
354 if isinstance(err, RuntimeError) or err.errno == errno.ESRCH:
355 try:
356 # status() is not supposed to lie and correctly detect
357 # zombies so if it raises ESRCH it's true.
358 status = proc.status()
359 except NoSuchProcess:
360 raise err
361 else:
362 if status == _common.STATUS_ZOMBIE:
363 raise ZombieProcess(proc.pid, proc._name, proc._ppid)
364 else:
365 raise AccessDenied(proc.pid, proc._name)
366 else:
367 raise
368
369
370 class Process(object):
371 """Wrapper class around underlying C implementation."""
372
373 __slots__ = ["pid", "_name", "_ppid", "_cache"]
374
375 def __init__(self, pid):
376 self.pid = pid
377 self._name = None
378 self._ppid = None
379
380 @wrap_exceptions
381 @memoize_when_activated
382 def _get_kinfo_proc(self):
383 # Note: should work with all PIDs without permission issues.
384 ret = cext.proc_kinfo_oneshot(self.pid)
385 assert len(ret) == len(kinfo_proc_map)
386 return ret
387
388 @wrap_exceptions
389 @memoize_when_activated
390 def _get_pidtaskinfo(self):
391 # Note: should work for PIDs owned by user only.
392 with catch_zombie(self):
393 ret = cext.proc_pidtaskinfo_oneshot(self.pid)
394 assert len(ret) == len(pidtaskinfo_map)
395 return ret
396
397 def oneshot_enter(self):
398 self._get_kinfo_proc.cache_activate(self)
399 self._get_pidtaskinfo.cache_activate(self)
400
401 def oneshot_exit(self):
402 self._get_kinfo_proc.cache_deactivate(self)
403 self._get_pidtaskinfo.cache_deactivate(self)
404
405 @wrap_exceptions
406 def name(self):
407 name = self._get_kinfo_proc()[kinfo_proc_map['name']]
408 return name if name is not None else cext.proc_name(self.pid)
409
410 @wrap_exceptions
411 def exe(self):
412 with catch_zombie(self):
413 return cext.proc_exe(self.pid)
414
415 @wrap_exceptions
416 def cmdline(self):
417 with catch_zombie(self):
418 return cext.proc_cmdline(self.pid)
419
420 @wrap_exceptions
421 def environ(self):
422 with catch_zombie(self):
423 return parse_environ_block(cext.proc_environ(self.pid))
424
425 @wrap_exceptions
426 def ppid(self):
427 self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
428 return self._ppid
429
430 @wrap_exceptions
431 def cwd(self):
432 with catch_zombie(self):
433 return cext.proc_cwd(self.pid)
434
435 @wrap_exceptions
436 def uids(self):
437 rawtuple = self._get_kinfo_proc()
438 return _common.puids(
439 rawtuple[kinfo_proc_map['ruid']],
440 rawtuple[kinfo_proc_map['euid']],
441 rawtuple[kinfo_proc_map['suid']])
442
443 @wrap_exceptions
444 def gids(self):
445 rawtuple = self._get_kinfo_proc()
446 return _common.puids(
447 rawtuple[kinfo_proc_map['rgid']],
448 rawtuple[kinfo_proc_map['egid']],
449 rawtuple[kinfo_proc_map['sgid']])
450
451 @wrap_exceptions
452 def terminal(self):
453 tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']]
454 tmap = _psposix.get_terminal_map()
455 try:
456 return tmap[tty_nr]
457 except KeyError:
458 return None
459
460 @wrap_exceptions
461 def memory_info(self):
462 rawtuple = self._get_pidtaskinfo()
463 return pmem(
464 rawtuple[pidtaskinfo_map['rss']],
465 rawtuple[pidtaskinfo_map['vms']],
466 rawtuple[pidtaskinfo_map['pfaults']],
467 rawtuple[pidtaskinfo_map['pageins']],
468 )
469
470 @wrap_exceptions
471 def memory_full_info(self):
472 basic_mem = self.memory_info()
473 uss = cext.proc_memory_uss(self.pid)
474 return pfullmem(*basic_mem + (uss, ))
475
476 @wrap_exceptions
477 def cpu_times(self):
478 rawtuple = self._get_pidtaskinfo()
479 return _common.pcputimes(
480 rawtuple[pidtaskinfo_map['cpuutime']],
481 rawtuple[pidtaskinfo_map['cpustime']],
482 # children user / system times are not retrievable (set to 0)
483 0.0, 0.0)
484
485 @wrap_exceptions
486 def create_time(self):
487 return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
488
489 @wrap_exceptions
490 def num_ctx_switches(self):
491 # Unvoluntary value seems not to be available;
492 # getrusage() numbers seems to confirm this theory.
493 # We set it to 0.
494 vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']]
495 return _common.pctxsw(vol, 0)
496
497 @wrap_exceptions
498 def num_threads(self):
499 return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
500
501 @wrap_exceptions
502 def open_files(self):
503 if self.pid == 0:
504 return []
505 files = []
506 with catch_zombie(self):
507 rawlist = cext.proc_open_files(self.pid)
508 for path, fd in rawlist:
509 if isfile_strict(path):
510 ntuple = _common.popenfile(path, fd)
511 files.append(ntuple)
512 return files
513
514 @wrap_exceptions
515 def connections(self, kind='inet'):
516 if kind not in conn_tmap:
517 raise ValueError("invalid %r kind argument; choose between %s"
518 % (kind, ', '.join([repr(x) for x in conn_tmap])))
519 families, types = conn_tmap[kind]
520 with catch_zombie(self):
521 rawlist = cext.proc_connections(self.pid, families, types)
522 ret = []
523 for item in rawlist:
524 fd, fam, type, laddr, raddr, status = item
525 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
526 TCP_STATUSES)
527 ret.append(nt)
528 return ret
529
530 @wrap_exceptions
531 def num_fds(self):
532 if self.pid == 0:
533 return 0
534 with catch_zombie(self):
535 return cext.proc_num_fds(self.pid)
536
537 @wrap_exceptions
538 def wait(self, timeout=None):
539 return _psposix.wait_pid(self.pid, timeout, self._name)
540
541 @wrap_exceptions
542 def nice_get(self):
543 with catch_zombie(self):
544 return cext_posix.getpriority(self.pid)
545
546 @wrap_exceptions
547 def nice_set(self, value):
548 with catch_zombie(self):
549 return cext_posix.setpriority(self.pid, value)
550
551 @wrap_exceptions
552 def status(self):
553 code = self._get_kinfo_proc()[kinfo_proc_map['status']]
554 # XXX is '?' legit? (we're not supposed to return it anyway)
555 return PROC_STATUSES.get(code, '?')
556
557 @wrap_exceptions
558 def threads(self):
559 rawlist = cext.proc_threads(self.pid)
560 retlist = []
561 for thread_id, utime, stime in rawlist:
562 ntuple = _common.pthread(thread_id, utime, stime)
563 retlist.append(ntuple)
564 return retlist