codekingpro/portable-devtools
114k
1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""psutil is a cross-platform library for retrieving information on
8running processes and system utilization (CPU, memory, disks, network,
9sensors) in Python. Supported platforms:
10
11 - Linux
12 - Windows
13 - macOS
14 - FreeBSD
15 - OpenBSD
16 - NetBSD
17 - Sun Solaris
18 - AIX
19
20Works with Python versions 2.7 and 3.6+.
21"""
22
23from __future__ import division
24
25import collections
26import contextlib
27import datetime
28import functools
29import os
30import signal
31import subprocess
32import sys
33import threading
34import time
35
36
37try:
38 import pwd
39except ImportError:
40 pwd = None
41
42from . import _common
43from ._common import AIX
44from ._common import BSD
45from ._common import CONN_CLOSE
46from ._common import CONN_CLOSE_WAIT
47from ._common import CONN_CLOSING
48from ._common import CONN_ESTABLISHED
49from ._common import CONN_FIN_WAIT1
50from ._common import CONN_FIN_WAIT2
51from ._common import CONN_LAST_ACK
52from ._common import CONN_LISTEN
53from ._common import CONN_NONE
54from ._common import CONN_SYN_RECV
55from ._common import CONN_SYN_SENT
56from ._common import CONN_TIME_WAIT
57from ._common import FREEBSD # NOQA
58from ._common import LINUX
59from ._common import MACOS
60from ._common import NETBSD # NOQA
61from ._common import NIC_DUPLEX_FULL
62from ._common import NIC_DUPLEX_HALF
63from ._common import NIC_DUPLEX_UNKNOWN
64from ._common import OPENBSD # NOQA
65from ._common import OSX # deprecated alias
66from ._common import POSIX # NOQA
67from ._common import POWER_TIME_UNKNOWN
68from ._common import POWER_TIME_UNLIMITED
69from ._common import STATUS_DEAD
70from ._common import STATUS_DISK_SLEEP
71from ._common import STATUS_IDLE
72from ._common import STATUS_LOCKED
73from ._common import STATUS_PARKED
74from ._common import STATUS_RUNNING
75from ._common import STATUS_SLEEPING
76from ._common import STATUS_STOPPED
77from ._common import STATUS_TRACING_STOP
78from ._common import STATUS_WAITING
79from ._common import STATUS_WAKING
80from ._common import STATUS_ZOMBIE
81from ._common import SUNOS
82from ._common import WINDOWS
83from ._common import AccessDenied
84from ._common import Error
85from ._common import NoSuchProcess
86from ._common import TimeoutExpired
87from ._common import ZombieProcess
88from ._common import memoize_when_activated
89from ._common import wrap_numbers as _wrap_numbers
90from ._compat import PY3 as _PY3
91from ._compat import PermissionError
92from ._compat import ProcessLookupError
93from ._compat import SubprocessTimeoutExpired as _SubprocessTimeoutExpired
94from ._compat import long
95
96
97if LINUX:
98 # This is public API and it will be retrieved from _pslinux.py
99 # via sys.modules.
100 PROCFS_PATH = "/proc"
101
102 from . import _pslinux as _psplatform
103 from ._pslinux import IOPRIO_CLASS_BE # NOQA
104 from ._pslinux import IOPRIO_CLASS_IDLE # NOQA
105 from ._pslinux import IOPRIO_CLASS_NONE # NOQA
106 from ._pslinux import IOPRIO_CLASS_RT # NOQA
107
108elif WINDOWS:
109 from . import _pswindows as _psplatform
110 from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS # NOQA
111 from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS # NOQA
112 from ._psutil_windows import HIGH_PRIORITY_CLASS # NOQA
113 from ._psutil_windows import IDLE_PRIORITY_CLASS # NOQA
114 from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
115 from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
116 from ._pswindows import CONN_DELETE_TCB # NOQA
117 from ._pswindows import IOPRIO_HIGH # NOQA
118 from ._pswindows import IOPRIO_LOW # NOQA
119 from ._pswindows import IOPRIO_NORMAL # NOQA
120 from ._pswindows import IOPRIO_VERYLOW # NOQA
121
122elif MACOS:
123 from . import _psosx as _psplatform
124
125elif BSD:
126 from . import _psbsd as _psplatform
127
128elif SUNOS:
129 from . import _pssunos as _psplatform
130 from ._pssunos import CONN_BOUND # NOQA
131 from ._pssunos import CONN_IDLE # NOQA
132
133 # This is public writable API which is read from _pslinux.py and
134 # _pssunos.py via sys.modules.
135 PROCFS_PATH = "/proc"
136
137elif AIX:
138 from . import _psaix as _psplatform
139
140 # This is public API and it will be retrieved from _pslinux.py
141 # via sys.modules.
142 PROCFS_PATH = "/proc"
143
144else: # pragma: no cover
145 raise NotImplementedError('platform %s is not supported' % sys.platform)
146
147
148# fmt: off
149__all__ = [
150 # exceptions
151 "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
152 "TimeoutExpired",
153
154 # constants
155 "version_info", "__version__",
156
157 "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
158 "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
159 "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED",
160 "STATUS_PARKED",
161
162 "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
163 "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
164 "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE",
165 # "CONN_IDLE", "CONN_BOUND",
166
167 "AF_LINK",
168
169 "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN",
170
171 "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
172
173 "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX",
174 "SUNOS", "WINDOWS", "AIX",
175
176 # "RLIM_INFINITY", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA",
177 # "RLIMIT_FSIZE", "RLIMIT_LOCKS", "RLIMIT_MEMLOCK", "RLIMIT_NOFILE",
178 # "RLIMIT_NPROC", "RLIMIT_RSS", "RLIMIT_STACK", "RLIMIT_MSGQUEUE",
179 # "RLIMIT_NICE", "RLIMIT_RTPRIO", "RLIMIT_RTTIME", "RLIMIT_SIGPENDING",
180
181 # classes
182 "Process", "Popen",
183
184 # functions
185 "pid_exists", "pids", "process_iter", "wait_procs", # proc
186 "virtual_memory", "swap_memory", # memory
187 "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
188 "cpu_stats", # "cpu_freq", "getloadavg"
189 "net_io_counters", "net_connections", "net_if_addrs", # network
190 "net_if_stats",
191 "disk_io_counters", "disk_partitions", "disk_usage", # disk
192 # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
193 "users", "boot_time", # others
194]
195# fmt: on
196
197
198__all__.extend(_psplatform.__extra__all__)
199
200# Linux, FreeBSD
201if hasattr(_psplatform.Process, "rlimit"):
202 # Populate global namespace with RLIM* constants.
203 from . import _psutil_posix
204
205 _globals = globals()
206 _name = None
207 for _name in dir(_psutil_posix):
208 if _name.startswith('RLIM') and _name.isupper():
209 _globals[_name] = getattr(_psutil_posix, _name)
210 __all__.append(_name)
211 del _globals, _name
212
213AF_LINK = _psplatform.AF_LINK
214
215__author__ = "Giampaolo Rodola'"
216__version__ = "5.9.8"
217version_info = tuple([int(num) for num in __version__.split('.')])
218
219_timer = getattr(time, 'monotonic', time.time)
220_TOTAL_PHYMEM = None
221_LOWEST_PID = None
222_SENTINEL = object()
223
224# Sanity check in case the user messed up with psutil installation
225# or did something weird with sys.path. In this case we might end
226# up importing a python module using a C extension module which
227# was compiled for a different version of psutil.
228# We want to prevent that by failing sooner rather than later.
229# See: https://github.com/giampaolo/psutil/issues/564
230if int(__version__.replace('.', '')) != getattr(
231 _psplatform.cext, 'version', None
232):
233 msg = "version conflict: %r C extension " % _psplatform.cext.__file__
234 msg += "module was built for another version of psutil"
235 if hasattr(_psplatform.cext, 'version'):
236 msg += " (%s instead of %s)" % (
237 '.'.join([x for x in str(_psplatform.cext.version)]),
238 __version__,
239 )
240 else:
241 msg += " (different than %s)" % __version__
242 msg += "; you may try to 'pip uninstall psutil', manually remove %s" % (
243 getattr(
244 _psplatform.cext,
245 "__file__",
246 "the existing psutil install directory",
247 )
248 )
249 msg += " or clean the virtual env somehow, then reinstall"
250 raise ImportError(msg)
251
252
253# =====================================================================
254# --- Utils
255# =====================================================================
256
257
258if hasattr(_psplatform, 'ppid_map'):
259 # Faster version (Windows and Linux).
260 _ppid_map = _psplatform.ppid_map
261else: # pragma: no cover
262
263 def _ppid_map():
264 """Return a {pid: ppid, ...} dict for all running processes in
265 one shot. Used to speed up Process.children().
266 """
267 ret = {}
268 for pid in pids():
269 try:
270 ret[pid] = _psplatform.Process(pid).ppid()
271 except (NoSuchProcess, ZombieProcess):
272 pass
273 return ret
274
275
276def _pprint_secs(secs):
277 """Format seconds in a human readable form."""
278 now = time.time()
279 secs_ago = int(now - secs)
280 fmt = "%H:%M:%S" if secs_ago < 60 * 60 * 24 else "%Y-%m-%d %H:%M:%S"
281 return datetime.datetime.fromtimestamp(secs).strftime(fmt)
282
283
284# =====================================================================
285# --- Process class
286# =====================================================================
287
288
289class Process(object): # noqa: UP004
290 """Represents an OS process with the given PID.
291 If PID is omitted current process PID (os.getpid()) is used.
292 Raise NoSuchProcess if PID does not exist.
293
294 Note that most of the methods of this class do not make sure
295 the PID of the process being queried has been reused over time.
296 That means you might end up retrieving an information referring
297 to another process in case the original one this instance
298 refers to is gone in the meantime.
299
300 The only exceptions for which process identity is pre-emptively
301 checked and guaranteed are:
302
303 - parent()
304 - children()
305 - nice() (set)
306 - ionice() (set)
307 - rlimit() (set)
308 - cpu_affinity (set)
309 - suspend()
310 - resume()
311 - send_signal()
312 - terminate()
313 - kill()
314
315 To prevent this problem for all other methods you can:
316 - use is_running() before querying the process
317 - if you're continuously iterating over a set of Process
318 instances use process_iter() which pre-emptively checks
319 process identity for every yielded instance
320 """
321
322 def __init__(self, pid=None):
323 self._init(pid)
324
325 def _init(self, pid, _ignore_nsp=False):
326 if pid is None:
327 pid = os.getpid()
328 else:
329 if not _PY3 and not isinstance(pid, (int, long)):
330 msg = "pid must be an integer (got %r)" % pid
331 raise TypeError(msg)
332 if pid < 0:
333 msg = "pid must be a positive integer (got %s)" % pid
334 raise ValueError(msg)
335 try:
336 _psplatform.cext.check_pid_range(pid)
337 except OverflowError:
338 msg = "process PID out of range (got %s)" % pid
339 raise NoSuchProcess(pid, msg=msg)
340
341 self._pid = pid
342 self._name = None
343 self._exe = None
344 self._create_time = None
345 self._gone = False
346 self._pid_reused = False
347 self._hash = None
348 self._lock = threading.RLock()
349 # used for caching on Windows only (on POSIX ppid may change)
350 self._ppid = None
351 # platform-specific modules define an _psplatform.Process
352 # implementation class
353 self._proc = _psplatform.Process(pid)
354 self._last_sys_cpu_times = None
355 self._last_proc_cpu_times = None
356 self._exitcode = _SENTINEL
357 # cache creation time for later use in is_running() method
358 try:
359 self.create_time()
360 except AccessDenied:
361 # We should never get here as AFAIK we're able to get
362 # process creation time on all platforms even as a
363 # limited user.
364 pass
365 except ZombieProcess:
366 # Zombies can still be queried by this class (although
367 # not always) and pids() return them so just go on.
368 pass
369 except NoSuchProcess:
370 if not _ignore_nsp:
371 msg = "process PID not found"
372 raise NoSuchProcess(pid, msg=msg)
373 else:
374 self._gone = True
375 # This pair is supposed to identify a Process instance
376 # univocally over time (the PID alone is not enough as
377 # it might refer to a process whose PID has been reused).
378 # This will be used later in __eq__() and is_running().
379 self._ident = (self.pid, self._create_time)
380
381 def __str__(self):
382 info = collections.OrderedDict()
383 info["pid"] = self.pid
384 if self._name:
385 info['name'] = self._name
386 with self.oneshot():
387 try:
388 info["name"] = self.name()
389 info["status"] = self.status()
390 except ZombieProcess:
391 info["status"] = "zombie"
392 except NoSuchProcess:
393 info["status"] = "terminated"
394 except AccessDenied:
395 pass
396 if self._exitcode not in (_SENTINEL, None):
397 info["exitcode"] = self._exitcode
398 if self._create_time is not None:
399 info['started'] = _pprint_secs(self._create_time)
400 return "%s.%s(%s)" % (
401 self.__class__.__module__,
402 self.__class__.__name__,
403 ", ".join(["%s=%r" % (k, v) for k, v in info.items()]),
404 )
405
406 __repr__ = __str__
407
408 def __eq__(self, other):
409 # Test for equality with another Process object based
410 # on PID and creation time.
411 if not isinstance(other, Process):
412 return NotImplemented
413 if OPENBSD or NETBSD: # pragma: no cover
414 # Zombie processes on Open/NetBSD have a creation time of
415 # 0.0. This covers the case when a process started normally
416 # (so it has a ctime), then it turned into a zombie. It's
417 # important to do this because is_running() depends on
418 # __eq__.
419 pid1, ctime1 = self._ident
420 pid2, ctime2 = other._ident
421 if pid1 == pid2:
422 if ctime1 and not ctime2:
423 try:
424 return self.status() == STATUS_ZOMBIE
425 except Error:
426 pass
427 return self._ident == other._ident
428
429 def __ne__(self, other):
430 return not self == other
431
432 def __hash__(self):
433 if self._hash is None:
434 self._hash = hash(self._ident)
435 return self._hash
436
437 def _raise_if_pid_reused(self):
438 """Raises NoSuchProcess in case process PID has been reused."""
439 if not self.is_running() and self._pid_reused:
440 # We may directly raise NSP in here already if PID is just
441 # not running, but I prefer NSP to be raised naturally by
442 # the actual Process API call. This way unit tests will tell
443 # us if the API is broken (aka don't raise NSP when it
444 # should). We also remain consistent with all other "get"
445 # APIs which don't use _raise_if_pid_reused().
446 msg = "process no longer exists and its PID has been reused"
447 raise NoSuchProcess(self.pid, self._name, msg=msg)
448
449 @property
450 def pid(self):
451 """The process PID."""
452 return self._pid
453
454 # --- utility methods
455
456 @contextlib.contextmanager
457 def oneshot(self):
458 """Utility context manager which considerably speeds up the
459 retrieval of multiple process information at the same time.
460
461 Internally different process info (e.g. name, ppid, uids,
462 gids, ...) may be fetched by using the same routine, but
463 only one information is returned and the others are discarded.
464 When using this context manager the internal routine is
465 executed once (in the example below on name()) and the
466 other info are cached.
467
468 The cache is cleared when exiting the context manager block.
469 The advice is to use this every time you retrieve more than
470 one information about the process. If you're lucky, you'll
471 get a hell of a speedup.
472
473 >>> import psutil
474 >>> p = psutil.Process()
475 >>> with p.oneshot():
476 ... p.name() # collect multiple info
477 ... p.cpu_times() # return cached value
478 ... p.cpu_percent() # return cached value
479 ... p.create_time() # return cached value
480 ...
481 >>>
482 """
483 with self._lock:
484 if hasattr(self, "_cache"):
485 # NOOP: this covers the use case where the user enters the
486 # context twice:
487 #
488 # >>> with p.oneshot():
489 # ... with p.oneshot():
490 # ...
491 #
492 # Also, since as_dict() internally uses oneshot()
493 # I expect that the code below will be a pretty common
494 # "mistake" that the user will make, so let's guard
495 # against that:
496 #
497 # >>> with p.oneshot():
498 # ... p.as_dict()
499 # ...
500 yield
501 else:
502 try:
503 # cached in case cpu_percent() is used
504 self.cpu_times.cache_activate(self)
505 # cached in case memory_percent() is used
506 self.memory_info.cache_activate(self)
507 # cached in case parent() is used
508 self.ppid.cache_activate(self)
509 # cached in case username() is used
510 if POSIX:
511 self.uids.cache_activate(self)
512 # specific implementation cache
513 self._proc.oneshot_enter()
514 yield
515 finally:
516 self.cpu_times.cache_deactivate(self)
517 self.memory_info.cache_deactivate(self)
518 self.ppid.cache_deactivate(self)
519 if POSIX:
520 self.uids.cache_deactivate(self)
521 self._proc.oneshot_exit()
522
523 def as_dict(self, attrs=None, ad_value=None):
524 """Utility method returning process information as a
525 hashable dictionary.
526 If *attrs* is specified it must be a list of strings
527 reflecting available Process class' attribute names
528 (e.g. ['cpu_times', 'name']) else all public (read
529 only) attributes are assumed.
530 *ad_value* is the value which gets assigned in case
531 AccessDenied or ZombieProcess exception is raised when
532 retrieving that particular process information.
533 """
534 valid_names = _as_dict_attrnames
535 if attrs is not None:
536 if not isinstance(attrs, (list, tuple, set, frozenset)):
537 msg = "invalid attrs type %s" % type(attrs)
538 raise TypeError(msg)
539 attrs = set(attrs)
540 invalid_names = attrs - valid_names
541 if invalid_names:
542 msg = "invalid attr name%s %s" % (
543 "s" if len(invalid_names) > 1 else "",
544 ", ".join(map(repr, invalid_names)),
545 )
546 raise ValueError(msg)
547
548 retdict = {}
549 ls = attrs or valid_names
550 with self.oneshot():
551 for name in ls:
552 try:
553 if name == 'pid':
554 ret = self.pid
555 else:
556 meth = getattr(self, name)
557 ret = meth()
558 except (AccessDenied, ZombieProcess):
559 ret = ad_value
560 except NotImplementedError:
561 # in case of not implemented functionality (may happen
562 # on old or exotic systems) we want to crash only if
563 # the user explicitly asked for that particular attr
564 if attrs:
565 raise
566 continue
567 retdict[name] = ret
568 return retdict
569
570 def parent(self):
571 """Return the parent process as a Process object pre-emptively
572 checking whether PID has been reused.
573 If no parent is known return None.
574 """
575 lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0]
576 if self.pid == lowest_pid:
577 return None
578 ppid = self.ppid()
579 if ppid is not None:
580 ctime = self.create_time()
581 try:
582 parent = Process(ppid)
583 if parent.create_time() <= ctime:
584 return parent
585 # ...else ppid has been reused by another process
586 except NoSuchProcess:
587 pass
588
589 def parents(self):
590 """Return the parents of this process as a list of Process
591 instances. If no parents are known return an empty list.
592 """
593 parents = []
594 proc = self.parent()
595 while proc is not None:
596 parents.append(proc)
597 proc = proc.parent()
598 return parents
599
600 def is_running(self):
601 """Return whether this process is running.
602 It also checks if PID has been reused by another process in
603 which case return False.
604 """
605 if self._gone or self._pid_reused:
606 return False
607 try:
608 # Checking if PID is alive is not enough as the PID might
609 # have been reused by another process: we also want to
610 # verify process identity.
611 # Process identity / uniqueness over time is guaranteed by
612 # (PID + creation time) and that is verified in __eq__.
613 self._pid_reused = self != Process(self.pid)
614 return not self._pid_reused
615 except ZombieProcess:
616 # We should never get here as it's already handled in
617 # Process.__init__; here just for extra safety.
618 return True
619 except NoSuchProcess:
620 self._gone = True
621 return False
622
623 # --- actual API
624
625 @memoize_when_activated
626 def ppid(self):
627 """The process parent PID.
628 On Windows the return value is cached after first call.
629 """
630 # On POSIX we don't want to cache the ppid as it may unexpectedly
631 # change to 1 (init) in case this process turns into a zombie:
632 # https://github.com/giampaolo/psutil/issues/321
633 # http://stackoverflow.com/questions/356722/
634
635 # XXX should we check creation time here rather than in
636 # Process.parent()?
637 self._raise_if_pid_reused()
638 if POSIX:
639 return self._proc.ppid()
640 else: # pragma: no cover
641 self._ppid = self._ppid or self._proc.ppid()
642 return self._ppid
643
644 def name(self):
645 """The process name. The return value is cached after first call."""
646 # Process name is only cached on Windows as on POSIX it may
647 # change, see:
648 # https://github.com/giampaolo/psutil/issues/692
649 if WINDOWS and self._name is not None:
650 return self._name
651 name = self._proc.name()
652 if POSIX and len(name) >= 15:
653 # On UNIX the name gets truncated to the first 15 characters.
654 # If it matches the first part of the cmdline we return that
655 # one instead because it's usually more explicative.
656 # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon".
657 try:
658 cmdline = self.cmdline()
659 except (AccessDenied, ZombieProcess):
660 # Just pass and return the truncated name: it's better
661 # than nothing. Note: there are actual cases where a
662 # zombie process can return a name() but not a
663 # cmdline(), see:
664 # https://github.com/giampaolo/psutil/issues/2239
665 pass
666 else:
667 if cmdline:
668 extended_name = os.path.basename(cmdline[0])
669 if extended_name.startswith(name):
670 name = extended_name
671 self._name = name
672 self._proc._name = name
673 return name
674
675 def exe(self):
676 """The process executable as an absolute path.
677 May also be an empty string.
678 The return value is cached after first call.
679 """
680
681 def guess_it(fallback):
682 # try to guess exe from cmdline[0] in absence of a native
683 # exe representation
684 cmdline = self.cmdline()
685 if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'):
686 exe = cmdline[0] # the possible exe
687 # Attempt to guess only in case of an absolute path.
688 # It is not safe otherwise as the process might have
689 # changed cwd.
690 if (
691 os.path.isabs(exe)
692 and os.path.isfile(exe)
693 and os.access(exe, os.X_OK)
694 ):
695 return exe
696 if isinstance(fallback, AccessDenied):
697 raise fallback
698 return fallback
699
700 if self._exe is None:
701 try:
702 exe = self._proc.exe()
703 except AccessDenied as err:
704 return guess_it(fallback=err)
705 else:
706 if not exe:
707 # underlying implementation can legitimately return an
708 # empty string; if that's the case we don't want to
709 # raise AD while guessing from the cmdline
710 try:
711 exe = guess_it(fallback=exe)
712 except AccessDenied:
713 pass
714 self._exe = exe
715 return self._exe
716
717 def cmdline(self):
718 """The command line this process has been called with."""
719 return self._proc.cmdline()
720
721 def status(self):
722 """The process current status as a STATUS_* constant."""
723 try:
724 return self._proc.status()
725 except ZombieProcess:
726 return STATUS_ZOMBIE
727
728 def username(self):
729 """The name of the user that owns the process.
730 On UNIX this is calculated by using *real* process uid.
731 """
732 if POSIX:
733 if pwd is None:
734 # might happen if python was installed from sources
735 msg = "requires pwd module shipped with standard python"
736 raise ImportError(msg)
737 real_uid = self.uids().real
738 try:
739 return pwd.getpwuid(real_uid).pw_name
740 except KeyError:
741 # the uid can't be resolved by the system
742 return str(real_uid)
743 else:
744 return self._proc.username()
745
746 def create_time(self):
747 """The process creation time as a floating point number
748 expressed in seconds since the epoch.
749 The return value is cached after first call.
750 """
751 if self._create_time is None:
752 self._create_time = self._proc.create_time()
753 return self._create_time
754
755 def cwd(self):
756 """Process current working directory as an absolute path."""
757 return self._proc.cwd()
758
759 def nice(self, value=None):
760 """Get or set process niceness (priority)."""
761 if value is None:
762 return self._proc.nice_get()
763 else:
764 self._raise_if_pid_reused()
765 self._proc.nice_set(value)
766
767 if POSIX:
768
769 @memoize_when_activated
770 def uids(self):
771 """Return process UIDs as a (real, effective, saved)
772 namedtuple.
773 """
774 return self._proc.uids()
775
776 def gids(self):
777 """Return process GIDs as a (real, effective, saved)
778 namedtuple.
779 """
780 return self._proc.gids()
781
782 def terminal(self):
783 """The terminal associated with this process, if any,
784 else None.
785 """
786 return self._proc.terminal()
787
788 def num_fds(self):
789 """Return the number of file descriptors opened by this
790 process (POSIX only).
791 """
792 return self._proc.num_fds()
793
794 # Linux, BSD, AIX and Windows only
795 if hasattr(_psplatform.Process, "io_counters"):
796
797 def io_counters(self):
798 """Return process I/O statistics as a
799 (read_count, write_count, read_bytes, write_bytes)
800 namedtuple.
801 Those are the number of read/write calls performed and the
802 amount of bytes read and written by the process.
803 """
804 return self._proc.io_counters()
805
806 # Linux and Windows
807 if hasattr(_psplatform.Process, "ionice_get"):
808
809 def ionice(self, ioclass=None, value=None):
810 """Get or set process I/O niceness (priority).
811
812 On Linux *ioclass* is one of the IOPRIO_CLASS_* constants.
813 *value* is a number which goes from 0 to 7. The higher the
814 value, the lower the I/O priority of the process.
815
816 On Windows only *ioclass* is used and it can be set to 2
817 (normal), 1 (low) or 0 (very low).
818
819 Available on Linux and Windows > Vista only.
820 """
821 if ioclass is None:
822 if value is not None:
823 msg = "'ioclass' argument must be specified"
824 raise ValueError(msg)
825 return self._proc.ionice_get()
826 else:
827 self._raise_if_pid_reused()
828 return self._proc.ionice_set(ioclass, value)
829
830 # Linux / FreeBSD only
831 if hasattr(_psplatform.Process, "rlimit"):
832
833 def rlimit(self, resource, limits=None):
834 """Get or set process resource limits as a (soft, hard)
835 tuple.
836
837 *resource* is one of the RLIMIT_* constants.
838 *limits* is supposed to be a (soft, hard) tuple.
839
840 See "man prlimit" for further info.
841 Available on Linux and FreeBSD only.
842 """
843 if limits is not None:
844 self._raise_if_pid_reused()
845 return self._proc.rlimit(resource, limits)
846
847 # Windows, Linux and FreeBSD only
848 if hasattr(_psplatform.Process, "cpu_affinity_get"):
849
850 def cpu_affinity(self, cpus=None):
851 """Get or set process CPU affinity.
852 If specified, *cpus* must be a list of CPUs for which you
853 want to set the affinity (e.g. [0, 1]).
854 If an empty list is passed, all egible CPUs are assumed
855 (and set).
856 (Windows, Linux and BSD only).
857 """
858 if cpus is None:
859 return sorted(set(self._proc.cpu_affinity_get()))
860 else:
861 self._raise_if_pid_reused()
862 if not cpus:
863 if hasattr(self._proc, "_get_eligible_cpus"):
864 cpus = self._proc._get_eligible_cpus()
865 else:
866 cpus = tuple(range(len(cpu_times(percpu=True))))
867 self._proc.cpu_affinity_set(list(set(cpus)))
868
869 # Linux, FreeBSD, SunOS
870 if hasattr(_psplatform.Process, "cpu_num"):
871
872 def cpu_num(self):
873 """Return what CPU this process is currently running on.
874 The returned number should be <= psutil.cpu_count()
875 and <= len(psutil.cpu_percent(percpu=True)).
876 It may be used in conjunction with
877 psutil.cpu_percent(percpu=True) to observe the system
878 workload distributed across CPUs.
879 """
880 return self._proc.cpu_num()
881
882 # All platforms has it, but maybe not in the future.
883 if hasattr(_psplatform.Process, "environ"):
884
885 def environ(self):
886 """The environment variables of the process as a dict. Note: this
887 might not reflect changes made after the process started.
888 """
889 return self._proc.environ()
890
891 if WINDOWS:
892
893 def num_handles(self):
894 """Return the number of handles opened by this process
895 (Windows only).
896 """
897 return self._proc.num_handles()
898
899 def num_ctx_switches(self):
900 """Return the number of voluntary and involuntary context
901 switches performed by this process.
902 """
903 return self._proc.num_ctx_switches()
904
905 def num_threads(self):
906 """Return the number of threads used by this process."""
907 return self._proc.num_threads()
908
909 if hasattr(_psplatform.Process, "threads"):
910
911 def threads(self):
912 """Return threads opened by process as a list of
913 (id, user_time, system_time) namedtuples representing
914 thread id and thread CPU times (user/system).
915 On OpenBSD this method requires root access.
916 """
917 return self._proc.threads()
918
919 def children(self, recursive=False):
920 """Return the children of this process as a list of Process
921 instances, pre-emptively checking whether PID has been reused.
922 If *recursive* is True return all the parent descendants.
923
924 Example (A == this process):
925
926 A ─┐
927 │
928 ├─ B (child) ─┐
929 │ └─ X (grandchild) ─┐
930 │ └─ Y (great grandchild)
931 ├─ C (child)
932 └─ D (child)
933
934 >>> import psutil
935 >>> p = psutil.Process()
936 >>> p.children()
937 B, C, D
938 >>> p.children(recursive=True)
939 B, X, Y, C, D
940
941 Note that in the example above if process X disappears
942 process Y won't be listed as the reference to process A
943 is lost.
944 """
945 self._raise_if_pid_reused()
946 ppid_map = _ppid_map()
947 ret = []
948 if not recursive:
949 for pid, ppid in ppid_map.items():
950 if ppid == self.pid:
951 try:
952 child = Process(pid)
953 # if child happens to be older than its parent
954 # (self) it means child's PID has been reused
955 if self.create_time() <= child.create_time():
956 ret.append(child)
957 except (NoSuchProcess, ZombieProcess):
958 pass
959 else:
960 # Construct a {pid: [child pids]} dict
961 reverse_ppid_map = collections.defaultdict(list)
962 for pid, ppid in ppid_map.items():
963 reverse_ppid_map[ppid].append(pid)
964 # Recursively traverse that dict, starting from self.pid,
965 # such that we only call Process() on actual children
966 seen = set()
967 stack = [self.pid]
968 while stack:
969 pid = stack.pop()
970 if pid in seen:
971 # Since pids can be reused while the ppid_map is
972 # constructed, there may be rare instances where
973 # there's a cycle in the recorded process "tree".
974 continue
975 seen.add(pid)
976 for child_pid in reverse_ppid_map[pid]:
977 try:
978 child = Process(child_pid)
979 # if child happens to be older than its parent
980 # (self) it means child's PID has been reused
981 intime = self.create_time() <= child.create_time()
982 if intime:
983 ret.append(child)
984 stack.append(child_pid)
985 except (NoSuchProcess, ZombieProcess):
986 pass
987 return ret
988
989 def cpu_percent(self, interval=None):
990 """Return a float representing the current process CPU
991 utilization as a percentage.
992
993 When *interval* is 0.0 or None (default) compares process times
994 to system CPU times elapsed since last call, returning
995 immediately (non-blocking). That means that the first time
996 this is called it will return a meaningful 0.0 value.
997
998 When *interval* is > 0.0 compares process times to system CPU
999 times elapsed before and after the interval (blocking).
1000
1001 In this case is recommended for accuracy that this function
1002 be called with at least 0.1 seconds between calls.
1003
1004 A value > 100.0 can be returned in case of processes running
1005 multiple threads on different CPU cores.
1006
1007 The returned value is explicitly NOT split evenly between
1008 all available logical CPUs. This means that a busy loop process
1009 running on a system with 2 logical CPUs will be reported as
1010 having 100% CPU utilization instead of 50%.
1011
1012 Examples:
1013
1014 >>> import psutil
1015 >>> p = psutil.Process(os.getpid())
1016 >>> # blocking
1017 >>> p.cpu_percent(interval=1)
1018 2.0
1019 >>> # non-blocking (percentage since last call)
1020 >>> p.cpu_percent(interval=None)
1021 2.9
1022 >>>
1023 """
1024 blocking = interval is not None and interval > 0.0
1025 if interval is not None and interval < 0:
1026 msg = "interval is not positive (got %r)" % interval
1027 raise ValueError(msg)
1028 num_cpus = cpu_count() or 1
1029
1030 def timer():
1031 return _timer() * num_cpus
1032
1033 if blocking:
1034 st1 = timer()
1035 pt1 = self._proc.cpu_times()
1036 time.sleep(interval)
1037 st2 = timer()
1038 pt2 = self._proc.cpu_times()
1039 else:
1040 st1 = self._last_sys_cpu_times
1041 pt1 = self._last_proc_cpu_times
1042 st2 = timer()
1043 pt2 = self._proc.cpu_times()
1044 if st1 is None or pt1 is None:
1045 self._last_sys_cpu_times = st2
1046 self._last_proc_cpu_times = pt2
1047 return 0.0
1048
1049 delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system)
1050 delta_time = st2 - st1
1051 # reset values for next call in case of interval == None
1052 self._last_sys_cpu_times = st2
1053 self._last_proc_cpu_times = pt2
1054
1055 try:
1056 # This is the utilization split evenly between all CPUs.
1057 # E.g. a busy loop process on a 2-CPU-cores system at this
1058 # point is reported as 50% instead of 100%.
1059 overall_cpus_percent = (delta_proc / delta_time) * 100
1060 except ZeroDivisionError:
1061 # interval was too low
1062 return 0.0
1063 else:
1064 # Note 1:
1065 # in order to emulate "top" we multiply the value for the num
1066 # of CPU cores. This way the busy process will be reported as
1067 # having 100% (or more) usage.
1068 #
1069 # Note 2:
1070 # taskmgr.exe on Windows differs in that it will show 50%
1071 # instead.
1072 #
1073 # Note 3:
1074 # a percentage > 100 is legitimate as it can result from a
1075 # process with multiple threads running on different CPU
1076 # cores (top does the same), see:
1077 # http://stackoverflow.com/questions/1032357
1078 # https://github.com/giampaolo/psutil/issues/474
1079 single_cpu_percent = overall_cpus_percent * num_cpus
1080 return round(single_cpu_percent, 1)
1081
1082 @memoize_when_activated
1083 def cpu_times(self):
1084 """Return a (user, system, children_user, children_system)
1085 namedtuple representing the accumulated process time, in
1086 seconds.
1087 This is similar to os.times() but per-process.
1088 On macOS and Windows children_user and children_system are
1089 always set to 0.
1090 """
1091 return self._proc.cpu_times()
1092
1093 @memoize_when_activated
1094 def memory_info(self):
1095 """Return a namedtuple with variable fields depending on the
1096 platform, representing memory information about the process.
1097
1098 The "portable" fields available on all platforms are `rss` and `vms`.
1099
1100 All numbers are expressed in bytes.
1101 """
1102 return self._proc.memory_info()
1103
1104 @_common.deprecated_method(replacement="memory_info")
1105 def memory_info_ex(self):
1106 return self.memory_info()
1107
1108 def memory_full_info(self):
1109 """This method returns the same information as memory_info(),
1110 plus, on some platform (Linux, macOS, Windows), also provides
1111 additional metrics (USS, PSS and swap).
1112 The additional metrics provide a better representation of actual
1113 process memory usage.
1114
1115 Namely USS is the memory which is unique to a process and which
1116 would be freed if the process was terminated right now.
1117
1118 It does so by passing through the whole process address.
1119 As such it usually requires higher user privileges than
1120 memory_info() and is considerably slower.
1121 """
1122 return self._proc.memory_full_info()
1123
1124 def memory_percent(self, memtype="rss"):
1125 """Compare process memory to total physical system memory and
1126 calculate process memory utilization as a percentage.
1127 *memtype* argument is a string that dictates what type of
1128 process memory you want to compare against (defaults to "rss").
1129 The list of available strings can be obtained like this:
1130
1131 >>> psutil.Process().memory_info()._fields
1132 ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
1133 """
1134 valid_types = list(_psplatform.pfullmem._fields)
1135 if memtype not in valid_types:
1136 msg = "invalid memtype %r; valid types are %r" % (
1137 memtype,
1138 tuple(valid_types),
1139 )
1140 raise ValueError(msg)
1141 fun = (
1142 self.memory_info
1143 if memtype in _psplatform.pmem._fields
1144 else self.memory_full_info
1145 )
1146 metrics = fun()
1147 value = getattr(metrics, memtype)
1148
1149 # use cached value if available
1150 total_phymem = _TOTAL_PHYMEM or virtual_memory().total
1151 if not total_phymem > 0:
1152 # we should never get here
1153 msg = (
1154 "can't calculate process memory percent because total physical"
1155 " system memory is not positive (%r)" % (total_phymem)
1156 )
1157 raise ValueError(msg)
1158 return (value / float(total_phymem)) * 100
1159
1160 if hasattr(_psplatform.Process, "memory_maps"):
1161
1162 def memory_maps(self, grouped=True):
1163 """Return process' mapped memory regions as a list of namedtuples
1164 whose fields are variable depending on the platform.
1165
1166 If *grouped* is True the mapped regions with the same 'path'
1167 are grouped together and the different memory fields are summed.
1168
1169 If *grouped* is False every mapped region is shown as a single
1170 entity and the namedtuple will also include the mapped region's
1171 address space ('addr') and permission set ('perms').
1172 """
1173 it = self._proc.memory_maps()
1174 if grouped:
1175 d = {}
1176 for tupl in it:
1177 path = tupl[2]
1178 nums = tupl[3:]
1179 try:
1180 d[path] = map(lambda x, y: x + y, d[path], nums)
1181 except KeyError:
1182 d[path] = nums
1183 nt = _psplatform.pmmap_grouped
1184 return [nt(path, *d[path]) for path in d] # NOQA
1185 else:
1186 nt = _psplatform.pmmap_ext
1187 return [nt(*x) for x in it]
1188
1189 def open_files(self):
1190 """Return files opened by process as a list of
1191 (path, fd) namedtuples including the absolute file name
1192 and file descriptor number.
1193 """
1194 return self._proc.open_files()
1195
1196 def connections(self, kind='inet'):
1197 """Return socket connections opened by process as a list of
1198 (fd, family, type, laddr, raddr, status) namedtuples.
1199 The *kind* parameter filters for connections that match the
1200 following criteria:
