Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/psutil/_psosx.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
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 = cext_posix.getpagesize() | |
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 maxfile = maxpath = None # set later | |
205 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, | |
206 maxfile, maxpath) | |
207 retlist.append(ntuple) | |
208 return retlist | |
209 | |
210 | |
211 # ===================================================================== | |
212 # --- sensors | |
213 # ===================================================================== | |
214 | |
215 | |
216 def sensors_battery(): | |
217 """Return battery information.""" | |
218 try: | |
219 percent, minsleft, power_plugged = cext.sensors_battery() | |
220 except NotImplementedError: | |
221 # no power source - return None according to interface | |
222 return None | |
223 power_plugged = power_plugged == 1 | |
224 if power_plugged: | |
225 secsleft = _common.POWER_TIME_UNLIMITED | |
226 elif minsleft == -1: | |
227 secsleft = _common.POWER_TIME_UNKNOWN | |
228 else: | |
229 secsleft = minsleft * 60 | |
230 return _common.sbattery(percent, secsleft, power_plugged) | |
231 | |
232 | |
233 # ===================================================================== | |
234 # --- network | |
235 # ===================================================================== | |
236 | |
237 | |
238 net_io_counters = cext.net_io_counters | |
239 net_if_addrs = cext_posix.net_if_addrs | |
240 | |
241 | |
242 def net_connections(kind='inet'): | |
243 """System-wide network connections.""" | |
244 # Note: on macOS this will fail with AccessDenied unless | |
245 # the process is owned by root. | |
246 ret = [] | |
247 for pid in pids(): | |
248 try: | |
249 cons = Process(pid).connections(kind) | |
250 except NoSuchProcess: | |
251 continue | |
252 else: | |
253 if cons: | |
254 for c in cons: | |
255 c = list(c) + [pid] | |
256 ret.append(_common.sconn(*c)) | |
257 return ret | |
258 | |
259 | |
260 def net_if_stats(): | |
261 """Get NIC stats (isup, duplex, speed, mtu).""" | |
262 names = net_io_counters().keys() | |
263 ret = {} | |
264 for name in names: | |
265 try: | |
266 mtu = cext_posix.net_if_mtu(name) | |
267 isup = cext_posix.net_if_is_running(name) | |
268 duplex, speed = cext_posix.net_if_duplex_speed(name) | |
269 except OSError as err: | |
270 # https://github.com/giampaolo/psutil/issues/1279 | |
271 if err.errno != errno.ENODEV: | |
272 raise | |
273 else: | |
274 if hasattr(_common, 'NicDuplex'): | |
275 duplex = _common.NicDuplex(duplex) | |
276 ret[name] = _common.snicstats(isup, duplex, speed, mtu) | |
277 return ret | |
278 | |
279 | |
280 # ===================================================================== | |
281 # --- other system functions | |
282 # ===================================================================== | |
283 | |
284 | |
285 def boot_time(): | |
286 """The system boot time expressed in seconds since the epoch.""" | |
287 return cext.boot_time() | |
288 | |
289 | |
290 def users(): | |
291 """Return currently connected users as a list of namedtuples.""" | |
292 retlist = [] | |
293 rawlist = cext.users() | |
294 for item in rawlist: | |
295 user, tty, hostname, tstamp, pid = item | |
296 if tty == '~': | |
297 continue # reboot or shutdown | |
298 if not tstamp: | |
299 continue | |
300 nt = _common.suser(user, tty or None, hostname or None, tstamp, pid) | |
301 retlist.append(nt) | |
302 return retlist | |
303 | |
304 | |
305 # ===================================================================== | |
306 # --- processes | |
307 # ===================================================================== | |
308 | |
309 | |
310 def pids(): | |
311 ls = cext.pids() | |
312 if 0 not in ls: | |
313 # On certain macOS versions pids() C doesn't return PID 0 but | |
314 # "ps" does and the process is querable via sysctl(): | |
315 # https://travis-ci.org/giampaolo/psutil/jobs/309619941 | |
316 try: | |
317 Process(0).create_time() | |
318 ls.insert(0, 0) | |
319 except NoSuchProcess: | |
320 pass | |
321 except AccessDenied: | |
322 ls.insert(0, 0) | |
323 return ls | |
324 | |
325 | |
326 pid_exists = _psposix.pid_exists | |
327 | |
328 | |
329 def is_zombie(pid): | |
330 try: | |
331 st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']] | |
332 return st == cext.SZOMB | |
333 except Exception: | |
334 return False | |
335 | |
336 | |
337 def wrap_exceptions(fun): | |
338 """Decorator which translates bare OSError exceptions into | |
339 NoSuchProcess and AccessDenied. | |
340 """ | |
341 @functools.wraps(fun) | |
342 def wrapper(self, *args, **kwargs): | |
343 try: | |
344 return fun(self, *args, **kwargs) | |
345 except ProcessLookupError: | |
346 if is_zombie(self.pid): | |
347 raise ZombieProcess(self.pid, self._name, self._ppid) | |
348 else: | |
349 raise NoSuchProcess(self.pid, self._name) | |
350 except PermissionError: | |
351 raise AccessDenied(self.pid, self._name) | |
352 except cext.ZombieProcessError: | |
353 raise ZombieProcess(self.pid, self._name, self._ppid) | |
354 return wrapper | |
355 | |
356 | |
357 @contextlib.contextmanager | |
358 def catch_zombie(proc): | |
359 """There are some poor C APIs which incorrectly raise ESRCH when | |
360 the process is still alive or it's a zombie, or even RuntimeError | |
361 (those who don't set errno). This is here in order to solve: | |
362 https://github.com/giampaolo/psutil/issues/1044 | |
363 """ | |
364 try: | |
365 yield | |
366 except (OSError, RuntimeError) as err: | |
367 if isinstance(err, RuntimeError) or err.errno == errno.ESRCH: | |
368 try: | |
369 # status() is not supposed to lie and correctly detect | |
370 # zombies so if it raises ESRCH it's true. | |
371 status = proc.status() | |
372 except NoSuchProcess: | |
373 raise err | |
374 else: | |
375 if status == _common.STATUS_ZOMBIE: | |
376 raise ZombieProcess(proc.pid, proc._name, proc._ppid) | |
377 else: | |
378 raise AccessDenied(proc.pid, proc._name) | |
379 else: | |
380 raise | |
381 | |
382 | |
383 class Process(object): | |
384 """Wrapper class around underlying C implementation.""" | |
385 | |
386 __slots__ = ["pid", "_name", "_ppid", "_cache"] | |
387 | |
388 def __init__(self, pid): | |
389 self.pid = pid | |
390 self._name = None | |
391 self._ppid = None | |
392 | |
393 @wrap_exceptions | |
394 @memoize_when_activated | |
395 def _get_kinfo_proc(self): | |
396 # Note: should work with all PIDs without permission issues. | |
397 ret = cext.proc_kinfo_oneshot(self.pid) | |
398 assert len(ret) == len(kinfo_proc_map) | |
399 return ret | |
400 | |
401 @wrap_exceptions | |
402 @memoize_when_activated | |
403 def _get_pidtaskinfo(self): | |
404 # Note: should work for PIDs owned by user only. | |
405 with catch_zombie(self): | |
406 ret = cext.proc_pidtaskinfo_oneshot(self.pid) | |
407 assert len(ret) == len(pidtaskinfo_map) | |
408 return ret | |
409 | |
410 def oneshot_enter(self): | |
411 self._get_kinfo_proc.cache_activate(self) | |
412 self._get_pidtaskinfo.cache_activate(self) | |
413 | |
414 def oneshot_exit(self): | |
415 self._get_kinfo_proc.cache_deactivate(self) | |
416 self._get_pidtaskinfo.cache_deactivate(self) | |
417 | |
418 @wrap_exceptions | |
419 def name(self): | |
420 name = self._get_kinfo_proc()[kinfo_proc_map['name']] | |
421 return name if name is not None else cext.proc_name(self.pid) | |
422 | |
423 @wrap_exceptions | |
424 def exe(self): | |
425 with catch_zombie(self): | |
426 return cext.proc_exe(self.pid) | |
427 | |
428 @wrap_exceptions | |
429 def cmdline(self): | |
430 with catch_zombie(self): | |
431 return cext.proc_cmdline(self.pid) | |
432 | |
433 @wrap_exceptions | |
434 def environ(self): | |
435 with catch_zombie(self): | |
436 return parse_environ_block(cext.proc_environ(self.pid)) | |
437 | |
438 @wrap_exceptions | |
439 def ppid(self): | |
440 self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']] | |
441 return self._ppid | |
442 | |
443 @wrap_exceptions | |
444 def cwd(self): | |
445 with catch_zombie(self): | |
446 return cext.proc_cwd(self.pid) | |
447 | |
448 @wrap_exceptions | |
449 def uids(self): | |
450 rawtuple = self._get_kinfo_proc() | |
451 return _common.puids( | |
452 rawtuple[kinfo_proc_map['ruid']], | |
453 rawtuple[kinfo_proc_map['euid']], | |
454 rawtuple[kinfo_proc_map['suid']]) | |
455 | |
456 @wrap_exceptions | |
457 def gids(self): | |
458 rawtuple = self._get_kinfo_proc() | |
459 return _common.puids( | |
460 rawtuple[kinfo_proc_map['rgid']], | |
461 rawtuple[kinfo_proc_map['egid']], | |
462 rawtuple[kinfo_proc_map['sgid']]) | |
463 | |
464 @wrap_exceptions | |
465 def terminal(self): | |
466 tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']] | |
467 tmap = _psposix.get_terminal_map() | |
468 try: | |
469 return tmap[tty_nr] | |
470 except KeyError: | |
471 return None | |
472 | |
473 @wrap_exceptions | |
474 def memory_info(self): | |
475 rawtuple = self._get_pidtaskinfo() | |
476 return pmem( | |
477 rawtuple[pidtaskinfo_map['rss']], | |
478 rawtuple[pidtaskinfo_map['vms']], | |
479 rawtuple[pidtaskinfo_map['pfaults']], | |
480 rawtuple[pidtaskinfo_map['pageins']], | |
481 ) | |
482 | |
483 @wrap_exceptions | |
484 def memory_full_info(self): | |
485 basic_mem = self.memory_info() | |
486 uss = cext.proc_memory_uss(self.pid) | |
487 return pfullmem(*basic_mem + (uss, )) | |
488 | |
489 @wrap_exceptions | |
490 def cpu_times(self): | |
491 rawtuple = self._get_pidtaskinfo() | |
492 return _common.pcputimes( | |
493 rawtuple[pidtaskinfo_map['cpuutime']], | |
494 rawtuple[pidtaskinfo_map['cpustime']], | |
495 # children user / system times are not retrievable (set to 0) | |
496 0.0, 0.0) | |
497 | |
498 @wrap_exceptions | |
499 def create_time(self): | |
500 return self._get_kinfo_proc()[kinfo_proc_map['ctime']] | |
501 | |
502 @wrap_exceptions | |
503 def num_ctx_switches(self): | |
504 # Unvoluntary value seems not to be available; | |
505 # getrusage() numbers seems to confirm this theory. | |
506 # We set it to 0. | |
507 vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']] | |
508 return _common.pctxsw(vol, 0) | |
509 | |
510 @wrap_exceptions | |
511 def num_threads(self): | |
512 return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']] | |
513 | |
514 @wrap_exceptions | |
515 def open_files(self): | |
516 if self.pid == 0: | |
517 return [] | |
518 files = [] | |
519 with catch_zombie(self): | |
520 rawlist = cext.proc_open_files(self.pid) | |
521 for path, fd in rawlist: | |
522 if isfile_strict(path): | |
523 ntuple = _common.popenfile(path, fd) | |
524 files.append(ntuple) | |
525 return files | |
526 | |
527 @wrap_exceptions | |
528 def connections(self, kind='inet'): | |
529 if kind not in conn_tmap: | |
530 raise ValueError("invalid %r kind argument; choose between %s" | |
531 % (kind, ', '.join([repr(x) for x in conn_tmap]))) | |
532 families, types = conn_tmap[kind] | |
533 with catch_zombie(self): | |
534 rawlist = cext.proc_connections(self.pid, families, types) | |
535 ret = [] | |
536 for item in rawlist: | |
537 fd, fam, type, laddr, raddr, status = item | |
538 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, | |
539 TCP_STATUSES) | |
540 ret.append(nt) | |
541 return ret | |
542 | |
543 @wrap_exceptions | |
544 def num_fds(self): | |
545 if self.pid == 0: | |
546 return 0 | |
547 with catch_zombie(self): | |
548 return cext.proc_num_fds(self.pid) | |
549 | |
550 @wrap_exceptions | |
551 def wait(self, timeout=None): | |
552 return _psposix.wait_pid(self.pid, timeout, self._name) | |
553 | |
554 @wrap_exceptions | |
555 def nice_get(self): | |
556 with catch_zombie(self): | |
557 return cext_posix.getpriority(self.pid) | |
558 | |
559 @wrap_exceptions | |
560 def nice_set(self, value): | |
561 with catch_zombie(self): | |
562 return cext_posix.setpriority(self.pid, value) | |
563 | |
564 @wrap_exceptions | |
565 def status(self): | |
566 code = self._get_kinfo_proc()[kinfo_proc_map['status']] | |
567 # XXX is '?' legit? (we're not supposed to return it anyway) | |
568 return PROC_STATUSES.get(code, '?') | |
569 | |
570 @wrap_exceptions | |
571 def threads(self): | |
572 rawlist = cext.proc_threads(self.pid) | |
573 retlist = [] | |
574 for thread_id, utime, stime in rawlist: | |
575 ntuple = _common.pthread(thread_id, utime, stime) | |
576 retlist.append(ntuple) | |
577 return retlist |