2 new commits in resourceMonitor: https://bitbucket.org/nvdaaddonteam/resourcemonitor/commits/4fe80f24ed5b/ Changeset: 4fe80f24ed5b Branch: None User: josephsl Date: 2013-08-18 14:40:06 Summary: Merge branch 'psutil-next' Affected #: 14 files diff --git a/addon/globalPlugins/resourceMonitor/__init__.py b/addon/globalPlugins/resourceMonitor/__init__.py index 6300a7d..bf03380 100644 --- a/addon/globalPlugins/resourceMonitor/__init__.py +++ b/addon/globalPlugins/resourceMonitor/__init__.py @@ -3,7 +3,13 @@ #Authors: Alex Hall (core mechanics and messages), Joseph Lee (internationalization), Beqa Gozalishvili (updated psutil to 0.6.1, and made needed changes to make code run). import globalPluginHandler, ui -import psutil, battery +import sys +import os +impPath = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(impPath) +import psutil +del sys.path[-1] +import battery import addonHandler addonHandler.initTranslation() diff --git a/addon/globalPlugins/resourceMonitor/psutil/__init__.py b/addon/globalPlugins/resourceMonitor/psutil/__init__.py index b2b3d2f..9e74aca 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/__init__.py +++ b/addon/globalPlugins/resourceMonitor/psutil/__init__.py @@ -1,9 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# -# $Id: __init__.py 1525 2012-08-16 16:32:03Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -14,7 +12,7 @@ Python. from __future__ import division -__version__ = "0.6.1" +__version__ = "1.0.1" version_info = tuple([int(num) for num in __version__.split('.')]) __all__ = [ @@ -26,13 +24,18 @@ __all__ = [ "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", "STATUS_WAKING", "STATUS_LOCKED", + "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", + "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE", # classes "Process", "Popen", # functions - "test", "pid_exists", "get_pid_list", "process_iter", "get_process_list", - "virtual_memory", "swap_memory", - "cpu_times", "cpu_percent", "per_cpu_percent", - "network_io_counters", "disk_io_counters", + "pid_exists", "get_pid_list", "process_iter", # proc + "virtual_memory", "swap_memory", # memory + "cpu_times", "cpu_percent", "cpu_times_percent", # cpu + "net_io_counters", # network + "disk_io_counters", "disk_partitions", "disk_usage", # disk + "get_users", "get_boot_time", # others ] import sys @@ -47,18 +50,24 @@ try: except ImportError: pwd = None -from error import Error, NoSuchProcess, AccessDenied, TimeoutExpired -from _compat import property, callable, defaultdict -from _common import cached_property -from _common import (deprecated as _deprecated, +from psutil._error import Error, NoSuchProcess, AccessDenied, TimeoutExpired +from psutil._common import cached_property +from psutil._compat import (property, callable, defaultdict, namedtuple, + wraps as _wraps, PY3 as _PY3) +from psutil._common import (deprecated as _deprecated, nt_disk_iostat as _nt_disk_iostat, nt_net_iostat as _nt_net_iostat, nt_sysmeminfo as _nt_sysmeminfo, isfile_strict as _isfile_strict) -from _common import (STATUS_RUNNING, STATUS_IDLE, STATUS_SLEEPING, +from psutil._common import (STATUS_RUNNING, STATUS_IDLE, STATUS_SLEEPING, STATUS_DISK_SLEEP, STATUS_STOPPED, STATUS_TRACING_STOP, STATUS_ZOMBIE, STATUS_DEAD, - STATUS_WAKING, STATUS_LOCKED) + STATUS_WAKING, STATUS_LOCKED, + # + CONN_ESTABLISHED, CONN_SYN_SENT, CONN_SYN_RECV, + CONN_FIN_WAIT1, CONN_FIN_WAIT2, CONN_TIME_WAIT, + CONN_CLOSE, CONN_CLOSE_WAIT, CONN_LAST_ACK, + CONN_LISTEN, CONN_CLOSING, CONN_NONE) # import the appropriate module for our platform only if sys.platform.startswith("linux"): @@ -73,13 +82,14 @@ if sys.platform.startswith("linux"): cached_phymem = _psplatform.cached_phymem elif sys.platform.startswith("win32"): - import _psmswindows as _psplatform - from _psmswindows import (ABOVE_NORMAL_PRIORITY_CLASS, + import psutil._psmswindows as _psplatform + from psutil._psmswindows import (ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, - REALTIME_PRIORITY_CLASS) + REALTIME_PRIORITY_CLASS, + CONN_DELETE_TCB) elif sys.platform.startswith("darwin"): import psutil._psosx as _psplatform @@ -87,6 +97,11 @@ elif sys.platform.startswith("darwin"): elif sys.platform.startswith("freebsd"): import psutil._psbsd as _psplatform +elif sys.platform.startswith("sunos"): + import psutil._pssunos as _psplatform + from psutil._pssunos import (CONN_IDLE, + CONN_BOUND) + else: raise NotImplementedError('platform %s is not supported' % sys.platform) @@ -97,15 +112,56 @@ BOOT_TIME = _psplatform.BOOT_TIME TOTAL_PHYMEM = _psplatform.TOTAL_PHYMEM +def _assert_pid_not_reused(fun): + """Decorator which raises NoSuchProcess in case a process is no + longer running or its PID has been reused. + """ + @_wraps(fun) + def wrapper(self, *args, **kwargs): + if not self.is_running(): + raise NoSuchProcess(self.pid, self._platform_impl._process_name) + return fun(self, *args, **kwargs) + return wrapper + + class Process(object): """Represents an OS process.""" def __init__(self, pid): """Create a new Process object for the given pid. Raises NoSuchProcess if pid does not exist. + + Note that most of the methods of this class do not make sure + the PID of the process being queried has been reused. + That means you might end up retrieving an information referring + to another process in case the original one this instance + refers to is gone in the meantime. + + The only exceptions for which process identity is pre-emptively + checked are: + - parent + - get_children() + - set_nice() + - suspend() + - resume() + - send_signal() + - terminate() + - kill() + + To prevent this problem for all other methods you can: + - use is_running() before querying the process + - if you're continuously iterating over a set of Process + instances use process_iter() which pre-emptively checks + process identity for every yielded instance """ + if not _PY3: + if not isinstance(pid, (int, long)): + raise TypeError('pid must be an integer') + if pid < 0: + raise ValueError('pid must be a positive integer') self._pid = pid self._gone = False + self._ppid = None # platform-specific modules define an _psplatform.Process # implementation class self._platform_impl = _psplatform.Process(pid) @@ -135,6 +191,8 @@ class Process(object): def __repr__(self): return "<%s at %s>" % (self.__str__(), id(self)) + # --- utility methods + def as_dict(self, attrs=[], ad_value=None): """Utility method returning process information as a hashable dictionary. @@ -185,27 +243,46 @@ class Process(object): return retdict @property - def pid(self): - """The process pid.""" - return self._pid - - @cached_property - def ppid(self): - """The process parent pid.""" - return self._platform_impl.get_process_ppid() - - @property + @_assert_pid_not_reused def parent(self): - """Return the parent process as a Process object. If no parent - pid is known return None. + """Return the parent process as a Process object pre-emptively + checking whether PID has been reused. + If no parent is known return None. """ ppid = self.ppid if ppid is not None: try: - return Process(ppid) + parent = Process(ppid) + if parent.create_time <= self.create_time: + return parent + # ...else ppid has been reused by another process except NoSuchProcess: pass + # --- actual API + + @property + def pid(self): + """The process pid.""" + return self._pid + + @property + def ppid(self): + """The process parent pid.""" + # On POSIX we don't want to cache the ppid as it may unexpectedly + # change to 1 (init) in case this process turns into a zombie: + # https://code.google.com/p/psutil/issues/detail?id=321 + # http://stackoverflow.com/questions/356722/ + + # XXX should we check creation time here rather than in + # Process.parent? + if os.name == 'posix': + return self._platform_impl.get_process_ppid() + else: + if self._ppid is None: + self._ppid = self._platform_impl.get_process_ppid() + return self._ppid + @cached_property def name(self): """The process name.""" @@ -237,9 +314,11 @@ class Process(object): cmdline = self.cmdline if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): exe = cmdline[0] # the possible exe - rexe = os.path.realpath(exe) # ...in case it's a symlink - if os.path.isabs(rexe) and os.path.isfile(rexe) \ - and os.access(rexe, os.X_OK): + # Attempt to guess only in case of an absolute path. + # It is not safe otherwise as the process might have + # changed cwd. + if os.path.isabs(exe) and os.path.isfile(exe) \ + and os.access(exe, os.X_OK): return exe if isinstance(fallback, AccessDenied): raise fallback @@ -261,7 +340,7 @@ class Process(object): pass return exe - @cached_property + @property def cmdline(self): """The command line process has been called with.""" return self._platform_impl.get_process_cmdline() @@ -334,22 +413,37 @@ class Process(object): """Get process niceness (priority).""" return self._platform_impl.get_process_nice() + @_assert_pid_not_reused def set_nice(self, value): - """Set process niceness (priority).""" + """Set process niceness (priority) pre-emptively checking + whether PID has been reused.""" return self._platform_impl.set_process_nice(value) - # available only on Linux + # available only on Linux and Windows >= Vista if hasattr(_psplatform.Process, "get_process_ionice"): def get_ionice(self): - """Return process I/O niceness (priority) as a namedtuple.""" + """Return process I/O niceness (priority). + + On Linux this is a (ioclass, value) namedtuple. + On Windows it's an integer which can be equal to 2 (normal), + 1 (low) or 0 (very low). + + Available on Linux and Windows > Vista only. + """ return self._platform_impl.get_process_ionice() def set_ionice(self, ioclass, value=None): """Set process I/O niceness (priority). - ioclass is one of the IOPRIO_CLASS_* constants. - iodata is a number which goes from 0 to 7. The higher the + + On Linux 'ioclass' is one of the IOPRIO_CLASS_* constants. + 'value' is a number which goes from 0 to 7. The higher the value, the lower the I/O priority of the process. + + On Windows only 'ioclass' is used and it can be set to 2 + (normal), 1 (low) or 0 (very low). + + Available on Linux and Windows > Vista only. """ return self._platform_impl.set_process_ionice(ioclass, value) @@ -399,9 +493,10 @@ class Process(object): """ return self._platform_impl.get_process_threads() + @_assert_pid_not_reused def get_children(self, recursive=False): """Return the children of this process as a list of Process - objects. + objects pre-emptively checking whether PID has been reused. If recursive is True return all the parent descendants. Example (A == this process): @@ -423,10 +518,6 @@ class Process(object): process Y won't be returned either as the reference to process A is lost. """ - if not self.is_running(): - name = self._platform_impl._process_name - raise NoSuchProcess(self.pid, name) - ret = [] if not recursive: for p in process_iter(): @@ -474,10 +565,22 @@ class Process(object): When interval is > 0.0 compares process times to system CPU times elapsed before and after the interval (blocking). - When interval is 0.0 or None compares process times to system CPU - times elapsed since last call, returning immediately. - In this case is recommended for accuracy that this function be - called with at least 0.1 seconds between calls. + When interval is 0.0 or None compares process times to system + CPU times elapsed since last call, returning immediately + (non-blocking). + In this case is recommended for accuracy that this function + be called with at least 0.1 seconds between calls. + + Examples: + + >>> p = psutil.Process(os.getpid()) + >>> # blocking + >>> p.get_cpu_percent(interval=1) + 2.0 + >>> # non-blocking (percentage since last call) + >>> p.get_cpu_percent(interval=0) + 2.9 + >>> """ blocking = interval is not None and interval > 0.0 if blocking: @@ -544,8 +647,8 @@ class Process(object): return self._platform_impl.get_ext_memory_info() def get_memory_percent(self): - """Compare physical system memory to process resident memory and - calculate process memory utilization as a percentage. + """Compare physical system memory to process resident memory + (RSS) and calculate process memory utilization as a percentage. """ rss = self._platform_impl.get_memory_info()[0] try: @@ -607,7 +710,10 @@ class Process(object): return self._platform_impl.get_connections(kind) def is_running(self): - """Return whether this process is running.""" + """Return whether this process is running. + It also checks if PID has been reused by another process in + which case returns False. + """ if self._gone: return False try: @@ -621,16 +727,13 @@ class Process(object): self._gone = True return False + @_assert_pid_not_reused def send_signal(self, sig): - """Send a signal to process (see signal module constants). + """Send a signal to process pre-emptively checking whether + PID has been reused (see signal module constants) . On Windows only SIGTERM is valid and is treated as an alias for kill(). """ - # safety measure in case the current process has been killed in - # meantime and the kernel reused its PID - if not self.is_running(): - name = self._platform_impl._process_name - raise NoSuchProcess(self.pid, name) if os.name == 'posix': try: os.kill(self.pid, sig) @@ -638,6 +741,7 @@ class Process(object): err = sys.exc_info()[1] name = self._platform_impl._process_name if err.errno == errno.ESRCH: + self._gone = True raise NoSuchProcess(self.pid, name) if err.errno == errno.EPERM: raise AccessDenied(self.pid, name) @@ -648,47 +752,43 @@ class Process(object): else: raise ValueError("only SIGTERM is supported on Windows") + @_assert_pid_not_reused def suspend(self): - """Suspend process execution.""" - # safety measure in case the current process has been killed in - # meantime and the kernel reused its PID - if not self.is_running(): - name = self._platform_impl._process_name - raise NoSuchProcess(self.pid, name) - # windows + """Suspend process execution with SIGSTOP pre-emptively checking + whether PID has been reused. + On Windows it suspends all process threads. + """ if hasattr(self._platform_impl, "suspend_process"): + # windows self._platform_impl.suspend_process() else: # posix self.send_signal(signal.SIGSTOP) + @_assert_pid_not_reused def resume(self): - """Resume process execution.""" - # safety measure in case the current process has been killed in - # meantime and the kernel reused its PID - if not self.is_running(): - name = self._platform_impl._process_name - raise NoSuchProcess(self.pid, name) - # windows + """Resume process execution with SIGCONT pre-emptively checking + whether PID has been reused. + On Windows it resumes all process threads. + """ if hasattr(self._platform_impl, "resume_process"): + # windows self._platform_impl.resume_process() else: # posix self.send_signal(signal.SIGCONT) def terminate(self): - """Terminate the process with SIGTERM. + """Terminate the process with SIGTERM pre-emptively checking + whether PID has been reused. On Windows this is an alias for kill(). """ self.send_signal(signal.SIGTERM) + @_assert_pid_not_reused def kill(self): - """Kill the current process.""" - # safety measure in case the current process has been killed in - # meantime and the kernel reused its PID - if not self.is_running(): - name = self._platform_impl._process_name - raise NoSuchProcess(self.pid, name) + """Kill the current process with SIGKILL pre-emptively checking + whether PID has been reused.""" if os.name == 'posix': self.send_signal(signal.SIGKILL) else: @@ -755,6 +855,7 @@ class Popen(Process): self.__subproc = subprocess.Popen(*args, **kwargs) self._pid = self.__subproc.pid self._gone = False + self._ppid = None self._platform_impl = _psplatform.Process(self._pid) self._last_sys_cpu_times = None self._last_proc_cpu_times = None @@ -764,7 +865,7 @@ class Popen(Process): pass except NoSuchProcess: raise NoSuchProcess(self._pid, None, - "no process found with pid %s" % pid) + "no process found with pid %s" % self._pid) def __dir__(self): return list(set(dir(Popen) + dir(subprocess.Popen))) @@ -796,6 +897,10 @@ def process_iter(): Every new Process instance is only created once and then cached into an internal table which is updated every time this is used. + Cached Process instances are checked for identity so that you're + safe in case a PID has been reused by another process, in which + case the cached instance is updated. + The sorting order in which processes are yielded is based on their PIDs. """ @@ -850,6 +955,9 @@ def cpu_times(percpu=False): - iowait (Linux) - irq (Linux, FreeBSD) - softirq (Linux) + - steal (Linux >= 2.6.11) + - guest (Linux >= 2.6.24) + - guest_nice (Linux >= 3.2.0) When percpu is True return a list of nameduples for each CPU. First element of the list refers to first CPU, second element @@ -882,6 +990,21 @@ def cpu_percent(interval=0.1, percpu=False): First element of the list refers to first CPU, second element to second CPU and so on. The order of the list is consistent across calls. + + Examples: + + >>> # blocking, system-wide + >>> psutil.cpu_percent(interval=1) + 2.0 + >>> + >>> # blocking, per-cpu + >>> psutil.cpu_percent(interval=1, percpu=True) + [2.0, 1.0] + >>> + >>> # non-blocking (percentage since last call) + >>> psutil.cpu_percent(interval=0) + 2.9 + >>> """ global _last_cpu_times global _last_per_cpu_times @@ -925,6 +1048,68 @@ def cpu_percent(interval=0.1, percpu=False): ret.append(calculate(t1, t2)) return ret + +# Use separate global vars for cpu_times_percent() so that it's +# independent from cpu_percent() and they can both be used within +# the same program. +_last_cpu_times_2 = _last_cpu_times +_last_per_cpu_times_2 = _last_per_cpu_times +_ptime_cpu_perc_nt = None + +def cpu_times_percent(interval=0.1, percpu=False): + """Same as cpu_percent() but provides utilization percentages + for each specific CPU time as is returned by cpu_times(). + For instance, on Linux we'll get: + + >>> cpu_times_percent() + cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0, + irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) + >>> + + interval and percpu arguments have the same meaning as in + cpu_percent(). + """ + global _last_cpu_times_2 + global _last_per_cpu_times_2 + blocking = interval is not None and interval > 0.0 + + def calculate(t1, t2): + global _ptime_cpu_perc_nt + nums = [] + all_delta = sum(t2) - sum(t1) + for field in t1._fields: + field_delta = getattr(t2, field) - getattr(t1, field) + try: + field_perc = (100 * field_delta) / all_delta + except ZeroDivisionError: + field_perc = 0.0 + nums.append(round(field_perc, 1)) + if _ptime_cpu_perc_nt is None: + _ptime_cpu_perc_nt = namedtuple('cpupercent', ' '.join(t1._fields)) + return _ptime_cpu_perc_nt(*nums) + + # system-wide usage + if not percpu: + if blocking: + t1 = cpu_times() + time.sleep(interval) + else: + t1 = _last_cpu_times_2 + _last_cpu_times_2 = cpu_times() + return calculate(t1, _last_cpu_times_2) + # per-cpu usage + else: + ret = [] + if blocking: + tot1 = cpu_times(percpu=True) + time.sleep(interval) + else: + tot1 = _last_per_cpu_times_2 + _last_per_cpu_times_2 = cpu_times(percpu=True) + for t1, t2 in zip(tot1, _last_per_cpu_times_2): + ret.append(calculate(t1, t2)) + return ret + # ===================================================================== # --- system memory related functions # ===================================================================== @@ -1035,6 +1220,9 @@ def disk_io_counters(perdisk=False): physical disk installed on the system as a dictionary with partition names as the keys and the namedutuple described above as the values. + + On recent Windows versions 'diskperf -y' command may need to be + executed first otherwise this function won't find any disk. """ rawdict = _psplatform.disk_io_counters() if not rawdict: @@ -1050,7 +1238,7 @@ def disk_io_counters(perdisk=False): # --- network related functions # ===================================================================== -def network_io_counters(pernic=False): +def net_io_counters(pernic=False): """Return network I/O statistics as a namedtuple including the following attributes: @@ -1069,7 +1257,7 @@ def network_io_counters(pernic=False): with network interface names as the keys and the namedtuple described above as the values. """ - rawdict = _psplatform.network_io_counters() + rawdict = _psplatform.net_io_counters() if not rawdict: raise RuntimeError("couldn't find any network interface") if pernic: @@ -1083,6 +1271,12 @@ def network_io_counters(pernic=False): # --- other system related functions # ===================================================================== +def get_boot_time(): + """Return the system boot time expressed in seconds since the epoch. + This is also available as psutil.BOOT_TIME. + """ + return _psplatform.get_system_boot_time() + def get_users(): """Return users currently connected on the system as a list of namedtuples including the following attributes. @@ -1139,6 +1333,10 @@ def used_virtmem(): def avail_virtmem(): return virtmem_usage().free +@_deprecated("psutil.net_io_counters()") +def network_io_counters(pernic=False): + return net_io_counters(pernic) + def test(): """List info of all currently running processes emulating ps aux output. @@ -1148,9 +1346,10 @@ def test(): today_day = datetime.date.today() templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s %s" - attrs = ['pid', 'username', 'get_cpu_percent', 'get_memory_percent', 'name', + attrs = ['pid', 'get_cpu_percent', 'get_memory_percent', 'name', 'get_cpu_times', 'create_time', 'get_memory_info'] if os.name == 'posix': + attrs.append('uids') attrs.append('terminal') print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "START", "TIME", "COMMAND")) @@ -1166,8 +1365,21 @@ def test(): ctime = ctime.strftime("%H:%M") else: ctime = ctime.strftime("%b%d") + else: + ctime = '' cputime = time.strftime("%M:%S", time.localtime(sum(pinfo['cpu_times']))) - user = pinfo['username'] + try: + user = p.username + except KeyError: + if os.name == 'posix': + if pinfo['uids']: + user = str(pinfo['uids'].real) + else: + user = '' + else: + raise + except Error: + user = '' if os.name == 'nt' and '\\' in user: user = user.split('\\')[1] vms = pinfo['memory_info'] and \ diff --git a/addon/globalPlugins/resourceMonitor/psutil/_common.py b/addon/globalPlugins/resourceMonitor/psutil/_common.py index 9a44511..b0d8423 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_common.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_common.py @@ -1,8 +1,6 @@ #/usr/bin/env python -# -#$Id: _common.py 1524 2012-08-16 15:06:32Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -15,7 +13,7 @@ import stat import errno import warnings -from _compat import namedtuple, long, wraps +from psutil._compat import namedtuple, long, wraps # --- functions @@ -134,6 +132,19 @@ STATUS_IDLE = constant(9, "idle") # BSD STATUS_LOCKED = constant(10, "locked") # BSD STATUS_WAITING = constant(11, "waiting") # BSD +CONN_ESTABLISHED = constant(0, "ESTABLISHED") +CONN_SYN_SENT = constant(1, "SYN_SENT") +CONN_SYN_RECV = constant(2, "SYN_RECV") +CONN_FIN_WAIT1 = constant(3, "FIN_WAIT1") +CONN_FIN_WAIT2 = constant(4, "FIN_WAIT2") +CONN_TIME_WAIT = constant(5, "TIME_WAIT") +CONN_CLOSE = constant(6, "CLOSE") +CONN_CLOSE_WAIT = constant(7, "CLOSE_WAIT") +CONN_LAST_ACK = constant(8, "LAST_ACK") +CONN_LISTEN = constant(9, "LISTEN") +CONN_CLOSING = constant(10, "CLOSING") +CONN_NONE = constant(20, "NONE") + # --- Process.get_connections() 'kind' parameter mapping import socket @@ -169,7 +180,6 @@ del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM, socket # --- namedtuples # system -nt_sys_cputimes = namedtuple('cputimes', 'user nice system idle iowait irq softirq') nt_sysmeminfo = namedtuple('usage', 'total used free percent') # XXX - would 'available' be better than 'free' as for virtual_memory() nt? nt_swapmeminfo = namedtuple('swap', 'total used free percent sin sout') @@ -184,10 +194,25 @@ nt_user = namedtuple('user', 'name terminal host started') nt_meminfo = namedtuple('meminfo', 'rss vms') nt_cputimes = namedtuple('cputimes', 'user system') nt_openfile = namedtuple('openfile', 'path fd') -nt_connection = namedtuple('connection', 'fd family type local_address remote_address status') nt_thread = namedtuple('thread', 'id user_time system_time') nt_uids = namedtuple('user', 'real effective saved') nt_gids = namedtuple('group', 'real effective saved') nt_io = namedtuple('io', 'read_count write_count read_bytes write_bytes') nt_ionice = namedtuple('ionice', 'ioclass value') nt_ctxsw = namedtuple('amount', 'voluntary involuntary') + +class nt_connection(namedtuple('connection', + 'fd family type laddr raddr status')): + __slots__ = () + + @property + def local_address(self): + warnings.warn("'local_address' field is deprecated; use 'laddr'" \ + "instead", category=DeprecationWarning, stacklevel=2) + return self.laddr + + @property + def remote_address(self): + warnings.warn("'remote_address' field is deprecated; use 'raddr'" \ + "instead", category=DeprecationWarning, stacklevel=2) + return self.raddr diff --git a/addon/globalPlugins/resourceMonitor/psutil/_compat.py b/addon/globalPlugins/resourceMonitor/psutil/_compat.py index dee9e05..363712b 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_compat.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_compat.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _compat.py 1524 2012-08-16 15:06:32Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. diff --git a/addon/globalPlugins/resourceMonitor/psutil/_error.py b/addon/globalPlugins/resourceMonitor/psutil/_error.py new file mode 100644 index 0000000..5388c22 --- /dev/null +++ b/addon/globalPlugins/resourceMonitor/psutil/_error.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +# 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. + +"""psutil exception classes. +Not supposed to be used / imported directly. +Instead use psutil.NoSuchProcess, etc. +""" + + +class Error(Exception): + """Base exception class. All other psutil exceptions inherit + from this one. + """ + +class NoSuchProcess(Error): + """Exception raised when a process with a certain PID doesn't + or no longer exists (zombie). + """ + + def __init__(self, pid, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process no longer exists " + details + + def __str__(self): + return self.msg + + +class AccessDenied(Error): + """Exception raised when permission to perform an action is denied.""" + + def __init__(self, pid=None, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + def __str__(self): + return self.msg + + +class TimeoutExpired(Error): + """Raised on Process.wait(timeout) if timeout expires and process + is still alive. + """ + + def __init__(self, pid=None, name=None): + self.pid = pid + self.name = name + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + def __str__(self): + return self.msg diff --git a/addon/globalPlugins/resourceMonitor/psutil/_psbsd.py b/addon/globalPlugins/resourceMonitor/psutil/_psbsd.py index 282765a..087b5e3 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_psbsd.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_psbsd.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _psbsd.py 1498 2012-07-24 21:41:28Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -11,27 +9,47 @@ import errno import os import sys +import warnings import _psutil_bsd import _psutil_posix from psutil import _psposix -from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired -from psutil._compat import namedtuple +from psutil._error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple, wraps from psutil._common import * __extra__all__ = [] # --- constants -NUM_CPUS = _psutil_bsd.get_num_cpus() -BOOT_TIME = _psutil_bsd.get_system_boot_time() -TOTAL_PHYMEM = _psutil_bsd.get_virtual_mem()[0] -_TERMINAL_MAP = _psposix._get_terminal_map() +# Since these constants get determined at import time we do not want to +# crash immediately; instead we'll set them to None and most likely +# we'll crash later as they're used for determining process CPU stats +# and creation_time +try: + NUM_CPUS = _psutil_bsd.get_num_cpus() +except Exception: + NUM_CPUS = None + warnings.warn("couldn't determine platform's NUM_CPUS", RuntimeWarning) +try: + TOTAL_PHYMEM = _psutil_bsd.get_virtual_mem()[0] +except Exception: + TOTAL_PHYMEM = None + warnings.warn("couldn't determine platform's TOTAL_PHYMEM", RuntimeWarning) +try: + BOOT_TIME = _psutil_bsd.get_system_boot_time() +except Exception: + BOOT_TIME = None + warnings.warn("couldn't determine platform's BOOT_TIME", RuntimeWarning) + + _PAGESIZE = os.sysconf("SC_PAGE_SIZE") _cputimes_ntuple = namedtuple('cputimes', 'user nice system idle irq') # --- public functions +get_system_boot_time = _psutil_bsd.get_system_boot_time + nt_virtmem_info = namedtuple('vmem', ' '.join([ # all platforms 'total', 'available', 'percent', 'used', 'free', @@ -120,18 +138,18 @@ def get_system_users(): get_pid_list = _psutil_bsd.get_pid_list pid_exists = _psposix.pid_exists get_disk_usage = _psposix.get_disk_usage -network_io_counters = _psutil_bsd.get_network_io_counters +net_io_counters = _psutil_bsd.get_net_io_counters disk_io_counters = _psutil_bsd.get_disk_io_counters -def wrap_exceptions(method): - """Call method(self, pid) into a try/except clause so that if an - OSError "No such process" exception is raised we assume the process - has died and raise psutil.NoSuchProcess instead. +def wrap_exceptions(fun): + """Decorator which translates bare OSError exceptions into + NoSuchProcess and AccessDenied. """ + @wraps(fun) def wrapper(self, *args, **kwargs): try: - return method(self, *args, **kwargs) + return fun(self, *args, **kwargs) except OSError: err = sys.exc_info()[1] if err.errno == errno.ESRCH: @@ -151,6 +169,20 @@ _status_map = { _psutil_bsd.SZOMB : STATUS_ZOMBIE, } +_conn_status_map = {_psutil_bsd.TCPS_ESTABLISHED : CONN_ESTABLISHED, + _psutil_bsd.TCPS_SYN_SENT : CONN_SYN_SENT, + _psutil_bsd.TCPS_SYN_RECEIVED : CONN_SYN_RECV, + _psutil_bsd.TCPS_FIN_WAIT_1 : CONN_FIN_WAIT1, + _psutil_bsd.TCPS_FIN_WAIT_2 : CONN_FIN_WAIT2, + _psutil_bsd.TCPS_TIME_WAIT : CONN_TIME_WAIT, + _psutil_bsd.TCPS_CLOSED : CONN_CLOSE, + _psutil_bsd.TCPS_CLOSE_WAIT : CONN_CLOSE_WAIT, + _psutil_bsd.TCPS_LAST_ACK : CONN_LAST_ACK, + _psutil_bsd.TCPS_LISTEN : CONN_LISTEN, + _psutil_bsd.TCPS_CLOSING : CONN_CLOSING, + _psutil_bsd.PSUTIL_CONN_NONE : CONN_NONE, + } + class Process(object): """Wrapper class around underlying C implementation.""" @@ -179,8 +211,9 @@ class Process(object): @wrap_exceptions def get_process_terminal(self): tty_nr = _psutil_bsd.get_process_tty_nr(self.pid) + tmap = _psposix._get_terminal_map() try: - return _TERMINAL_MAP[tty_nr] + return tmap[tty_nr] except KeyError: return None @@ -279,8 +312,14 @@ class Process(object): raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in conn_tmap]))) families, types = conn_tmap[kind] - ret = _psutil_bsd.get_process_connections(self.pid, families, types) - return [nt_connection(*conn) for conn in ret] + rawlist = _psutil_bsd.get_process_connections(self.pid, families, types) + ret = [] + for item in rawlist: + fd, fam, type, laddr, raddr, status = item + status = _conn_status_map[status] + nt = nt_connection(fd, fam, type, laddr, raddr, status) + ret.append(nt) + return ret @wrap_exceptions def process_wait(self, timeout=None): diff --git a/addon/globalPlugins/resourceMonitor/psutil/_pslinux.py b/addon/globalPlugins/resourceMonitor/psutil/_pslinux.py index 70fb8d3..cb88422 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_pslinux.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_pslinux.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _pslinux.py 1513 2012-08-14 11:01:37Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -17,36 +15,40 @@ import struct import sys import base64 import re +import warnings import _psutil_posix import _psutil_linux from psutil import _psposix -from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._error import AccessDenied, NoSuchProcess, TimeoutExpired from psutil._common import * -from psutil._compat import PY3, xrange, long, namedtuple +from psutil._compat import PY3, xrange, long, namedtuple, wraps __extra__all__ = [ + # io prio constants "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", "IOPRIO_CLASS_IDLE", + # connection status constants + "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", + "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", + # other "phymem_buffers", "cached_phymem"] -def _get_boot_time(): - """Return system boot time (epoch in seconds)""" +def get_system_boot_time(): + """Return the system boot time expressed in seconds since the epoch.""" f = open('/proc/stat', 'r') try: for line in f: if line.startswith('btime'): return float(line.strip().split()[1]) - raise RuntimeError("line not found") + raise RuntimeError("line 'btime' not found") finally: f.close() def _get_num_cpus(): """Return the number of CPUs on the system""" - # we try to determine num CPUs by using different approaches. - # SC_NPROCESSORS_ONLN seems to be the safer and it is also - # used by multiprocessing module try: return os.sysconf("SC_NPROCESSORS_ONLN") except ValueError: @@ -77,17 +79,35 @@ def _get_num_cpus(): num += 1 if num == 0: - raise RuntimeError("can't determine number of CPUs") + raise RuntimeError("couldn't determine platform's NUM_CPUS") return num # Number of clock ticks per second -_CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) +_CLOCK_TICKS = os.sysconf("SC_CLK_TCK") _PAGESIZE = os.sysconf("SC_PAGE_SIZE") -_TERMINAL_MAP = _psposix._get_terminal_map() -BOOT_TIME = _get_boot_time() -NUM_CPUS = _get_num_cpus() -TOTAL_PHYMEM = _psutil_linux.get_sysinfo()[0] + +# Since these constants get determined at import time we do not want to +# crash immediately; instead we'll set them to None and most likely +# we'll crash later as they're used for determining process CPU stats +# and creation_time +try: + BOOT_TIME = get_system_boot_time() +except Exception: + BOOT_TIME = None + warnings.warn("couldn't determine platform's BOOT_TIME", RuntimeWarning) +try: + NUM_CPUS = _get_num_cpus() +except Exception: + NUM_CPUS = None + warnings.warn("couldn't determine platform's NUM_CPUS", RuntimeWarning) +try: + TOTAL_PHYMEM = _psutil_linux.get_sysinfo()[0] +except Exception: + TOTAL_PHYMEM = None + warnings.warn("couldn't determine platform's TOTAL_PHYMEM", RuntimeWarning) + + # ioprio_* constants http://linux.die.net/man/2/ioprio_get IOPRIO_CLASS_NONE = 0 IOPRIO_CLASS_RT = 1 @@ -95,17 +115,17 @@ IOPRIO_CLASS_BE = 2 IOPRIO_CLASS_IDLE = 3 # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h -_TCP_STATES_TABLE = {"01" : "ESTABLISHED", - "02" : "SYN_SENT", - "03" : "SYN_RECV", - "04" : "FIN_WAIT1", - "05" : "FIN_WAIT2", - "06" : "TIME_WAIT", - "07" : "CLOSE", - "08" : "CLOSE_WAIT", - "09" : "LAST_ACK", - "0A" : "LISTEN", - "0B" : "CLOSING" +_TCP_STATES_TABLE = {"01" : CONN_ESTABLISHED, + "02" : CONN_SYN_SENT, + "03" : CONN_SYN_RECV, + "04" : CONN_FIN_WAIT1, + "05" : CONN_FIN_WAIT2, + "06" : CONN_TIME_WAIT, + "07" : CONN_CLOSE, + "08" : CONN_CLOSE_WAIT, + "09" : CONN_LAST_ACK, + "0A" : CONN_LISTEN, + "0B" : CONN_CLOSING } # --- system memory functions @@ -136,7 +156,12 @@ def virtual_memory(): and inactive is not None: break else: - raise RuntimeError("line(s) not found") + # we might get here when dealing with exotic Linux flavors, see: + # http://code.google.com/p/psutil/issues/detail?id=313 + msg = "'cached', 'active' and 'inactive' memory stats couldn't " \ + "be determined and were set to 0" + warnings.warn(msg, RuntimeWarning) + cached = active = inactive = 0 finally: f.close() avail = free + buffers + cached @@ -162,7 +187,12 @@ def swap_memory(): if sin is not None and sout is not None: break else: - raise RuntimeError("line(s) not found") + # we might get here when dealing with exotic Linux flavors, see: + # http://code.google.com/p/psutil/issues/detail?id=313 + msg = "'sin' and 'sout' swap memory stats couldn't " \ + "be determined and were set to 0" + warnings.warn(msg, RuntimeWarning) + sin = sout = 0 finally: f.close() return nt_swapmeminfo(total, used, free, percent, sin, sout) @@ -180,34 +210,65 @@ def phymem_buffers(): # --- system CPU functions +@memoize +def _get_cputimes_ntuple(): + """ Return a (nt, rindex) tuple depending on the CPU times available + on this Linux kernel version which may be: + user, nice, system, idle, iowait, irq, softirq [steal, [guest, [guest_nice]]] + """ + f = open('/proc/stat', 'r') + try: + values = f.readline().split()[1:] + finally: + f.close() + fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq'] + rindex = 8 + vlen = len(values) + if vlen >= 8: + # Linux >= 2.6.11 + fields.append('steal') + rindex += 1 + if vlen >= 9: + # Linux >= 2.6.24 + fields.append('guest') + rindex += 1 + if vlen >= 10: + # Linux >= 3.2.0 + fields.append('guest_nice') + rindex += 1 + return (namedtuple('cputimes', ' '.join(fields)), rindex) + def get_system_cpu_times(): - """Return a named tuple representing the following CPU times: - user, nice, system, idle, iowait, irq, softirq. + """Return a named tuple representing the following system-wide + CPU times: + user, nice, system, idle, iowait, irq, softirq [steal, [guest, [guest_nice]]] + Last 3 fields may not be available on all Linux kernel versions. """ f = open('/proc/stat', 'r') try: values = f.readline().split() finally: f.close() - - values = values[1:8] - values = tuple([float(x) / _CLOCK_TICKS for x in values]) - return nt_sys_cputimes(*values[:7]) + nt, rindex = _get_cputimes_ntuple() + fields = values[1:rindex] + fields = [float(x) / _CLOCK_TICKS for x in fields] + return nt(*fields) def get_system_per_cpu_times(): """Return a list of namedtuple representing the CPU times for every CPU available on the system. """ + nt, rindex = _get_cputimes_ntuple() cpus = [] f = open('/proc/stat', 'r') - # get rid of the first line who refers to system wide CPU stats try: + # get rid of the first line which refers to system wide CPU stats f.readline() - for line in f.readlines(): + for line in f: if line.startswith('cpu'): - values = line.split()[1:8] - values = tuple([float(x) / _CLOCK_TICKS for x in values]) - entry = nt_sys_cputimes(*values[:7]) + fields = line.split()[1:rindex] + fields = [float(x) / _CLOCK_TICKS for x in fields] + entry = nt(*fields) cpus.append(entry) return cpus finally: @@ -251,7 +312,7 @@ def get_system_users(): rawlist = _psutil_linux.get_system_users() for item in rawlist: user, tty, hostname, tstamp, user_process = item - # XXX the underlying C function includes entries about + # 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: @@ -273,7 +334,7 @@ def pid_exists(pid): """Check For the existence of a unix pid.""" return _psposix.pid_exists(pid) -def network_io_counters(): +def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ @@ -317,10 +378,19 @@ def disk_io_counters(): lines = f.readlines()[2:] finally: f.close() - for line in lines: + for line in reversed(lines): _, _, _, name = line.split() if name[-1].isdigit(): + # we're dealing with a partition (e.g. 'sda1'); 'sda' will + # also be around but we want to omit it partitions.append(name) + else: + if not partitions or not partitions[-1].startswith(name): + # we're dealing with a disk entity for which no + # partitions have been defined (e.g. 'sda' but + # 'sda1' was not around), see: + # http://code.google.com/p/psutil/issues/detail?id=338 + partitions.append(name) # retdict = {} f = open("/proc/diskstats", "r") @@ -329,6 +399,7 @@ def disk_io_counters(): finally: f.close() for line in lines: + # http://www.mjmwired.net/kernel/Documentation/iostats.txt _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \ line.split()[:11] if name in partitions: @@ -336,8 +407,6 @@ def disk_io_counters(): wbytes = int(wbytes) * SECTOR_SIZE reads = int(reads) writes = int(writes) - # TODO: times are expressed in milliseconds while OSX/BSD has - # these expressed in nanoseconds; figure this out. rtime = int(rtime) wtime = int(wtime) retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime) @@ -358,13 +427,14 @@ _status_map = {"R" : STATUS_RUNNING, # --- decorators -def wrap_exceptions(callable): - """Call callable into a try/except clause and translate ENOENT, - EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. +def wrap_exceptions(fun): + """Decorator which translates bare OSError and IOError exceptions + into NoSuchProcess and AccessDenied. """ + @wraps(fun) def wrapper(self, *args, **kwargs): try: - return callable(self, *args, **kwargs) + return fun(self, *args, **kwargs) except EnvironmentError: # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if @@ -384,8 +454,6 @@ class Process(object): __slots__ = ["pid", "_process_name"] def __init__(self, pid): - if not isinstance(pid, int): - raise TypeError('pid must be an integer') self.pid = pid self._process_name = None @@ -440,13 +508,14 @@ class Process(object): @wrap_exceptions def get_process_terminal(self): + tmap = _psposix._get_terminal_map() f = open("/proc/%s/stat" % self.pid) try: tty_nr = int(f.read().split(' ')[6]) finally: f.close() try: - return _TERMINAL_MAP[tty_nr] + return tmap[tty_nr] except KeyError: return None @@ -469,7 +538,8 @@ class Process(object): if not os.path.exists('/proc/%s/io' % os.getpid()): def get_process_io_counters(self): - raise NotImplementedError('/proc/PID/io is not available') + raise NotImplementedError("couldn't find /proc/%s/io (kernel " \ + "too old?)" % self.pid) @wrap_exceptions def get_cpu_times(self): @@ -563,11 +633,20 @@ class Process(object): data = {} for line in f: fields = line.split(None, 5) - if len(fields) >= 5: + if not fields[0].endswith(':'): + # new block section yield (current_block.pop(), data) current_block.append(line) else: - data[fields[0]] = int(fields[1]) * 1024 + try: + data[fields[0]] = int(fields[1]) * 1024 + except ValueError: + if fields[0].startswith('VmFlags:'): + # see issue #369 + continue + else: + raise ValueError("don't know how to interpret" \ + " line %r" % line) yield (current_block.pop(), data) if first_line: # smaps file can be empty @@ -583,13 +662,15 @@ class Process(object): path = path.strip() yield (addr, perms, path, data['Rss:'], - data['Size:'], + data.get('Size:', 0), data.get('Pss:', 0), - data['Shared_Clean:'], data['Shared_Clean:'], - data['Private_Clean:'], data['Private_Dirty:'], - data['Referenced:'], - data['Anonymous:'], - data['Swap:']) + data.get('Shared_Clean:', 0), + data.get('Shared_Dirty:', 0), + data.get('Private_Clean:', 0), + data.get('Private_Dirty:', 0), + data.get('Referenced:', 0), + data.get('Anonymous:', 0), + data.get('Swap:', 0)) f.close() except EnvironmentError: # XXX - Can't use wrap_exceptions decorator as we're @@ -607,12 +688,12 @@ class Process(object): if f is not None: f.close() raise + f.close() if not os.path.exists('/proc/%s/smaps' % os.getpid()): - def get_shared_libs(self, ext): - msg = "this Linux version does not support /proc/PID/smaps " \ - "(kernel < 2.6.14 or CONFIG_MMU kernel configuration " \ - "option is not enabled)" + def get_memory_maps(self, ext): + msg = "couldn't find /proc/%s/smaps; kernel < 2.6.14 or CONFIG_MMU " \ + "kernel configuration option is not enabled" % self.pid raise NotImplementedError(msg) @wrap_exceptions @@ -635,7 +716,10 @@ class Process(object): unvol = int(line.split()[1]) if vol is not None and unvol is not None: return nt_ctxsw(vol, unvol) - raise RuntimeError("line not found") + raise NotImplementedError("the 'voluntary_ctxt_switches' and " \ + "'nonvoluntary_ctxt_switches' fields were not found in " \ + "/proc/%s/status; the kernel is probably older than 2.6.23" \ + % self.pid) finally: f.close() @@ -646,7 +730,7 @@ class Process(object): for line in f: if line.startswith("Threads:"): return int(line.split()[1]) - raise RuntimeError("line not found") + raise NotImplementedError("line not found") finally: f.close() @@ -863,7 +947,7 @@ class Process(object): if type_ == socket.SOCK_STREAM: status = _TCP_STATES_TABLE[status] else: - status = "" + status = CONN_NONE fd = int(inodes[inode]) conn = nt_connection(fd, family, type_, laddr, raddr, status) @@ -880,7 +964,7 @@ class Process(object): fd = int(inodes[inode]) type_ = int(type_) conn = nt_connection(fd, family, type_, path, - None, "") + None, CONN_NONE) retlist.append(conn) else: raise ValueError(family) @@ -917,13 +1001,6 @@ class Process(object): os.stat('/proc/%s' % self.pid) return ret - -# --- lsof implementation -# -# def get_connections(self): -# lsof = _psposix.LsofParser(self.pid, self._process_name) -# return lsof.get_process_connections() - @wrap_exceptions def get_num_fds(self): return len(os.listdir("/proc/%s/fd" % self.pid)) @@ -936,7 +1013,7 @@ class Process(object): if line.startswith("PPid:"): # PPid: nnnn return int(line.split()[1]) - raise RuntimeError("line not found") + raise NotImplementedError("line not found") finally: f.close() @@ -948,7 +1025,7 @@ class Process(object): if line.startswith('Uid:'): _, real, effective, saved, fs = line.split() return nt_uids(int(real), int(effective), int(saved)) - raise RuntimeError("line not found") + raise NotImplementedError("line not found") finally: f.close() @@ -960,7 +1037,7 @@ class Process(object): if line.startswith('Gid:'): _, real, effective, saved, fs = line.split() return nt_gids(int(real), int(effective), int(saved)) - raise RuntimeError("line not found") + raise NotImplementedError("line not found") finally: f.close() diff --git a/addon/globalPlugins/resourceMonitor/psutil/_psmswindows.py b/addon/globalPlugins/resourceMonitor/psutil/_psmswindows.py index 7c3f229..b2dfb37 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_psmswindows.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_psmswindows.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _psmswindows.py 1514 2012-08-14 11:16:56Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -11,27 +9,65 @@ import errno import os import sys -import platform +#import platform +import warnings import _psutil_mswindows from _psutil_mswindows import ERROR_ACCESS_DENIED -from error import AccessDenied, NoSuchProcess, TimeoutExpired -from _common import * -from _compat import PY3, xrange, long +from psutil._error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._common import * +from psutil._compat import PY3, xrange, long, wraps # Windows specific extended namespace __extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", - "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS"] + "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS", + # + "CONN_DELETE_TCB", + ] # --- module level constants (gets pushed up to psutil module) -NUM_CPUS = _psutil_mswindows.get_num_cpus() -BOOT_TIME = _psutil_mswindows.get_system_uptime() -TOTAL_PHYMEM = _psutil_mswindows.get_virtual_mem()[0] +# Since these constants get determined at import time we do not want to +# crash immediately; instead we'll set them to None and most likely +# we'll crash later as they're used for determining process CPU stats +# and creation_time +try: + NUM_CPUS = _psutil_mswindows.get_num_cpus() +except Exception: + NUM_CPUS = None + warnings.warn("couldn't determine platform's NUM_CPUS", RuntimeWarning) +try: + BOOT_TIME = _psutil_mswindows.get_system_boot_time() +except Exception: + BOOT_TIME = None + warnings.warn("couldn't determine platform's BOOT_TIME", RuntimeWarning) +try: + TOTAL_PHYMEM = _psutil_mswindows.get_virtual_mem()[0] +except Exception: + TOTAL_PHYMEM = None + warnings.warn("couldn't determine platform's TOTAL_PHYMEM", RuntimeWarning) + +CONN_DELETE_TCB = constant(11, "DELETE_TCB") WAIT_TIMEOUT = 0x00000102 # 258 in decimal ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED]) +TCP_STATES_TABLE = { + _psutil_mswindows.MIB_TCP_STATE_ESTAB : CONN_ESTABLISHED, + _psutil_mswindows.MIB_TCP_STATE_SYN_SENT : CONN_SYN_SENT, + _psutil_mswindows.MIB_TCP_STATE_SYN_RCVD : CONN_SYN_RECV, + _psutil_mswindows.MIB_TCP_STATE_FIN_WAIT1 : CONN_FIN_WAIT1, + _psutil_mswindows.MIB_TCP_STATE_FIN_WAIT2 : CONN_FIN_WAIT2, + _psutil_mswindows.MIB_TCP_STATE_TIME_WAIT : CONN_TIME_WAIT, + _psutil_mswindows.MIB_TCP_STATE_CLOSED : CONN_CLOSE, + _psutil_mswindows.MIB_TCP_STATE_CLOSE_WAIT : CONN_CLOSE_WAIT, + _psutil_mswindows.MIB_TCP_STATE_LAST_ACK : CONN_LAST_ACK, + _psutil_mswindows.MIB_TCP_STATE_LISTEN : CONN_LISTEN, + _psutil_mswindows.MIB_TCP_STATE_CLOSING : CONN_CLOSING, + _psutil_mswindows.MIB_TCP_STATE_DELETE_TCB : CONN_DELETE_TCB, + _psutil_mswindows.PSUTIL_CONN_NONE : CONN_NONE, +} + # process priority constants: # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx @@ -60,6 +96,8 @@ def _convert_raw_path(s): # --- public functions +get_system_boot_time = _psutil_mswindows.get_system_boot_time + nt_virtmem_info = namedtuple('vmem', ' '.join([ # all platforms 'total', 'available', 'percent', 'used', 'free'])) @@ -137,19 +175,19 @@ def get_system_users(): get_pid_list = _psutil_mswindows.get_pid_list pid_exists = _psutil_mswindows.pid_exists -network_io_counters = _psutil_mswindows.get_network_io_counters +net_io_counters = _psutil_mswindows.get_net_io_counters disk_io_counters = _psutil_mswindows.get_disk_io_counters # --- decorator -def wrap_exceptions(callable): - """Call callable into a try/except clause so that if a - WindowsError 5 AccessDenied exception is raised we translate it - into psutil.AccessDenied +def wrap_exceptions(fun): + """Decorator which translates bare OSError and WindowsError + exceptions into NoSuchProcess and AccessDenied. """ + @wraps(fun) def wrapper(self, *args, **kwargs): try: - return callable(self, *args, **kwargs) + return fun(self, *args, **kwargs) except OSError: err = sys.exc_info()[1] if err.errno in ACCESS_DENIED_SET: @@ -348,8 +386,15 @@ class Process(object): raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in conn_tmap]))) families, types = conn_tmap[kind] - ret = _psutil_mswindows.get_process_connections(self.pid, families, types) - return [nt_connection(*conn) for conn in ret] + rawlist = _psutil_mswindows.get_process_connections(self.pid, families, + types) + ret = [] + for item in rawlist: + fd, fam, type, laddr, raddr, status = item + status = TCP_STATES_TABLE[status] + nt = nt_connection(fd, fam, type, laddr, raddr, status) + ret.append(nt) + return ret @wrap_exceptions def get_process_nice(self): @@ -359,6 +404,22 @@ class Process(object): def set_process_nice(self, value): return _psutil_mswindows.set_process_priority(self.pid, value) + # available on Windows >= Vista + if hasattr(_psutil_mswindows, "get_process_io_priority"): + @wrap_exceptions + def get_process_ionice(self): + return _psutil_mswindows.get_process_io_priority(self.pid) + + @wrap_exceptions + def set_process_ionice(self, value, _): + if _: + raise TypeError("set_process_ionice() on Windows takes only " \ + "1 argument (2 given)") + if value not in (2, 1, 0): + raise ValueError("value must be 2 (normal), 1 (low) or 0 " \ + "(very low); got %r" % value) + return _psutil_mswindows.set_process_io_priority(self.pid, value) + @wrap_exceptions def get_process_io_counters(self): try: @@ -392,8 +453,6 @@ class Process(object): raise ValueError("invalid argument %r" % l) out = 0 for b in l: - if not isinstance(b, (int, long)) or b < 0: - raise ValueError("invalid argument %r" % b) out |= 2**b return out @@ -403,7 +462,7 @@ class Process(object): allcpus = list(range(len(get_system_per_cpu_times()))) for cpu in value: if cpu not in allcpus: - raise ValueError("invalid CPU %i" % cpu) + raise ValueError("invalid CPU %r" % cpu) bitmask = to_bitmask(value) _psutil_mswindows.set_process_cpu_affinity(self.pid, bitmask) diff --git a/addon/globalPlugins/resourceMonitor/psutil/_psosx.py b/addon/globalPlugins/resourceMonitor/psutil/_psosx.py index 90cead2..eb28b34 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_psosx.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_psosx.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _psosx.py 1498 2012-07-24 21:41:28Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -11,27 +9,60 @@ import errno import os import sys +import warnings import _psutil_osx import _psutil_posix from psutil import _psposix -from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired -from psutil._compat import namedtuple +from psutil._error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple, wraps from psutil._common import * __extra__all__ = [] # --- constants -NUM_CPUS = _psutil_osx.get_num_cpus() -BOOT_TIME = _psutil_osx.get_system_boot_time() -TOTAL_PHYMEM = _psutil_osx.get_virtual_mem()[0] +# Since these constants get determined at import time we do not want to +# crash immediately; instead we'll set them to None and most likely +# we'll crash later as they're used for determining process CPU stats +# and creation_time +try: + NUM_CPUS = _psutil_osx.get_num_cpus() +except Exception: + NUM_CPUS = None + warnings.warn("couldn't determine platform's NUM_CPUS", RuntimeWarning) +try: + BOOT_TIME = _psutil_osx.get_system_boot_time() +except Exception: + BOOT_TIME = None + warnings.warn("couldn't determine platform's BOOT_TIME", RuntimeWarning) +try: + TOTAL_PHYMEM = _psutil_osx.get_virtual_mem()[0] +except Exception: + TOTAL_PHYMEM = None + warnings.warn("couldn't determine platform's TOTAL_PHYMEM", RuntimeWarning) + _PAGESIZE = os.sysconf("SC_PAGE_SIZE") -_TERMINAL_MAP = _psposix._get_terminal_map() _cputimes_ntuple = namedtuple('cputimes', 'user nice system idle') +# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +_TCP_STATES_TABLE = {_psutil_osx.TCPS_ESTABLISHED : CONN_ESTABLISHED, + _psutil_osx.TCPS_SYN_SENT : CONN_SYN_SENT, + _psutil_osx.TCPS_SYN_RECEIVED : CONN_SYN_RECV, + _psutil_osx.TCPS_FIN_WAIT_1 : CONN_FIN_WAIT1, + _psutil_osx.TCPS_FIN_WAIT_2 : CONN_FIN_WAIT2, + _psutil_osx.TCPS_TIME_WAIT : CONN_TIME_WAIT, + _psutil_osx.TCPS_CLOSED : CONN_CLOSE, + _psutil_osx.TCPS_CLOSE_WAIT : CONN_CLOSE_WAIT, + _psutil_osx.TCPS_LAST_ACK : CONN_LAST_ACK, + _psutil_osx.TCPS_LISTEN : CONN_LISTEN, + _psutil_osx.TCPS_CLOSING : CONN_CLOSING, + _psutil_osx.PSUTIL_CONN_NONE : CONN_NONE, + } # --- functions +get_system_boot_time = _psutil_osx.get_system_boot_time + nt_virtmem_info = namedtuple('vmem', ' '.join([ # all platforms 'total', 'available', 'percent', 'used', 'free', @@ -101,19 +132,19 @@ def get_system_users(): get_pid_list = _psutil_osx.get_pid_list pid_exists = _psposix.pid_exists get_disk_usage = _psposix.get_disk_usage -network_io_counters = _psutil_osx.get_network_io_counters +net_io_counters = _psutil_osx.get_net_io_counters disk_io_counters = _psutil_osx.get_disk_io_counters # --- decorator -def wrap_exceptions(callable): - """Call callable into a try/except clause so that if an - OSError EPERM exception is raised we translate it into - psutil.AccessDenied. +def wrap_exceptions(fun): + """Decorator which translates bare OSError exceptions into + NoSuchProcess and AccessDenied. """ + @wraps(fun) def wrapper(self, *args, **kwargs): try: - return callable(self, *args, **kwargs) + return fun(self, *args, **kwargs) except OSError: err = sys.exc_info()[1] if err.errno == errno.ESRCH: @@ -179,8 +210,9 @@ class Process(object): @wrap_exceptions def get_process_terminal(self): tty_nr = _psutil_osx.get_process_tty_nr(self.pid) + tmap = _psposix._get_terminal_map() try: - return _TERMINAL_MAP[tty_nr] + return tmap[tty_nr] except KeyError: return None @@ -242,8 +274,14 @@ class Process(object): raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in conn_tmap]))) families, types = conn_tmap[kind] - ret = _psutil_osx.get_process_connections(self.pid, families, types) - return [nt_connection(*conn) for conn in ret] + rawlist = _psutil_osx.get_process_connections(self.pid, families, types) + ret = [] + for item in rawlist: + fd, fam, type, laddr, raddr, status = item + status = _TCP_STATES_TABLE[status] + nt = nt_connection(fd, fam, type, laddr, raddr, status) + ret.append(nt) + return ret @wrap_exceptions def get_num_fds(self): diff --git a/addon/globalPlugins/resourceMonitor/psutil/_psposix.py b/addon/globalPlugins/resourceMonitor/psutil/_psposix.py index 899f77a..a3a1eb3 100644 --- a/addon/globalPlugins/resourceMonitor/psutil/_psposix.py +++ b/addon/globalPlugins/resourceMonitor/psutil/_psposix.py @@ -1,8 +1,6 @@ #!/usr/bin/env python -# -# $Id: _psposix.py 1409 2012-07-04 08:21:06Z g.rodola $ -# -# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + +# 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. @@ -15,14 +13,12 @@ import sys import time import glob -from psutil.error import TimeoutExpired -from psutil._common import nt_diskinfo, usage_percent +from psutil._error import TimeoutExpired +from psutil._common import nt_diskinfo, usage_percent, memoize def pid_exists(pid): """Check whether pid exists in the current process table.""" - if not isinstance(pid, int): - raise TypeError('an integer is required') if pid < 0: return False try: @@ -46,14 +42,15 @@ def wait_pid(pid, timeout=None): """ def check_timeout(delay): if timeout is not None: - if time.time() >= stop_at: + if timer() >= stop_at: raise TimeoutExpired(pid) time.sleep(delay) return min(delay * 2, 0.04) + timer = getattr(time, 'monotonic', time.time) if timeout is not None: waitcall = lambda: os.waitpid(pid, os.WNOHANG) - stop_at = time.time() + timeout + stop_at = timer() + timeout else: waitcall = lambda: os.waitpid(pid, 0) @@ -109,10 +106,16 @@ def get_disk_usage(path): # http://goo.gl/sWGbH return nt_diskinfo(total, used, free, percent) +@memoize def _get_terminal_map(): ret = {} ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') for name in ls: assert name not in ret - ret[os.stat(name).st_rdev] = name + try: + ret[os.stat(name).st_rdev] = name + except OSError: + err = sys.exc_info()[1] + if err.errno != errno.ENOENT: + raise return ret This diff is so big that we needed to truncate the remainder. https://bitbucket.org/nvdaaddonteam/resourcemonitor/commits/576f1a1e0e1e/ Changeset: 576f1a1e0e1e Branch: None User: josephsl Date: 2013-08-18 14:44:51 Summary: Let's get ready for 3.0-dev. First, officially update psutil to 1.0.1. Affected #: 1 file diff --git a/readme.md b/readme.md index 19f3c7f..370532f 100644 --- a/readme.md +++ b/readme.md @@ -13,6 +13,11 @@ This plugin gives information about CPU load, memory usage, battery and disk usa * NVDA+Shift+3 Presents the used and total space of the static and removable drives on this computer, * NVDA+Shift+4 Presents battery percentage, charging status, remaining time (if not charging), and a warning if the battery is low or critical, +## Changes for 3.0 ## + +* Updated psutil dependency to 1.0.1. + + ## Changes for 2.4 ## * New languages: Chinese (simplified), Ukrainian. Repository URL: https://bitbucket.org/nvdaaddonteam/resourcemonitor/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.