diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-23 23:06:30 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-23 23:06:30 +0100 |
commit | 5f186957a4ae6505810488c7b3118453c459fdec (patch) | |
tree | 43ee8a0811724b2d6bbd267808ee49b3becf08f6 | |
parent | 29496900f772c8c594fa9c5142a88a5291c05f55 (diff) | |
parent | f8c07ae1082ee07fd08c25fa163fc8edb31e5b7f (diff) | |
download | psutil-5f186957a4ae6505810488c7b3118453c459fdec.tar.gz |
merge from master
32 files changed, 840 insertions, 334 deletions
diff --git a/.travis.yml b/.travis.yml index 17206c58..48c84a7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ matrix: - python: 3.3 - python: 3.4 - python: 3.5 + - python: 3.6 - "pypy" # XXX - commented because OSX builds are deadly slow # - language: generic @@ -420,3 +420,8 @@ I: 919 N: Max Bélanger W: https://github.com/maxbelanger I: 936 + +N: Pierre Fersing +C: France +E: pierre.fersing@bleemeo.com +I: 950 diff --git a/DEVGUIDE.rst b/DEVGUIDE.rst index 95bea79a..93dfa690 100644 --- a/DEVGUIDE.rst +++ b/DEVGUIDE.rst @@ -1,6 +1,6 @@ -===== -Setup -===== +======================= +Setup and running tests +======================= If you plan on hacking on psutil this is what you're supposed to do first: diff --git a/HISTORY.rst b/HISTORY.rst index f93e0974..c6db2aa0 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,10 +1,24 @@ *Bug tracker at https://github.com/giampaolo/psutil/issues* -5.0.1 +5.0.2 ===== *XXXX-XX-XX* +**Bug fixes** + +- 687_: [Linux] pid_exists() no longer returns True if passed a process thread + ID. +- 948_: cannot install psutil with PYTHONOPTIMIZE=2. +- 950_: [Windows] Process.cpu_percent() was calculated incorrectly and showed + higher number than real usage. + + +5.0.1 +===== + +*2016-12-21* + **Enhancements** - 939_: tar.gz distribution went from 1.8M to 258K. @@ -13,10 +27,12 @@ **Bug fixes** +- 609_: [SunOS] psutil does not compile on Solaris 10. - 936_: [Windows] fix compilation error on VS 2013 (patch by Max Bélanger). - 940_: [Linux] cpu_percent() and cpu_times_percent() was calculated incorrectly as "iowait", "guest" and "guest_nice" times were not properly taken into account. +- 944_: [OpenBSD] psutil.pids() was omitting PID 0. 5.0.0 diff --git a/INSTALL.rst b/INSTALL.rst index b1b40d68..d731bd3d 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -5,12 +5,13 @@ pip is the easiest way to install psutil. It is shipped by default with Python 2.7.9+ and 3.4+. If you're using an older Python version `install pip <https://pip.pypa.io/en/latest/installing/>`__ first. -If you GIT cloned psutil source code you can also install pip with:: +If you GIT cloned psutil source code you can also install pip and/or upgrade +it to latest version with:: make install-pip Unless you're on Windows, in order to install psutil with pip you'll also need -a C compiler installed. +a C compiler installed (e.g. gcc). pip will retrieve psutil source code or binaries from `PYPI <https://pypi.python.org/pypi/psutil>`__ repository. @@ -62,7 +63,7 @@ versions. OSX === -Install `XcodeTools <https://developer.apple.com/downloads/?name=Xcode>`__ +Install `Xcode <https://developer.apple.com/downloads/?name=Xcode>`__ first, then: :: @@ -137,6 +138,14 @@ Install: pkg install gcc python -m pip install psutil +Install from sources +==================== + + git clone https://github.com/giampaolo/psutil.git + cd psutil + python setup.py install + + Dev Guide ========= diff --git a/MANIFEST.in b/MANIFEST.in index 94532292..b0c15645 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,18 @@ +include *.bat include *.rst +include .coveragerc include CREDITS* +include IDEAS include INSTALL* include LICENSE* -include make.bar +include HISTORY* include Makefile -recursive-include psutil *.py *.c *.h +include tox.ini + +recursive-include psutil *.py *.c *.h *.rst +recursive-include scripts *.py +recursive-include README* + +recursive-include docs *.conf *.rst *.js *.html *.css *.py *.bat *Makefile* README* +recursive-exclude docs/_build * +recursive-exclude .ci * @@ -8,6 +8,7 @@ ARGS = # List of nice-to-have dev libs. DEPS = argparse \ + check-manifest \ coverage \ flake8 \ futures \ @@ -171,6 +172,9 @@ pyflakes: flake8: @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 +check-manifest: + $(PYTHON) -m check_manifest -v $(ARGS) + # =================================================================== # GIT # =================================================================== @@ -206,7 +210,7 @@ win-download-exes: # Upload exes/wheels in dist/* directory to PYPI. win-upload-exes: $(PYTHON) -m twine upload dist/*.exe - $(PYTHON) -m twine upload dist/*.wheel + $(PYTHON) -m twine upload dist/*.whl # All the necessary steps before making a release. pre-release: @@ -249,3 +253,9 @@ bench-oneshot: install # same as above but using perf module (supposed to be more precise) bench-oneshot-2: install $(PYTHON) scripts/internal/bench_oneshot_2.py + +# generate a doc.zip file and manually upload it to PYPI. +doc: + cd docs && make html && cd _build/html/ && zip doc.zip -r . + mv docs/_build/html/doc.zip . + echo "done; now manually upload doc.zip from here: https://pypi.python.org/pypi?:action=pkg_edit&name=psutil" @@ -71,7 +71,7 @@ Projects using psutil ===================== At the time of writing there are over -`4000 projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__ +`4200 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__ on github which depend from psutil. Here's some I find particularly interesting: @@ -122,6 +122,9 @@ CPU >>> >>> psutil.cpu_stats() scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0) + >>> + >>> psutil.cpu_freq() + scpufreq(current=931.42925, min=800.0, max=3500.0) Memory ====== diff --git a/appveyor.yml b/appveyor.yml index 927d9cb3..4428f776 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,6 +30,10 @@ environment: PYTHON_VERSION: "3.5.x" PYTHON_ARCH: "32" + - PYTHON: "C:\\Python36" + PYTHON_VERSION: "3.6.x" + PYTHON_ARCH: "32" + # 64 bits - PYTHON: "C:\\Python27-x64" @@ -51,6 +55,13 @@ environment: VS_VER: "2015" INSTANCENAME: "SQL2012SP1" + - PYTHON: "C:\\Python36-x64" + PYTHON_VERSION: "3.6.x" + PYTHON_ARCH: "64" + ARCH: x86_64 + VS_VER: "2015" + INSTANCENAME: "SQL2012SP1" + # Also build on a Python version not pre-installed by Appveyor. # See: https://github.com/ogrisel/python-appveyor-demo/issues/10 diff --git a/docs/_themes/pydoctheme/static/pydoctheme.css b/docs/_themes/pydoctheme/static/pydoctheme.css index 4196e558..c6f19ab7 100644 --- a/docs/_themes/pydoctheme/static/pydoctheme.css +++ b/docs/_themes/pydoctheme/static/pydoctheme.css @@ -185,3 +185,13 @@ div.body h3 { div.body h2 { padding-left:10px; } + +.data { + margin-top: 4px !important; + margin-bottom: 4px !important; +} + +.data dd { + margin-top: 0px !important; + margin-bottom: 0px !important; +} diff --git a/docs/conf.py b/docs/conf.py index 2267b5d1..f0a206db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,7 +46,7 @@ needs_sphinx = '1.0' # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', + 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx'] diff --git a/docs/index.rst b/docs/index.rst index a18bba81..7efddb24 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -39,11 +39,12 @@ The psutil documentation you're reading is distributed as a single HTML page. Install ------- -On Windows, or on UNIX if you have a C compiler installed, the easiest way to -install psutil is via ``pip``:: +The easiest way to install psutil is via ``pip``:: pip install psutil +On UNIX this requires a C compiler (e.g. gcc) installed. On Windows pip will +automatically retrieve a pre-compiled wheel version. Alternatively, see more detailed `install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_ instructions. @@ -138,6 +139,8 @@ CPU :func:`psutil.cpu_times(percpu=True)<cpu_times()>`. *interval* and *percpu* arguments have the same meaning as in :func:`cpu_percent()`. + On Linux "guest" and "guest_nice" percentages are not accounted in "user" + and "user_nice" percentages. .. warning:: the first time this function is called with *interval* = ``0.0`` or @@ -154,13 +157,14 @@ CPU in Python 3.4). If *logical* is ``False`` return the number of physical cores only (hyper thread CPUs are excluded). Return ``None`` if undetermined. + On OpenBSD and NetBSD ``psutil.cpu_count(logical=False)`` always return + ``None``. Example on a system having 2 physical hyper-thread CPU cores: >>> import psutil >>> psutil.cpu_count() 4 >>> psutil.cpu_count(logical=False) 2 - >>> .. function:: cpu_stats() @@ -187,6 +191,33 @@ CPU .. versionadded:: 4.1.0 +.. function:: cpu_freq(percpu=False) + + Return CPU frequency as a nameduple including *current*, *min* and *max* + frequencies expressed in Mhz. + If *percpu* is ``True`` and the system supports per-cpu frequency + retrieval (Linux only) a list of frequencies is returned for each CPU, + if not, a list with a single element is returned. + If *min* and *max* cannot be determined they are set to ``0``. + + Example (Linux): + + .. code-block:: python + + >>> import psutil + >>> psutil.cpu_freq() + scpufreq(current=931.42925, min=800.0, max=3500.0) + >>> psutil.cpu_freq(percpu=True) + [scpufreq(current=2394.945, min=800.0, max=3500.0), + scpufreq(current=2236.812, min=800.0, max=3500.0), + scpufreq(current=1703.609, min=800.0, max=3500.0), + scpufreq(current=1754.289, min=800.0, max=3500.0)] + + Availability: Linux, OSX, Windows + + .. versionadded:: 5.1.0 + + Memory ------ @@ -451,7 +482,7 @@ Network else ``None``. On some platforms (e.g. Linux) the availability of this field changes depending on process privileges (root is needed). - The *kind* parameter is a string which filters for connections that fit the + The *kind* parameter is a string which filters for connections matching the following criteria: .. table:: @@ -459,27 +490,27 @@ Network +----------------+-----------------------------------------------------+ | **Kind value** | **Connections using** | +================+=====================================================+ - | "inet" | IPv4 and IPv6 | + | ``"inet"`` | IPv4 and IPv6 | +----------------+-----------------------------------------------------+ - | "inet4" | IPv4 | + | ``"inet4"`` | IPv4 | +----------------+-----------------------------------------------------+ - | "inet6" | IPv6 | + | ``"inet6"`` | IPv6 | +----------------+-----------------------------------------------------+ - | "tcp" | TCP | + | ``"tcp"`` | TCP | +----------------+-----------------------------------------------------+ - | "tcp4" | TCP over IPv4 | + | ``"tcp4"`` | TCP over IPv4 | +----------------+-----------------------------------------------------+ - | "tcp6" | TCP over IPv6 | + | ``"tcp6"`` | TCP over IPv6 | +----------------+-----------------------------------------------------+ - | "udp" | UDP | + | ``"udp"`` | UDP | +----------------+-----------------------------------------------------+ - | "udp4" | UDP over IPv4 | + | ``"udp4"`` | UDP over IPv4 | +----------------+-----------------------------------------------------+ - | "udp6" | UDP over IPv6 | + | ``"udp6"`` | UDP over IPv6 | +----------------+-----------------------------------------------------+ - | "unix" | UNIX socket (both UDP and TCP protocols) | + | ``"unix"`` | UNIX socket (both UDP and TCP protocols) | +----------------+-----------------------------------------------------+ - | "all" | the sum of all the possible families and protocols | + | ``"all"`` | the sum of all the possible families and protocols | +----------------+-----------------------------------------------------+ On OSX this function requires root privileges. @@ -630,12 +661,16 @@ Functions .. function:: pids() Return a list of current running PIDs. To iterate over all processes - :func:`process_iter()` should be preferred. + and avoid race conditions :func:`process_iter()` should be preferred. + + >>> import psutil + >>> psutil.pids() + [1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, ..., 32498] .. function:: pid_exists(pid) Check whether the given PID exists in the current process list. This is - faster than doing ``"pid in psutil.pids()"`` and should be preferred. + faster than doing ``pid in psutil.pids()`` and should be preferred. .. function:: process_iter() @@ -676,18 +711,18 @@ Functions - give them some time to terminate - send SIGKILL to those ones which are still alive - Example:: + Example which terminates and waits all the children of this process:: import psutil def on_terminate(proc): print("process {} terminated with exit code {}".format(proc, proc.returncode)) - procs = [...] # a list of Process instances + procs = psutil.Process().children() for p in procs: p.terminate() - gone, alive = psutil.wait_procs(procs, timeout=3, callback=on_terminate) - for p in alive: + gone, still_alive = psutil.wait_procs(procs, timeout=3, callback=on_terminate) + for p in still_alive: p.kill() Exceptions @@ -700,8 +735,8 @@ Exceptions .. class:: NoSuchProcess(pid, name=None, msg=None) Raised by :class:`Process` class methods when no process with the given - pid* is found in the current process list or when a process no longer - exists. "name" is the name the process had before disappearing + *pid* is found in the current process list or when a process no longer + exists. *name* is the name the process had before disappearing and gets set only if :meth:`Process.name()` was previously called. .. class:: ZombieProcess(pid, name=None, ppid=None, msg=None) @@ -731,10 +766,12 @@ Process class .. class:: Process(pid=None) - Represents an OS process with the given *pid*. If *pid* is omitted current - process *pid* (`os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__) - is used. + Represents an OS process with the given *pid*. + If *pid* is omitted current process *pid* + (`os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__) is used. Raise :class:`NoSuchProcess` if *pid* does not exist. + On Linux *pid* can also refer to a thread ID (the *id* field returned by + :meth:`threads` method). When accessing methods of this class always be prepared to catch :class:`NoSuchProcess`, :class:`ZombieProcess` and :class:`AccessDenied` exceptions. @@ -856,7 +893,7 @@ Process class .. method:: ppid() - The process parent pid. On Windows the return value is cached after first + The process parent PID. On Windows the return value is cached after first call. Not on POSIX because `ppid may change <https://github.com/giampaolo/psutil/issues/321>`__ if process becomes a zombie. @@ -873,16 +910,28 @@ Process class On some systems this may also be an empty string. The return value is cached after first call. + >>> import psutil + >>> psutil.Process().exe() + '/usr/bin/python2.7' + .. method:: cmdline() - The command line this process has been called with. The return value is not - cached because the cmdline of a process may change. + The command line this process has been called with as a list of strings. + The return value is not cached because the cmdline of a process may change. + + >>> import psutil + >>> psutil.Process().cmdline() + ['python', 'manage.py', 'runserver'] .. method:: environ() The environment variables of the process as a dict. Note: this might not reflect changes made after the process started. + >>> import psutil + >>> psutil.Process().environ() + {'LC_NUMERIC': 'it_IT.UTF-8', 'QT_QPA_PLATFORMTHEME': 'appmenu-qt5', 'IM_CONFIG_PHASE': '1', 'XDG_GREETER_DATA_DIR': '/var/lib/lightdm-data/giampaolo', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'XDG_CURRENT_DESKTOP': 'Unity', 'UPSTART_EVENTS': 'started starting', 'GNOME_KEYRING_PID': '', 'XDG_VTNR': '7', 'QT_IM_MODULE': 'ibus', 'LOGNAME': 'giampaolo', 'USER': 'giampaolo', 'PATH': '/home/giampaolo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/giampaolo/svn/sysconf/bin', 'LC_PAPER': 'it_IT.UTF-8', 'GNOME_KEYRING_CONTROL': '', 'GTK_IM_MODULE': 'ibus', 'DISPLAY': ':0', 'LANG': 'en_US.UTF-8', 'LESS_TERMCAP_se': '\x1b[0m', 'TERM': 'xterm-256color', 'SHELL': '/bin/bash', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XAUTHORITY': '/home/giampaolo/.Xauthority', 'LANGUAGE': 'en_US', 'COMPIZ_CONFIG_PROFILE': 'ubuntu', 'LC_MONETARY': 'it_IT.UTF-8', 'QT_LINUX_ACCESSIBILITY_ALWAYS_ON': '1', 'LESS_TERMCAP_me': '\x1b[0m', 'LESS_TERMCAP_md': '\x1b[01;38;5;74m', 'LESS_TERMCAP_mb': '\x1b[01;31m', 'HISTSIZE': '100000', 'UPSTART_INSTANCE': '', 'CLUTTER_IM_MODULE': 'xim', 'WINDOWID': '58786407', 'EDITOR': 'vim', 'SESSIONTYPE': 'gnome-session', 'XMODIFIERS': '@im=ibus', 'GPG_AGENT_INFO': '/home/giampaolo/.gnupg/S.gpg-agent:0:1', 'HOME': '/home/giampaolo', 'HISTFILESIZE': '100000', 'QT4_IM_MODULE': 'xim', 'GTK2_MODULES': 'overlay-scrollbar', 'XDG_SESSION_DESKTOP': 'ubuntu', 'SHLVL': '1', 'XDG_RUNTIME_DIR': '/run/user/1000', 'INSTANCE': 'Unity', 'LC_ADDRESS': 'it_IT.UTF-8', 'SSH_AUTH_SOCK': '/run/user/1000/keyring/ssh', 'VTE_VERSION': '4205', 'GDMSESSION': 'ubuntu', 'MANDATORY_PATH': '/usr/share/gconf/ubuntu.mandatory.path', 'VISUAL': 'vim', 'DESKTOP_SESSION': 'ubuntu', 'QT_ACCESSIBILITY': '1', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'XDG_SESSION_ID': 'c2', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-9GAJpvnt8r', '_': '/usr/bin/python', 'DEFAULTS_PATH': '/usr/share/gconf/ubuntu.default.path', 'LC_IDENTIFICATION': 'it_IT.UTF-8', 'LESS_TERMCAP_ue': '\x1b[0m', 'UPSTART_SESSION': 'unix:abstract=/com/ubuntu/upstart-session/1000/1294', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', 'GTK_MODULES': 'gail:atk-bridge:unity-gtk-module', 'XDG_SESSION_TYPE': 'x11', 'PYTHONSTARTUP': '/home/giampaolo/.pythonstart', 'LC_NAME': 'it_IT.UTF-8', 'OLDPWD': '/home/giampaolo/svn/curio_giampaolo/tests', 'GDM_LANG': 'en_US', 'LC_TELEPHONE': 'it_IT.UTF-8', 'HISTCONTROL': 'ignoredups:erasedups', 'LC_MEASUREMENT': 'it_IT.UTF-8', 'PWD': '/home/giampaolo/svn/curio_giampaolo', 'JOB': 'gnome-session', 'LESS_TERMCAP_us': '\x1b[04;38;5;146m', 'UPSTART_JOB': 'unity-settings-daemon', 'LC_TIME': 'it_IT.UTF-8', 'LESS_TERMCAP_so': '\x1b[38;5;246m', 'PAGER': 'less', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'XDG_SEAT': 'seat0'} + Availability: Linux, OSX, Windows .. versionadded:: 4.0.0 @@ -1098,7 +1147,7 @@ Process class Return threads opened by process as a list of namedtuples including thread id and thread CPU times (user/system). On OpenBSD this method requires - root access. + root privileges. .. method:: cpu_times() @@ -1116,9 +1165,9 @@ Process class .. method:: cpu_percent(interval=None) - Return a float representing the process CPU utilization as a percentage. - The returned value refers to the utilization of a single CPU, i.e. it is - not evenly split between the number of available CPU cores. + Return a float representing the process CPU utilization as a percentage + which can also be ``> 100.0`` in case of a process running multiple threads + on different CPUs. 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 @@ -1130,30 +1179,28 @@ Process class >>> import psutil >>> p = psutil.Process() - >>> >>> # blocking >>> p.cpu_percent(interval=1) 2.0 >>> # non-blocking (percentage since last call) >>> p.cpu_percent(interval=None) 2.9 - >>> .. note:: - a percentage > 100 is legitimate as it can result from a process with - multiple threads running on different CPU cores. + the returned value can be > 100.0 in case of a process running multiple + threads on different CPU cores. .. note:: - the returned value is explicitly **not** split evenly between all CPUs - cores (differently from :func:`psutil.cpu_percent()`). - This means that a busy loop process running on a system with 2 CPU - cores will be reported as having 100% CPU utilization instead of 50%. - This was done in order to be consistent with UNIX's "top" utility + the returned value is explicitly *not* split evenly between all available + CPUs (differently from :func:`psutil.cpu_percent()`). + This means that a busy loop process running on a system with 2 logical + CPUs will be reported as having 100% CPU utilization instead of 50%. + This was done in order to be consistent with ``top`` UNIX utility and also to make it easier to identify processes hogging CPU resources - (independently from the number of CPU cores). - It must be noted that in the example above taskmgr.exe on Windows will - report 50% usage instead. - To emulate Windows's taskmgr.exe behavior you can do: + independently from the number of CPUs. + It must be noted that ``taskmgr.exe`` on Windows does not behave like + this (it would report 50% usage instead). + To emulate Windows ``taskmgr.exe`` behavior you can do: ``p.cpu_percent() / psutil.cpu_count()``. .. warning:: @@ -1301,8 +1348,8 @@ Process class some platform (Linux, OSX, Windows), also provides additional metrics (USS, PSS and swap). The additional metrics provide a better representation of "effective" - process memory consumption (in case of USS) as explained in detail - `here <http://grodola.blogspot.com/2016/02/psutil-4-real-process-memory-and-environ.html>`__. + process memory consumption (in case of USS) as explained in detail in this + `blog post <http://grodola.blogspot.com/2016/02/psutil-4-real-process-memory-and-environ.html>`__. It does so by passing through the whole process address. As such it usually requires higher user privileges than :meth:`memory_info` and is considerably slower. @@ -1400,7 +1447,13 @@ Process class pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0), pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0), ...] - >>> + >>> p.memory_maps(grouped=False) + [pmmap_ext(addr='00400000-006ea000', perms='r-xp', path='/usr/bin/python2.7', rss=2293760, size=3055616, pss=1157120, shared_clean=2273280, shared_dirty=0, private_clean=20480, private_dirty=0, referenced=2293760, anonymous=0, swap=0), + pmmap_ext(addr='008e9000-008eb000', perms='r--p', path='/usr/bin/python2.7', rss=8192, size=8192, pss=6144, shared_clean=4096, shared_dirty=0, private_clean=0, private_dirty=4096, referenced=8192, anonymous=4096, swap=0), + pmmap_ext(addr='008eb000-00962000', perms='rw-p', path='/usr/bin/python2.7', rss=417792, size=487424, pss=317440, shared_clean=200704, shared_dirty=0, private_clean=16384, private_dirty=200704, referenced=417792, anonymous=200704, swap=0), + pmmap_ext(addr='00962000-00985000', perms='rw-p', path='[anon]', rss=139264, size=143360, pss=139264, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=139264, referenced=139264, anonymous=139264, swap=0), + pmmap_ext(addr='02829000-02ccf000', perms='rw-p', path='[heap]', rss=4743168, size=4874240, pss=4743168, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=4743168, referenced=4718592, anonymous=4743168, swap=0), + ...] Availability: All platforms except OpenBSD and NetBSD. @@ -1409,7 +1462,7 @@ Process class Return the children of this process as a list of :Class:`Process` objects, preemptively checking whether PID has been reused. If recursive is `True` return all the parent descendants. - Example assuming *A == this process*: + Pseudo code example assuming *A == this process*: :: A ─┐ @@ -1452,8 +1505,7 @@ Process class >>> f = open('file.ext', 'w') >>> p = psutil.Process() >>> p.open_files() - [popenfile(path='/home/giampaolo/svn/psutil/setup.py', fd=3, position=0, mode='r', flags=32768), - popenfile(path='/var/log/monitd', fd=4, position=235542, mode='a', flags=33793)] + [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3, position=0, mode='w', flags=32769)] .. warning:: on Windows this is not fully reliable as due to some limitations of the @@ -1512,27 +1564,27 @@ Process class +----------------+-----------------------------------------------------+ | **Kind value** | **Connections using** | +================+=====================================================+ - | "inet" | IPv4 and IPv6 | + | ``"inet"`` | IPv4 and IPv6 | +----------------+-----------------------------------------------------+ - | "inet4" | IPv4 | + | ``"inet4"`` | IPv4 | +----------------+-----------------------------------------------------+ - | "inet6" | IPv6 | + | ``"inet6"`` | IPv6 | +----------------+-----------------------------------------------------+ - | "tcp" | TCP | + | ``"tcp"`` | TCP | +----------------+-----------------------------------------------------+ - | "tcp4" | TCP over IPv4 | + | ``"tcp4"`` | TCP over IPv4 | +----------------+-----------------------------------------------------+ - | "tcp6" | TCP over IPv6 | + | ``"tcp6"`` | TCP over IPv6 | +----------------+-----------------------------------------------------+ - | "udp" | UDP | + | ``"udp"`` | UDP | +----------------+-----------------------------------------------------+ - | "udp4" | UDP over IPv4 | + | ``"udp4"`` | UDP over IPv4 | +----------------+-----------------------------------------------------+ - | "udp6" | UDP over IPv6 | + | ``"udp6"`` | UDP over IPv6 | +----------------+-----------------------------------------------------+ - | "unix" | UNIX socket (both UDP and TCP protocols) | + | ``"unix"`` | UNIX socket (both UDP and TCP protocols) | +----------------+-----------------------------------------------------+ - | "all" | the sum of all the possible families and protocols | + | ``"all"`` | the sum of all the possible families and protocols | +----------------+-----------------------------------------------------+ Example: @@ -1612,6 +1664,10 @@ Process class either return immediately or raise :class:`TimeoutExpired`. To wait for multiple processes use :func:`psutil.wait_procs()`. + >>> import psutil + >>> p = psutil.Process(9891) + >>> p.terminate() + >>> p.wait() Popen class ----------- @@ -1766,14 +1822,14 @@ Constants .. _const-oses: .. data:: POSIX - WINDOWS - LINUX - OSX - FREEBSD - NETBSD - OPENBSD - BSD - SUNOS +.. data:: WINDOWS +.. data:: LINUX +.. data:: OSX +.. data:: FREEBSD +.. data:: NETBSD +.. data:: OPENBSD +.. data:: BSD +.. data:: SUNOS ``bool`` constants which define what platform you're on. E.g. if on Windows, *WINDOWS* constant will be ``True``, all others will be ``False``. @@ -1794,18 +1850,18 @@ Constants .. _const-pstatus: .. data:: STATUS_RUNNING - STATUS_SLEEPING - STATUS_DISK_SLEEP - STATUS_STOPPED - STATUS_TRACING_STOP - STATUS_ZOMBIE - STATUS_DEAD - STATUS_WAKE_KILL - STATUS_WAKING - STATUS_IDLE (OSX, FreeBSD) - STATUS_LOCKED (FreeBSD) - STATUS_WAITING (FreeBSD) - STATUS_SUSPENDED (NetBSD) +.. data:: STATUS_SLEEPING +.. data:: STATUS_DISK_SLEEP +.. data:: STATUS_STOPPED +.. data:: STATUS_TRACING_STOP +.. data:: STATUS_ZOMBIE +.. data:: STATUS_DEAD +.. data:: STATUS_WAKE_KILL +.. data:: STATUS_WAKING +.. data:: STATUS_IDLE (OSX, FreeBSD) +.. data:: STATUS_LOCKED (FreeBSD) +.. data:: STATUS_WAITING (FreeBSD) +.. data:: STATUS_SUSPENDED (NetBSD) A set of strings representing the status of a process. Returned by :meth:`psutil.Process.status()`. @@ -1814,31 +1870,31 @@ Constants .. _const-conn: .. data:: 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 - CONN_DELETE_TCB (Windows) - CONN_IDLE (Solaris) - CONN_BOUND (Solaris) +.. data:: CONN_SYN_SENT +.. data:: CONN_SYN_RECV +.. data:: CONN_FIN_WAIT1 +.. data:: CONN_FIN_WAIT2 +.. data:: CONN_TIME_WAIT +.. data:: CONN_CLOSE +.. data:: CONN_CLOSE_WAIT +.. data:: CONN_LAST_ACK +.. data:: CONN_LISTEN +.. data:: CONN_CLOSING +.. data:: CONN_NONE +.. data:: CONN_DELETE_TCB (Windows) +.. data:: CONN_IDLE (Solaris) +.. data:: CONN_BOUND (Solaris) A set of strings representing the status of a TCP connection. Returned by :meth:`psutil.Process.connections()` (`status` field). .. _const-prio: .. data:: ABOVE_NORMAL_PRIORITY_CLASS - BELOW_NORMAL_PRIORITY_CLASS - HIGH_PRIORITY_CLASS - IDLE_PRIORITY_CLASS - NORMAL_PRIORITY_CLASS - REALTIME_PRIORITY_CLASS +.. data:: BELOW_NORMAL_PRIORITY_CLASS +.. data:: HIGH_PRIORITY_CLASS +.. data:: IDLE_PRIORITY_CLASS +.. data:: NORMAL_PRIORITY_CLASS +.. data:: REALTIME_PRIORITY_CLASS A set of integers representing the priority of a process on Windows (see `MSDN documentation <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__). @@ -1854,9 +1910,9 @@ Constants .. _const-ioprio: .. data:: IOPRIO_CLASS_NONE - IOPRIO_CLASS_RT - IOPRIO_CLASS_BE - IOPRIO_CLASS_IDLE +.. data:: IOPRIO_CLASS_RT +.. data:: IOPRIO_CLASS_BE +.. data:: IOPRIO_CLASS_IDLE A set of integers representing the I/O priority of a process on Linux. They can be used in conjunction with :meth:`psutil.Process.ionice()` to get or set @@ -1882,22 +1938,22 @@ Constants .. _const-rlimit: .. data:: RLIM_INFINITY - RLIMIT_AS - RLIMIT_CORE - RLIMIT_CPU - RLIMIT_DATA - RLIMIT_FSIZE - RLIMIT_LOCKS - RLIMIT_MEMLOCK - RLIMIT_MSGQUEUE - RLIMIT_NICE - RLIMIT_NOFILE - RLIMIT_NPROC - RLIMIT_RSS - RLIMIT_RTPRIO - RLIMIT_RTTIME - RLIMIT_SIGPENDING - RLIMIT_STACK +.. data:: RLIMIT_AS +.. data:: RLIMIT_CORE +.. data:: RLIMIT_CPU +.. data:: RLIMIT_DATA +.. data:: RLIMIT_FSIZE +.. data:: RLIMIT_LOCKS +.. data:: RLIMIT_MEMLOCK +.. data:: RLIMIT_MSGQUEUE +.. data:: RLIMIT_NICE +.. data:: RLIMIT_NOFILE +.. data:: RLIMIT_NPROC +.. data:: RLIMIT_RSS +.. data:: RLIMIT_RTPRIO +.. data:: RLIMIT_RTTIME +.. data:: RLIMIT_SIGPENDING +.. data:: RLIMIT_STACK Constants used for getting and setting process resource limits to be used in conjunction with :meth:`psutil.Process.rlimit()`. See @@ -1915,8 +1971,8 @@ Constants .. _const-duplex: .. data:: NIC_DUPLEX_FULL - NIC_DUPLEX_HALF - NIC_DUPLEX_UNKNOWN +.. data:: NIC_DUPLEX_HALF +.. data:: NIC_DUPLEX_UNKNOWN Constants which identifies whether a NIC (network interface card) has full or half mode speed. NIC_DUPLEX_FULL means the NIC is able to send and receive @@ -1935,6 +1991,36 @@ Constants >>> if psutil.version_info >= (4, 5): ... pass +Q&A +=== + +* Q: What Windows versions are supported? +* A: From Windows **Vista** onwards, both 32 and 64 bit versions. + Latest binary (wheel / exe) release which supports Windows **2000**, **XP** + and **2003 server** is + `psutil 3.4.2 <https://pypi.python.org/pypi?name=psutil&version=3.4.2&:action=files>`__. + On such old systems psutil is no longer tested or maintained, but it can + still be compiled from sources (you'll need `Visual Studio <(https://github.com/giampaolo/psutil/blob/master/INSTALL.rst#windows>`__) + and it should "work" (more or less). + +---- + +* Q: What SunOS versions are supported? +* A: From Solaris 10 onwards. + +---- + +* Q: Why do I get :class:`AccessDenied` for certain processes? +* A: This may happen when you query processess owned by another user, + especially on `OSX <https://github.com/giampaolo/psutil/issues/883>`__ and + Windows. + Unfortunately there's not much you can do about this except running the + Python process with higher privileges. + On Unix you may run the the Python process as root or use the SUID bit + (this is the trick used by tools such as ``ps`` and ``netstat``). + On Windows you may run the Python process as NT AUTHORITY\\SYSTEM or install + the Python script as a Windows service (this is the trick used by tools + such as ProcessHacker). Development guide ================= @@ -1943,23 +2029,11 @@ If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug) take a look at the `development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_. - -Q&A -=== - -* Q: What Windows versions are supported? -* A: From Windows **Vista** onwards. Latest binary (wheel / exe) release - supporting Windows **2000**, **XP** and **2003 server** which can installed - via pip without a compiler being installed is - `psutil 3.4.2 <https://pypi.python.org/pypi?name=psutil&version=3.4.2&:action=files>`__. - More recent psutil versions may still be compiled from sources and work - (more or less) but they are no longer being tested or maintained. - - Timeline ======== -- 2016-11-06: `5.5.0 <https://pypi.python.org/pypi?name=psutil&version=5.5.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#550>`__ +- 2016-12-21: `5.0.1 <https://pypi.python.org/pypi?name=psutil&version=5.0.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#550>`__ +- 2016-11-06: `5.0.0 <https://pypi.python.org/pypi?name=psutil&version=5.0.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#550>`__ - 2016-10-26: `4.4.2 <https://pypi.python.org/pypi?name=psutil&version=4.4.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#442>`__ - 2016-10-25: `4.4.1 <https://pypi.python.org/pypi?name=psutil&version=4.4.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#441>`__ - 2016-10-23: `4.4.0 <https://pypi.python.org/pypi?name=psutil&version=4.4.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#440>`__ diff --git a/psutil/__init__.py b/psutil/__init__.py index 0ed28945..68fdee8f 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -143,8 +143,8 @@ elif SUNOS: from ._pssunos import CONN_BOUND # NOQA from ._pssunos import CONN_IDLE # NOQA - # This is public API and it will be retrieved from _pssunos.py - # via sys.modules. + # This is public writable API which is read from _pslinux.py and + # _pssunos.py via sys.modules. PROCFS_PATH = "/proc" else: # pragma: no cover @@ -181,7 +181,7 @@ __all__ = [ "pid_exists", "pids", "process_iter", "wait_procs", # proc "virtual_memory", "swap_memory", # memory "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu - "cpu_stats", + "cpu_stats", # "cpu_freq", "net_io_counters", "net_connections", "net_if_addrs", # network "net_if_stats", "disk_io_counters", "disk_partitions", "disk_usage", # disk @@ -189,7 +189,7 @@ __all__ = [ ] __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "5.0.1" +__version__ = "5.0.2" version_info = tuple([int(num) for num in __version__.split('.')]) AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None @@ -222,6 +222,7 @@ if (int(__version__.replace('.', '')) != # --- exceptions # ===================================================================== + class Error(Exception): """Base exception class. All other psutil exceptions inherit from this one. @@ -453,6 +454,11 @@ class Process(object): self._hash = hash(self._ident) return self._hash + @property + def pid(self): + """The process PID.""" + return self._pid + # --- utility methods @contextlib.contextmanager @@ -601,11 +607,6 @@ class Process(object): # --- actual API - @property - def pid(self): - """The process PID.""" - return self._pid - @memoize_when_activated def ppid(self): """The process parent PID. @@ -993,6 +994,14 @@ class Process(object): In this case is recommended for accuracy that this function be called with at least 0.1 seconds between calls. + A value > 100.0 can be returned in case of processes running + multiple threads on different CPU cores. + + The returned value is explicitly *not* split evenly between + all available logical CPUs. This means that a busy loop process + running on a system with 2 logical CPUs will be reported as + having 100% CPU utilization instead of 50%. + Examples: >>> import psutil @@ -1010,13 +1019,8 @@ class Process(object): raise ValueError("interval is not positive (got %r)" % interval) num_cpus = cpu_count() or 1 - if POSIX: - def timer(): - return _timer() * num_cpus - else: - def timer(): - t = cpu_times() - return sum((t.user, t.system)) + def timer(): + return _timer() * num_cpus if blocking: st1 = timer() @@ -1198,6 +1202,8 @@ class Process(object): """ return self._proc.connections(kind) + # --- signals + if POSIX: def _send_signal(self, sig): assert not self.pid < 0, self.pid @@ -1858,6 +1864,36 @@ def cpu_stats(): return _psplatform.cpu_stats() +if hasattr(_psplatform, "cpu_freq"): + + def cpu_freq(percpu=False): + """Return CPU frequency as a nameduple including current, + min and max frequency expressed in Mhz. + + If percpu is True and the system supports per-cpu frequency + retrieval (Linux only) a list of frequencies is returned for + each CPU. If not a list with one element is returned. + """ + ret = _psplatform.cpu_freq() + if percpu: + return ret + else: + num_cpus = len(ret) + if num_cpus == 1: + return ret[0] + currs, mins, maxs = [], [], [] + for cpu in ret: + currs.append(cpu.current) + mins.append(cpu.min) + maxs.append(cpu.max) + return _common.scpufreq( + sum(currs) / num_cpus, + sum(mins) / num_cpus, + sum(maxs) / num_cpus) + + __all__.append("cpu_freq") + + # ===================================================================== # --- system memory related functions # ===================================================================== diff --git a/psutil/_common.py b/psutil/_common.py index 3879a1d7..68134820 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -156,6 +156,8 @@ snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu']) # psutil.cpu_stats() scpustats = namedtuple( 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']) +# psutil.cpu_freq() +scpufreq = namedtuple('scpufreq', ['current', 'min', 'max']) # --- for Process methods diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 0a14bf80..022f5758 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -17,6 +17,7 @@ from . import _psutil_bsd as cext from . import _psutil_posix as cext_posix from ._common import conn_tmap from ._common import FREEBSD +from ._common import memoize from ._common import memoize_when_activated from ._common import NETBSD from ._common import OPENBSD @@ -421,7 +422,26 @@ def users(): # ===================================================================== -pids = cext.pids +@memoize +def _pid_0_exists(): + try: + Process(0).name() + except NoSuchProcess: + return False + except AccessDenied: + return True + else: + return True + + +def pids(): + ret = cext.pids() + if OPENBSD and (0 not in ret) and _pid_0_exists(): + # On OpenBSD the kernel does not return PID 0 (neither does + # ps) but it's actually querable (Process(0) will succeed). + ret.insert(0, 0) + return ret + if OPENBSD or NETBSD: def pid_exists(pid): diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index c2c17a9a..ad95e39a 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -9,6 +9,7 @@ from __future__ import division import base64 import errno import functools +import glob import os import re import socket @@ -133,6 +134,8 @@ TCP_STATUSES = { "0B": _common.CONN_CLOSING } +_DEFAULT = object() + # ===================================================================== # -- exceptions @@ -276,6 +279,18 @@ def set_scputimes_ntuple(procfs_path): return scputimes +def cat(fname, fallback=_DEFAULT, binary=True): + """Return file content.""" + try: + with open_binary(fname) if binary else open_text(fname) as f: + return f.read() + except IOError: + if fallback != _DEFAULT: + return fallback + else: + raise + + try: scputimes = set_scputimes_ntuple("/proc") except Exception: @@ -607,6 +622,26 @@ def cpu_stats(): ctx_switches, interrupts, soft_interrupts, syscalls) +if os.path.exists("/sys/devices/system/cpu/cpufreq"): + + def cpu_freq(): + # scaling_* files seem preferable to cpuinfo_*, see: + # http://unix.stackexchange.com/a/87537/168884 + ret = [] + ls = glob.glob("/sys/devices/system/cpu/cpufreq/policy*") + # Sort the list so that '10' comes after '2'. This should + # ensure the CPU order is consistent with other CPU functions + # having a 'percpu' argument and returning results for multiple + # CPUs (cpu_times(), cpu_percent(), cpu_times_percent()). + ls.sort(key=lambda x: int(os.path.basename(x)[6:])) + for path in ls: + curr = int(cat(os.path.join(path, "scaling_cur_freq"))) / 1000 + max_ = int(cat(os.path.join(path, "scaling_max_freq"))) / 1000 + min_ = int(cat(os.path.join(path, "scaling_min_freq"))) / 1000 + ret.append(_common.scpufreq(curr, min_, max_)) + return ret + + # ===================================================================== # --- network # ===================================================================== @@ -1070,8 +1105,32 @@ def pids(): def pid_exists(pid): - """Check For the existence of a unix pid.""" - return _psposix.pid_exists(pid) + """Check for the existence of a unix PID.""" + if not _psposix.pid_exists(pid): + return False + else: + # Linux's apparently does not distinguish between PIDs and TIDs + # (thread IDs). + # listdir("/proc") won't show any TID (only PIDs) but + # os.stat("/proc/{tid}") will succeed if {tid} exists. + # os.kill() can also be passed a TID. This is quite confusing. + # In here we want to enforce this distinction and support PIDs + # only, see: + # https://github.com/giampaolo/psutil/issues/687 + try: + # Note: already checked that this is faster than using a + # regular expr. Also (a lot) faster than doing + # 'return pid in pids()' + with open_binary("%s/%s/status" % (get_procfs_path(), pid)) as f: + for line in f: + if line.startswith(b"Tgid:"): + tgid = int(line.split()[1]) + # If tgid and pid are the same then we're + # dealing with a process PID. + return tgid == pid + raise ValueError("'Tgid' line not found") + except (EnvironmentError, ValueError): + return pid in pids() def wrap_exceptions(fun): diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 2665080e..f7adb43a 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -165,6 +165,16 @@ def cpu_stats(): ctx_switches, interrupts, soft_interrupts, syscalls) +def cpu_freq(): + """Return CPU frequency. + On OSX per-cpu frequency is not supported. + Also, the returned frequency never changes, see: + https://arstechnica.com/civis/viewtopic.php?f=19&t=465002 + """ + curr, min_, max_ = cext.cpu_freq() + return [_common.scpufreq(curr, min_, max_)] + + # ===================================================================== # --- disks # ===================================================================== diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index b1bce487..adcedf79 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -15,8 +15,8 @@ * - psutil.Process.memory_maps() */ -#if defined(__NetBSD__) -#define _KMEMUSER +#if defined(PSUTIL_NETBSD) + #define _KMEMUSER #endif #include <Python.h> @@ -61,17 +61,10 @@ #include "_psutil_common.h" -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD #include "arch/bsd/freebsd.h" #include "arch/bsd/freebsd_socks.h" -#elif __OpenBSD__ - #include "arch/bsd/openbsd.h" -#elif __NetBSD__ - #include "arch/bsd/netbsd.h" - #include "arch/bsd/netbsd_socks.h" -#endif -#ifdef __FreeBSD__ #include <net/if_media.h> #include <devstat.h> // get io counters #include <libutil.h> // process open files, shared libs (kinfo_getvmmap) @@ -80,37 +73,39 @@ #else #include <utmpx.h> #endif -#endif +#elif PSUTIL_OPENBSD + #include "arch/bsd/openbsd.h" -#ifdef __OpenBSD__ #include <utmp.h> #include <sys/vnode.h> // for VREG #define _KERNEL // for DTYPE_VNODE #include <sys/file.h> #undef _KERNEL #include <sys/sched.h> // for CPUSTATES & CP_* -#endif +#elif PSUTIL_NETBSD + #include "arch/bsd/netbsd.h" + #include "arch/bsd/netbsd_socks.h" -#if defined(__NetBSD__) #include <utmpx.h> #include <sys/vnode.h> // for VREG #include <sys/sched.h> // for CPUSTATES & CP_* #ifndef DTYPE_VNODE - #define DTYPE_VNODE 1 + #define DTYPE_VNODE 1 #endif #endif + // convert a timeval struct to a double #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD // convert a bintime struct to milliseconds #define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * \ (uint32_t) (bt.frac >> 32) ) >> 32 ) / 1000000) #endif -#if defined(__OpenBSD__) || defined (__NetBSD__) +#if defined(PSUTIL_OPENBSD) || defined (PSUTIL_NETBSD) #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0) #endif @@ -146,9 +141,9 @@ psutil_pids(PyObject *self, PyObject *args) { if (num_processes > 0) { orig_address = proclist; // save so we can free it after we're done for (idx = 0; idx < num_processes; idx++) { -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD py_pid = Py_BuildValue("i", proclist->ki_pid); -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) py_pid = Py_BuildValue("i", proclist->p_pid); #endif if (!py_pid) @@ -216,9 +211,9 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { return NULL; // Process -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD sprintf(str, "%s", kp.ki_comm); -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) sprintf(str, "%s", kp.p_comm); #endif #if PY_MAJOR_VERSION >= 3 @@ -234,7 +229,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { } // Calculate memory. -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD rss = (long)kp.ki_rssize * pagesize; vms = (long)kp.ki_size; memtext = (long)kp.ki_tsize * pagesize; @@ -242,12 +237,12 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memstack = (long)kp.ki_ssize * pagesize; #else rss = (long)kp.p_vm_rssize * pagesize; - #ifdef __OpenBSD__ + #ifdef PSUTIL_OPENBSD // VMS, this is how ps determines it on OpenBSD: // http://anoncvs.spacehopper.org/openbsd-src/tree/bin/ps/print.c#n461 // vms vms = (long)(kp.p_vm_dsize + kp.p_vm_ssize + kp.p_vm_tsize) * pagesize; - #elif __NetBSD__ + #elif PSUTIL_NETBSD // VMS, this is how top determines it on NetBSD: // ftp://ftp.iij.ad.jp/pub/NetBSD/NetBSD-release-6/src/external/bsd/ // top/dist/machine/m_netbsd.c @@ -258,7 +253,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memstack = (long)kp.p_vm_ssize * pagesize; #endif -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD // what CPU we're on; top was used as an example: // https://svnweb.freebsd.org/base/head/usr.bin/top/machine.c? // view=markup&pathrev=273835 @@ -277,7 +272,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { // Return a single big tuple with all process info. py_retlist = Py_BuildValue( "(lillllllidllllddddlllllbO)", -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD + // (long)kp.ki_ppid, // (long) ppid (int)kp.ki_stat, // (int) status // UIDs @@ -310,7 +306,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memstack, // (long) mem stack // others oncpu, // (unsigned char) the CPU we are on -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) + // (long)kp.p_ppid, // (long) ppid (int)kp.p_stat, // (int) status // UIDs @@ -371,9 +368,9 @@ psutil_proc_name(PyObject *self, PyObject *args) { if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD sprintf(str, "%s", kp.ki_comm); -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) sprintf(str, "%s", kp.p_comm); #endif @@ -431,7 +428,7 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) { */ static PyObject * psutil_cpu_times(PyObject *self, PyObject *args) { -#if defined(__NetBSD__) +#ifdef PSUTIL_NETBSD u_int64_t cpu_time[CPUSTATES]; #else long cpu_time[CPUSTATES]; @@ -439,9 +436,9 @@ psutil_cpu_times(PyObject *self, PyObject *args) { size_t size = sizeof(cpu_time); int ret; -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) ret = sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0); -#elif __OpenBSD__ +#elif PSUTIL_OPENBSD int mib[] = {CTL_KERN, KERN_CPTIME}; ret = sysctl(mib, 2, &cpu_time, &size, NULL, 0); #endif @@ -466,7 +463,7 @@ psutil_cpu_times(PyObject *self, PyObject *args) { * utility has the same problem see: * https://github.com/giampaolo/psutil/issues/595 */ -#if (defined(__FreeBSD_version) && __FreeBSD_version >= 800000) || __OpenBSD__ || defined(__NetBSD__) +#if (defined(__FreeBSD_version) && __FreeBSD_version >= 800000) || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD) static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) { long pid; @@ -493,17 +490,17 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { for (i = 0; i < cnt; i++) { kif = &freep[i]; -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD if ((kif->kf_type == KF_TYPE_VNODE) && (kif->kf_vnode_type == KF_VTYPE_VREG)) { py_tuple = Py_BuildValue("(si)", kif->kf_path, kif->kf_fd); -#elif defined(__OpenBSD__) +#elif PSUTIL_OPENBSD if ((kif->f_type == DTYPE_VNODE) && (kif->v_type == VREG)) { py_tuple = Py_BuildValue("(si)", "", kif->fd_fd); -#elif defined(__NetBSD__) +#elif PSUTIL_NETBSD if ((kif->ki_ftype == DTYPE_VNODE) && (kif->ki_vtype == VREG)) { @@ -540,7 +537,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { long len; uint64_t flags; char opts[200]; -#if defined(__NetBSD__) +#ifdef PSUTIL_NETBSD struct statvfs *fs = NULL; #else struct statfs *fs = NULL; @@ -553,7 +550,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { // get the number of mount points Py_BEGIN_ALLOW_THREADS -#if defined(__NetBSD__) +#ifdef PSUTIL_NETBSD num = getvfsstat(NULL, 0, MNT_NOWAIT); #else num = getfsstat(NULL, 0, MNT_NOWAIT); @@ -572,7 +569,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { } Py_BEGIN_ALLOW_THREADS -#if defined(__NetBSD__) +#ifdef PSUTIL_NETBSD num = getvfsstat(fs, len, MNT_NOWAIT); #else num = getfsstat(fs, len, MNT_NOWAIT); @@ -586,7 +583,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { for (i = 0; i < num; i++) { py_tuple = NULL; opts[0] = 0; -#if defined(__NetBSD__) +#ifdef PSUTIL_NETBSD flags = fs[i].f_flag; #else flags = fs[i].f_flags; @@ -609,7 +606,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { strlcat(opts, ",noatime", sizeof(opts)); if (flags & MNT_SOFTDEP) strlcat(opts, ",softdep", sizeof(opts)); -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD if (flags & MNT_UNION) strlcat(opts, ",union", sizeof(opts)); if (flags & MNT_SUIDDIR) @@ -630,24 +627,24 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { strlcat(opts, ",noclusterw", sizeof(opts)); if (flags & MNT_NFS4ACLS) strlcat(opts, ",nfs4acls", sizeof(opts)); -#elif __NetBSD__ +#elif PSUTIL_NETBSD if (flags & MNT_NODEV) strlcat(opts, ",nodev", sizeof(opts)); if (flags & MNT_UNION) strlcat(opts, ",union", sizeof(opts)); if (flags & MNT_NOCOREDUMP) strlcat(opts, ",nocoredump", sizeof(opts)); -#if defined(MNT_RELATIME) +#ifdef MNT_RELATIME if (flags & MNT_RELATIME) strlcat(opts, ",relatime", sizeof(opts)); #endif if (flags & MNT_IGNORE) strlcat(opts, ",ignore", sizeof(opts)); -#if defined(MNT_DISCARD) +#ifdef MNT_DISCARD if (flags & MNT_DISCARD) strlcat(opts, ",discard", sizeof(opts)); #endif -#if defined(MNT_EXTATTR) +#ifdef MNT_EXTATTR if (flags & MNT_EXTATTR) strlcat(opts, ",extattr", sizeof(opts)); #endif @@ -788,7 +785,7 @@ psutil_users(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; -#if (defined(__FreeBSD_version) && (__FreeBSD_version < 900000)) || __OpenBSD__ +#if (defined(__FreeBSD_version) && (__FreeBSD_version < 900000)) || PSUTIL_OPENBSD struct utmp ut; FILE *fp; @@ -868,7 +865,7 @@ PsutilMethods[] = { "Return multiple info about a process"}, {"proc_name", psutil_proc_name, METH_VARARGS, "Return process name"}, -#if !defined(__NetBSD__) +#if !defined(PSUTIL_NETBSD) {"proc_connections", psutil_proc_connections, METH_VARARGS, "Return connections opened by process"}, #endif @@ -876,25 +873,25 @@ PsutilMethods[] = { "Return process cmdline as a list of cmdline arguments"}, {"proc_threads", psutil_proc_threads, METH_VARARGS, "Return process threads"}, -#if defined(__FreeBSD__) || defined(__OpenBSD__) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) {"proc_cwd", psutil_proc_cwd, METH_VARARGS, "Return process current working directory."}, #endif -#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || __OpenBSD__ || defined(__NetBSD__) +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD) {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS, "Return the number of file descriptors opened by this process"}, #endif -#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || __OpenBSD__ || defined(__NetBSD__) +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD) {"proc_open_files", psutil_proc_open_files, METH_VARARGS, "Return files opened by process as a list of (path, fd) tuples"}, #endif -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) {"proc_exe", psutil_proc_exe, METH_VARARGS, "Return process pathname executable"}, {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS, "Return number of threads used by process"}, -#if defined(__FreeBSD__) +#if defined(PSUTIL_FREEBSD) {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, "Return a list of tuples for every process's memory map"}, {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, @@ -933,7 +930,7 @@ PsutilMethods[] = { "Return currently connected users as a list of tuples"}, {"cpu_stats", psutil_cpu_stats, METH_VARARGS, "Return CPU statistics"}, -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) {"net_connections", psutil_net_connections, METH_VARARGS, "Return system-wide open connections."}, #endif @@ -995,7 +992,7 @@ void init_psutil_bsd(void) PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); // process status constants -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD PyModule_AddIntConstant(module, "SIDL", SIDL); PyModule_AddIntConstant(module, "SRUN", SRUN); PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); @@ -1003,7 +1000,7 @@ void init_psutil_bsd(void) PyModule_AddIntConstant(module, "SZOMB", SZOMB); PyModule_AddIntConstant(module, "SWAIT", SWAIT); PyModule_AddIntConstant(module, "SLOCK", SLOCK); -#elif __OpenBSD__ +#elif PSUTIL_OPENBSD PyModule_AddIntConstant(module, "SIDL", SIDL); PyModule_AddIntConstant(module, "SRUN", SRUN); PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); @@ -1011,7 +1008,7 @@ void init_psutil_bsd(void) PyModule_AddIntConstant(module, "SZOMB", SZOMB); // unused PyModule_AddIntConstant(module, "SDEAD", SDEAD); PyModule_AddIntConstant(module, "SONPROC", SONPROC); -#elif defined(__NetBSD__) +#elif defined(PSUTIL_NETBSD) PyModule_AddIntConstant(module, "SIDL", LSIDL); PyModule_AddIntConstant(module, "SRUN", LSRUN); PyModule_AddIntConstant(module, "SSLEEP", LSSLEEP); diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c index a1168c29..fb26dc9b 100644 --- a/psutil/_psutil_osx.c +++ b/psutil/_psutil_osx.c @@ -809,6 +809,35 @@ error: /* + * Retrieve CPU frequency. + */ +static PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + int64_t curr; + int64_t min; + int64_t max; + size_t size = sizeof(int64_t); + + if (sysctlbyname("hw.cpufrequency", &curr, &size, NULL, 0)) + goto error; + if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0)) + goto error; + if (sysctlbyname("hw.cpufrequency_max", &max, &size, NULL, 0)) + goto error; + + return Py_BuildValue( + "KKK", + curr / 1000 / 1000, + min / 1000 / 1000, + max / 1000 / 1000); + +error: + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} + + +/* * Return a Python float indicating the system boot time expressed in * seconds since the epoch. */ @@ -1778,6 +1807,8 @@ PsutilMethods[] = { "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS, "Return system per-cpu times as a list of tuples"}, + {"cpu_freq", psutil_cpu_freq, METH_VARARGS, + "Return cpu current frequency"}, {"boot_time", psutil_boot_time, METH_VARARGS, "Return the system boot time expressed in seconds since the epoch."}, {"disk_partitions", psutil_disk_partitions, METH_VARARGS, diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c index 2d9630ac..7aa4b553 100644 --- a/psutil/_psutil_posix.c +++ b/psutil/_psutil_posix.c @@ -16,25 +16,21 @@ #include <net/if.h> #ifdef PSUTIL_SUNOS10 -#include "arch/solaris/v10/ifaddrs.h" + #include "arch/solaris/v10/ifaddrs.h" #else -#include <ifaddrs.h> + #include <ifaddrs.h> #endif -#ifdef __linux -#include <netdb.h> -#include <linux/if_packet.h> -#endif // end linux - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) -#include <netdb.h> -#include <netinet/in.h> -#include <net/if_dl.h> -#endif - -#if defined(__sun) -#include <netdb.h> -#include <sys/sockio.h> +#if defined(PSUTIL_LINUX) + #include <netdb.h> + #include <linux/if_packet.h> +#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX) + #include <netdb.h> + #include <netinet/in.h> + #include <net/if_dl.h> +#elif defined(PSUTIL_SUNOS) + #include <netdb.h> + #include <sys/sockio.h> #endif @@ -50,7 +46,7 @@ psutil_posix_getpriority(PyObject *self, PyObject *args) { if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; -#if defined(__APPLE__) +#ifdef PSUTIL_OSX priority = getpriority(PRIO_PROCESS, (id_t)pid); #else priority = getpriority(PRIO_PROCESS, pid); @@ -73,7 +69,7 @@ psutil_posix_setpriority(PyObject *self, PyObject *args) { if (! PyArg_ParseTuple(args, "li", &pid, &priority)) return NULL; -#if defined(__APPLE__) +#ifdef PSUTIL_OSX retval = setpriority(PRIO_PROCESS, (id_t)pid, priority); #else retval = setpriority(PRIO_PROCESS, pid, priority); @@ -122,14 +118,13 @@ psutil_convert_ipaddr(struct sockaddr *addr, int family) { return Py_BuildValue("s", buf); } } -#ifdef __linux +#ifdef PSUTIL_LINUX else if (family == AF_PACKET) { struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr; len = lladdr->sll_halen; data = (const char *)lladdr->sll_addr; } -#endif -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX) else if (addr->sa_family == AF_LINK) { // Note: prior to Python 3.4 socket module does not expose // AF_LINK so we'll do. @@ -264,8 +259,11 @@ psutil_net_if_mtu(PyObject *self, PyObject *args) { char *nic_name; int sock = 0; int ret; - int mtu; +#ifdef PSUTIL_SUNOS10 + struct lifreq lifr; +#else struct ifreq ifr; +#endif if (! PyArg_ParseTuple(args, "s", &nic_name)) return NULL; @@ -274,14 +272,22 @@ psutil_net_if_mtu(PyObject *self, PyObject *args) { if (sock == -1) goto error; +#ifdef PSUTIL_SUNOS10 + strncpy(lifr.lifr_name, nic_name, sizeof(lifr.lifr_name)); + ret = ioctl(sock, SIOCGIFMTU, &lifr); +#else strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); ret = ioctl(sock, SIOCGIFMTU, &ifr); +#endif if (ret == -1) goto error; close(sock); - mtu = ifr.ifr_mtu; - return Py_BuildValue("i", mtu); +#ifdef PSUTIL_SUNOS10 + return Py_BuildValue("i", lifr.lifr_mtu); +#else + return Py_BuildValue("i", ifr.ifr_mtu); +#endif error: if (sock != 0) @@ -332,7 +338,7 @@ error: /* * net_if_stats() OSX/BSD implementation. */ -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) #include <sys/sockio.h> #include <net/if_media.h> @@ -369,7 +375,7 @@ int psutil_get_nic_speed(int ifm_active) { case(IFM_1000_SX): // 1000BaseSX - multi-mode fiber case(IFM_1000_LX): // 1000baseLX - single-mode fiber case(IFM_1000_CX): // 1000baseCX - 150ohm STP -#if defined(IFM_1000_TX) && !defined(__OpenBSD__) +#if defined(IFM_1000_TX) && !defined(PSUTIL_OPENBSD) // FreeBSD 4 and others (but NOT OpenBSD) -> #define IFM_1000_T in net/if_media.h case(IFM_1000_TX): #endif @@ -542,7 +548,7 @@ PsutilMethods[] = { "Retrieve NIC MTU"}, {"net_if_flags", psutil_net_if_flags, METH_VARARGS, "Retrieve NIC flags"}, -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS, "Return NIC stats."}, #endif @@ -567,6 +573,7 @@ psutil_posix_traverse(PyObject *m, visitproc visit, void *arg) { return 0; } + static int psutil_posix_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->error); @@ -601,7 +608,7 @@ void init_psutil_posix(void) PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods); #endif -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__sun) || defined(__NetBSD__) +#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) PyModule_AddIntConstant(module, "AF_LINK", AF_LINK); #endif diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 4d939aff..4caace7d 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -24,6 +24,7 @@ #include <iphlpapi.h> #include <wtsapi32.h> #include <Winsvc.h> +#include <PowrProf.h> // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") @@ -145,6 +146,16 @@ typedef struct _MIB_UDP6TABLE_OWNER_PID { } MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; #endif +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; + + PIP_ADAPTER_ADDRESSES psutil_get_nic_addresses() { // allocate a 15 KB buffer to start with @@ -3391,6 +3402,60 @@ error: } +/* + * Return CPU frequency. + */ +static PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + PROCESSOR_POWER_INFORMATION *ppi; + NTSTATUS ret; + size_t size; + LPBYTE pBuffer = NULL; + ULONG current; + ULONG max; + unsigned int num_cpus; + SYSTEM_INFO system_info; + system_info.dwNumberOfProcessors = 0; + + // Get the number of CPUs. + GetSystemInfo(&system_info); + if (system_info.dwNumberOfProcessors == 0) + num_cpus = 1; + else + num_cpus = system_info.dwNumberOfProcessors; + + // Allocate size. + size = num_cpus * sizeof(PROCESSOR_POWER_INFORMATION); + pBuffer = (BYTE*)LocalAlloc(LPTR, size); + if (! pBuffer) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + + // Syscall. + ret = CallNtPowerInformation( + ProcessorInformation, NULL, 0, pBuffer, size); + if (ret != 0) { + PyErr_SetString(PyExc_RuntimeError, + "CallNtPowerInformation syscall failed"); + goto error; + } + + // Results. + ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer; + max = ppi->MaxMhz; + current = ppi->CurrentMhz; + LocalFree(pBuffer); + + return Py_BuildValue("kk", current, max); + +error: + if (pBuffer != NULL) + LocalFree(pBuffer); + return NULL; +} + + // ------------------------ Python init --------------------------- static PyMethodDef @@ -3495,6 +3560,8 @@ PsutilMethods[] = { "Return NICs stats."}, {"cpu_stats", psutil_cpu_stats, METH_VARARGS, "Return NICs stats."}, + {"cpu_freq", psutil_cpu_freq, METH_VARARGS, + "Return CPU frequency."}, // --- windows services {"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS, diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index cb816f73..da8552e1 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -299,6 +299,15 @@ def cpu_stats(): syscalls) +def cpu_freq(): + """Return CPU frequency. + On Windows per-cpu frequency is not supported. + """ + curr, max_ = cext.cpu_freq() + min_ = 0.0 + return [_common.scpufreq(float(curr), min_, float(max_))] + + # ===================================================================== # --- network # ===================================================================== diff --git a/psutil/arch/bsd/netbsd.c b/psutil/arch/bsd/netbsd.c index 2cf2ef22..d5c3e3b9 100644 --- a/psutil/arch/bsd/netbsd.c +++ b/psutil/arch/bsd/netbsd.c @@ -7,8 +7,8 @@ * Platform-specific module methods for NetBSD. */ -#if defined(__NetBSD__) -#define _KMEMUSER +#if defined(PSUTIL_NETBSD) + #define _KMEMUSER #endif #include <Python.h> diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 883d9285..b119a788 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -506,6 +506,7 @@ def chdir(dirname): def create_exe(outpath, c_code=None): + """Creates an executable file in the given location.""" assert not os.path.exists(outpath), outpath if which("gcc"): if c_code is None: @@ -526,6 +527,9 @@ def create_exe(outpath, c_code=None): safe_rmpath(f.name) else: # fallback - use python's executable + if c_code is not None: + raise ValueError( + "can't specify c_code arg as gcc is not installed") shutil.copyfile(sys.executable, outpath) if POSIX: st = os.stat(outpath) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index d063fb40..37352ecf 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -40,6 +40,7 @@ from psutil.tests import safe_rmpath from psutil.tests import sh from psutil.tests import skip_on_not_implemented from psutil.tests import TESTFN +from psutil.tests import ThreadTask from psutil.tests import TRAVIS from psutil.tests import unittest from psutil.tests import which @@ -461,6 +462,21 @@ class TestSystemCPU(unittest.TestCase): else: self.assertNotIn('guest_nice', fields) + @unittest.skipUnless(os.path.exists("/sys/devices/system/cpu/online"), + "/sys/devices/system/cpu/online does not exist") + def test_cpu_count_logical_w_sysdev_cpu_online(self): + with open("/sys/devices/system/cpu/online") as f: + value = f.read().strip() + value = int(value.split('-')[1]) + 1 + self.assertEqual(psutil.cpu_count(), value) + + @unittest.skipUnless(os.path.exists("/sys/devices/system/cpu"), + "/sys/devices/system/cpu does not exist") + def test_cpu_count_logical_w_sysdev_cpu_num(self): + ls = os.listdir("/sys/devices/system/cpu") + count = len([x for x in ls if re.search("cpu\d+$", x) is not None]) + self.assertEqual(psutil.cpu_count(), count) + @unittest.skipUnless(which("nproc"), "nproc utility not available") def test_cpu_count_logical_w_nproc(self): num = int(sh("nproc --all")) @@ -565,7 +581,8 @@ class TestSystemNetwork(unittest.TestCase): except RuntimeError: pass else: - self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) + # Not always reliable. + # self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) self.assertEqual(stats.mtu, int(re.findall('MTU:(\d+)', out)[0])) @@ -989,6 +1006,24 @@ class TestMisc(unittest.TestCase): importlib.reload(psutil._pslinux) importlib.reload(psutil) + def test_issue_687(self): + # In case of thread ID: + # - pid_exists() is supposed to return False + # - Process(tid) is supposed to work + # - pids() should not return the TID + # See: https://github.com/giampaolo/psutil/issues/687 + t = ThreadTask() + t.start() + try: + p = psutil.Process() + tid = p.threads()[1].id + assert not psutil.pid_exists(tid), tid + pt = psutil.Process(tid) + pt.as_dict() + self.assertNotIn(tid, psutil.pids()) + finally: + t.stop() + # ===================================================================== # test process @@ -1003,49 +1038,6 @@ class TestProcess(unittest.TestCase): tearDown = setUp - def test_compare_stat_and_status_files(self): - # /proc/pid/stat and /proc/pid/status have many values in common. - # Whenever possible, psutil uses /proc/pid/stat (it's faster). - # For all those cases we check that the value found in - # /proc/pid/stat (by psutil) matches the one found in - # /proc/pid/status. - p = psutil.Process() - with psutil._psplatform.open_text('/proc/%s/status' % p.pid) as f: - for line in f: - line = line.strip() - if line.startswith('Name:'): - name = line.split()[1] - # Name is truncated to 15 chars - self.assertEqual(p.name()[:15], name[:15]) - elif line.startswith('State:'): - status = line[line.find('(') + 1:line.rfind(')')] - status = status.replace(' ', '-') - self.assertEqual(p.status(), status) - elif line.startswith('PPid:'): - ppid = int(line.split()[1]) - self.assertEqual(p.ppid(), ppid) - # The ones below internally are determined by reading - # 'status' file but we use a re to extract the info - # so it makes sense to check them. - elif line.startswith('Threads:'): - num_threads = int(line.split()[1]) - self.assertEqual(p.num_threads(), num_threads) - elif line.startswith('Uid:'): - uids = tuple(map(int, line.split()[1:4])) - self.assertEqual(tuple(p.uids()), uids) - elif line.startswith('Gid:'): - gids = tuple(map(int, line.split()[1:4])) - self.assertEqual(tuple(p.gids()), gids) - elif line.startswith('voluntary_ctxt_switches:'): - vol = int(line.split()[1]) - self.assertAlmostEqual( - p.num_ctx_switches().voluntary, vol, delta=2) - elif line.startswith('nonvoluntary_ctxt_switches:'): - invol = int(line.split()[1]) - self.assertAlmostEqual( - p.num_ctx_switches().involuntary, invol, - delta=2) - def test_memory_full_info(self): src = textwrap.dedent(""" import time @@ -1217,5 +1209,74 @@ class TestProcess(unittest.TestCase): self.assertRaises(psutil.ZombieProcess, psutil.Process().exe) +@unittest.skipUnless(LINUX, "LINUX only") +class TestProcessAgainstStatus(unittest.TestCase): + """/proc/pid/stat and /proc/pid/status have many values in common. + Whenever possible, psutil uses /proc/pid/stat (it's faster). + For all those cases we check that the value found in + /proc/pid/stat (by psutil) matches the one found in + /proc/pid/status. + """ + + @classmethod + def setUpClass(cls): + cls.proc = psutil.Process() + + def read_status_file(self, linestart): + with psutil._psplatform.open_text( + '/proc/%s/status' % self.proc.pid) as f: + for line in f: + line = line.strip() + if line.startswith(linestart): + value = line.partition('\t')[2] + try: + return int(value) + except ValueError: + return value + else: + raise ValueError("can't find %r" % linestart) + + def test_name(self): + value = self.read_status_file("Name:") + self.assertEqual(self.proc.name(), value) + + def test_status(self): + value = self.read_status_file("State:") + value = value[value.find('(') + 1:value.rfind(')')] + value = value.replace(' ', '-') + self.assertEqual(self.proc.status(), value) + + def test_ppid(self): + value = self.read_status_file("PPid:") + self.assertEqual(self.proc.ppid(), value) + + def test_num_threads(self): + value = self.read_status_file("Threads:") + self.assertEqual(self.proc.num_threads(), value) + + def test_uids(self): + value = self.read_status_file("Uid:") + value = tuple(map(int, value.split()[1:4])) + self.assertEqual(self.proc.uids(), value) + + def test_gids(self): + value = self.read_status_file("Gid:") + value = tuple(map(int, value.split()[1:4])) + self.assertEqual(self.proc.gids(), value) + + @retry_before_failing() + def test_num_ctx_switches(self): + value = self.read_status_file("voluntary_ctxt_switches:") + self.assertEqual(self.proc.num_ctx_switches().voluntary, value) + value = self.read_status_file("nonvoluntary_ctxt_switches:") + self.assertEqual(self.proc.num_ctx_switches().involuntary, value) + + def test_cpu_affinity(self): + value = self.read_status_file("Cpus_allowed_list:") + min_, max_ = map(int, value.split('-')) + self.assertEqual( + self.proc.cpu_affinity(), list(range(min_, max_ + 1))) + + if __name__ == '__main__': run_test_module_by_name(__file__) diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index 46186e41..f1a951f0 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -458,6 +458,11 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_cpu_stats(self): self.execute(psutil.cpu_stats) + @skip_if_linux() + @unittest.skipUnless(hasattr(psutil, "cpu_freq"), "platform not supported") + def test_cpu_freq(self): + self.execute(psutil.cpu_freq) + # --- mem def test_virtual_memory(self): diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py index 7b61bc74..6e7a5891 100755 --- a/psutil/tests/test_osx.py +++ b/psutil/tests/test_osx.py @@ -111,6 +111,8 @@ class TestProcess(unittest.TestCase): @unittest.skipUnless(OSX, "OSX only") class TestSystemAPIs(unittest.TestCase): + # --- disk + def test_disks(self): # test psutil.disk_usage() and psutil.disk_partitions() # against "df -a" @@ -138,6 +140,8 @@ class TestSystemAPIs(unittest.TestCase): if abs(usage.used - used) > 10 * 1024 * 1024: self.fail("psutil=%s, df=%s" % usage.used, used) + # --- cpu + def test_cpu_count_logical(self): num = sysctl("sysctl hw.logicalcpu") self.assertEqual(num, psutil.cpu_count(logical=True)) @@ -146,6 +150,15 @@ class TestSystemAPIs(unittest.TestCase): num = sysctl("sysctl hw.physicalcpu") self.assertEqual(num, psutil.cpu_count(logical=False)) + def test_cpu_freq(self): + freq = psutil.cpu_freq()[0] + self.assertEqual( + freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency")) + self.assertEqual( + freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min")) + self.assertEqual( + freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max")) + # --- virtual mem def test_vmem_total(self): @@ -206,6 +219,8 @@ class TestSystemAPIs(unittest.TestCase): # self.assertEqual(psutil_smem.used, human2bytes(used)) # self.assertEqual(psutil_smem.free, human2bytes(free)) + # --- network + def test_net_if_stats(self): for name, stats in psutil.net_if_stats().items(): try: diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 62fa36ff..74750473 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -312,7 +312,7 @@ class TestProcess(unittest.TestCase): @unittest.skipIf(TRAVIS, 'not reliable on TRAVIS') def test_terminal(self): terminal = psutil.Process().terminal() - if sys.stdin.isatty(): + if sys.stdin.isatty() or sys.stdout.isatty(): tty = os.path.realpath(sh('tty')) self.assertEqual(terminal, tty) else: @@ -511,10 +511,8 @@ class TestProcess(unittest.TestCase): try: step2 = p.num_threads() self.assertEqual(step2, step1 + 1) - thread.stop() finally: - if thread._running: - thread.stop() + thread.stop() @unittest.skipUnless(WINDOWS, 'WINDOWS only') def test_num_handles(self): @@ -534,7 +532,6 @@ class TestProcess(unittest.TestCase): thread = ThreadTask() thread.start() - try: step2 = p.threads() self.assertEqual(len(step2), len(step1) + 1) @@ -546,11 +543,8 @@ class TestProcess(unittest.TestCase): self.assertEqual(athread.id, athread[0]) self.assertEqual(athread.user_time, athread[1]) self.assertEqual(athread.system_time, athread[2]) - # test num threads - thread.stop() finally: - if thread._running: - thread.stop() + thread.stop() @retry_before_failing() # see: https://travis-ci.org/giampaolo/psutil/jobs/111842553 @@ -855,6 +849,7 @@ class TestProcess(unittest.TestCase): def test_cpu_affinity(self): p = psutil.Process() initial = p.cpu_affinity() + self.addCleanup(p.cpu_affinity, initial) if hasattr(os, "sched_getaffinity"): self.assertEqual(initial, list(os.sched_getaffinity(p.pid))) self.assertEqual(len(initial), len(set(initial))) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index e2220be4..4cbdb056 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -697,6 +697,25 @@ class TestSystemAPIs(unittest.TestCase): if name in ('ctx_switches', 'interrupts'): self.assertGreater(value, 0) + @unittest.skipUnless(hasattr(psutil, "cpu_freq"), + "platform not suported") + def test_cpu_freq(self): + def check_ls(ls): + for nt in ls: + self.assertLessEqual(nt.current, nt.max) + for name in nt._fields: + value = getattr(nt, name) + self.assertGreaterEqual(value, 0) + + ls = psutil.cpu_freq(percpu=True) + if not TRAVIS: + assert ls, ls + + check_ls([psutil.cpu_freq(percpu=False)]) + + if LINUX: + self.assertEqual(len(ls), psutil.cpu_count()) + def test_os_constants(self): names = ["POSIX", "WINDOWS", "LINUX", "OSX", "FREEBSD", "OPENBSD", "NETBSD", "BSD", "SUNOS"] diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 802242b5..07f1d796 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -85,6 +85,12 @@ class TestSystemAPIs(unittest.TestCase): num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) self.assertEqual(num_cpus, psutil.cpu_count()) + def test_cpu_freq(self): + w = wmi.WMI() + proc = w.Win32_Processor()[0] + self.assertEqual(proc.CurrentClockSpeed, psutil.cpu_freq().current) + self.assertEqual(proc.MaxClockSpeed, psutil.cpu_freq().max) + def test_total_phymem(self): w = wmi.WMI().Win32_ComputerSystem()[0] self.assertEqual(int(w.TotalPhysicalMemory), diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index bbe73e0d..8ce51ed0 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -171,6 +171,20 @@ def build(): @cmd +def build_exe(): + """Create exe file.""" + build() + sh("%s setup.py bdist_wininst" % PYTHON) + + +@cmd +def build_wheel(): + """Create wheel file.""" + build() + sh("%s setup.py bdist_wheel" % PYTHON) + + +@cmd def install_pip(): """Install pip""" try: @@ -211,11 +225,12 @@ def install(): @cmd def uninstall(): """Uninstall psutil""" - clean() try: import psutil except ImportError: + clean() return + clean() install_pip() sh("%s -m pip uninstall -y psutil" % PYTHON) @@ -230,6 +245,7 @@ def uninstall(): try: import psutil # NOQA except ImportError: + clean() return sh("%s -m pip uninstall -y psutil" % PYTHON) finally: @@ -85,18 +85,6 @@ def silenced_output(stream_name): VERSION = get_version() macros.append(('PSUTIL_VERSION', int(VERSION.replace('.', '')))) - -# POSIX -if POSIX: - posix_extension = Extension( - 'psutil._psutil_posix', - sources=['psutil/_psutil_posix.c']) - if SUNOS: - posix_extension.libraries.append('socket') - if platform.release() == '5.10': - posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c') - posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1)) - # Windows if WINDOWS: def get_winver(): @@ -134,12 +122,11 @@ if WINDOWS: define_macros=macros, libraries=[ "psapi", "kernel32", "advapi32", "shell32", "netapi32", - "iphlpapi", "wtsapi32", "ws2_32", + "iphlpapi", "wtsapi32", "ws2_32", "PowrProf", ], # extra_compile_args=["/Z7"], # extra_link_args=["/DEBUG"] ) - extensions = [ext] # OS X elif OSX: @@ -155,7 +142,6 @@ elif OSX: extra_link_args=[ '-framework', 'CoreFoundation', '-framework', 'IOKit' ]) - extensions = [ext, posix_extension] # FreeBSD elif FREEBSD: @@ -170,7 +156,6 @@ elif FREEBSD: ], define_macros=macros, libraries=["devstat"]) - extensions = [ext, posix_extension] # OpenBSD elif OPENBSD: @@ -184,7 +169,6 @@ elif OPENBSD: ], define_macros=macros, libraries=["kvm"]) - extensions = [ext, posix_extension] # NetBSD elif NETBSD: @@ -199,7 +183,6 @@ elif NETBSD: ], define_macros=macros, libraries=["kvm"]) - extensions = [ext, posix_extension] # Linux elif LINUX: @@ -229,15 +212,15 @@ elif LINUX: else: return None - macros.append(("PSUTIL_LINUX", 1)) ETHTOOL_MACRO = get_ethtool_macro() + + macros.append(("PSUTIL_LINUX", 1)) if ETHTOOL_MACRO is not None: macros.append(ETHTOOL_MACRO) ext = Extension( 'psutil._psutil_linux', sources=['psutil/_psutil_linux.c'], define_macros=macros) - extensions = [ext, posix_extension] # Solaris elif SUNOS: @@ -247,17 +230,32 @@ elif SUNOS: sources=['psutil/_psutil_sunos.c'], define_macros=macros, libraries=['kstat', 'nsl', 'socket']) - extensions = [ext, posix_extension] else: sys.exit('platform %s is not supported' % sys.platform) +# POSIX +if POSIX: + posix_extension = Extension( + 'psutil._psutil_posix', + define_macros=macros, + sources=['psutil/_psutil_posix.c']) + if SUNOS: + posix_extension.libraries.append('socket') + if platform.release() == '5.10': + posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c') + posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1)) + + extensions = [ext, posix_extension] +else: + extensions = [ext] + def main(): - setup_args = dict( + setup( name='psutil', version=VERSION, - description=__doc__.replace('\n', '').strip(), + description=__doc__ .replace('\n', '').strip() if __doc__ else '', long_description=get_description(), keywords=[ 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty', @@ -271,6 +269,7 @@ def main(): platforms='Platform Independent', license='BSD', packages=['psutil', 'psutil.tests'], + ext_modules=extensions, # see: python setup.py register --list-classifiers classifiers=[ 'Development Status :: 5 - Production/Stable', @@ -302,6 +301,7 @@ def main(): 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python', @@ -317,9 +317,6 @@ def main(): 'Topic :: Utilities', ], ) - if extensions is not None: - setup_args["ext_modules"] = extensions - setup(**setup_args) if __name__ == '__main__': |