# 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. """OSX platform implementation.""" import errno import functools import os from collections import namedtuple from . import _common from . import _psposix from . import _psutil_osx as cext from . import _psutil_posix as cext_posix from ._common import conn_tmap from ._common import isfile_strict from ._common import parse_environ_block from ._common import sockfam_to_enum from ._common import socktype_to_enum from ._common import usage_percent __extra__all__ = [] # ===================================================================== # --- constants # ===================================================================== PAGESIZE = os.sysconf("SC_PAGE_SIZE") AF_LINK = cext_posix.AF_LINK # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h TCP_STATUSES = { cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, cext.TCPS_SYN_RECEIVED: _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, } PROC_STATUSES = { cext.SIDL: _common.STATUS_IDLE, cext.SRUN: _common.STATUS_RUNNING, cext.SSLEEP: _common.STATUS_SLEEPING, cext.SSTOP: _common.STATUS_STOPPED, cext.SZOMB: _common.STATUS_ZOMBIE, } scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle']) svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', 'active', 'inactive', 'wired']) pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) pmmap_grouped = namedtuple( 'pmmap_grouped', 'path rss private swapped dirtied ref_count shadow_depth') pmmap_ext = namedtuple( 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) # these get overwritten on "import psutil" from the __init__.py file NoSuchProcess = None ZombieProcess = None AccessDenied = None TimeoutExpired = None # ===================================================================== # --- memory # ===================================================================== def virtual_memory(): """System virtual memory as a namedtuple.""" total, active, inactive, wired, free = cext.virtual_mem() avail = inactive + free used = active + inactive + wired percent = usage_percent((total - avail), total, _round=1) return svmem(total, avail, percent, used, free, active, inactive, wired) def swap_memory(): """Swap system memory as a (total, used, free, sin, sout) tuple.""" total, used, free, sin, sout = cext.swap_mem() percent = usage_percent(used, total, _round=1) return _common.sswap(total, used, free, percent, sin, sout) # ===================================================================== # --- CPU # ===================================================================== def cpu_times(): """Return system CPU times as a namedtuple.""" user, nice, system, idle = cext.cpu_times() return scputimes(user, nice, system, idle) def per_cpu_times(): """Return system CPU times as a named tuple""" ret = [] for cpu_t in cext.per_cpu_times(): user, nice, system, idle = cpu_t item = scputimes(user, nice, system, idle) ret.append(item) return ret def cpu_count_logical(): """Return the number of logical CPUs in the system.""" return cext.cpu_count_logical() def cpu_count_physical(): """Return the number of physical CPUs in the system.""" return cext.cpu_count_phys() def cpu_stats(): ctx_switches, interrupts, soft_interrupts, syscalls, traps = \ cext.cpu_stats() return _common.scpustats( ctx_switches, interrupts, soft_interrupts, syscalls) # ===================================================================== # --- disks # ===================================================================== disk_usage = _psposix.disk_usage disk_io_counters = cext.disk_io_counters def disk_partitions(all=False): retlist = [] partitions = cext.disk_partitions() for partition in partitions: device, mountpoint, fstype, opts = partition if device == 'none': device = '' if not all: if not os.path.isabs(device) or not os.path.exists(device): continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) 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='inet'): # Note: on OSX this will fail with AccessDenied unless # the process is owned by root. ret = [] for pid in pids(): try: cons = Process(pid).connections(kind) except NoSuchProcess: continue else: if cons: for c in cons: c = list(c) + [pid] ret.append(_common.sconn(*c)) return ret def net_if_stats(): """Get NIC stats (isup, duplex, speed, mtu).""" names = net_io_counters().keys() ret = {} for name in names: isup, duplex, speed, mtu = cext_posix.net_if_stats(name) 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(): retlist = [] rawlist = cext.users() for item in rawlist: user, tty, hostname, tstamp = item if tty == '~': continue # reboot or shutdown if not tstamp: continue nt = _common.suser(user, tty or None, hostname or None, tstamp) retlist.append(nt) return retlist # ===================================================================== # --- processes # ===================================================================== pids = cext.pids pid_exists = _psposix.pid_exists def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) except OSError as err: if err.errno == errno.ESRCH: if not pid_exists(self.pid): raise NoSuchProcess(self.pid, self._name) else: raise ZombieProcess(self.pid, self._name, self._ppid) if err.errno in (errno.EPERM, errno.EACCES): raise AccessDenied(self.pid, self._name) raise return wrapper class Process(object): """Wrapper class around underlying C implementation.""" __slots__ = ["pid", "_name", "_ppid"] def __init__(self, pid): self.pid = pid self._name = None self._ppid = None @wrap_exceptions def name(self): return cext.proc_name(self.pid) @wrap_exceptions def exe(self): return cext.proc_exe(self.pid) @wrap_exceptions def cmdline(self): if not pid_exists(self.pid): raise NoSuchProcess(self.pid, self._name) return cext.proc_cmdline(self.pid) @wrap_exceptions def environ(self): if not pid_exists(self.pid): raise NoSuchProcess(self.pid, self._name) return parse_environ_block(cext.proc_environ(self.pid)) @wrap_exceptions def ppid(self): self._ppid = cext.proc_ppid(self.pid) return self._ppid @wrap_exceptions def cwd(self): return cext.proc_cwd(self.pid) @wrap_exceptions def uids(self): real, effective, saved = cext.proc_uids(self.pid) return _common.puids(real, effective, saved) @wrap_exceptions def gids(self): real, effective, saved = cext.proc_gids(self.pid) return _common.pgids(real, effective, saved) @wrap_exceptions def terminal(self): tty_nr = cext.proc_tty_nr(self.pid) tmap = _psposix.get_terminal_map() try: return tmap[tty_nr] except KeyError: return None @wrap_exceptions def memory_info(self): rss, vms, pfaults, pageins = cext.proc_memory_info(self.pid) return pmem(rss, vms, pfaults, pageins) @wrap_exceptions def memory_full_info(self): basic_mem = self.memory_info() uss = cext.proc_memory_uss(self.pid) return pfullmem(*basic_mem + (uss, )) @wrap_exceptions def cpu_times(self): user, system = cext.proc_cpu_times(self.pid) # Children user/system times are not retrievable (set to 0). return _common.pcputimes(user, system, 0, 0) @wrap_exceptions def create_time(self): return cext.proc_create_time(self.pid) @wrap_exceptions def num_ctx_switches(self): return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid)) @wrap_exceptions def num_threads(self): return cext.proc_num_threads(self.pid) @wrap_exceptions def open_files(self): if self.pid == 0: return [] files = [] rawlist = cext.proc_open_files(self.pid) for path, fd in rawlist: if isfile_strict(path): ntuple = _common.popenfile(path, fd) files.append(ntuple) return files @wrap_exceptions def connections(self, kind='inet'): if kind not in conn_tmap: raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in conn_tmap]))) families, types = conn_tmap[kind] rawlist = cext.proc_connections(self.pid, families, types) ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item status = TCP_STATUSES[status] fam = sockfam_to_enum(fam) type = socktype_to_enum(type) nt = _common.pconn(fd, fam, type, laddr, raddr, status) ret.append(nt) return ret @wrap_exceptions def num_fds(self): if self.pid == 0: return 0 return cext.proc_num_fds(self.pid) @wrap_exceptions def wait(self, timeout=None): try: return _psposix.wait_pid(self.pid, timeout) except _psposix.TimeoutExpired: raise TimeoutExpired(timeout, self.pid, self._name) @wrap_exceptions def nice_get(self): return cext_posix.getpriority(self.pid) @wrap_exceptions def nice_set(self, value): return cext_posix.setpriority(self.pid, value) @wrap_exceptions def status(self): code = cext.proc_status(self.pid) # XXX is '?' legit? (we're not supposed to return it anyway) return PROC_STATUSES.get(code, '?') @wrap_exceptions def threads(self): rawlist = cext.proc_threads(self.pid) retlist = [] for thread_id, utime, stime in rawlist: ntuple = _common.pthread(thread_id, utime, stime) retlist.append(ntuple) return retlist @wrap_exceptions def memory_maps(self): return cext.proc_memory_maps(self.pid)