123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728 |
- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Sun OS Solaris platform implementation."""
- import errno
- import functools
- import os
- import socket
- import subprocess
- import sys
- from collections import namedtuple
- from socket import AF_INET
- from . import _common
- from . import _psposix
- from . import _psutil_posix as cext_posix
- from . import _psutil_sunos as cext
- from ._common import AF_INET6
- from ._common import AccessDenied
- from ._common import NoSuchProcess
- from ._common import ZombieProcess
- from ._common import debug
- from ._common import get_procfs_path
- from ._common import isfile_strict
- from ._common import memoize_when_activated
- from ._common import sockfam_to_enum
- from ._common import socktype_to_enum
- from ._common import usage_percent
- from ._compat import PY3
- from ._compat import FileNotFoundError
- from ._compat import PermissionError
- from ._compat import ProcessLookupError
- from ._compat import b
- __extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
- # =====================================================================
- # --- globals
- # =====================================================================
- PAGE_SIZE = cext_posix.getpagesize()
- AF_LINK = cext_posix.AF_LINK
- IS_64_BIT = sys.maxsize > 2**32
- CONN_IDLE = "IDLE"
- CONN_BOUND = "BOUND"
- PROC_STATUSES = {
- cext.SSLEEP: _common.STATUS_SLEEPING,
- cext.SRUN: _common.STATUS_RUNNING,
- cext.SZOMB: _common.STATUS_ZOMBIE,
- cext.SSTOP: _common.STATUS_STOPPED,
- cext.SIDL: _common.STATUS_IDLE,
- cext.SONPROC: _common.STATUS_RUNNING, # same as run
- cext.SWAIT: _common.STATUS_WAITING,
- }
- TCP_STATUSES = {
- cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
- cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
- cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
- cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
- cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
- cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
- cext.TCPS_CLOSED: _common.CONN_CLOSE,
- cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
- cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
- cext.TCPS_LISTEN: _common.CONN_LISTEN,
- cext.TCPS_CLOSING: _common.CONN_CLOSING,
- cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
- cext.TCPS_IDLE: CONN_IDLE, # sunos specific
- cext.TCPS_BOUND: CONN_BOUND, # sunos specific
- }
- proc_info_map = dict(
- ppid=0,
- rss=1,
- vms=2,
- create_time=3,
- nice=4,
- num_threads=5,
- status=6,
- ttynr=7,
- uid=8,
- euid=9,
- gid=10,
- egid=11)
- # =====================================================================
- # --- named tuples
- # =====================================================================
- # psutil.cpu_times()
- scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
- # psutil.cpu_times(percpu=True)
- pcputimes = namedtuple('pcputimes',
- ['user', 'system', 'children_user', 'children_system'])
- # psutil.virtual_memory()
- svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
- # psutil.Process.memory_info()
- pmem = namedtuple('pmem', ['rss', 'vms'])
- pfullmem = pmem
- # psutil.Process.memory_maps(grouped=True)
- pmmap_grouped = namedtuple('pmmap_grouped',
- ['path', 'rss', 'anonymous', 'locked'])
- # psutil.Process.memory_maps(grouped=False)
- pmmap_ext = namedtuple(
- 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
- # =====================================================================
- # --- memory
- # =====================================================================
- def virtual_memory():
- """Report virtual memory metrics."""
- # we could have done this with kstat, but IMHO this is good enough
- total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
- # note: there's no difference on Solaris
- free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
- used = total - free
- percent = usage_percent(used, total, round_=1)
- return svmem(total, avail, percent, used, free)
- def swap_memory():
- """Report swap memory metrics."""
- sin, sout = cext.swap_mem()
- # XXX
- # we are supposed to get total/free by doing so:
- # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
- # usr/src/cmd/swap/swap.c
- # ...nevertheless I can't manage to obtain the same numbers as 'swap'
- # cmdline utility, so let's parse its output (sigh!)
- p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
- os.environ['PATH'], 'swap', '-l'],
- stdout=subprocess.PIPE)
- stdout, _ = p.communicate()
- if PY3:
- stdout = stdout.decode(sys.stdout.encoding)
- if p.returncode != 0:
- raise RuntimeError("'swap -l' failed (retcode=%s)" % p.returncode)
- lines = stdout.strip().split('\n')[1:]
- if not lines:
- msg = 'no swap device(s) configured'
- raise RuntimeError(msg)
- total = free = 0
- for line in lines:
- line = line.split()
- t, f = line[3:5]
- total += int(int(t) * 512)
- free += int(int(f) * 512)
- used = total - free
- percent = usage_percent(used, total, round_=1)
- return _common.sswap(total, used, free, percent,
- sin * PAGE_SIZE, sout * PAGE_SIZE)
- # =====================================================================
- # --- CPU
- # =====================================================================
- def cpu_times():
- """Return system-wide CPU times as a named tuple."""
- ret = cext.per_cpu_times()
- return scputimes(*[sum(x) for x in zip(*ret)])
- def per_cpu_times():
- """Return system per-CPU times as a list of named tuples."""
- ret = cext.per_cpu_times()
- return [scputimes(*x) for x in ret]
- def cpu_count_logical():
- """Return the number of logical CPUs in the system."""
- try:
- return os.sysconf("SC_NPROCESSORS_ONLN")
- except ValueError:
- # mimic os.cpu_count() behavior
- return None
- def cpu_count_cores():
- """Return the number of CPU cores in the system."""
- return cext.cpu_count_cores()
- def cpu_stats():
- """Return various CPU stats as a named tuple."""
- ctx_switches, interrupts, syscalls, traps = cext.cpu_stats()
- soft_interrupts = 0
- return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
- syscalls)
- # =====================================================================
- # --- disks
- # =====================================================================
- disk_io_counters = cext.disk_io_counters
- disk_usage = _psposix.disk_usage
- def disk_partitions(all=False):
- """Return system disk partitions."""
- # TODO - the filtering logic should be better checked so that
- # it tries to reflect 'df' as much as possible
- retlist = []
- partitions = cext.disk_partitions()
- for partition in partitions:
- device, mountpoint, fstype, opts = partition
- if device == 'none':
- device = ''
- if not all:
- # Differently from, say, Linux, we don't have a list of
- # common fs types so the best we can do, AFAIK, is to
- # filter by filesystem having a total size > 0.
- try:
- if not disk_usage(mountpoint).total:
- continue
- except OSError as err:
- # https://github.com/giampaolo/psutil/issues/1674
- debug("skipping %r: %s" % (mountpoint, err))
- continue
- maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
- retlist.append(ntuple)
- return retlist
- # =====================================================================
- # --- network
- # =====================================================================
- net_io_counters = cext.net_io_counters
- net_if_addrs = cext_posix.net_if_addrs
- def net_connections(kind, _pid=-1):
- """Return socket connections. If pid == -1 return system-wide
- connections (as opposed to connections opened by one process only).
- Only INET sockets are returned (UNIX are not).
- """
- cmap = _common.conn_tmap.copy()
- if _pid == -1:
- cmap.pop('unix', 0)
- if kind not in cmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in cmap])))
- families, types = _common.conn_tmap[kind]
- rawlist = cext.net_connections(_pid)
- ret = set()
- for item in rawlist:
- fd, fam, type_, laddr, raddr, status, pid = item
- if fam not in families:
- continue
- if type_ not in types:
- continue
- # TODO: refactor and use _common.conn_to_ntuple.
- if fam in (AF_INET, AF_INET6):
- if laddr:
- laddr = _common.addr(*laddr)
- if raddr:
- raddr = _common.addr(*raddr)
- status = TCP_STATUSES[status]
- fam = sockfam_to_enum(fam)
- type_ = socktype_to_enum(type_)
- if _pid == -1:
- nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
- else:
- nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
- ret.add(nt)
- return list(ret)
- def net_if_stats():
- """Get NIC stats (isup, duplex, speed, mtu)."""
- ret = cext.net_if_stats()
- for name, items in ret.items():
- isup, duplex, speed, mtu = items
- if hasattr(_common, 'NicDuplex'):
- duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu, '')
- return ret
- # =====================================================================
- # --- other system functions
- # =====================================================================
- def boot_time():
- """The system boot time expressed in seconds since the epoch."""
- return cext.boot_time()
- def users():
- """Return currently connected users as a list of namedtuples."""
- retlist = []
- rawlist = cext.users()
- localhost = (':0.0', ':0')
- for item in rawlist:
- user, tty, hostname, tstamp, user_process, pid = item
- # note: the underlying C function includes entries about
- # system boot, run level and others. We might want
- # to use them in the future.
- if not user_process:
- continue
- if hostname in localhost:
- hostname = 'localhost'
- nt = _common.suser(user, tty, hostname, tstamp, pid)
- retlist.append(nt)
- return retlist
- # =====================================================================
- # --- processes
- # =====================================================================
- def pids():
- """Returns a list of PIDs currently running on the system."""
- return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()]
- def pid_exists(pid):
- """Check for the existence of a unix pid."""
- return _psposix.pid_exists(pid)
- def wrap_exceptions(fun):
- """Call callable into a try/except clause and translate ENOENT,
- EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
- """
- @functools.wraps(fun)
- def wrapper(self, *args, **kwargs):
- try:
- return fun(self, *args, **kwargs)
- except (FileNotFoundError, ProcessLookupError):
- # ENOENT (no such file or directory) gets raised on open().
- # ESRCH (no such process) can get raised on read() if
- # process is gone in meantime.
- if not pid_exists(self.pid):
- raise NoSuchProcess(self.pid, self._name)
- else:
- raise ZombieProcess(self.pid, self._name, self._ppid)
- except PermissionError:
- raise AccessDenied(self.pid, self._name)
- except OSError:
- if self.pid == 0:
- if 0 in pids():
- raise AccessDenied(self.pid, self._name)
- else:
- raise
- raise
- return wrapper
- class Process:
- """Wrapper class around underlying C implementation."""
- __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
- def __init__(self, pid):
- self.pid = pid
- self._name = None
- self._ppid = None
- self._procfs_path = get_procfs_path()
- def _assert_alive(self):
- """Raise NSP if the process disappeared on us."""
- # For those C function who do not raise NSP, possibly returning
- # incorrect or incomplete result.
- os.stat('%s/%s' % (self._procfs_path, self.pid))
- def oneshot_enter(self):
- self._proc_name_and_args.cache_activate(self)
- self._proc_basic_info.cache_activate(self)
- self._proc_cred.cache_activate(self)
- def oneshot_exit(self):
- self._proc_name_and_args.cache_deactivate(self)
- self._proc_basic_info.cache_deactivate(self)
- self._proc_cred.cache_deactivate(self)
- @wrap_exceptions
- @memoize_when_activated
- def _proc_name_and_args(self):
- return cext.proc_name_and_args(self.pid, self._procfs_path)
- @wrap_exceptions
- @memoize_when_activated
- def _proc_basic_info(self):
- if self.pid == 0 and not \
- os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
- raise AccessDenied(self.pid)
- ret = cext.proc_basic_info(self.pid, self._procfs_path)
- assert len(ret) == len(proc_info_map)
- return ret
- @wrap_exceptions
- @memoize_when_activated
- def _proc_cred(self):
- return cext.proc_cred(self.pid, self._procfs_path)
- @wrap_exceptions
- def name(self):
- # note: max len == 15
- return self._proc_name_and_args()[0]
- @wrap_exceptions
- def exe(self):
- try:
- return os.readlink(
- "%s/%s/path/a.out" % (self._procfs_path, self.pid))
- except OSError:
- pass # continue and guess the exe name from the cmdline
- # Will be guessed later from cmdline but we want to explicitly
- # invoke cmdline here in order to get an AccessDenied
- # exception if the user has not enough privileges.
- self.cmdline()
- return ""
- @wrap_exceptions
- def cmdline(self):
- return self._proc_name_and_args()[1].split(' ')
- @wrap_exceptions
- def environ(self):
- return cext.proc_environ(self.pid, self._procfs_path)
- @wrap_exceptions
- def create_time(self):
- return self._proc_basic_info()[proc_info_map['create_time']]
- @wrap_exceptions
- def num_threads(self):
- return self._proc_basic_info()[proc_info_map['num_threads']]
- @wrap_exceptions
- def nice_get(self):
- # Note #1: getpriority(3) doesn't work for realtime processes.
- # Psinfo is what ps uses, see:
- # https://github.com/giampaolo/psutil/issues/1194
- return self._proc_basic_info()[proc_info_map['nice']]
- @wrap_exceptions
- def nice_set(self, value):
- if self.pid in (2, 3):
- # Special case PIDs: internally setpriority(3) return ESRCH
- # (no such process), no matter what.
- # The process actually exists though, as it has a name,
- # creation time, etc.
- raise AccessDenied(self.pid, self._name)
- return cext_posix.setpriority(self.pid, value)
- @wrap_exceptions
- def ppid(self):
- self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
- return self._ppid
- @wrap_exceptions
- def uids(self):
- try:
- real, effective, saved, _, _, _ = self._proc_cred()
- except AccessDenied:
- real = self._proc_basic_info()[proc_info_map['uid']]
- effective = self._proc_basic_info()[proc_info_map['euid']]
- saved = None
- return _common.puids(real, effective, saved)
- @wrap_exceptions
- def gids(self):
- try:
- _, _, _, real, effective, saved = self._proc_cred()
- except AccessDenied:
- real = self._proc_basic_info()[proc_info_map['gid']]
- effective = self._proc_basic_info()[proc_info_map['egid']]
- saved = None
- return _common.puids(real, effective, saved)
- @wrap_exceptions
- def cpu_times(self):
- try:
- times = cext.proc_cpu_times(self.pid, self._procfs_path)
- except OSError as err:
- if err.errno == errno.EOVERFLOW and not IS_64_BIT:
- # We may get here if we attempt to query a 64bit process
- # with a 32bit python.
- # Error originates from read() and also tools like "cat"
- # fail in the same way (!).
- # Since there simply is no way to determine CPU times we
- # return 0.0 as a fallback. See:
- # https://github.com/giampaolo/psutil/issues/857
- times = (0.0, 0.0, 0.0, 0.0)
- else:
- raise
- return _common.pcputimes(*times)
- @wrap_exceptions
- def cpu_num(self):
- return cext.proc_cpu_num(self.pid, self._procfs_path)
- @wrap_exceptions
- def terminal(self):
- procfs_path = self._procfs_path
- hit_enoent = False
- tty = wrap_exceptions(
- self._proc_basic_info()[proc_info_map['ttynr']])
- if tty != cext.PRNODEV:
- for x in (0, 1, 2, 255):
- try:
- return os.readlink(
- '%s/%d/path/%d' % (procfs_path, self.pid, x))
- except FileNotFoundError:
- hit_enoent = True
- continue
- if hit_enoent:
- self._assert_alive()
- @wrap_exceptions
- def cwd(self):
- # /proc/PID/path/cwd may not be resolved by readlink() even if
- # it exists (ls shows it). If that's the case and the process
- # is still alive return None (we can return None also on BSD).
- # Reference: http://goo.gl/55XgO
- procfs_path = self._procfs_path
- try:
- return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
- except FileNotFoundError:
- os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
- return ""
- @wrap_exceptions
- def memory_info(self):
- ret = self._proc_basic_info()
- rss = ret[proc_info_map['rss']] * 1024
- vms = ret[proc_info_map['vms']] * 1024
- return pmem(rss, vms)
- memory_full_info = memory_info
- @wrap_exceptions
- def status(self):
- code = self._proc_basic_info()[proc_info_map['status']]
- # XXX is '?' legit? (we're not supposed to return it anyway)
- return PROC_STATUSES.get(code, '?')
- @wrap_exceptions
- def threads(self):
- procfs_path = self._procfs_path
- ret = []
- tids = os.listdir('%s/%d/lwp' % (procfs_path, self.pid))
- hit_enoent = False
- for tid in tids:
- tid = int(tid)
- try:
- utime, stime = cext.query_process_thread(
- self.pid, tid, procfs_path)
- except EnvironmentError as err:
- if err.errno == errno.EOVERFLOW and not IS_64_BIT:
- # We may get here if we attempt to query a 64bit process
- # with a 32bit python.
- # Error originates from read() and also tools like "cat"
- # fail in the same way (!).
- # Since there simply is no way to determine CPU times we
- # return 0.0 as a fallback. See:
- # https://github.com/giampaolo/psutil/issues/857
- continue
- # ENOENT == thread gone in meantime
- if err.errno == errno.ENOENT:
- hit_enoent = True
- continue
- raise
- else:
- nt = _common.pthread(tid, utime, stime)
- ret.append(nt)
- if hit_enoent:
- self._assert_alive()
- return ret
- @wrap_exceptions
- def open_files(self):
- retlist = []
- hit_enoent = False
- procfs_path = self._procfs_path
- pathdir = '%s/%d/path' % (procfs_path, self.pid)
- for fd in os.listdir('%s/%d/fd' % (procfs_path, self.pid)):
- path = os.path.join(pathdir, fd)
- if os.path.islink(path):
- try:
- file = os.readlink(path)
- except FileNotFoundError:
- hit_enoent = True
- continue
- else:
- if isfile_strict(file):
- retlist.append(_common.popenfile(file, int(fd)))
- if hit_enoent:
- self._assert_alive()
- return retlist
- def _get_unix_sockets(self, pid):
- """Get UNIX sockets used by process by parsing 'pfiles' output."""
- # TODO: rewrite this in C (...but the damn netstat source code
- # does not include this part! Argh!!)
- cmd = ["pfiles", str(pid)]
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- stdout, stderr = p.communicate()
- if PY3:
- stdout, stderr = (x.decode(sys.stdout.encoding)
- for x in (stdout, stderr))
- if p.returncode != 0:
- if 'permission denied' in stderr.lower():
- raise AccessDenied(self.pid, self._name)
- if 'no such process' in stderr.lower():
- raise NoSuchProcess(self.pid, self._name)
- raise RuntimeError("%r command error\n%s" % (cmd, stderr))
- lines = stdout.split('\n')[2:]
- for i, line in enumerate(lines):
- line = line.lstrip()
- if line.startswith('sockname: AF_UNIX'):
- path = line.split(' ', 2)[2]
- type = lines[i - 2].strip()
- if type == 'SOCK_STREAM':
- type = socket.SOCK_STREAM
- elif type == 'SOCK_DGRAM':
- type = socket.SOCK_DGRAM
- else:
- type = -1
- yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
- @wrap_exceptions
- def connections(self, kind='inet'):
- ret = net_connections(kind, _pid=self.pid)
- # The underlying C implementation retrieves all OS connections
- # and filters them by PID. At this point we can't tell whether
- # an empty list means there were no connections for process or
- # process is no longer active so we force NSP in case the PID
- # is no longer there.
- if not ret:
- # will raise NSP if process is gone
- os.stat('%s/%s' % (self._procfs_path, self.pid))
- # UNIX sockets
- if kind in ('all', 'unix'):
- ret.extend([_common.pconn(*conn) for conn in
- self._get_unix_sockets(self.pid)])
- return ret
- nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
- nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')
- @wrap_exceptions
- def memory_maps(self):
- def toaddr(start, end):
- return '%s-%s' % (hex(start)[2:].strip('L'),
- hex(end)[2:].strip('L'))
- procfs_path = self._procfs_path
- retlist = []
- try:
- rawlist = cext.proc_memory_maps(self.pid, procfs_path)
- except OSError as err:
- if err.errno == errno.EOVERFLOW and not IS_64_BIT:
- # We may get here if we attempt to query a 64bit process
- # with a 32bit python.
- # Error originates from read() and also tools like "cat"
- # fail in the same way (!).
- # Since there simply is no way to determine CPU times we
- # return 0.0 as a fallback. See:
- # https://github.com/giampaolo/psutil/issues/857
- return []
- else:
- raise
- hit_enoent = False
- for item in rawlist:
- addr, addrsize, perm, name, rss, anon, locked = item
- addr = toaddr(addr, addrsize)
- if not name.startswith('['):
- try:
- name = os.readlink(
- '%s/%s/path/%s' % (procfs_path, self.pid, name))
- except OSError as err:
- if err.errno == errno.ENOENT:
- # sometimes the link may not be resolved by
- # readlink() even if it exists (ls shows it).
- # If that's the case we just return the
- # unresolved link path.
- # This seems an incosistency with /proc similar
- # to: http://goo.gl/55XgO
- name = '%s/%s/path/%s' % (procfs_path, self.pid, name)
- hit_enoent = True
- else:
- raise
- retlist.append((addr, perm, name, rss, anon, locked))
- if hit_enoent:
- self._assert_alive()
- return retlist
- @wrap_exceptions
- def num_fds(self):
- return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
- @wrap_exceptions
- def num_ctx_switches(self):
- return _common.pctxsw(
- *cext.proc_num_ctx_switches(self.pid, self._procfs_path))
- @wrap_exceptions
- def wait(self, timeout=None):
- return _psposix.wait_pid(self.pid, timeout, self._name)
|