diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-10 14:59:20 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2017-01-10 14:59:20 +0100 |
commit | 12a2a793f45a445275ec667feb701692621a875f (patch) | |
tree | 35daa0f9eadf7c062c30f2a1da35dfa5ab5b11ed | |
parent | c5cc270ed2b8953de81a4f4a29ce48808b9abafb (diff) | |
parent | 6e60cc93c68f243dcaf3c895fcd8a9fabcedba4b (diff) | |
download | psutil-12a2a793f45a445275ec667feb701692621a875f.tar.gz |
Merge branch 'master' into 941-cpu-freq
-rw-r--r-- | DEVGUIDE.rst | 6 | ||||
-rw-r--r-- | HISTORY.rst | 16 | ||||
-rw-r--r-- | INSTALL.rst | 2 | ||||
-rw-r--r-- | MANIFEST.in | 15 | ||||
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | README.rst | 2 | ||||
-rw-r--r-- | docs/_themes/pydoctheme/static/pydoctheme.css | 10 | ||||
-rw-r--r-- | docs/conf.py | 2 | ||||
-rw-r--r-- | docs/index.rst | 314 | ||||
-rw-r--r-- | psutil/__init__.py | 27 | ||||
-rw-r--r-- | psutil/_psbsd.py | 22 | ||||
-rw-r--r-- | psutil/_pslinux.py | 28 | ||||
-rw-r--r-- | psutil/_psutil_bsd.c | 105 | ||||
-rw-r--r-- | psutil/_psutil_posix.c | 63 | ||||
-rw-r--r-- | psutil/arch/bsd/netbsd.c | 4 | ||||
-rw-r--r-- | psutil/tests/__init__.py | 4 | ||||
-rwxr-xr-x | psutil/tests/test_linux.py | 19 | ||||
-rwxr-xr-x | psutil/tests/test_process.py | 13 | ||||
-rwxr-xr-x | setup.py | 46 |
19 files changed, 434 insertions, 274 deletions
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..92413ec1 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,10 +1,22 @@ *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. + + +5.0.1 +===== + +*2016-12-21* + **Enhancements** - 939_: tar.gz distribution went from 1.8M to 258K. @@ -13,10 +25,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..fcbc3736 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -62,7 +62,7 @@ versions. OSX === -Install `XcodeTools <https://developer.apple.com/downloads/?name=Xcode>`__ +Install `Xcode <https://developer.apple.com/downloads/?name=Xcode>`__ first, then: :: 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 # =================================================================== @@ -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: 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 bd2773da..022c6a6b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -156,13 +156,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() @@ -453,7 +454,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:: @@ -461,27 +462,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. @@ -632,12 +633,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() @@ -678,18 +683,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 @@ -702,8 +707,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) @@ -733,10 +738,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. @@ -858,7 +865,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. @@ -875,16 +882,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 @@ -1100,7 +1119,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() @@ -1118,9 +1137,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 @@ -1132,30 +1151,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:: @@ -1291,8 +1308,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. @@ -1390,7 +1407,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. @@ -1399,7 +1422,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 ─┐ @@ -1442,8 +1465,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 @@ -1502,27 +1524,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: @@ -1602,6 +1624,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 ----------- @@ -1756,14 +1782,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``. @@ -1784,18 +1810,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()`. @@ -1804,31 +1830,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>`__). @@ -1844,9 +1870,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 @@ -1872,22 +1898,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 @@ -1905,8 +1931,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 @@ -1925,6 +1951,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 ================= @@ -1933,23 +1989,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 6994c476..94ace42a 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 @@ -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. @@ -981,6 +982,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 @@ -1186,6 +1195,8 @@ class Process(object): """ return self._proc.connections(kind) + # --- signals + if POSIX: def _send_signal(self, sig): assert not self.pid < 0, self.pid diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index a3301504..acf6c7c3 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 @@ -420,7 +421,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 e073b06e..9154896a 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1105,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/_psutil_bsd.c b/psutil/_psutil_bsd.c index 33448ccf..afe3834e 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) @@ -215,9 +210,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 @@ -233,7 +228,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; @@ -241,12 +236,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 @@ -260,7 +255,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { // Return a single big tuple with all process info. py_retlist = Py_BuildValue( "(lillllllidllllddddlllllO)", -#ifdef __FreeBSD__ +#ifdef PSUTIL_FREEBSD // (long)kp.ki_ppid, // (long) ppid (int)kp.ki_stat, // (int) status @@ -292,7 +287,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memtext, // (long) mem text memdata, // (long) mem data memstack, // (long) mem stack -#elif defined(__OpenBSD__) || defined(__NetBSD__) +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) // (long)kp.p_ppid, // (long) ppid (int)kp.p_stat, // (int) status @@ -352,9 +347,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 @@ -412,7 +407,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]; @@ -420,9 +415,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 @@ -447,7 +442,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; @@ -474,17 +469,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)) { @@ -521,7 +516,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; @@ -534,7 +529,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); @@ -553,7 +548,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); @@ -567,7 +562,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; @@ -590,7 +585,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) @@ -611,24 +606,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 @@ -769,7 +764,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; @@ -849,7 +844,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 @@ -857,25 +852,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, @@ -914,7 +909,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 @@ -976,7 +971,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); @@ -984,7 +979,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); @@ -992,7 +987,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_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/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 871cb74e..028a41d9 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 @@ -1004,6 +1005,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 diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index b0176083..d25f4474 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -302,7 +302,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: @@ -501,10 +501,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): @@ -524,7 +522,6 @@ class TestProcess(unittest.TestCase): thread = ThreadTask() thread.start() - try: step2 = p.threads() self.assertEqual(len(step2), len(step1) + 1) @@ -536,11 +533,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 @@ -845,6 +839,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))) @@ -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(): @@ -139,7 +127,6 @@ if WINDOWS: # 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', @@ -317,9 +316,6 @@ def main(): 'Topic :: Utilities', ], ) - if extensions is not None: - setup_args["ext_modules"] = extensions - setup(**setup_args) if __name__ == '__main__': |