From f46e46f7fae52adb6f9bd80d62e9e07a97e770a0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 6 Mar 2020 21:03:01 +0000 Subject: [PATCH] Bug 1620513 - Upgrade psutil to 5.7.0. r=rstewart Differential Revision: https://phabricator.services.mozilla.com/D65685 --HG-- rename : third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/METADATA => third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/METADATA rename : third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/WHEEL => third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/WHEEL rename : third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/top_level.txt => third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/top_level.txt rename : third_party/python/psutil/psutil/tests/runner.py => third_party/python/psutil-cp27-none-win_amd64/psutil/tests/runner.py rename : third_party/python/psutil/psutil/arch/windows/wmi.h => third_party/python/psutil/psutil/arch/windows/net.h rename : third_party/python/psutil/psutil/arch/windows/wmi.h => third_party/python/psutil/psutil/arch/windows/socks.h rename : third_party/python/psutil/scripts/internal/download_exes.py => third_party/python/psutil/scripts/internal/win_download_wheels.py extra : moz-landing-system : lando --- .../psutil-5.4.3.dist-info/DESCRIPTION.rst | 472 ---- .../psutil-5.4.3.dist-info/RECORD | 34 - .../psutil-5.4.3.dist-info/metadata.json | 1 - .../psutil-5.7.0.dist-info/LICENSE | 29 + .../METADATA | 266 +- .../psutil-5.7.0.dist-info/RECORD | 33 + .../WHEEL | 2 +- .../top_level.txt | 0 .../psutil/__init__.py | 406 +-- .../psutil/_common.py | 347 ++- .../psutil/_compat.py | 110 +- .../psutil/_exceptions.py | 94 - .../psutil/_psaix.py | 159 +- .../psutil/_psbsd.py | 232 +- .../psutil/_pslinux.py | 788 +++--- .../psutil/_psosx.py | 111 +- .../psutil/_psposix.py | 67 +- .../psutil/_pssunos.py | 166 +- .../psutil/_psutil_windows.pyd | Bin 59904 -> 62976 bytes .../psutil/_pswindows.py | 418 +-- .../psutil/tests/__init__.py | 295 +-- .../psutil/tests/__main__.py | 87 +- .../psutil/tests/runner.py | 162 ++ .../psutil/tests/test_aix.py | 24 +- .../psutil/tests/test_bsd.py | 138 +- .../psutil/tests/test_connections.py | 288 ++- .../psutil/tests/test_contracts.py | 359 +-- .../psutil/tests/test_linux.py | 1250 +++++---- .../psutil/tests/test_memory_leaks.py | 81 +- .../psutil/tests/test_misc.py | 54 +- .../psutil/tests/test_osx.py | 43 +- .../psutil/tests/test_posix.py | 162 +- .../psutil/tests/test_process.py | 308 ++- .../psutil/tests/test_sunos.py | 6 +- .../psutil/tests/test_system.py | 482 ++-- .../psutil/tests/test_unicode.py | 95 +- .../psutil/tests/test_windows.py | 220 +- third_party/python/psutil/.cirrus.yml | 31 + third_party/python/psutil/CREDITS | 46 +- third_party/python/psutil/HISTORY.rst | 611 +++++ third_party/python/psutil/INSTALL.rst | 2 +- third_party/python/psutil/MANIFEST.in | 20 +- third_party/python/psutil/Makefile | 57 +- third_party/python/psutil/PKG-INFO | 71 +- third_party/python/psutil/README.rst | 58 +- third_party/python/psutil/docs/DEVGUIDE.rst | 42 +- third_party/python/psutil/docs/DEVNOTES | 5 +- third_party/python/psutil/docs/Makefile | 9 +- .../python/psutil/docs/_static/css/custom.css | 22 +- third_party/python/psutil/docs/index.rst | 414 ++- third_party/python/psutil/make.bat | 6 +- third_party/python/psutil/psutil/__init__.py | 154 +- third_party/python/psutil/psutil/_common.py | 236 +- third_party/python/psutil/psutil/_compat.py | 85 +- third_party/python/psutil/psutil/_psaix.py | 67 +- third_party/python/psutil/psutil/_psbsd.py | 127 +- third_party/python/psutil/psutil/_pslinux.py | 236 +- third_party/python/psutil/psutil/_psosx.py | 38 +- third_party/python/psutil/psutil/_psposix.py | 69 +- third_party/python/psutil/psutil/_pssunos.py | 72 +- .../python/psutil/psutil/_psutil_aix.c | 31 +- .../python/psutil/psutil/_psutil_bsd.c | 278 +- .../python/psutil/psutil/_psutil_common.c | 347 ++- .../python/psutil/psutil/_psutil_common.h | 122 +- .../python/psutil/psutil/_psutil_linux.c | 221 +- .../python/psutil/psutil/_psutil_osx.c | 255 +- .../python/psutil/psutil/_psutil_posix.c | 164 +- .../python/psutil/psutil/_psutil_posix.h | 4 +- .../python/psutil/psutil/_psutil_sunos.c | 70 +- .../python/psutil/psutil/_psutil_windows.c | 2294 ++--------------- .../python/psutil/psutil/_pswindows.py | 258 +- .../python/psutil/psutil/arch/aix/common.c | 2 +- .../python/psutil/psutil/arch/aix/ifaddrs.c | 2 +- .../python/psutil/psutil/arch/aix/ifaddrs.h | 3 +- .../psutil/psutil/arch/aix/net_connections.h | 2 +- .../psutil/arch/aix/net_kernel_structs.h | 2 +- .../psutil/psutil/arch/freebsd/proc_socks.c | 7 +- .../psutil/psutil/arch/freebsd/specific.c | 142 +- .../psutil/psutil/arch/freebsd/sys_socks.c | 11 +- .../psutil/psutil/arch/netbsd/specific.c | 60 +- .../psutil/psutil/arch/netbsd/specific.h | 1 + .../psutil/psutil/arch/openbsd/specific.c | 77 +- .../psutil/psutil/arch/openbsd/specific.h | 6 +- .../psutil/psutil/arch/osx/process_info.c | 26 +- .../psutil/psutil/arch/osx/process_info.h | 10 +- .../psutil/psutil/arch/solaris/environ.c | 5 +- .../python/psutil/psutil/arch/windows/cpu.c | 415 +++ .../python/psutil/psutil/arch/windows/cpu.h | 14 + .../python/psutil/psutil/arch/windows/disk.c | 373 +++ .../python/psutil/psutil/arch/windows/disk.h | 12 + .../psutil/psutil/arch/windows/global.c | 234 -- .../psutil/psutil/arch/windows/global.h | 77 - .../psutil/psutil/arch/windows/inet_ntop.c | 45 - .../psutil/psutil/arch/windows/inet_ntop.h | 17 - .../python/psutil/psutil/arch/windows/net.c | 443 ++++ .../python/psutil/psutil/arch/windows/net.h | 11 + .../psutil/psutil/arch/windows/ntextapi.h | 406 +-- .../psutil/arch/windows/process_handles.c | 687 ++--- .../psutil/arch/windows/process_handles.h | 2 +- .../psutil/psutil/arch/windows/process_info.c | 629 ++--- .../psutil/psutil/arch/windows/process_info.h | 30 +- .../psutil/arch/windows/process_utils.c | 171 ++ .../psutil/arch/windows/process_utils.h | 11 + .../psutil/psutil/arch/windows/security.c | 2 +- .../psutil/psutil/arch/windows/services.c | 10 +- .../psutil/psutil/arch/windows/services.h | 2 +- .../python/psutil/psutil/arch/windows/socks.c | 471 ++++ .../python/psutil/psutil/arch/windows/socks.h | 9 + .../python/psutil/psutil/arch/windows/wmi.c | 12 +- .../python/psutil/psutil/arch/windows/wmi.h | 4 +- .../python/psutil/psutil/tests/README.rst | 5 +- .../python/psutil/psutil/tests/__init__.py | 149 +- .../python/psutil/psutil/tests/__main__.py | 85 +- .../python/psutil/psutil/tests/runner.py | 114 +- .../python/psutil/psutil/tests/test_aix.py | 2 +- .../python/psutil/psutil/tests/test_bsd.py | 68 +- .../psutil/psutil/tests/test_connections.py | 275 +- .../psutil/psutil/tests/test_contracts.py | 200 +- .../python/psutil/psutil/tests/test_linux.py | 72 +- .../psutil/psutil/tests/test_memory_leaks.py | 42 +- .../python/psutil/psutil/tests/test_misc.py | 18 +- .../python/psutil/psutil/tests/test_osx.py | 2 +- .../python/psutil/psutil/tests/test_posix.py | 13 +- .../psutil/psutil/tests/test_process.py | 49 +- .../python/psutil/psutil/tests/test_sunos.py | 2 +- .../python/psutil/psutil/tests/test_system.py | 438 ++-- .../psutil/psutil/tests/test_unicode.py | 80 +- .../psutil/psutil/tests/test_windows.py | 91 +- third_party/python/psutil/scripts/battery.py | 2 +- .../python/psutil/scripts/cpu_distribution.py | 4 +- .../python/psutil/scripts/disk_usage.py | 2 +- third_party/python/psutil/scripts/fans.py | 2 +- third_party/python/psutil/scripts/free.py | 2 +- third_party/python/psutil/scripts/ifconfig.py | 2 +- .../psutil/scripts/internal/.git-pre-commit | 18 +- .../psutil/scripts/internal/bench_oneshot.py | 2 +- .../scripts/internal/bench_oneshot_2.py | 6 +- .../scripts/internal/check_broken_links.py | 8 +- .../python/psutil/scripts/internal/clinter.py | 76 + .../psutil/scripts/internal/fix_flake8.py | 185 ++ .../scripts/internal/generate_manifest.py | 2 +- .../scripts/internal/print_access_denied.py | 20 +- .../psutil/scripts/internal/print_announce.py | 2 +- .../scripts/internal/print_api_speed.py | 67 +- .../psutil/scripts/internal/print_timeline.py | 4 +- .../scripts/internal/purge_installation.py | 2 +- .../psutil/scripts/internal/scriptutils.py | 54 - ...ownload_exes.py => win_download_wheels.py} | 12 +- .../python/psutil/scripts/internal/winmake.py | 288 ++- third_party/python/psutil/scripts/iotop.py | 2 +- third_party/python/psutil/scripts/killall.py | 2 +- third_party/python/psutil/scripts/meminfo.py | 2 +- third_party/python/psutil/scripts/netstat.py | 4 +- third_party/python/psutil/scripts/nettop.py | 2 +- third_party/python/psutil/scripts/pidof.py | 4 +- third_party/python/psutil/scripts/pmap.py | 2 +- third_party/python/psutil/scripts/procinfo.py | 2 +- third_party/python/psutil/scripts/procsmem.py | 4 +- third_party/python/psutil/scripts/ps.py | 2 +- third_party/python/psutil/scripts/pstree.py | 2 +- third_party/python/psutil/scripts/sensors.py | 2 +- .../python/psutil/scripts/temperatures.py | 2 +- third_party/python/psutil/scripts/top.py | 2 +- third_party/python/psutil/scripts/who.py | 2 +- .../python/psutil/scripts/winservices.py | 2 +- third_party/python/psutil/setup.py | 122 +- third_party/python/psutil/tox.ini | 4 +- third_party/python/requirements.in | 2 +- third_party/python/requirements.txt | 23 +- 169 files changed, 11451 insertions(+), 9957 deletions(-) delete mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/DESCRIPTION.rst delete mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/RECORD delete mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/metadata.json create mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/LICENSE rename third_party/python/psutil-cp27-none-win_amd64/{psutil-5.4.3.dist-info => psutil-5.7.0.dist-info}/METADATA (65%) create mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/RECORD rename third_party/python/psutil-cp27-none-win_amd64/{psutil-5.4.3.dist-info => psutil-5.7.0.dist-info}/WHEEL (65%) rename third_party/python/psutil-cp27-none-win_amd64/{psutil-5.4.3.dist-info => psutil-5.7.0.dist-info}/top_level.txt (100%) delete mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil/_exceptions.py create mode 100644 third_party/python/psutil-cp27-none-win_amd64/psutil/tests/runner.py create mode 100644 third_party/python/psutil/.cirrus.yml create mode 100644 third_party/python/psutil/psutil/arch/windows/cpu.c create mode 100644 third_party/python/psutil/psutil/arch/windows/cpu.h create mode 100644 third_party/python/psutil/psutil/arch/windows/disk.c create mode 100644 third_party/python/psutil/psutil/arch/windows/disk.h delete mode 100644 third_party/python/psutil/psutil/arch/windows/global.c delete mode 100644 third_party/python/psutil/psutil/arch/windows/global.h delete mode 100644 third_party/python/psutil/psutil/arch/windows/inet_ntop.c delete mode 100644 third_party/python/psutil/psutil/arch/windows/inet_ntop.h create mode 100644 third_party/python/psutil/psutil/arch/windows/net.c create mode 100644 third_party/python/psutil/psutil/arch/windows/net.h create mode 100644 third_party/python/psutil/psutil/arch/windows/process_utils.c create mode 100644 third_party/python/psutil/psutil/arch/windows/process_utils.h create mode 100644 third_party/python/psutil/psutil/arch/windows/socks.c create mode 100644 third_party/python/psutil/psutil/arch/windows/socks.h mode change 100644 => 100755 third_party/python/psutil/scripts/internal/bench_oneshot_2.py create mode 100755 third_party/python/psutil/scripts/internal/clinter.py create mode 100755 third_party/python/psutil/scripts/internal/fix_flake8.py mode change 100644 => 100755 third_party/python/psutil/scripts/internal/print_access_denied.py mode change 100644 => 100755 third_party/python/psutil/scripts/internal/print_api_speed.py mode change 100644 => 100755 third_party/python/psutil/scripts/internal/print_timeline.py delete mode 100644 third_party/python/psutil/scripts/internal/scriptutils.py rename third_party/python/psutil/scripts/internal/{download_exes.py => win_download_wheels.py} (93%) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/DESCRIPTION.rst b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/DESCRIPTION.rst deleted file mode 100644 index b406cbb0fd3b..000000000000 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,472 +0,0 @@ -.. image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux%20/%20OSX - :target: https://travis-ci.org/giampaolo/psutil - :alt: Linux tests (Travis) - -.. image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows - :target: https://ci.appveyor.com/project/giampaolo/psutil - :alt: Windows tests (Appveyor) - -.. image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master - :target: https://coveralls.io/github/giampaolo/psutil?branch=master - :alt: Test coverage (coverall.io) - -.. image:: https://readthedocs.org/projects/psutil/badge/?version=latest - :target: http://psutil.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -.. image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi - :target: https://pypi.python.org/pypi/psutil/ - :alt: Latest version - -.. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg - :target: https://github.com/giampaolo/psutil/ - :alt: Github stars - -.. image:: https://img.shields.io/pypi/l/psutil.svg - :target: https://pypi.python.org/pypi/psutil/ - :alt: License - -=========== -Quick links -=========== - -- `Home page `_ -- `Install `_ -- `Documentation `_ -- `Download `_ -- `Forum `_ -- `Blog `_ -- `Development guide `_ -- `What's new `_ - -======= -Summary -======= - -psutil (process and system utilities) is a cross-platform library for -retrieving information on **running processes** and **system utilization** -(CPU, memory, disks, network, sensors) in Python. -It is useful mainly for **system monitoring**, **profiling and limiting process -resources** and **management of running processes**. -It implements many functionalities offered by UNIX command line tools such as: -ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, ionice, iostat, -iotop, uptime, pidof, tty, taskset, pmap. -psutil currently supports the following platforms: - -- **Linux** -- **Windows** -- **OSX**, -- **FreeBSD, OpenBSD**, **NetBSD** -- **Sun Solaris** -- **AIX** - -...both **32-bit** and **64-bit** architectures, with Python -versions from **2.6 to 3.6**. -`PyPy `__ is also known to work. - -==================== -Example applications -==================== - -+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/top-small.png | -| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/top.png | -+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap-small.png | -| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap.png | -+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ - -Also see `scripts directory `__ -and `doc recipes `__. - -===================== -Projects using psutil -===================== - -At the time of writing psutil has roughly -`2.9 milion downloads `__ -per month and there are over -`6000 open source projects `__ -on github which depend from psutil. -Here's some I find particularly interesting: - -- https://github.com/facebook/osquery/ -- https://github.com/nicolargo/glances -- https://github.com/google/grr -- https://github.com/Jahaja/psdash -- https://github.com/ajenti/ajenti -- https://github.com/home-assistant/home-assistant/ - -======== -Portings -======== - -- Go: https://github.com/shirou/gopsutil -- C: https://github.com/hamon-in/cpslib -- Node: https://github.com/christkv/node-psutil -- Rust: https://github.com/borntyping/rust-psutil -- Ruby: https://github.com/spacewander/posixpsutil -- Nim: https://github.com/johnscillieri/psutil-nim - -============== -Example usages -============== - -CPU -=== - -.. code-block:: python - - >>> import psutil - >>> psutil.cpu_times() - scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0) - >>> - >>> for x in range(3): - ... psutil.cpu_percent(interval=1) - ... - 4.0 - 5.9 - 3.8 - >>> - >>> for x in range(3): - ... psutil.cpu_percent(interval=1, percpu=True) - ... - [4.0, 6.9, 3.7, 9.2] - [7.0, 8.5, 2.4, 2.1] - [1.2, 9.0, 9.9, 7.2] - >>> - >>> for x in range(3): - ... psutil.cpu_times_percent(interval=1, percpu=False) - ... - scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0) - >>> - >>> psutil.cpu_count() - 4 - >>> psutil.cpu_count(logical=False) - 2 - >>> - >>> 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 -====== - -.. code-block:: python - - >>> import psutil - >>> psutil.virtual_memory() - svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304) - >>> psutil.swap_memory() - sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944) - >>> - -Disks -===== - -.. code-block:: python - - >>> import psutil - >>> psutil.disk_partitions() - [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'), - sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')] - >>> - >>> psutil.disk_usage('/') - sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5) - >>> - >>> psutil.disk_io_counters(perdisk=False) - sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412) - >>> - -Network -======= - -.. code-block:: python - - >>> import psutil - >>> psutil.net_io_counters(pernic=True) - {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0), - 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} - >>> - >>> psutil.net_connections() - [sconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED', pid=1254), - sconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING', pid=2987), - sconn(fd=-1, family=, type=, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED', pid=None), - sconn(fd=-1, family=, type=, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT', pid=None) - ...] - >>> - >>> psutil.net_if_addrs() - {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), - snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), - snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], - 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), - snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), - snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} - >>> - >>> psutil.net_if_stats() - {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), - 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} - >>> - -Sensors -======= - -.. code-block:: python - - >>> import psutil - >>> psutil.sensors_temperatures() - {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)], - 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)], - 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0), - shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0), - shwtemp(label='Core 1', current=52.0, high=100.0, critical=100.0), - shwtemp(label='Core 2', current=45.0, high=100.0, critical=100.0), - shwtemp(label='Core 3', current=47.0, high=100.0, critical=100.0)]} - >>> - >>> psutil.sensors_fans() - {'asus': [sfan(label='cpu_fan', current=3200)]} - >>> - >>> psutil.sensors_battery() - sbattery(percent=93, secsleft=16628, power_plugged=False) - >>> - -Other system info -================= - -.. code-block:: python - - >>> import psutil - >>> psutil.users() - [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352), - suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)] - >>> - >>> psutil.boot_time() - 1365519115.0 - >>> - -Process management -================== - -.. code-block:: python - - >>> import psutil - >>> psutil.pids() - [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215, 1216, 1220, 1221, 1243, 1244, - 1301, 1601, 2237, 2355, 2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, - 4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446, - 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071] - >>> - >>> p = psutil.Process(7055) - >>> p.name() - 'python' - >>> p.exe() - '/usr/bin/python' - >>> p.cwd() - '/home/giampaolo' - >>> p.cmdline() - ['/usr/bin/python', 'main.py'] - >>> - >>> p.pid - 7055 - >>> p.ppid() - 7054 - >>> p.parent() - - >>> p.children() - [, - ] - >>> - >>> p.status() - 'running' - >>> p.username() - 'giampaolo' - >>> p.create_time() - 1267551141.5019531 - >>> p.terminal() - '/dev/pts/0' - >>> - >>> p.uids() - puids(real=1000, effective=1000, saved=1000) - >>> p.gids() - pgids(real=1000, effective=1000, saved=1000) - >>> - >>> p.cpu_times() - pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1) - >>> p.cpu_percent(interval=1.0) - 12.1 - >>> p.cpu_affinity() - [0, 1, 2, 3] - >>> p.cpu_affinity([0, 1]) # set - >>> p.cpu_num() - 1 - >>> - >>> p.memory_info() - pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0) - >>> p.memory_full_info() # "real" USS memory usage (Linux, OSX, Win only) - pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0) - >>> p.memory_percent() - 0.7823 - >>> p.memory_maps() - [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0), - pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0), - pmmap_grouped(path='/lib/x8664-linux-gnu/libcrypto.so.0.1', rss=34124, rss=32768, size=2134016, pss=15360, shared_clean=24576, shared_dirty=0, private_clean=0, private_dirty=8192, referenced=24576, anonymous=8192, swap=0), - 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.io_counters() - pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543) - >>> - >>> 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)] - >>> - >>> p.connections() - [pconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'), - pconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING'), - pconn(fd=119, family=, type=, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED'), - pconn(fd=123, family=, type=, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT')] - >>> - >>> p.num_threads() - 4 - >>> p.num_fds() - 8 - >>> p.threads() - [pthread(id=5234, user_time=22.5, system_time=9.2891), - pthread(id=5235, user_time=0.0, system_time=0.0), - pthread(id=5236, user_time=0.0, system_time=0.0), - pthread(id=5237, user_time=0.0707, system_time=1.1)] - >>> - >>> p.num_ctx_switches() - pctxsw(voluntary=78, involuntary=19) - >>> - >>> p.nice() - 0 - >>> p.nice(10) # set - >>> - >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only) - >>> p.ionice() - pionice(ioclass=, value=0) - >>> - >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only) - >>> p.rlimit(psutil.RLIMIT_NOFILE) - (5, 5) - >>> - >>> p.environ() - {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto', - 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', 'COLORTERM': 'gnome-terminal', - ...} - >>> - >>> p.as_dict() - {'status': 'running', 'num_ctx_switches': pctxsw(voluntary=63, involuntary=1), 'pid': 5457, ...} - >>> p.is_running() - True - >>> p.suspend() - >>> p.resume() - >>> - >>> p.terminate() - >>> p.wait(timeout=3) - 0 - >>> - >>> psutil.test() - USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND - root 1 0.0 0.0 24584 2240 Jun17 00:00 init - root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd - root 3 0.0 0.0 0 0 Jun17 00:05 ksoftirqd/0 - ... - giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4 - giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome - root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1 - >>> - -Further process APIs -==================== - -.. code-block:: python - - >>> import psutil - >>> for proc in psutil.process_iter(attrs=['pid', 'name']): - ... print(proc.info) - ... - {'pid': 1, 'name': 'systemd'} - {'pid': 2, 'name': 'kthreadd'} - {'pid': 3, 'name': 'ksoftirqd/0'} - ... - >>> - >>> psutil.pid_exists(3) - True - >>> - >>> def on_terminate(proc): - ... print("process {} terminated".format(proc)) - ... - >>> # waits for multiple processes to terminate - >>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate) - >>> - -Popen wrapper: - -.. code-block:: python - - >>> import psutil - >>> from subprocess import PIPE - >>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE) - >>> p.name() - 'python' - >>> p.username() - 'giampaolo' - >>> p.communicate() - ('hello\n', None) - >>> p.wait(timeout=2) - 0 - >>> - -Windows services -================ - -.. code-block:: python - - >>> list(psutil.win_service_iter()) - [, - , - , - , - ...] - >>> s = psutil.win_service_get('alg') - >>> s.as_dict() - {'binpath': 'C:\\Windows\\System32\\alg.exe', - 'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing', - 'display_name': 'Application Layer Gateway Service', - 'name': 'alg', - 'pid': None, - 'start_type': 'manual', - 'status': 'stopped', - 'username': 'NT AUTHORITY\\LocalService'} - -Other samples -============= - -See `doc recipes `__. - -====== -Author -====== - -psutil was created and is maintained by -`Giampaolo Rodola' `__. -A lot of time and effort went into making psutil as it is right now. -If you feel psutil is useful to you or your business and want to support its -future development please consider donating me -(`Giampaolo `__) some money. - -.. image:: http://www.paypal.com/en_US/i/btn/x-click-but04.gif - :target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 - :alt: Donate via PayPal - -Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin `_. - - diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/RECORD b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/RECORD deleted file mode 100644 index 68d0df0b5182..000000000000 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/RECORD +++ /dev/null @@ -1,34 +0,0 @@ -psutil/__init__.py,sha256=220_B4k7vC9eoVP_OW8SFcaXCSBGpj_nacxvfBQ5O2o,84989 -psutil/_common.py,sha256=df5J2HqrWvGoFdumxDL1q9fgJOOYyiCmV0EFxb4ghuY,17852 -psutil/_compat.py,sha256=0VqlfcCGD5tZRBW-mFroguS0J7YNw6y3LjEcX5ChXDc,8170 -psutil/_exceptions.py,sha256=37eRgLuwTy7X4tjovePfZfYif-veW6LFeRlPqsgnNBU,3009 -psutil/_psaix.py,sha256=uAqwhQHS0upGc8OIk8Qa5PScCwXNvaMkoIdltZZ0n8A,19376 -psutil/_psbsd.py,sha256=ewmj2_DfzZpQZEcRvo1xvjVFhQqJidnENLgS8jrBb68,30244 -psutil/_pslinux.py,sha256=ME3wEgxfOWSFxgKXpTKbFw29ur1toMT4DdkKLJGkaD4,74850 -psutil/_psosx.py,sha256=X2WzhpuGjffjzFt3B3hphX7piPRCrPsjJrk6p6vu96w,17213 -psutil/_psposix.py,sha256=AmpZmKnvzCFcf7RpYypiUxOxKoRdXHRy2p9hFEIAJVc,6345 -psutil/_pssunos.py,sha256=uEsntvZPXplCuP9ER2W1QaGXjOqUZx_qFx2iLeyc5V8,25650 -psutil/_psutil_windows.pyd,sha256=wbQ_dSXTvdmCyQV02P7uCk95MwDkZ_D1g5r4G7IiYr8,59904 -psutil/_pswindows.py,sha256=_KYB-Vyhnz2A3l8I7tuXvFSrBPqKIb9slcwh5Azj0QU,33121 -psutil/tests/__init__.py,sha256=hkGHtLQNHjKrqhuV3Sk0kkHir17Mlam-amZBkLdfGwA,37656 -psutil/tests/__main__.py,sha256=dssAzXI-sAHkObtruDvc1cYUFNWOi-ZXETQgwaLKJS0,2784 -psutil/tests/test_aix.py,sha256=_ItFqb-CmYHlnlcduSKbgsPBw_xEIfE5v_pOr_YD8gw,4483 -psutil/tests/test_bsd.py,sha256=aif308A3TRnPZGY1tmNlH25YCweeSMpwbkah2K13wZs,17785 -psutil/tests/test_connections.py,sha256=eXOPOkusHO6Xc8gNrDxPBhq1Ni2fYYe0AAzeGU4gijQ,20676 -psutil/tests/test_contracts.py,sha256=ryPd7QDkQKxh8Mjcl-iPflXw5OTi8TWWLzQjIJ4EW2w,24136 -psutil/tests/test_linux.py,sha256=_gQBxUSiRSygoGfFXn-2m5XpXJQMQSiurVobrwkaAgc,78746 -psutil/tests/test_memory_leaks.py,sha256=v-RmU0BAzH05pkQ1dUV1hUXxlMva7943GFvT9Erl6J4,18259 -psutil/tests/test_misc.py,sha256=4qu61Sdmvzbaz2heOXKlg_PveYrzamBIUJ5QlLIYmSQ,37821 -psutil/tests/test_osx.py,sha256=UL3sovPp-5f0LIjNqjHu12NnpXF2uuv8Z62wbeAJj2c,9652 -psutil/tests/test_posix.py,sha256=4gm2VeCYoPYnstn_2GMG18c6xvMV7APXA26W5Dcm99s,16397 -psutil/tests/test_process.py,sha256=IE-v3DHKIje6Kwnas8kzfsbz15L4-0dpI555lXg7-NE,60495 -psutil/tests/test_sunos.py,sha256=UNywEx_qlSOyUHV48lMKqmnu8RKPBDCQrO0llulCUHY,1322 -psutil/tests/test_system.py,sha256=fv3otFKJVOn3VpRrD5IMivbI-hfajL6wmzVQF_JJqDI,34885 -psutil/tests/test_unicode.py,sha256=M0VTr_kGgmDKiNq6nSvraaw-711pRXe_6R948qHAr8I,12913 -psutil/tests/test_windows.py,sha256=AG2Feks59VOahEW_hJs_o4f5wt20vS1jxxaUCijcwAQ,32609 -psutil-5.4.3.dist-info/DESCRIPTION.rst,sha256=oATWrkVX56oC_b77TIXQsrm1CxSgD93PM71km6ud5G4,19853 -psutil-5.4.3.dist-info/METADATA,sha256=neLhT7NMfWptGzaSxueDDYqVDOR0C9cNvE8pAK14C2c,22835 -psutil-5.4.3.dist-info/RECORD,, -psutil-5.4.3.dist-info/WHEEL,sha256=oT-jJdPOIfRdvpqTJO9X6OjCsyUImH2THoT54KREdwI,106 -psutil-5.4.3.dist-info/metadata.json,sha256=RqO1wxtF8F5B2HqYztzUkdKh5XmF8Wq_PxB9l571GM0,2492 -psutil-5.4.3.dist-info/top_level.txt,sha256=gCNhn57wzksDjSAISmgMJ0aiXzQulk0GJhb2-BAyYgw,7 diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/metadata.json b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/metadata.json deleted file mode 100644 index 626dd41dd053..000000000000 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Win32 (MS Windows)", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: BSD License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows :: Windows NT/2000", "Operating System :: Microsoft", "Operating System :: OS Independent", "Operating System :: POSIX :: BSD :: FreeBSD", "Operating System :: POSIX :: BSD :: NetBSD", "Operating System :: POSIX :: BSD :: OpenBSD", "Operating System :: POSIX :: BSD", "Operating System :: POSIX :: Linux", "Operating System :: POSIX :: SunOS/Solaris", "Operating System :: POSIX", "Programming Language :: C", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 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", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries", "Topic :: System :: Benchmark", "Topic :: System :: Hardware", "Topic :: System :: Monitoring", "Topic :: System :: Networking :: Monitoring", "Topic :: System :: Networking", "Topic :: System :: Operating System", "Topic :: System :: Systems Administration", "Topic :: Utilities"], "extensions": {"python.details": {"contacts": [{"email": "g.rodola@gmail.com", "name": "Giampaolo Rodola", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/giampaolo/psutil"}}}, "extras": ["enum"], "generator": "bdist_wheel (0.30.0)", "keywords": ["ps", "top", "kill", "free", "lsof", "netstat", "nice", "tty", "ionice", "uptime", "taskmgr", "process", "df", "iotop", "iostat", "ifconfig", "taskset", "who", "pidof", "pmap", "smem", "pstree", "monitoring", "ulimit", "prlimit", "smem"], "license": "BSD", "metadata_version": "2.0", "name": "psutil", "platform": "Platform Independent", "run_requires": [{"extra": "enum", "requires": ["enum34"]}], "summary": "Cross-platform lib for process and system monitoring in Python.", "test_requires": [{"requires": ["ipaddress", "mock"]}], "version": "5.4.3"} \ No newline at end of file diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/LICENSE b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/LICENSE new file mode 100644 index 000000000000..0bf4a7fc04a1 --- /dev/null +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola' +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the psutil authors nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/METADATA b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/METADATA similarity index 65% rename from third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/METADATA rename to third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/METADATA index d20907aa5524..18b1e9280530 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/METADATA +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/METADATA @@ -1,12 +1,12 @@ -Metadata-Version: 2.0 +Metadata-Version: 2.1 Name: psutil -Version: 5.4.3 +Version: 5.7.0 Summary: Cross-platform lib for process and system monitoring in Python. Home-page: https://github.com/giampaolo/psutil Author: Giampaolo Rodola Author-email: g.rodola@gmail.com License: BSD -Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem +Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability Platform: Platform Independent Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console @@ -16,9 +16,16 @@ Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 +Classifier: Operating System :: Microsoft :: Windows :: Windows 10 +Classifier: Operating System :: Microsoft :: Windows :: Windows 7 +Classifier: Operating System :: Microsoft :: Windows :: Windows 8 +Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1 +Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003 +Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008 +Classifier: Operating System :: Microsoft :: Windows :: Windows Vista Classifier: Operating System :: Microsoft Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX :: AIX Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: BSD :: NetBSD Classifier: Operating System :: POSIX :: BSD :: OpenBSD @@ -31,67 +38,108 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Benchmark +Classifier: Topic :: System :: Hardware :: Hardware Drivers Classifier: Topic :: System :: Hardware Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog Classifier: Topic :: System :: Networking :: Monitoring Classifier: Topic :: System :: Networking Classifier: Topic :: System :: Operating System Classifier: Topic :: System :: Systems Administration Classifier: Topic :: Utilities +Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* Provides-Extra: enum -Requires-Dist: enum34; extra == 'enum' +Requires-Dist: enum34 ; extra == 'enum' -.. image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux%20/%20OSX +| |downloads| |stars| |forks| |contributors| |coverage| |quality| +| |version| |py-versions| |packages| |license| +| |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift| + +.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg + :target: https://pepy.tech/project/psutil + :alt: Downloads + +.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/stargazers + :alt: Github stars + +.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/network/members + :alt: Github forks + +.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg + :target: https://github.com/giampaolo/psutil/graphs/contributors + :alt: Contributors + +.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg + :target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade + :alt: Code quality + +.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy :target: https://travis-ci.org/giampaolo/psutil :alt: Linux tests (Travis) -.. image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows +.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows :target: https://ci.appveyor.com/project/giampaolo/psutil :alt: Windows tests (Appveyor) -.. image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master +.. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD + :target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci + :alt: FreeBSD tests (Cirrus-Ci) + +.. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage :target: https://coveralls.io/github/giampaolo/psutil?branch=master :alt: Test coverage (coverall.io) -.. image:: https://readthedocs.org/projects/psutil/badge/?version=latest +.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest :target: http://psutil.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -.. image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi - :target: https://pypi.python.org/pypi/psutil/ +.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi + :target: https://pypi.org/project/psutil :alt: Latest version -.. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg - :target: https://github.com/giampaolo/psutil/ - :alt: Github stars +.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg + :target: https://pypi.org/project/psutil + :alt: Supported Python versions -.. image:: https://img.shields.io/pypi/l/psutil.svg - :target: https://pypi.python.org/pypi/psutil/ +.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg + :target: https://repology.org/metapackage/python:psutil/versions + :alt: Binary packages + +.. |license| image:: https://img.shields.io/pypi/l/psutil.svg + :target: https://github.com/giampaolo/psutil/blob/master/LICENSE :alt: License -=========== +.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF + :target: https://twitter.com/grodola + :alt: Twitter Follow + +.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat + :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + :alt: Tidelift + +----- + Quick links =========== - `Home page `_ - `Install `_ - `Documentation `_ -- `Download `_ +- `Download `_ - `Forum `_ +- `StackOverflow `_ - `Blog `_ -- `Development guide `_ +- `Development guide `_ - `What's new `_ -======= Summary ======= @@ -100,23 +148,48 @@ retrieving information on **running processes** and **system utilization** (CPU, memory, disks, network, sensors) in Python. It is useful mainly for **system monitoring**, **profiling and limiting process resources** and **management of running processes**. -It implements many functionalities offered by UNIX command line tools such as: -ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, ionice, iostat, -iotop, uptime, pidof, tty, taskset, pmap. +It implements many functionalities offered by classic UNIX command line tools +such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others. psutil currently supports the following platforms: - **Linux** - **Windows** -- **OSX**, +- **macOS** - **FreeBSD, OpenBSD**, **NetBSD** - **Sun Solaris** - **AIX** -...both **32-bit** and **64-bit** architectures, with Python -versions from **2.6 to 3.6**. -`PyPy `__ is also known to work. +...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 `__ is also known to work. + +psutil for enterprise +===================== + +.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 150 + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 150 + + * - |tideliftlogo| + - The maintainer of psutil and thousands of other packages are working + with Tidelift to deliver commercial support and maintenance for the open + source dependencies you use to build your applications. Save time, + reduce risk, and improve code health, while paying the maintainers of + the exact dependencies you use. + `Learn more `__. + + By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support + psutil future development. Alternatively consider making a small + `donation`_. + +Security +======== + +To report a security vulnerability, please use the `Tidelift security +contact`_. Tidelift will coordinate the fix and disclosure. -==================== Example applications ==================== @@ -131,45 +204,49 @@ Example applications Also see `scripts directory `__ and `doc recipes `__. -===================== Projects using psutil ===================== -At the time of writing psutil has roughly -`2.9 milion downloads `__ -per month and there are over -`6000 open source projects `__ +psutil has roughly the following monthly downloads: + +.. image:: https://img.shields.io/pypi/dm/psutil.svg + :target: https://pepy.tech/project/psutil + :alt: Downloads + +There are over +`10.000 open source projects `__ on github which depend from psutil. Here's some I find particularly interesting: +- https://github.com/google/grr - https://github.com/facebook/osquery/ - https://github.com/nicolargo/glances -- https://github.com/google/grr - https://github.com/Jahaja/psdash - https://github.com/ajenti/ajenti - https://github.com/home-assistant/home-assistant/ -======== + Portings ======== - Go: https://github.com/shirou/gopsutil - C: https://github.com/hamon-in/cpslib -- Node: https://github.com/christkv/node-psutil - Rust: https://github.com/borntyping/rust-psutil -- Ruby: https://github.com/spacewander/posixpsutil - Nim: https://github.com/johnscillieri/psutil-nim -============== + Example usages ============== +This represents pretty much the whole psutil API. + CPU -=== +--- .. code-block:: python >>> import psutil + >>> >>> psutil.cpu_times() scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0) >>> @@ -205,13 +282,14 @@ CPU >>> psutil.cpu_freq() scpufreq(current=931.42925, min=800.0, max=3500.0) >>> + >>> psutil.getloadavg() # also on Windows (emulated) + (3.14, 3.89, 4.67) Memory -====== +------ .. code-block:: python - >>> import psutil >>> psutil.virtual_memory() svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304) >>> psutil.swap_memory() @@ -219,11 +297,10 @@ Memory >>> Disks -===== +----- .. code-block:: python - >>> import psutil >>> psutil.disk_partitions() [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'), sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')] @@ -236,11 +313,10 @@ Disks >>> Network -======= +------- .. code-block:: python - >>> import psutil >>> psutil.net_io_counters(pernic=True) {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0), 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)} @@ -248,25 +324,23 @@ Network >>> psutil.net_connections() [sconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED', pid=1254), sconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING', pid=2987), - sconn(fd=-1, family=, type=, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED', pid=None), - sconn(fd=-1, family=, type=, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT', pid=None) ...] >>> >>> psutil.net_if_addrs() - {'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), - snic(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), - snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], - 'wlan0': [snic(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), - snic(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), - snic(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} + {'lo': [snicaddr(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None), + snicaddr(family=, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), + snicaddr(family=, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)], + 'wlan0': [snicaddr(family=, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None), + snicaddr(family=, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None), + snicaddr(family=, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]} >>> >>> psutil.net_if_stats() - {'eth0': snicstats(isup=True, duplex=, speed=100, mtu=1500), - 'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536)} + {'lo': snicstats(isup=True, duplex=, speed=0, mtu=65536), + 'wlan0': snicstats(isup=True, duplex=, speed=100, mtu=1500)} >>> Sensors -======= +------- .. code-block:: python @@ -275,10 +349,7 @@ Sensors {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)], 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)], 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0), - shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0), - shwtemp(label='Core 1', current=52.0, high=100.0, critical=100.0), - shwtemp(label='Core 2', current=45.0, high=100.0, critical=100.0), - shwtemp(label='Core 3', current=47.0, high=100.0, critical=100.0)]} + shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0)]} >>> >>> psutil.sensors_fans() {'asus': [sfan(label='cpu_fan', current=3200)]} @@ -288,7 +359,7 @@ Sensors >>> Other system info -================= +----------------- .. code-block:: python @@ -302,7 +373,7 @@ Other system info >>> Process management -================== +------------------ .. code-block:: python @@ -314,6 +385,8 @@ Process management 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071] >>> >>> p = psutil.Process(7055) + >>> p + psutil.Process(pid=7055, name='python', started='09:04:44') >>> p.name() 'python' >>> p.exe() @@ -327,11 +400,16 @@ Process management 7055 >>> p.ppid() 7054 + >>> p.children(recursive=True) + [psutil.Process(pid=29835, name='python2.7', started='11:45:38'), + psutil.Process(pid=29836, name='python2.7', started='11:43:39')] + >>> >>> p.parent() - - >>> p.children() - [, - ] + psutil.Process(pid=4699, name='bash', started='09:06:44') + >>> p.parents() + [psutil.Process(pid=4699, name='bash', started='09:06:44'), + psutil.Process(pid=4689, name='gnome-terminal-server', started='0:06:44'), + psutil.Process(pid=1, name='systemd', started='05:56:55')] >>> >>> p.status() 'running' @@ -348,7 +426,7 @@ Process management pgids(real=1000, effective=1000, saved=1000) >>> >>> p.cpu_times() - pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1) + pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0) >>> p.cpu_percent(interval=1.0) 12.1 >>> p.cpu_affinity() @@ -359,14 +437,13 @@ Process management >>> >>> p.memory_info() pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0) - >>> p.memory_full_info() # "real" USS memory usage (Linux, OSX, Win only) + >>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only) pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0) >>> p.memory_percent() 0.7823 >>> p.memory_maps() [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0), pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0), - pmmap_grouped(path='/lib/x8664-linux-gnu/libcrypto.so.0.1', rss=34124, rss=32768, size=2134016, pss=15360, shared_clean=24576, shared_dirty=0, private_clean=0, private_dirty=8192, referenced=24576, anonymous=8192, swap=0), 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), ...] @@ -375,14 +452,12 @@ Process management pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543) >>> >>> 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/monit.py', fd=3, position=0, mode='r', flags=32768), + popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)] >>> >>> p.connections() [pconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'), - pconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING'), - pconn(fd=119, family=, type=, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED'), - pconn(fd=123, family=, type=, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT')] + pconn(fd=117, family=, type=, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING')] >>> >>> p.num_threads() 4 @@ -390,8 +465,6 @@ Process management 8 >>> p.threads() [pthread(id=5234, user_time=22.5, system_time=9.2891), - pthread(id=5235, user_time=0.0, system_time=0.0), - pthread(id=5236, user_time=0.0, system_time=0.0), pthread(id=5237, user_time=0.0707, system_time=1.1)] >>> >>> p.num_ctx_switches() @@ -411,7 +484,7 @@ Process management >>> >>> p.environ() {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto', - 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', 'COLORTERM': 'gnome-terminal', + 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', ...} >>> >>> p.as_dict() @@ -422,6 +495,7 @@ Process management >>> p.resume() >>> >>> p.terminate() + >>> p.kill() >>> p.wait(timeout=3) 0 >>> @@ -429,7 +503,6 @@ Process management USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND root 1 0.0 0.0 24584 2240 Jun17 00:00 init root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd - root 3 0.0 0.0 0 0 Jun17 00:05 ksoftirqd/0 ... giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4 giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome @@ -437,12 +510,12 @@ Process management >>> Further process APIs -==================== +-------------------- .. code-block:: python >>> import psutil - >>> for proc in psutil.process_iter(attrs=['pid', 'name']): + >>> for proc in psutil.process_iter(['pid', 'name']): ... print(proc.info) ... {'pid': 1, 'name': 'systemd'} @@ -478,7 +551,7 @@ Popen wrapper: >>> Windows services -================ +---------------- .. code-block:: python @@ -499,26 +572,11 @@ Windows services 'status': 'stopped', 'username': 'NT AUTHORITY\\LocalService'} -Other samples -============= -See `doc recipes `__. - -====== -Author -====== - -psutil was created and is maintained by -`Giampaolo Rodola' `__. -A lot of time and effort went into making psutil as it is right now. -If you feel psutil is useful to you or your business and want to support its -future development please consider donating me -(`Giampaolo `__) some money. - -.. image:: http://www.paypal.com/en_US/i/btn/x-click-but04.gif - :target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 - :alt: Donate via PayPal - -Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin `_. +.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html +.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 +.. _Tidelift security contact: https://tidelift.com/security +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/RECORD b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/RECORD new file mode 100644 index 000000000000..90a918ccf197 --- /dev/null +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/RECORD @@ -0,0 +1,33 @@ +psutil/__init__.py,sha256=gkuk8_HMEBXTFl0y4l8A5QGNCjUH1T0tpF_Y8hPJCPA,87126 +psutil/_common.py,sha256=6kuDg4DKKv3OGndSXLQ3LftF_hPySIEOprC8HE49eag,26001 +psutil/_compat.py,sha256=lrMSZr32TO6y9FQw7C2xTNyKwAnPU2o11xQrA_fS_Qw,11607 +psutil/_psaix.py,sha256=u9M_jJqkiGqNgdhum9MZTbnS4pjIfdCHNr3HAYHA0GQ,18458 +psutil/_psbsd.py,sha256=LHfBJ-iKy4fuumIcSjO7b6VFOvU_d-Pzwp7Eg84rHSw,30534 +psutil/_pslinux.py,sha256=WtQtqOAhXd-zqtVPV-E0re91Vqj4DYrKQ3Mj9CfBWak,79806 +psutil/_psosx.py,sha256=kvG3GKxA-L581H_UgU_BYaZ3eMPX7qMtEBM7AGC-t10,17090 +psutil/_psposix.py,sha256=FtbNSosrJiiUb9l0gfFdTwNxUwQb6Icy4L3si0x6ZMA,6047 +psutil/_pssunos.py,sha256=c-0et4xslK83ZiTizQ9W3LH0pMZy11ATEIBbQBJ_v-g,25398 +psutil/_psutil_windows.pyd,sha256=jERIIv4AR_vfAvx8oOqkyv41JBPuMskRgnM1O8KdokE,62976 +psutil/_pswindows.py,sha256=iFcfbA5KZehjdZfe1b-cLGRdavGNryI4Zxpkqb12UjU,36839 +psutil/tests/__init__.py,sha256=uH3lPOnYc6179FxdSy4xfMeJOBvB9CZid268Pg_owC0,35141 +psutil/tests/__main__.py,sha256=bBfMPu_gPzyiA4gfAFSZLk7LyLSPTUIHQFU1kFcj_Ok,291 +psutil/tests/runner.py,sha256=l7BrTzIOdjPgNB3D3kiir8jBiK_QJKMlJSYu4Zr0_po,5188 +psutil/tests/test_aix.py,sha256=LA8PR-1vx5DN2OkjU57MsO8uzNOB0y1ScR1yx5bZJyc,4464 +psutil/tests/test_bsd.py,sha256=3zlUvefRm2fFTbyLrqM9u8NMK8CThLT7irioTGgnNts,20612 +psutil/tests/test_connections.py,sha256=U2KCl7eW6kRds8S7t8SmZ62OrVSPfdBuksHsDlk2C28,25293 +psutil/tests/test_contracts.py,sha256=BOKrRiZs625Xj3KZBzOZ8Kr6edAQgzHq0OlznoCxKzw,25355 +psutil/tests/test_linux.py,sha256=KvqJ-LiINvbQQ3y18uM2SZRKnpnH4PqlA7CqWLRIH5s,87573 +psutil/tests/test_memory_leaks.py,sha256=M5nWBv_PYfN7bIoaz6r0UqEYY79hO6wvvDfkKWFMETI,18499 +psutil/tests/test_misc.py,sha256=wqXpnXL2ycHyE1vu3GB43XTET5auTKz9VdBSjr-vdp0,38748 +psutil/tests/test_osx.py,sha256=oQIO0YReUtH0PFFAwyk8FpUqvjIiuhVPehMFS1AWJpM,9257 +psutil/tests/test_posix.py,sha256=fSi5J1LxPhA7ZocrnLnqCSBn6JUuOtBeAljBWughIf4,16537 +psutil/tests/test_process.py,sha256=JJB9VdAwKFYK-rlpvWn1NyeYasmr0nL_XetzFHHjaJU,63924 +psutil/tests/test_sunos.py,sha256=GwqWfAP46nrxWlJr-srg85cPNEU2DXvoMelAgqegfLA,1294 +psutil/tests/test_system.py,sha256=4nWe_fFRmnaB8epV7Eji_0JFnIJbvbykMgSi8LFzbKM,36162 +psutil/tests/test_unicode.py,sha256=5v1hFoOQoi9_2Wg4ZTIWY_YDd29G00-59ln9FOe8Tsg,13246 +psutil/tests/test_windows.py,sha256=cGjEspgL48Sb5BQeSG5kIU0DSeImPU1bL9cpFfNX2Tk,33666 +psutil-5.7.0.dist-info/LICENSE,sha256=JMEphFAMqgf_3OGe68BjlsXm0kS1c7xsQ49KbvjlbBs,1549 +psutil-5.7.0.dist-info/METADATA,sha256=RmX2_-V4HrxQ1p78U2Os4H5Q0ZFvF5oc0uUTUt9-sRo,25022 +psutil-5.7.0.dist-info/WHEEL,sha256=Yt_rpa18HI_NXs5dEYG1uWuZ-4QRLVJrN8TSetG3BOI,106 +psutil-5.7.0.dist-info/top_level.txt,sha256=gCNhn57wzksDjSAISmgMJ0aiXzQulk0GJhb2-BAyYgw,7 +psutil-5.7.0.dist-info/RECORD,, diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/WHEEL b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/WHEEL similarity index 65% rename from third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/WHEEL rename to third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/WHEEL index 1bbdeb4b3e47..2fff7c01bd13 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/WHEEL +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.30.0) +Generator: bdist_wheel (0.34.2) Root-Is-Purelib: false Tag: cp27-cp27m-win_amd64 diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/top_level.txt b/third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/top_level.txt similarity index 100% rename from third_party/python/psutil-cp27-none-win_amd64/psutil-5.4.3.dist-info/top_level.txt rename to third_party/python/psutil-cp27-none-win_amd64/psutil-5.7.0.dist-info/top_level.txt diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/__init__.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/__init__.py index 5e29a7fc5b6e..22bb46f3f9bd 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/__init__.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/__init__.py @@ -10,14 +10,14 @@ sensors) in Python. Supported platforms: - Linux - Windows - - OSX + - macOS - FreeBSD - OpenBSD - NetBSD - Sun Solaris - AIX -Works with Python versions from 2.6 to 3.X. +Works with Python versions from 2.6 to 3.4+. """ from __future__ import division @@ -25,37 +25,43 @@ from __future__ import division import collections import contextlib import datetime -import errno import functools import os import signal import subprocess import sys +import threading import time -import traceback try: import pwd except ImportError: pwd = None from . import _common +from ._common import AccessDenied from ._common import deprecated_method +from ._common import Error from ._common import memoize from ._common import memoize_when_activated +from ._common import NoSuchProcess +from ._common import TimeoutExpired from ._common import wrap_numbers as _wrap_numbers -from ._compat import callable +from ._common import ZombieProcess from ._compat import long +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 as _PY3 from ._common import STATUS_DEAD from ._common import STATUS_DISK_SLEEP -from ._common import STATUS_IDLE # bsd +from ._common import STATUS_IDLE from ._common import STATUS_LOCKED +from ._common import STATUS_PARKED from ._common import STATUS_RUNNING from ._common import STATUS_SLEEPING from ._common import STATUS_STOPPED from ._common import STATUS_TRACING_STOP -from ._common import STATUS_WAITING # bsd +from ._common import STATUS_WAITING from ._common import STATUS_WAKING from ._common import STATUS_ZOMBIE @@ -79,19 +85,14 @@ from ._common import AIX from ._common import BSD from ._common import FREEBSD # NOQA from ._common import LINUX +from ._common import MACOS from ._common import NETBSD # NOQA from ._common import OPENBSD # NOQA -from ._common import OSX +from ._common import OSX # deprecated alias from ._common import POSIX # NOQA from ._common import SUNOS from ._common import WINDOWS -from ._exceptions import AccessDenied -from ._exceptions import Error -from ._exceptions import NoSuchProcess -from ._exceptions import TimeoutExpired -from ._exceptions import ZombieProcess - if LINUX: # This is public API and it will be retrieved from _pslinux.py # via sys.modules. @@ -151,8 +152,12 @@ elif WINDOWS: from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA from ._pswindows import CONN_DELETE_TCB # NOQA + from ._pswindows import IOPRIO_VERYLOW # NOQA + from ._pswindows import IOPRIO_LOW # NOQA + from ._pswindows import IOPRIO_NORMAL # NOQA + from ._pswindows import IOPRIO_HIGH # NOQA -elif OSX: +elif MACOS: from . import _psosx as _psplatform elif BSD: @@ -189,6 +194,7 @@ __all__ = [ "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED", + "STATUS_PARKED", "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", @@ -200,8 +206,8 @@ __all__ = [ "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", - "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS", - "WINDOWS", "AIX", + "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX", + "SUNOS", "WINDOWS", "AIX", # classes "Process", "Popen", @@ -210,23 +216,26 @@ __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_freq", + "cpu_stats", # "cpu_freq", "getloadavg" "net_io_counters", "net_connections", "net_if_addrs", # network "net_if_stats", "disk_io_counters", "disk_partitions", "disk_usage", # disk # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors "users", "boot_time", # others ] + + __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "5.4.3" +__version__ = "5.7.0" version_info = tuple([int(num) for num in __version__.split('.')]) + +_timer = getattr(time, 'monotonic', time.time) AF_LINK = _psplatform.AF_LINK POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED POWER_TIME_UNKNOWN = _common.POWER_TIME_UNKNOWN _TOTAL_PHYMEM = None -_timer = getattr(time, 'monotonic', time.time) - +_LOWEST_PID = None # Sanity check in case the user messed up with psutil installation # or did something weird with sys.path. In this case we might end @@ -266,13 +275,9 @@ else: ret = {} for pid in pids(): try: - proc = _psplatform.Process(pid) - ppid = proc.ppid() - except (NoSuchProcess, AccessDenied): - # Note: AccessDenied is unlikely to happen. + ret[pid] = _psplatform.Process(pid).ppid() + except (NoSuchProcess, ZombieProcess): pass - else: - ret[pid] = ppid return ret @@ -355,7 +360,7 @@ class Process(object): self._create_time = None self._gone = False self._hash = None - self._oneshot_inctx = False + self._lock = threading.RLock() # used for caching on Windows only (on POSIX ppid may change) self._ppid = None # platform-specific modules define an _psplatform.Process @@ -459,40 +464,45 @@ class Process(object): ... >>> """ - if self._oneshot_inctx: - # NOOP: this covers the use case where the user enters the - # context twice. Since as_dict() internally uses oneshot() - # I expect that the code below will be a pretty common - # "mistake" that the user will make, so let's guard - # against that: - # - # >>> with p.oneshot(): - # ... p.as_dict() - # ... - yield - else: - self._oneshot_inctx = True - try: - # cached in case cpu_percent() is used - self.cpu_times.cache_activate() - # cached in case memory_percent() is used - self.memory_info.cache_activate() - # cached in case parent() is used - self.ppid.cache_activate() - # cached in case username() is used - if POSIX: - self.uids.cache_activate() - # specific implementation cache - self._proc.oneshot_enter() + with self._lock: + if hasattr(self, "_cache"): + # NOOP: this covers the use case where the user enters the + # context twice: + # + # >>> with p.oneshot(): + # ... with p.oneshot(): + # ... + # + # Also, since as_dict() internally uses oneshot() + # I expect that the code below will be a pretty common + # "mistake" that the user will make, so let's guard + # against that: + # + # >>> with p.oneshot(): + # ... p.as_dict() + # ... yield - finally: - self.cpu_times.cache_deactivate() - self.memory_info.cache_deactivate() - self.ppid.cache_deactivate() - if POSIX: - self.uids.cache_deactivate() - self._proc.oneshot_exit() - self._oneshot_inctx = False + else: + try: + # cached in case cpu_percent() is used + self.cpu_times.cache_activate(self) + # cached in case memory_percent() is used + self.memory_info.cache_activate(self) + # cached in case parent() is used + self.ppid.cache_activate(self) + # cached in case username() is used + if POSIX: + self.uids.cache_activate(self) + # specific implementation cache + self._proc.oneshot_enter() + yield + finally: + self.cpu_times.cache_deactivate(self) + self.memory_info.cache_deactivate(self) + self.ppid.cache_deactivate(self) + if POSIX: + self.uids.cache_deactivate(self) + self._proc.oneshot_exit() def as_dict(self, attrs=None, ad_value=None): """Utility method returning process information as a @@ -543,6 +553,9 @@ class Process(object): checking whether PID has been reused. If no parent is known return None. """ + lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0] + if self.pid == lowest_pid: + return None ppid = self.ppid() if ppid is not None: ctime = self.create_time() @@ -554,6 +567,17 @@ class Process(object): except NoSuchProcess: pass + def parents(self): + """Return the parents of this process as a list of Process + instances. If no parents are known return an empty list. + """ + parents = [] + proc = self.parent() + while proc is not None: + parents.append(proc) + proc = proc.parent() + return parents + def is_running(self): """Return whether this process is running. It also checks if PID has been reused by another process in @@ -751,7 +775,7 @@ class Process(object): """ return self._proc.io_counters() - # Linux and Windows >= Vista only + # Linux and Windows if hasattr(_psplatform.Process, "ionice_get"): def ionice(self, ioclass=None, value=None): @@ -802,9 +826,6 @@ class Process(object): (and set). (Windows, Linux and BSD only). """ - # Automatically remove duplicates both on get and - # set (for get it's not really necessary, it's - # just for extra safety). if cpus is None: return list(set(self._proc.cpu_affinity_get())) else: @@ -828,7 +849,7 @@ class Process(object): """ return self._proc.cpu_num() - # Linux, OSX and Windows only + # Linux, macOS, Windows, Solaris, AIX if hasattr(_psplatform.Process, "environ"): def environ(self): @@ -1032,7 +1053,7 @@ class Process(object): namedtuple representing the accumulated process time, in seconds. This is similar to os.times() but per-process. - On OSX and Windows children_user and children_system are + On macOS and Windows children_user and children_system are always set to 0. """ return self._proc.cpu_times() @@ -1054,7 +1075,7 @@ class Process(object): def memory_full_info(self): """This method returns the same information as memory_info(), - plus, on some platform (Linux, OSX, Windows), also provides + plus, on some platform (Linux, macOS, Windows), also provides additional metrics (USS, PSS and swap). The additional metrics provide a better representation of actual process memory usage. @@ -1098,7 +1119,6 @@ class Process(object): return (value / float(total_phymem)) * 100 if hasattr(_psplatform.Process, "memory_maps"): - # Available everywhere except OpenBSD and NetBSD. def memory_maps(self, grouped=True): """Return process' mapped memory regions as a list of namedtuples whose fields are variable depending on the platform. @@ -1170,18 +1190,16 @@ class Process(object): "calling process (os.getpid()) instead of PID 0") try: os.kill(self.pid, sig) - except OSError as err: - if err.errno == errno.ESRCH: - if OPENBSD and pid_exists(self.pid): - # We do this because os.kill() lies in case of - # zombie processes. - raise ZombieProcess(self.pid, self._name, self._ppid) - else: - self._gone = True - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + except ProcessLookupError: + if OPENBSD and pid_exists(self.pid): + # We do this because os.kill() lies in case of + # zombie processes. + raise ZombieProcess(self.pid, self._name, self._ppid) + else: + self._gone = True + raise NoSuchProcess(self.pid, self._name) + except PermissionError: + raise AccessDenied(self.pid, self._name) @_assert_pid_not_reused def send_signal(self, sig): @@ -1193,16 +1211,7 @@ class Process(object): if POSIX: self._send_signal(sig) else: # pragma: no cover - if sig == signal.SIGTERM: - self._proc.kill() - # py >= 2.7 - elif sig in (getattr(signal, "CTRL_C_EVENT", object()), - getattr(signal, "CTRL_BREAK_EVENT", object())): - self._proc.send_signal(sig) - else: - raise ValueError( - "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " - "are supported on Windows") + self._proc.send_signal(sig) @_assert_pid_not_reused def suspend(self): @@ -1250,6 +1259,8 @@ class Process(object): def wait(self, timeout=None): """Wait for process to terminate and, if process is a children of os.getpid(), also return its exit code, else None. + On Windows there's no such limitation (exit code is always + returned). If the process is already terminated immediately return None instead of raising NoSuchProcess. @@ -1301,7 +1312,7 @@ class Popen(Process): http://bugs.python.org/issue6973. For a complete documentation refer to: - http://docs.python.org/library/subprocess.html + http://docs.python.org/3/library/subprocess.html """ def __init__(self, *args, **kwargs): @@ -1357,7 +1368,7 @@ class Popen(Process): _as_dict_attrnames = set( [x for x in dir(Process) if not x.startswith('_') and x not in ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', - 'is_running', 'as_dict', 'parent', 'children', 'rlimit', + 'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit', 'memory_info_ex', 'oneshot']]) @@ -1368,7 +1379,10 @@ _as_dict_attrnames = set( def pids(): """Return a list of current running PIDs.""" - return _psplatform.pids() + global _LOWEST_PID + ret = sorted(_psplatform.pids()) + _LOWEST_PID = ret[0] + return ret def pid_exists(pid): @@ -1390,6 +1404,7 @@ def pid_exists(pid): _pmap = {} +_lock = threading.Lock() def process_iter(attrs=None, ad_value=None): @@ -1417,21 +1432,26 @@ def process_iter(attrs=None, ad_value=None): proc = Process(pid) if attrs is not None: proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value) - _pmap[proc.pid] = proc + with _lock: + _pmap[proc.pid] = proc return proc def remove(pid): - _pmap.pop(pid, None) + with _lock: + _pmap.pop(pid, None) a = set(pids()) b = set(_pmap.keys()) new_pids = a - b gone_pids = b - a - for pid in gone_pids: remove(pid) - for pid, proc in sorted(list(_pmap.items()) + - list(dict.fromkeys(new_pids).items())): + + with _lock: + ls = sorted(list(_pmap.items()) + + list(dict.fromkeys(new_pids).items())) + + for pid, proc in ls: try: if proc is None: # new process yield add(pid) @@ -1505,6 +1525,7 @@ def wait_procs(procs, timeout=None, callback=None): pass else: if returncode is not None or not proc.is_running(): + # Set new Process instance attribute. proc.returncode = returncode gone.add(proc) if callback is not None: @@ -1611,14 +1632,12 @@ try: except Exception: # Don't want to crash at import time. _last_cpu_times = None - traceback.print_exc() try: _last_per_cpu_times = cpu_times(percpu=True) except Exception: # Don't want to crash at import time. _last_per_cpu_times = None - traceback.print_exc() def _cpu_tot_time(times): @@ -1656,6 +1675,27 @@ def _cpu_busy_time(times): return busy +def _cpu_times_deltas(t1, t2): + assert t1._fields == t2._fields, (t1, t2) + field_deltas = [] + for field in _psplatform.scputimes._fields: + field_delta = getattr(t2, field) - getattr(t1, field) + # CPU times are always supposed to increase over time + # or at least remain the same and that's because time + # cannot go backwards. + # Surprisingly sometimes this might not be the case (at + # least on Windows and Linux), see: + # https://github.com/giampaolo/psutil/issues/392 + # https://github.com/giampaolo/psutil/issues/645 + # https://github.com/giampaolo/psutil/issues/1210 + # Trim negative deltas to zero to ignore decreasing fields. + # top does the same. Reference: + # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063 + field_delta = max(0, field_delta) + field_deltas.append(field_delta) + return _psplatform.scputimes(*field_deltas) + + def cpu_percent(interval=None, percpu=False): """Return a float representing the current system-wide CPU utilization as a percentage. @@ -1698,18 +1738,11 @@ def cpu_percent(interval=None, percpu=False): raise ValueError("interval is not positive (got %r)" % interval) def calculate(t1, t2): - t1_all = _cpu_tot_time(t1) - t1_busy = _cpu_busy_time(t1) + times_delta = _cpu_times_deltas(t1, t2) - t2_all = _cpu_tot_time(t2) - t2_busy = _cpu_busy_time(t2) + all_delta = _cpu_tot_time(times_delta) + busy_delta = _cpu_busy_time(times_delta) - # this usually indicates a float precision issue - if t2_busy <= t1_busy: - return 0.0 - - busy_delta = t2_busy - t1_busy - all_delta = t2_all - t1_all try: busy_perc = (busy_delta / all_delta) * 100 except ZeroDivisionError: @@ -1778,28 +1811,18 @@ def cpu_times_percent(interval=None, percpu=False): def calculate(t1, t2): nums = [] - all_delta = _cpu_tot_time(t2) - _cpu_tot_time(t1) - for field in t1._fields: - field_delta = getattr(t2, field) - getattr(t1, field) - try: - field_perc = (100 * field_delta) / all_delta - except ZeroDivisionError: - field_perc = 0.0 + times_delta = _cpu_times_deltas(t1, t2) + all_delta = _cpu_tot_time(times_delta) + # "scale" is the value to multiply each delta with to get percentages. + # We use "max" to avoid division by zero (if all_delta is 0, then all + # fields are 0 so percentages will be 0 too. all_delta cannot be a + # fraction because cpu times are integers) + scale = 100.0 / max(1, all_delta) + for field_delta in times_delta: + field_perc = field_delta * scale field_perc = round(field_perc, 1) - # CPU times are always supposed to increase over time - # or at least remain the same and that's because time - # cannot go backwards. - # Surprisingly sometimes this might not be the case (at - # least on Windows and Linux), see: - # https://github.com/giampaolo/psutil/issues/392 - # https://github.com/giampaolo/psutil/issues/645 - # I really don't know what to do about that except - # forcing the value to 0 or 100. - if field_perc > 100.0: - field_perc = 100.0 - # `<=` because `-0.0 == 0.0` evaluates to True - elif field_perc <= 0.0: - field_perc = 0.0 + # make sure we don't return negative values or values over 100% + field_perc = min(max(0.0, field_perc), 100.0) nums.append(field_perc) return _psplatform.scputimes(*nums) @@ -1862,18 +1885,41 @@ if hasattr(_psplatform, "cpu_freq"): return ret[0] else: currs, mins, maxs = 0.0, 0.0, 0.0 + set_none = False for cpu in ret: currs += cpu.current + # On Linux if /proc/cpuinfo is used min/max are set + # to None. + if LINUX and cpu.min is None: + set_none = True + continue mins += cpu.min maxs += cpu.max + current = currs / num_cpus - min_ = mins / num_cpus - max_ = maxs / num_cpus + + if set_none: + min_ = max_ = None + else: + min_ = mins / num_cpus + max_ = maxs / num_cpus + return _common.scpufreq(current, min_, max_) __all__.append("cpu_freq") +if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"): + # Perform this hasattr check once on import time to either use the + # platform based code or proxy straight from the os module. + if hasattr(os, "getloadavg"): + getloadavg = os.getloadavg + else: + getloadavg = _psplatform.getloadavg + + __all__.append("getloadavg") + + # ===================================================================== # --- system memory related functions # ===================================================================== @@ -1899,9 +1945,9 @@ def virtual_memory(): - used: memory used, calculated differently depending on the platform and designed for informational purposes only: - OSX: active + inactive + wired + macOS: active + wired BSD: active + wired + cached - LINUX: total - free + Linux: total - free - free: memory not being used at all (zeroed) that is readily available; @@ -1919,10 +1965,10 @@ def virtual_memory(): - buffers (BSD, Linux): cache for things like file system metadata. - - cached (BSD, OSX): + - cached (BSD, macOS): cache for various things. - - wired (OSX, BSD): + - wired (macOS, BSD): memory that is marked to always stay in RAM. It is never moved to disk. - shared (BSD): @@ -2011,7 +2057,8 @@ def disk_io_counters(perdisk=False, nowrap=True): On recent Windows versions 'diskperf -y' command may need to be executed first otherwise this function won't find any disk. """ - rawdict = _psplatform.disk_io_counters() + kwargs = dict(perdisk=perdisk) if LINUX else {} + rawdict = _psplatform.disk_io_counters(**kwargs) if not rawdict: return {} if perdisk else None if nowrap: @@ -2047,7 +2094,7 @@ def net_io_counters(pernic=False, nowrap=True): - errout: total number of errors while sending - dropin: total number of incoming packets which were dropped - dropout: total number of outgoing packets which were dropped - (always 0 on OSX and BSD) + (always 0 on macOS and BSD) If *pernic* is True return the same information for every network interface installed on the system as a dictionary @@ -2103,7 +2150,7 @@ def net_connections(kind='inet'): | all | the sum of all the possible families and protocols | +------------+----------------------------------------------------+ - On OSX this function requires root privileges. + On macOS this function requires root privileges. """ return _psplatform.net_connections(kind) @@ -2152,7 +2199,7 @@ def net_if_addrs(): separator = ":" if POSIX else "-" while addr.count(separator) < 5: addr += "%s00" % separator - ret[name].append(_common.snic(fam, addr, mask, broadcast, ptp)) + ret[name].append(_common.snicaddr(fam, addr, mask, broadcast, ptp)) return dict(ret) @@ -2176,7 +2223,7 @@ def net_if_stats(): # ===================================================================== -# Linux +# Linux, macOS if hasattr(_psplatform, "sensors_temperatures"): def sensors_temperatures(fahrenheit=False): @@ -2214,7 +2261,7 @@ if hasattr(_psplatform, "sensors_temperatures"): __all__.append("sensors_temperatures") -# Linux +# Linux, macOS if hasattr(_psplatform, "sensors_fans"): def sensors_fans(): @@ -2227,7 +2274,7 @@ if hasattr(_psplatform, "sensors_fans"): __all__.append("sensors_fans") -# Linux, Windows, FreeBSD, OSX +# Linux, Windows, FreeBSD, macOS if hasattr(_psplatform, "sensors_battery"): def sensors_battery(): @@ -2294,19 +2341,16 @@ if WINDOWS: def test(): # pragma: no cover - """List info of all currently running processes emulating ps aux - output. - """ + from ._common import bytes2human + from ._compat import get_terminal_size + today_day = datetime.date.today() - templ = "%-10s %5s %4s %7s %7s %-13s %5s %7s %s" - attrs = ['pid', 'memory_percent', 'name', 'cpu_times', 'create_time', - 'memory_info'] - if POSIX: - attrs.append('uids') - attrs.append('terminal') - print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "TTY", "START", "TIME", - "COMMAND")) - for p in process_iter(attrs=attrs, ad_value=''): + templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s %s" + attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times', + 'create_time', 'memory_info', 'status', 'nice', 'username'] + print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE", + "STATUS", "START", "TIME", "CMDLINE")) + for p in process_iter(attrs, ad_value=None): if p.info['create_time']: ctime = datetime.datetime.fromtimestamp(p.info['create_time']) if ctime.date() == today_day: @@ -2315,30 +2359,46 @@ def test(): # pragma: no cover ctime = ctime.strftime("%b%d") else: ctime = '' - cputime = time.strftime("%M:%S", - time.localtime(sum(p.info['cpu_times']))) - try: - user = p.username() - except Error: - user = '' - if WINDOWS and '\\' in user: + if p.info['cpu_times']: + cputime = time.strftime("%M:%S", + time.localtime(sum(p.info['cpu_times']))) + else: + cputime = '' + + user = p.info['username'] or '' + if not user and POSIX: + try: + user = p.uids()[0] + except Error: + pass + if user and WINDOWS and '\\' in user: user = user.split('\\')[1] - vms = p.info['memory_info'] and \ - int(p.info['memory_info'].vms / 1024) or '?' - rss = p.info['memory_info'] and \ - int(p.info['memory_info'].rss / 1024) or '?' - memp = p.info['memory_percent'] and \ - round(p.info['memory_percent'], 1) or '?' - print(templ % ( + user = user[:9] + vms = bytes2human(p.info['memory_info'].vms) if \ + p.info['memory_info'] is not None else '' + rss = bytes2human(p.info['memory_info'].rss) if \ + p.info['memory_info'] is not None else '' + memp = round(p.info['memory_percent'], 1) if \ + p.info['memory_percent'] is not None else '' + nice = int(p.info['nice']) if p.info['nice'] else '' + if p.info['cmdline']: + cmdline = ' '.join(p.info['cmdline']) + else: + cmdline = p.info['name'] + status = p.info['status'][:5] if p.info['status'] else '' + + line = templ % ( user[:10], p.info['pid'], memp, vms, rss, - p.info.get('terminal', '') or '?', + nice, + status, ctime, cputime, - p.info['name'].strip() or '?')) + cmdline) + print(line[:get_terminal_size()[0]]) del memoize, memoize_when_activated, division, deprecated_method diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_common.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_common.py index 870971e415b0..728d9c62585b 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_common.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_common.py @@ -7,7 +7,7 @@ # Note: this module is imported by setup.py so it should not import # psutil or third-party modules. -from __future__ import division +from __future__ import division, print_function import contextlib import errno @@ -23,6 +23,7 @@ from collections import namedtuple from socket import AF_INET from socket import SOCK_DGRAM from socket import SOCK_STREAM + try: from socket import AF_INET6 except ImportError: @@ -37,14 +38,14 @@ if sys.version_info >= (3, 4): else: enum = None + # can't take it from _common.py as this script is imported by setup.py PY3 = sys.version_info[0] == 3 __all__ = [ - # constants - 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'OSX', 'POSIX', 'SUNOS', - 'WINDOWS', - 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', + # OS constants + 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', + 'SUNOS', 'WINDOWS', # connection constants 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', @@ -55,15 +56,20 @@ __all__ = [ 'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED', 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED', 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL', - 'STATUS_WAKING', 'STATUS_ZOMBIE', + 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED', + # other constants + 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', # named tuples 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile', 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart', - 'sdiskusage', 'snetio', 'snic', 'snicstats', 'sswap', 'suser', + 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser', # utility functions 'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize', 'parse_environ_block', 'path_exists_strict', 'usage_percent', 'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers", + 'bytes2human', 'conn_to_ntuple', 'debug', + # shell utils + 'hilite', 'term_supports_colors', 'print_color', ] @@ -75,12 +81,13 @@ __all__ = [ POSIX = os.name == "posix" WINDOWS = os.name == "nt" LINUX = sys.platform.startswith("linux") -OSX = sys.platform.startswith("darwin") +MACOS = sys.platform.startswith("darwin") +OSX = MACOS # deprecated alias FREEBSD = sys.platform.startswith("freebsd") OPENBSD = sys.platform.startswith("openbsd") NETBSD = sys.platform.startswith("netbsd") BSD = FREEBSD or OPENBSD or NETBSD -SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris") +SUNOS = sys.platform.startswith(("sunos", "solaris")) AIX = sys.platform.startswith("aix") @@ -99,10 +106,11 @@ STATUS_ZOMBIE = "zombie" STATUS_DEAD = "dead" STATUS_WAKE_KILL = "wake-kill" STATUS_WAKING = "waking" -STATUS_IDLE = "idle" # FreeBSD, OSX +STATUS_IDLE = "idle" # Linux, macOS, FreeBSD STATUS_LOCKED = "locked" # FreeBSD STATUS_WAITING = "waiting" # FreeBSD STATUS_SUSPENDED = "suspended" # NetBSD +STATUS_PARKED = "parked" # Linux # Process.connections() and psutil.net_connections() CONN_ESTABLISHED = "ESTABLISHED" @@ -182,7 +190,8 @@ suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid']) sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status', 'pid']) # psutil.net_if_addrs() -snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp']) +snicaddr = namedtuple('snicaddr', + ['family', 'address', 'netmask', 'broadcast', 'ptp']) # psutil.net_if_stats() snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu']) # psutil.cpu_stats() @@ -195,7 +204,7 @@ shwtemp = namedtuple( 'shwtemp', ['label', 'current', 'high', 'critical']) # psutil.sensors_battery() sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) -# psutil.sensors_battery() +# psutil.sensors_fans() sfan = namedtuple('sfan', ['label', 'current']) # --- for Process methods @@ -253,7 +262,109 @@ if AF_UNIX is not None: "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), }) -del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM + +# ===================================================================== +# --- Exceptions +# ===================================================================== + + +class Error(Exception): + """Base exception class. All other psutil exceptions inherit + from this one. + """ + __module__ = 'psutil' + + def __init__(self, msg=""): + Exception.__init__(self, msg) + self.msg = msg + + def __repr__(self): + ret = "psutil.%s %s" % (self.__class__.__name__, self.msg) + return ret.strip() + + __str__ = __repr__ + + +class NoSuchProcess(Error): + """Exception raised when a process with a certain PID doesn't + or no longer exists. + """ + __module__ = 'psutil' + + def __init__(self, pid, name=None, msg=None): + Error.__init__(self, msg) + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process no longer exists " + details + + def __path__(self): + return 'xxx' + + +class ZombieProcess(NoSuchProcess): + """Exception raised when querying a zombie process. This is + raised on macOS, BSD and Solaris only, and not always: depending + on the query the OS may be able to succeed anyway. + On Linux all zombie processes are querable (hence this is never + raised). Windows doesn't have zombie processes. + """ + __module__ = 'psutil' + + def __init__(self, pid, name=None, ppid=None, msg=None): + NoSuchProcess.__init__(self, msg) + self.pid = pid + self.ppid = ppid + self.name = name + self.msg = msg + if msg is None: + args = ["pid=%s" % pid] + if name: + args.append("name=%s" % repr(self.name)) + if ppid: + args.append("ppid=%s" % self.ppid) + details = "(%s)" % ", ".join(args) + self.msg = "process still exists but it's a zombie " + details + + +class AccessDenied(Error): + """Exception raised when permission to perform an action is denied.""" + __module__ = 'psutil' + + def __init__(self, pid=None, name=None, msg=None): + Error.__init__(self, msg) + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + +class TimeoutExpired(Error): + """Raised on Process.wait(timeout) if timeout expires and process + is still alive. + """ + __module__ = 'psutil' + + def __init__(self, seconds, pid=None, name=None): + Error.__init__(self, "timeout after %s seconds" % seconds) + self.seconds = seconds + self.pid = pid + self.name = name + if (pid is not None) and (name is not None): + self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg += " (pid=%s)" % self.pid # =================================================================== @@ -261,15 +372,15 @@ del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM # =================================================================== -def usage_percent(used, total, _round=None): +def usage_percent(used, total, round_=None): """Calculate percentage usage of 'used' against 'total'.""" try: - ret = (used / total) * 100 + ret = (float(used) / total) * 100 except ZeroDivisionError: - ret = 0.0 if isinstance(used, float) or isinstance(total, float) else 0 - if _round is not None: - return round(ret, _round) + return 0.0 else: + if round_ is not None: + ret = round(ret, round_) return ret @@ -324,7 +435,7 @@ def memoize_when_activated(fun): 1 >>> >>> # activated - >>> foo.cache_activate() + >>> foo.cache_activate(self) >>> foo() 1 >>> foo() @@ -333,26 +444,30 @@ def memoize_when_activated(fun): """ @functools.wraps(fun) def wrapper(self): - if not wrapper.cache_activated: + try: + # case 1: we previously entered oneshot() ctx + ret = self._cache[fun] + except AttributeError: + # case 2: we never entered oneshot() ctx return fun(self) - else: - try: - ret = cache[fun] - except KeyError: - ret = cache[fun] = fun(self) - return ret + except KeyError: + # case 3: we entered oneshot() ctx but there's no cache + # for this entry yet + ret = self._cache[fun] = fun(self) + return ret - def cache_activate(): - """Activate cache.""" - wrapper.cache_activated = True + def cache_activate(proc): + """Activate cache. Expects a Process instance. Cache will be + stored as a "_cache" instance attribute.""" + proc._cache = {} - def cache_deactivate(): + def cache_deactivate(proc): """Deactivate and clear cache.""" - wrapper.cache_activated = False - cache.clear() + try: + del proc._cache + except AttributeError: + pass - cache = {} - wrapper.cache_activated = False wrapper.cache_activate = cache_activate wrapper.cache_deactivate = cache_deactivate return wrapper @@ -439,7 +554,7 @@ def sockfam_to_enum(num): else: # pragma: no cover try: return socket.AddressFamily(num) - except (ValueError, AttributeError): + except ValueError: return num @@ -451,11 +566,30 @@ def socktype_to_enum(num): return num else: # pragma: no cover try: - return socket.AddressType(num) - except (ValueError, AttributeError): + return socket.SocketKind(num) + except ValueError: return num +def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None): + """Convert a raw connection tuple to a proper ntuple.""" + if fam in (socket.AF_INET, AF_INET6): + if laddr: + laddr = addr(*laddr) + if raddr: + raddr = addr(*raddr) + if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6): + status = status_map.get(status, CONN_NONE) + else: + status = CONN_NONE # ignore whatever C returned to us + fam = sockfam_to_enum(fam) + type_ = socktype_to_enum(type_) + if pid is None: + return pconn(fd, fam, type_, laddr, raddr, status) + else: + return sconn(fd, fam, type_, laddr, raddr, status, pid) + + def deprecated_method(replacement): """A decorator which can be used to mark a method as deprecated 'replcement' is the method name which will be called instead. @@ -468,7 +602,7 @@ def deprecated_method(replacement): @functools.wraps(fun) def inner(self, *args, **kwargs): - warnings.warn(msg, category=FutureWarning, stacklevel=2) + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) return getattr(self, replacement)(*args, **kwargs) return inner return outer @@ -573,3 +707,140 @@ def wrap_numbers(input_dict, name): _wn = _WrapNumbers() wrap_numbers.cache_clear = _wn.cache_clear wrap_numbers.cache_info = _wn.cache_info + + +def open_binary(fname, **kwargs): + return open(fname, "rb", **kwargs) + + +def open_text(fname, **kwargs): + """On Python 3 opens a file in text mode by using fs encoding and + a proper en/decoding errors handler. + On Python 2 this is just an alias for open(name, 'rt'). + """ + if PY3: + # See: + # https://github.com/giampaolo/psutil/issues/675 + # https://github.com/giampaolo/psutil/pull/733 + kwargs.setdefault('encoding', ENCODING) + kwargs.setdefault('errors', ENCODING_ERRS) + return open(fname, "rt", **kwargs) + + +def bytes2human(n, format="%(value).1f%(symbol)s"): + """Used by various scripts. See: + http://goo.gl/zeJZl + + >>> bytes2human(10000) + '9.8K' + >>> bytes2human(100001221) + '95.4M' + """ + symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = {} + for i, s in enumerate(symbols[1:]): + prefix[s] = 1 << (i + 1) * 10 + for symbol in reversed(symbols[1:]): + if n >= prefix[symbol]: + value = float(n) / prefix[symbol] + return format % locals() + return format % dict(symbol=symbols[0], value=n) + + +def get_procfs_path(): + """Return updated psutil.PROCFS_PATH constant.""" + return sys.modules['psutil'].PROCFS_PATH + + +if PY3: + def decode(s): + return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) +else: + def decode(s): + return s + + +# ===================================================================== +# --- shell utils +# ===================================================================== + + +@memoize +def term_supports_colors(file=sys.stdout): + if os.name == 'nt': + return True + try: + import curses + assert file.isatty() + curses.setupterm() + assert curses.tigetnum("colors") > 0 + except Exception: + return False + else: + return True + + +def hilite(s, color="green", bold=False): + """Return an highlighted version of 'string'.""" + if not term_supports_colors(): + return s + attr = [] + colors = dict(green='32', red='91', brown='33') + colors[None] = '29' + try: + color = colors[color] + except KeyError: + raise ValueError("invalid color %r; choose between %r" % ( + list(colors.keys()))) + attr.append(color) + if bold: + attr.append('1') + return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) + + +def print_color(s, color="green", bold=False, file=sys.stdout): + """Print a colorized version of string.""" + if not term_supports_colors(): + print(s, file=file) + elif POSIX: + print(hilite(s, color, bold), file=file) + else: + import ctypes + + DEFAULT_COLOR = 7 + GetStdHandle = ctypes.windll.Kernel32.GetStdHandle + SetConsoleTextAttribute = \ + ctypes.windll.Kernel32.SetConsoleTextAttribute + + colors = dict(green=2, red=4, brown=6) + colors[None] = DEFAULT_COLOR + try: + color = colors[color] + except KeyError: + raise ValueError("invalid color %r; choose between %r" % ( + color, list(colors.keys()))) + if bold and color <= 7: + color += 8 + + handle_id = -12 if file is sys.stderr else -11 + GetStdHandle.restype = ctypes.c_ulong + handle = GetStdHandle(handle_id) + SetConsoleTextAttribute(handle, color) + try: + print(s, file=file) + finally: + SetConsoleTextAttribute(handle, DEFAULT_COLOR) + + +if bool(os.getenv('PSUTIL_DEBUG', 0)): + import inspect + + def debug(msg): + """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" + fname, lineno, func_name, lines, index = inspect.getframeinfo( + inspect.currentframe().f_back) + print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), + file=sys.stderr) +else: + def debug(msg): + pass diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_compat.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_compat.py index de91638f6f9a..a9371382bd6e 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_compat.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_compat.py @@ -5,12 +5,15 @@ """Module which provides compatibility with older Python versions.""" import collections +import errno import functools import os import sys __all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b", - "callable", "lru_cache", "which"] + "lru_cache", "which", "get_terminal_size", + "FileNotFoundError", "PermissionError", "ProcessLookupError", + "InterruptedError", "ChildProcessError", "FileExistsError"] PY3 = sys.version_info[0] == 3 @@ -38,12 +41,84 @@ else: return s -# removed in 3.0, reintroduced in 3.2 -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) +# --- exceptions + + +if PY3: + FileNotFoundError = FileNotFoundError # NOQA + PermissionError = PermissionError # NOQA + ProcessLookupError = ProcessLookupError # NOQA + InterruptedError = InterruptedError # NOQA + ChildProcessError = ChildProcessError # NOQA + FileExistsError = FileExistsError # NOQA +else: + # https://github.com/PythonCharmers/python-future/blob/exceptions/ + # src/future/types/exceptions/pep3151.py + import platform + + _singleton = object() + + def instance_checking_exception(base_exception=Exception): + def wrapped(instance_checker): + class TemporaryClass(base_exception): + + def __init__(self, *args, **kwargs): + if len(args) == 1 and isinstance(args[0], TemporaryClass): + unwrap_me = args[0] + for attr in dir(unwrap_me): + if not attr.startswith('__'): + setattr(self, attr, getattr(unwrap_me, attr)) + else: + super(TemporaryClass, self).__init__(*args, **kwargs) + + class __metaclass__(type): + def __instancecheck__(cls, inst): + return instance_checker(inst) + + def __subclasscheck__(cls, classinfo): + value = sys.exc_info()[1] + return isinstance(value, cls) + + TemporaryClass.__name__ = instance_checker.__name__ + TemporaryClass.__doc__ = instance_checker.__doc__ + return TemporaryClass + + return wrapped + + @instance_checking_exception(EnvironmentError) + def FileNotFoundError(inst): + return getattr(inst, 'errno', _singleton) == errno.ENOENT + + @instance_checking_exception(EnvironmentError) + def ProcessLookupError(inst): + return getattr(inst, 'errno', _singleton) == errno.ESRCH + + @instance_checking_exception(EnvironmentError) + def PermissionError(inst): + return getattr(inst, 'errno', _singleton) in ( + errno.EACCES, errno.EPERM) + + @instance_checking_exception(EnvironmentError) + def InterruptedError(inst): + return getattr(inst, 'errno', _singleton) == errno.EINTR + + @instance_checking_exception(EnvironmentError) + def ChildProcessError(inst): + return getattr(inst, 'errno', _singleton) == errno.ECHILD + + @instance_checking_exception(EnvironmentError) + def FileExistsError(inst): + return getattr(inst, 'errno', _singleton) == errno.EEXIST + + if platform.python_implementation() != "CPython": + try: + raise OSError(errno.EEXIST, "perm") + except FileExistsError: + pass + except OSError: + raise RuntimeError( + "broken / incompatible Python implementation, see: " + "https://github.com/giampaolo/psutil/issues/1659") # --- stdlib additions @@ -247,3 +322,24 @@ except ImportError: if _access_check(name, mode): return name return None + + +# python 3.3 +try: + from shutil import get_terminal_size +except ImportError: + def get_terminal_size(fallback=(80, 24)): + try: + import fcntl + import termios + import struct + except ImportError: + return fallback + else: + try: + # This should work on Linux. + res = struct.unpack( + 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')) + return (res[1], res[0]) + except Exception: + return fallback diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_exceptions.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_exceptions.py deleted file mode 100644 index c08e6d83c8a5..000000000000 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_exceptions.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - - -class Error(Exception): - """Base exception class. All other psutil exceptions inherit - from this one. - """ - - def __init__(self, msg=""): - Exception.__init__(self, msg) - self.msg = msg - - def __repr__(self): - ret = "psutil.%s %s" % (self.__class__.__name__, self.msg) - return ret.strip() - - __str__ = __repr__ - - -class NoSuchProcess(Error): - """Exception raised when a process with a certain PID doesn't - or no longer exists. - """ - - def __init__(self, pid, name=None, msg=None): - Error.__init__(self, msg) - self.pid = pid - self.name = name - self.msg = msg - if msg is None: - if name: - details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) - else: - details = "(pid=%s)" % self.pid - self.msg = "process no longer exists " + details - - -class ZombieProcess(NoSuchProcess): - """Exception raised when querying a zombie process. This is - raised on OSX, BSD and Solaris only, and not always: depending - on the query the OS may be able to succeed anyway. - On Linux all zombie processes are querable (hence this is never - raised). Windows doesn't have zombie processes. - """ - - def __init__(self, pid, name=None, ppid=None, msg=None): - NoSuchProcess.__init__(self, msg) - self.pid = pid - self.ppid = ppid - self.name = name - self.msg = msg - if msg is None: - args = ["pid=%s" % pid] - if name: - args.append("name=%s" % repr(self.name)) - if ppid: - args.append("ppid=%s" % self.ppid) - details = "(%s)" % ", ".join(args) - self.msg = "process still exists but it's a zombie " + details - - -class AccessDenied(Error): - """Exception raised when permission to perform an action is denied.""" - - def __init__(self, pid=None, name=None, msg=None): - Error.__init__(self, msg) - self.pid = pid - self.name = name - self.msg = msg - if msg is None: - if (pid is not None) and (name is not None): - self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) - elif (pid is not None): - self.msg = "(pid=%s)" % self.pid - else: - self.msg = "" - - -class TimeoutExpired(Error): - """Raised on Process.wait(timeout) if timeout expires and process - is still alive. - """ - - def __init__(self, seconds, pid=None, name=None): - Error.__init__(self, "timeout after %s seconds" % seconds) - self.seconds = seconds - self.pid = pid - self.name = name - if (pid is not None) and (name is not None): - self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) - elif (pid is not None): - self.msg += " (pid=%s)" % self.pid diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psaix.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psaix.py index 9abc8d17e51e..994366aaa1b9 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psaix.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psaix.py @@ -6,31 +6,32 @@ """AIX platform implementation.""" -import errno +import functools import glob import os import re import subprocess import sys from collections import namedtuple -from socket import AF_INET from . import _common from . import _psposix from . import _psutil_aix as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied +from ._common import conn_to_ntuple +from ._common import get_procfs_path from ._common import memoize_when_activated from ._common import NIC_DUPLEX_FULL from ._common import NIC_DUPLEX_HALF from ._common import NIC_DUPLEX_UNKNOWN -from ._common import sockfam_to_enum -from ._common import socktype_to_enum +from ._common import NoSuchProcess from ._common import usage_percent +from ._common import ZombieProcess +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import ZombieProcess __extra__all__ = ["PROCFS_PATH"] @@ -42,6 +43,8 @@ __extra__all__ = ["PROCFS_PATH"] HAS_THREADS = hasattr(cext, "proc_threads") +HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters") +HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters") PAGE_SIZE = os.sysconf('SC_PAGE_SIZE') AF_LINK = cext_posix.AF_LINK @@ -93,21 +96,6 @@ pfullmem = pmem scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) # psutil.virtual_memory() svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) -# psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked']) -# psutil.Process.memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) - - -# ===================================================================== -# --- utils -# ===================================================================== - - -def get_procfs_path(): - """Return updated psutil.PROCFS_PATH constant.""" - return sys.modules['psutil'].PROCFS_PATH # ===================================================================== @@ -117,7 +105,7 @@ def get_procfs_path(): def virtual_memory(): total, avail, free, pinned, inuse = cext.virtual_mem() - percent = usage_percent((total - avail), total, _round=1) + percent = usage_percent((total - avail), total, round_=1) return svmem(total, avail, percent, inuse, free) @@ -125,7 +113,7 @@ def swap_memory(): """Swap system memory as a (total, used, free, sin, sout) tuple.""" total, free, sin, sout = cext.swap_mem() used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sswap(total, used, free, percent, sin, sout) @@ -212,7 +200,9 @@ def disk_partitions(all=False): net_if_addrs = cext_posix.net_if_addrs -net_io_counters = cext.net_io_counters + +if HAS_NET_IO_COUNTERS: + net_io_counters = cext.net_io_counters def net_connections(kind, _pid=-1): @@ -225,27 +215,17 @@ def net_connections(kind, _pid=-1): % (kind, ', '.join([repr(x) for x in cmap]))) families, types = _common.conn_tmap[kind] rawlist = cext.net_connections(_pid) - ret = set() + ret = [] for item in rawlist: fd, fam, type_, laddr, raddr, status, pid = item if fam not in families: continue if type_ not in types: continue - status = TCP_STATUSES[status] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type_ = socktype_to_enum(type_) - if _pid == -1: - nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid) - else: - nt = _common.pconn(fd, fam, type_, laddr, raddr, status) - ret.add(nt) - return list(ret) + nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status, + TCP_STATUSES, pid=pid if _pid == -1 else None) + ret.append(nt) + return ret def net_if_stats(): @@ -269,7 +249,8 @@ def net_if_stats(): stdout, stderr = [x.decode(sys.stdout.encoding) for x in (stdout, stderr)] if p.returncode == 0: - re_result = re.search("Running: (\d+) Mbps.*?(\w+) Duplex", stdout) + re_result = re.search( + r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout) if re_result is not None: speed = int(re_result.group(1)) duplex = re_result.group(2) @@ -327,33 +308,27 @@ def wrap_exceptions(fun): """Call callable into a try/except clause and translate ENOENT, EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. """ - + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: - # support for private module import - if (NoSuchProcess is None or AccessDenied is None or - ZombieProcess is None): - raise + except (FileNotFoundError, ProcessLookupError): # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) return wrapper class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path"] + __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] def __init__(self, pid): self.pid = pid @@ -362,23 +337,19 @@ class Process(object): self._procfs_path = get_procfs_path() def oneshot_enter(self): - self._proc_name_and_args.cache_activate() - self._proc_basic_info.cache_activate() - self._proc_cred.cache_activate() + self._proc_basic_info.cache_activate(self) + self._proc_cred.cache_activate(self) def oneshot_exit(self): - self._proc_name_and_args.cache_deactivate() - self._proc_basic_info.cache_deactivate() - self._proc_cred.cache_deactivate() - - @memoize_when_activated - def _proc_name_and_args(self): - return cext.proc_name_and_args(self.pid, self._procfs_path) + self._proc_basic_info.cache_deactivate(self) + self._proc_cred.cache_deactivate(self) + @wrap_exceptions @memoize_when_activated def _proc_basic_info(self): return cext.proc_basic_info(self.pid, self._procfs_path) + @wrap_exceptions @memoize_when_activated def _proc_cred(self): return cext.proc_cred(self.pid, self._procfs_path) @@ -387,22 +358,25 @@ class Process(object): def name(self): if self.pid == 0: return "swapper" - # note: this is limited to 15 characters - return self._proc_name_and_args()[0].rstrip("\x00") + # note: max 16 characters + return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00") @wrap_exceptions def exe(self): # there is no way to get executable path in AIX other than to guess, # and guessing is more complex than what's in the wrapping class - exe = self.cmdline()[0] + cmdline = self.cmdline() + if not cmdline: + return '' + exe = cmdline[0] if os.path.sep in exe: # relative or absolute path if not os.path.isabs(exe): # if cwd has changed, we're out of luck - this may be wrong! exe = os.path.abspath(os.path.join(self.cwd(), exe)) if (os.path.isabs(exe) and - os.path.isfile(exe) and - os.access(exe, os.X_OK)): + os.path.isfile(exe) and + os.access(exe, os.X_OK)): return exe # not found, move to search in PATH using basename only exe = os.path.basename(exe) @@ -410,13 +384,17 @@ class Process(object): for path in os.environ["PATH"].split(":"): possible_exe = os.path.abspath(os.path.join(path, exe)) if (os.path.isfile(possible_exe) and - os.access(possible_exe, os.X_OK)): + os.access(possible_exe, os.X_OK)): return possible_exe return '' @wrap_exceptions def cmdline(self): - return self._proc_name_and_args()[1].split(' ') + return cext.proc_args(self.pid) + + @wrap_exceptions + def environ(self): + return cext.proc_environ(self.pid) @wrap_exceptions def create_time(self): @@ -502,11 +480,9 @@ class Process(object): try: result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid)) return result.rstrip('/') - except OSError as err: - if err.errno == errno.ENOENT: - os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD - return None - raise + except FileNotFoundError: + os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD + return None @wrap_exceptions def memory_info(self): @@ -534,7 +510,7 @@ class Process(object): for x in (stdout, stderr)] if "no such process" in stderr.lower(): raise NoSuchProcess(self.pid, self._name) - procfiles = re.findall("(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout) + procfiles = re.findall(r"(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout) retlist = [] for fd, path in procfiles: path = path.strip() @@ -560,14 +536,15 @@ class Process(object): def wait(self, timeout=None): return _psposix.wait_pid(self.pid, timeout, self._name) - @wrap_exceptions - def io_counters(self): - try: - rc, wc, rb, wb = cext.proc_io_counters(self.pid) - except OSError: - # if process is terminated, proc_io_counters returns OSError - # instead of NSP - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - raise - return _common.pio(rc, wc, rb, wb) + if HAS_PROC_IO_COUNTERS: + @wrap_exceptions + def io_counters(self): + try: + rc, wc, rb, wb = cext.proc_io_counters(self.pid) + except OSError: + # if process is terminated, proc_io_counters returns OSError + # instead of NSP + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + raise + return _common.pio(rc, wc, rb, wb) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psbsd.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psbsd.py index 0553401a55c3..49ad1e995cc1 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psbsd.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psbsd.py @@ -10,26 +10,28 @@ import functools import os import xml.etree.ElementTree as ET from collections import namedtuple -from socket import AF_INET +from collections import defaultdict from . import _common from . import _psposix from . import _psutil_bsd as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied from ._common import conn_tmap +from ._common import conn_to_ntuple from ._common import FREEBSD from ._common import memoize from ._common import memoize_when_activated from ._common import NETBSD +from ._common import NoSuchProcess from ._common import OPENBSD -from ._common import sockfam_to_enum -from ._common import socktype_to_enum from ._common import usage_percent +from ._common import ZombieProcess +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import which -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import ZombieProcess + __extra__all__ = [] @@ -103,6 +105,11 @@ else: PAGESIZE = os.sysconf("SC_PAGE_SIZE") AF_LINK = cext_posix.AF_LINK +HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times") +HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads") +HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files') +HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds') + kinfo_proc_map = dict( ppid=0, status=1, @@ -188,7 +195,7 @@ def virtual_memory(): shared = int(line.split()[1]) * 1024 avail = inactive + cached + free used = active + wired + cached - percent = usage_percent((total - avail), total, _round=1) + percent = usage_percent((total - avail), total, round_=1) return svmem(total, avail, percent, used, free, active, inactive, buffers, cached, shared, wired) @@ -196,7 +203,7 @@ def virtual_memory(): def swap_memory(): """System swap memory as (total, used, free, sin, sout) namedtuple.""" total, used, free, sin, sout = cext.swap_mem() - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sswap(total, used, free, percent, sin, sout) @@ -211,7 +218,7 @@ def cpu_times(): return scputimes(user, nice, system, idle, irq) -if hasattr(cext, "per_cpu_times"): +if HAS_PER_CPU_TIMES: def per_cpu_times(): """Return system CPU times as a namedtuple""" ret = [] @@ -345,12 +352,18 @@ def net_if_stats(): names = net_io_counters().keys() ret = {} for name in names: - mtu = cext_posix.net_if_mtu(name) - isup = cext_posix.net_if_flags(name) - duplex, speed = cext_posix.net_if_duplex_speed(name) - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - ret[name] = _common.snicstats(isup, duplex, speed, mtu) + try: + mtu = cext_posix.net_if_mtu(name) + isup = cext_posix.net_if_flags(name) + duplex, speed = cext_posix.net_if_duplex_speed(name) + except OSError as err: + # https://github.com/giampaolo/psutil/issues/1279 + if err.errno != errno.ENODEV: + raise + else: + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) + ret[name] = _common.snicstats(isup, duplex, speed, mtu) return ret @@ -383,22 +396,8 @@ def net_connections(kind): fd, fam, type, laddr, raddr, status, pid = item # TODO: apply filter at C level if fam in families and type in types: - try: - status = TCP_STATUSES[status] - except KeyError: - # XXX: Not sure why this happens. I saw this occurring - # with IPv6 sockets opened by 'vim'. Those sockets - # have a very short lifetime so maybe the kernel - # can't initialize their status? - status = TCP_STATUSES[cext.PSUTIL_CONN_NONE] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES, pid) ret.add(nt) return list(ret) @@ -426,6 +425,47 @@ if FREEBSD: secsleft = minsleft * 60 return _common.sbattery(percent, secsleft, power_plugged) + def sensors_temperatures(): + "Return CPU cores temperatures if available, else an empty dict." + ret = defaultdict(list) + num_cpus = cpu_count_logical() + for cpu in range(num_cpus): + try: + current, high = cext.sensors_cpu_temperature(cpu) + if high <= 0: + high = None + name = "Core %s" % cpu + ret["coretemp"].append( + _common.shwtemp(name, current, high, high)) + except NotImplementedError: + pass + + return ret + + def cpu_freq(): + """Return frequency metrics for CPUs. As of Dec 2018 only + CPU 0 appears to be supported by FreeBSD and all other cores + match the frequency of CPU 0. + """ + ret = [] + num_cpus = cpu_count_logical() + for cpu in range(num_cpus): + try: + current, available_freq = cext.cpu_frequency(cpu) + except NotImplementedError: + continue + if available_freq: + try: + min_freq = int(available_freq.split(" ")[-1].split("/")[0]) + except(IndexError, ValueError): + min_freq = None + try: + max_freq = int(available_freq.split(" ")[0].split("/")[0]) + except(IndexError, ValueError): + max_freq = None + ret.append(_common.scpufreq(current, min_freq, max_freq)) + return ret + # ===================================================================== # --- other system functions @@ -494,6 +534,14 @@ else: pid_exists = _psposix.pid_exists +def is_zombie(pid): + try: + st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']] + return st == cext.SZOMB + except Exception: + return False + + def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. @@ -502,19 +550,19 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError as err: + except ProcessLookupError: + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) + except OSError: if self.pid == 0: if 0 in pids(): raise AccessDenied(self.pid, self._name) else: raise - if err.errno == errno.ESRCH: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) raise return wrapper @@ -524,30 +572,35 @@ def wrap_exceptions_procfs(inst): """Same as above, for routines relying on reading /proc fs.""" try: yield - except EnvironmentError as err: + except (ProcessLookupError, FileNotFoundError): # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(inst.pid): - raise NoSuchProcess(inst.pid, inst._name) - else: - raise ZombieProcess(inst.pid, inst._name, inst._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(inst.pid, inst._name) - raise + if not pid_exists(inst.pid): + raise NoSuchProcess(inst.pid, inst._name) + else: + raise ZombieProcess(inst.pid, inst._name, inst._ppid) + except PermissionError: + raise AccessDenied(inst.pid, inst._name) class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid"] + __slots__ = ["pid", "_name", "_ppid", "_cache"] def __init__(self, pid): self.pid = pid self._name = None self._ppid = None + def _assert_alive(self): + """Raise NSP if the process disappeared on us.""" + # For those C function who do not raise NSP, possibly returning + # incorrect or incomplete result. + cext.proc_name(self.pid) + + @wrap_exceptions @memoize_when_activated def oneshot(self): """Retrieves multiple process info in one shot as a raw tuple.""" @@ -556,10 +609,10 @@ class Process(object): return ret def oneshot_enter(self): - self.oneshot.cache_activate() + self.oneshot.cache_activate(self) def oneshot_exit(self): - self.oneshot.cache_deactivate() + self.oneshot.cache_deactivate(self) @wrap_exceptions def name(self): @@ -569,6 +622,8 @@ class Process(object): @wrap_exceptions def exe(self): if FREEBSD: + if self.pid == 0: + return '' # else NSP return cext.proc_exe(self.pid) elif NETBSD: if self.pid == 0: @@ -584,7 +639,7 @@ class Process(object): # cmdline arg (may return None). cmdline = self.cmdline() if cmdline: - return which(cmdline[0]) + return which(cmdline[0]) or "" else: return "" @@ -601,10 +656,14 @@ class Process(object): return cext.proc_cmdline(self.pid) except OSError as err: if err.errno == errno.EINVAL: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: + if is_zombie(self.pid): raise ZombieProcess(self.pid, self._name, self._ppid) + elif not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name, self._ppid) + else: + # XXX: this happens with unicode tests. It means the C + # routine is unable to decode invalid unicode chars. + return [] else: raise else: @@ -672,7 +731,7 @@ class Process(object): @wrap_exceptions def num_threads(self): - if hasattr(cext, "proc_num_threads"): + if HAS_PROC_NUM_THREADS: # FreeBSD return cext.proc_num_threads(self.pid) else: @@ -694,10 +753,7 @@ class Process(object): ntuple = _common.pthread(thread_id, utime, stime) retlist.append(ntuple) if OPENBSD: - # On OpenBSD the underlying C function does not raise NSP - # in case the process is gone (and the returned list may - # incomplete). - self.name() # raise NSP if the process disappeared on us + self._assert_alive() return retlist @wrap_exceptions @@ -708,29 +764,16 @@ class Process(object): if NETBSD: families, types = conn_tmap[kind] - ret = set() + ret = [] rawlist = cext.net_connections(self.pid) for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item assert pid == self.pid if fam in families and type in types: - try: - status = TCP_STATUSES[status] - except KeyError: - status = TCP_STATUSES[cext.PSUTIL_CONN_NONE] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - nt = _common.pconn(fd, fam, type, laddr, raddr, status) - ret.add(nt) - # On NetBSD the underlying C function does not raise NSP - # in case the process is gone (and the returned list may - # incomplete). - self.name() # raise NSP if the process disappeared on us + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) + ret.append(nt) + self._assert_alive() return list(ret) families, types = conn_tmap[kind] @@ -738,21 +781,13 @@ class Process(object): ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - status = TCP_STATUSES[status] - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) ret.append(nt) + if OPENBSD: - # On OpenBSD the underlying C function does not raise NSP - # in case the process is gone (and the returned list may - # incomplete). - self.name() # raise NSP if the process disappeared on us + self._assert_alive() + return ret @wrap_exceptions @@ -789,10 +824,7 @@ class Process(object): # it into None if OPENBSD and self.pid == 0: return None # ...else it would raise EINVAL - elif NETBSD: - with wrap_exceptions_procfs(self): - return os.readlink("/proc/%s/cwd" % self.pid) - elif hasattr(cext, 'proc_open_files'): + elif NETBSD or HAS_PROC_OPEN_FILES: # FreeBSD < 8 does not support functions based on # kinfo_getfile() and kinfo_getvmmap() return cext.proc_cwd(self.pid) or None @@ -811,7 +843,7 @@ class Process(object): # FreeBSD < 8 does not support functions based on kinfo_getfile() # and kinfo_getvmmap() - if hasattr(cext, 'proc_open_files'): + if HAS_PROC_OPEN_FILES: @wrap_exceptions def open_files(self): """Return files opened by process as a list of namedtuples.""" @@ -822,15 +854,13 @@ class Process(object): # FreeBSD < 8 does not support functions based on kinfo_getfile() # and kinfo_getvmmap() - if hasattr(cext, 'proc_num_fds'): + if HAS_PROC_NUM_FDS: @wrap_exceptions def num_fds(self): """Return the number of file descriptors opened by this process.""" ret = cext.proc_num_fds(self.pid) if NETBSD: - # On NetBSD the underlying C function does not raise NSP - # in case the process is gone. - self.name() # raise NSP if the process disappeared on us + self._assert_alive() return ret else: num_fds = _not_implemented diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pslinux.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pslinux.py index b57adb34e5d5..9e32f25e7bf9 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pslinux.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pslinux.py @@ -25,25 +25,30 @@ from . import _common from . import _psposix from . import _psutil_linux as cext from . import _psutil_posix as cext_posix -from ._common import ENCODING -from ._common import ENCODING_ERRS +from ._common import AccessDenied +from ._common import debug +from ._common import decode +from ._common import get_procfs_path from ._common import isfile_strict from ._common import memoize from ._common import memoize_when_activated from ._common import NIC_DUPLEX_FULL from ._common import NIC_DUPLEX_HALF from ._common import NIC_DUPLEX_UNKNOWN +from ._common import NoSuchProcess +from ._common import open_binary +from ._common import open_text from ._common import parse_environ_block from ._common import path_exists_strict from ._common import supports_ipv6 from ._common import usage_percent +from ._common import ZombieProcess from ._compat import b from ._compat import basestring -from ._compat import long +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import ZombieProcess if sys.version_info >= (3, 4): import enum @@ -71,6 +76,8 @@ __extra__all__ = [ POWER_SUPPLY_PATH = "/sys/class/power_supply" HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid()) HAS_PRLIMIT = hasattr(cext, "linux_prlimit") +HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_ioprio_get") +HAS_CPU_AFFINITY = hasattr(cext, "proc_cpu_affinity_get") _DEFAULT = object() # RLIMIT_* constants, not guaranteed to be present on all kernels @@ -88,7 +95,20 @@ BOOT_TIME = None # set later # speedup, see: https://github.com/giampaolo/psutil/issues/708 BIGFILE_BUFFERING = -1 if PY3 else 8192 LITTLE_ENDIAN = sys.byteorder == 'little' -SECTOR_SIZE_FALLBACK = 512 + +# "man iostat" states that sectors are equivalent with blocks and have +# a size of 512 bytes. Despite this value can be queried at runtime +# via /sys/block/{DISK}/queue/hw_sector_size and results may vary +# between 1k, 2k, or 4k... 512 appears to be a magic constant used +# throughout Linux source code: +# * https://stackoverflow.com/a/38136179/376587 +# * https://lists.gt.net/linux/kernel/2241060 +# * https://github.com/giampaolo/psutil/issues/1305 +# * https://github.com/torvalds/linux/blob/ +# 4f671fe2f9523a1ea206f63fe60a7c7b3a56d5c7/include/linux/bio.h#L99 +# * https://lkml.org/lkml/2015/8/17/234 +DISK_SECTOR_SIZE = 512 + if enum is None: AF_LINK = socket.AF_PACKET else: @@ -111,7 +131,10 @@ else: globals().update(IOPriority.__members__) -# taken from /fs/proc/array.c +# See: +# https://github.com/torvalds/linux/blame/master/fs/proc/array.c +# ...and (TASK_* constants): +# https://github.com/torvalds/linux/blob/master/include/linux/sched.h PROC_STATUSES = { "R": _common.STATUS_RUNNING, "S": _common.STATUS_SLEEPING, @@ -122,7 +145,9 @@ PROC_STATUSES = { "X": _common.STATUS_DEAD, "x": _common.STATUS_DEAD, "K": _common.STATUS_WAKE_KILL, - "W": _common.STATUS_WAKING + "W": _common.STATUS_WAKING, + "I": _common.STATUS_IDLE, + "P": _common.STATUS_PARKED, } # https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h @@ -149,7 +174,7 @@ TCP_STATUSES = { # psutil.virtual_memory() svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', - 'active', 'inactive', 'buffers', 'cached', 'shared']) + 'active', 'inactive', 'buffers', 'cached', 'shared', 'slab']) # psutil.disk_io_counters() sdiskio = namedtuple( 'sdiskio', ['read_count', 'write_count', @@ -176,6 +201,10 @@ pmmap_ext = namedtuple( pio = namedtuple('pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes', 'read_chars', 'write_chars']) +# psutil.Process.cpu_times() +pcputimes = namedtuple('pcputimes', + ['user', 'system', 'children_user', 'children_system', + 'iowait']) # ===================================================================== @@ -183,37 +212,6 @@ pio = namedtuple('pio', ['read_count', 'write_count', # ===================================================================== -def open_binary(fname, **kwargs): - return open(fname, "rb", **kwargs) - - -def open_text(fname, **kwargs): - """On Python 3 opens a file in text mode by using fs encoding and - a proper en/decoding errors handler. - On Python 2 this is just an alias for open(name, 'rt'). - """ - if PY3: - # See: - # https://github.com/giampaolo/psutil/issues/675 - # https://github.com/giampaolo/psutil/pull/733 - kwargs.setdefault('encoding', ENCODING) - kwargs.setdefault('errors', ENCODING_ERRS) - return open(fname, "rt", **kwargs) - - -if PY3: - def decode(s): - return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) -else: - def decode(s): - return s - - -def get_procfs_path(): - """Return updated psutil.PROCFS_PATH constant.""" - return sys.modules['psutil'].PROCFS_PATH - - def readlink(path): """Wrapper around os.readlink().""" assert isinstance(path, basestring), path @@ -247,17 +245,23 @@ def file_flags_to_mode(flags): return mode -def get_sector_size(partition): - """Return the sector size of a partition. - Used by disk_io_counters(). +def is_storage_device(name): + """Return True if the given name refers to a root device (e.g. + "sda", "nvme0n1") as opposed to a logical partition (e.g. "sda1", + "nvme0n1p1"). If name is a virtual device (e.g. "loop1", "ram") + return True. """ - try: - with open("/sys/block/%s/queue/hw_sector_size" % partition, "rt") as f: - return int(f.read()) - except (IOError, ValueError): - # man iostat states that sectors are equivalent with blocks and - # have a size of 512 bytes since 2.4 kernels. - return SECTOR_SIZE_FALLBACK + # Readapted from iostat source code, see: + # https://github.com/sysstat/sysstat/blob/ + # 97912938cd476645b267280069e83b1c8dc0e1c7/common.c#L208 + # Some devices may have a slash in their name (e.g. cciss/c0d0...). + name = name.replace('/', '!') + including_virtual = True + if including_virtual: + path = "/sys/block/%s" % name + else: + path = "/sys/block/%s/device" % name + return os.access(path, os.F_OK) @memoize @@ -294,7 +298,7 @@ def cat(fname, fallback=_DEFAULT, binary=True): try: with open_binary(fname) if binary else open_text(fname) as f: return f.read().strip() - except IOError: + except (IOError, OSError): if fallback is not _DEFAULT: return fallback else: @@ -441,6 +445,11 @@ def virtual_memory(): inactive = 0 missing_fields.append('inactive') + try: + slab = mems[b"Slab:"] + except KeyError: + slab = 0 + used = total - free - cached - buffers if used < 0: # May be symptomatic of running within a LCX container where such @@ -471,7 +480,7 @@ def virtual_memory(): if avail > total: avail = free - percent = usage_percent((total - avail), total, _round=1) + percent = usage_percent((total - avail), total, round_=1) # Warn about missing metrics which are set to 0. if missing_fields: @@ -481,7 +490,7 @@ def virtual_memory(): warnings.warn(msg, RuntimeWarning) return svmem(total, avail, percent, used, free, - active, inactive, buffers, cached, shared) + active, inactive, buffers, cached, shared, slab) def swap_memory(): @@ -504,7 +513,7 @@ def swap_memory(): free *= unit_multiplier used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) # get pgin/pgouts try: f = open_binary("%s/vmstat" % get_procfs_path()) @@ -609,6 +618,17 @@ def cpu_count_logical(): def cpu_count_physical(): """Return the number of physical cores in the system.""" + # Method #1 + core_ids = set() + for path in glob.glob( + "/sys/devices/system/cpu/cpu[0-9]*/topology/core_id"): + with open_binary(path) as f: + core_ids.add(int(f.read())) + result = len(core_ids) + if result != 0: + return result + + # Method #2 mapping = {} current_info = {} with open_binary('%s/cpuinfo' % get_procfs_path()) as f: @@ -628,8 +648,8 @@ def cpu_count_physical(): key, value = line.split(b'\t:', 1) current_info[key] = int(value) - # mimic os.cpu_count() - return sum(mapping.values()) or None + result = sum(mapping.values()) + return result or None # mimic os.cpu_count() def cpu_stats(): @@ -653,30 +673,26 @@ def cpu_stats(): ctx_switches, interrupts, soft_interrupts, syscalls) -if os.path.exists("/sys/devices/system/cpu/cpufreq") or \ +if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \ os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"): def cpu_freq(): """Return frequency metrics for all CPUs. Contrarily to other OSes, Linux updates these values in real-time. """ - # scaling_* files seem preferable to cpuinfo_*, see: - # http://unix.stackexchange.com/a/87537/168884 - ret = [] - ls = glob.glob("/sys/devices/system/cpu/cpufreq/policy*") - if ls: - # 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:])) - else: - # https://github.com/giampaolo/psutil/issues/981 - ls = glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq") - ls.sort(key=lambda x: int(re.search('[0-9]+', x).group(0))) + def get_path(num): + for p in ("/sys/devices/system/cpu/cpufreq/policy%s" % num, + "/sys/devices/system/cpu/cpu%s/cpufreq" % num): + if os.path.exists(p): + return p - pjoin = os.path.join - for path in ls: + ret = [] + for n in range(cpu_count_logical()): + path = get_path(n) + if not path: + continue + + pjoin = os.path.join curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None) if curr is None: # Likely an old RedHat, see: @@ -691,6 +707,25 @@ if os.path.exists("/sys/devices/system/cpu/cpufreq") or \ ret.append(_common.scpufreq(curr, min_, max_)) return ret +elif os.path.exists("/proc/cpuinfo"): + def cpu_freq(): + """Alternate implementation using /proc/cpuinfo. + min and max frequencies are not available and are set to None. + """ + ret = [] + with open_binary('%s/cpuinfo' % get_procfs_path()) as f: + for line in f: + if line.lower().startswith(b'cpu mhz'): + key, value = line.split(b'\t:', 1) + ret.append(_common.scpufreq(float(value), 0., 0.)) + return ret + +else: + def cpu_freq(): + """Dummy implementation when none of the above files are present. + """ + return [] + # ===================================================================== # --- network @@ -717,6 +752,8 @@ class Connections: """ def __init__(self): + # The string represents the basename of the corresponding + # /proc/net/{proto_name} file. tcp4 = ("tcp", socket.AF_INET, socket.SOCK_STREAM) tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM) udp4 = ("udp", socket.AF_INET, socket.SOCK_DGRAM) @@ -742,17 +779,16 @@ class Connections: for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)): try: inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime; # os.stat('/proc/%s' % self.pid) will be done later # to force NSP (if it's the case) - if err.errno in (errno.ENOENT, errno.ESRCH): - continue - elif err.errno == errno.EINVAL: + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: if inode.startswith('socket:['): # the process is using a socket @@ -765,7 +801,7 @@ class Connections: for pid in pids(): try: inodes.update(self.get_proc_inodes(pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError, PermissionError): # os.listdir() is gonna raise a lot of access denied # exceptions in case of unprivileged user; that's fine # as we'll just end up returning a connection with PID @@ -773,9 +809,7 @@ class Connections: # Both netstat -an and lsof does the same so it's # unlikely we can do any better. # ENOENT just means a PID disappeared on us. - if err.errno not in ( - errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES): - raise + continue return inodes @staticmethod @@ -903,7 +937,7 @@ class Connections: path = tokens[-1] else: path = "" - type_ = int(type_) + type_ = _common.socktype_to_enum(int(type_)) # XXX: determining the remote endpoint of a # UNIX socket on Linux is not possible, see: # https://serverfault.com/questions/252723/ @@ -924,15 +958,14 @@ class Connections: else: inodes = self.get_all_inodes() ret = set() - for f, family, type_ in self.tmap[kind]: + for proto_name, family, type_ in self.tmap[kind]: + path = "%s/net/%s" % (self._procfs_path, proto_name) if family in (socket.AF_INET, socket.AF_INET6): ls = self.process_inet( - "%s/net/%s" % (self._procfs_path, f), - family, type_, inodes, filter_pid=pid) + path, family, type_, inodes, filter_pid=pid) else: ls = self.process_unix( - "%s/net/%s" % (self._procfs_path, f), - family, inodes, filter_pid=pid) + path, family, inodes, filter_pid=pid) for fd, family, type_, laddr, raddr, status, bound_pid in ls: if pid: conn = _common.pconn(fd, family, type_, laddr, raddr, @@ -997,10 +1030,16 @@ def net_if_stats(): names = net_io_counters().keys() ret = {} for name in names: - mtu = cext_posix.net_if_mtu(name) - isup = cext_posix.net_if_flags(name) - duplex, speed = cext.net_if_duplex_speed(name) - ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu) + try: + mtu = cext_posix.net_if_mtu(name) + isup = cext_posix.net_if_flags(name) + duplex, speed = cext.net_if_duplex_speed(name) + except OSError as err: + # https://github.com/giampaolo/psutil/issues/1279 + if err.errno != errno.ENODEV: + raise + else: + ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu) return ret @@ -1012,35 +1051,11 @@ def net_if_stats(): disk_usage = _psposix.disk_usage -def disk_io_counters(): +def disk_io_counters(perdisk=False): """Return disk I/O statistics for every disk installed on the system as a dict of raw tuples. """ - # determine partitions we want to look for - def get_partitions(): - partitions = [] - with open_text("%s/partitions" % get_procfs_path()) as f: - lines = f.readlines()[2:] - for line in reversed(lines): - _, _, _, name = line.split() - if name[-1].isdigit(): - # we're dealing with a partition (e.g. 'sda1'); 'sda' will - # also be around but we want to omit it - partitions.append(name) - else: - if not partitions or not partitions[-1].startswith(name): - # we're dealing with a disk entity for which no - # partitions have been defined (e.g. 'sda' but - # 'sda1' was not around), see: - # https://github.com/giampaolo/psutil/issues/338 - partitions.append(name) - return partitions - - retdict = {} - partitions = get_partitions() - with open_text("%s/diskstats" % get_procfs_path()) as f: - lines = f.readlines() - for line in lines: + def read_procfs(): # OK, this is a bit confusing. The format of /proc/diskstats can # have 3 variations. # On Linux 2.4 each line has always 15 fields, e.g.: @@ -1051,43 +1066,91 @@ def disk_io_counters(): # ...unless (Linux 2.6) the line refers to a partition instead # of a disk, in which case the line has less fields (7): # "3 1 hda1 8 8 8 8" + # 4.18+ has 4 fields added: + # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0" + # 5.5 has 2 more fields. # See: # https://www.kernel.org/doc/Documentation/iostats.txt # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats - fields = line.split() - fields_len = len(fields) - if fields_len == 15: - # Linux 2.4 - name = fields[3] - reads = int(fields[2]) - (reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time, _) = map(int, fields[4:14]) - elif fields_len == 14: - # Linux 2.6+, line referring to a disk - name = fields[2] - (reads, reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time, _) = map(int, fields[3:14]) - elif fields_len == 7: - # Linux 2.6+, line referring to a partition - name = fields[2] - reads, rbytes, writes, wbytes = map(int, fields[3:]) - rtime = wtime = reads_merged = writes_merged = busy_time = 0 - else: - raise ValueError("not sure how to interpret line %r" % line) + with open_text("%s/diskstats" % get_procfs_path()) as f: + lines = f.readlines() + for line in lines: + fields = line.split() + flen = len(fields) + if flen == 15: + # Linux 2.4 + name = fields[3] + reads = int(fields[2]) + (reads_merged, rbytes, rtime, writes, writes_merged, + wbytes, wtime, _, busy_time, _) = map(int, fields[4:14]) + elif flen == 14 or flen >= 18: + # Linux 2.6+, line referring to a disk + name = fields[2] + (reads, reads_merged, rbytes, rtime, writes, writes_merged, + wbytes, wtime, _, busy_time, _) = map(int, fields[3:14]) + elif flen == 7: + # Linux 2.6+, line referring to a partition + name = fields[2] + reads, rbytes, writes, wbytes = map(int, fields[3:]) + rtime = wtime = reads_merged = writes_merged = busy_time = 0 + else: + raise ValueError("not sure how to interpret line %r" % line) + yield (name, reads, writes, rbytes, wbytes, rtime, wtime, + reads_merged, writes_merged, busy_time) + + def read_sysfs(): + for block in os.listdir('/sys/block'): + for root, _, files in os.walk(os.path.join('/sys/block', block)): + if 'stat' not in files: + continue + with open_text(os.path.join(root, 'stat')) as f: + fields = f.read().strip().split() + name = os.path.basename(root) + (reads, reads_merged, rbytes, rtime, writes, writes_merged, + wbytes, wtime, _, busy_time) = map(int, fields[:10]) + yield (name, reads, writes, rbytes, wbytes, rtime, + wtime, reads_merged, writes_merged, busy_time) + + if os.path.exists('%s/diskstats' % get_procfs_path()): + gen = read_procfs() + elif os.path.exists('/sys/block'): + gen = read_sysfs() + else: + raise NotImplementedError( + "%s/diskstats nor /sys/block filesystem are available on this " + "system" % get_procfs_path()) + + retdict = {} + for entry in gen: + (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, + writes_merged, busy_time) = entry + if not perdisk and not is_storage_device(name): + # perdisk=False means we want to calculate totals so we skip + # partitions (e.g. 'sda1', 'nvme0n1p1') and only include + # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks + # include a total of all their partitions + some extra size + # of their own: + # $ cat /proc/diskstats + # 259 0 sda 10485760 ... + # 259 1 sda1 5186039 ... + # 259 1 sda2 5082039 ... + # See: + # https://github.com/giampaolo/psutil/pull/1313 + continue + + rbytes *= DISK_SECTOR_SIZE + wbytes *= DISK_SECTOR_SIZE + retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime, + reads_merged, writes_merged, busy_time) - if name in partitions: - ssize = get_sector_size(name) - rbytes *= ssize - wbytes *= ssize - retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime, - reads_merged, writes_merged, busy_time) return retdict def disk_partitions(all=False): """Return mounted disk partitions as a list of namedtuples.""" fstypes = set() - with open_text("%s/filesystems" % get_procfs_path()) as f: + procfs_path = get_procfs_path() + with open_text("%s/filesystems" % procfs_path) as f: for line in f: line = line.strip() if not line.startswith("nodev"): @@ -1098,8 +1161,14 @@ def disk_partitions(all=False): if fstype == "zfs": fstypes.add("zfs") + # See: https://github.com/giampaolo/psutil/issues/1307 + if procfs_path == "/proc" and os.path.isfile('/etc/mtab'): + mounts_path = os.path.realpath("/etc/mtab") + else: + mounts_path = os.path.realpath("%s/self/mounts" % procfs_path) + retlist = [] - partitions = cext.disk_partitions() + partitions = cext.disk_partitions(mounts_path) for partition in partitions: device, mountpoint, fstype, opts = partition if device == 'none': @@ -1109,6 +1178,7 @@ def disk_partitions(all=False): continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) retlist.append(ntuple) + return retlist @@ -1136,34 +1206,88 @@ def sensors_temperatures(): # https://github.com/giampaolo/psutil/issues/971 # https://github.com/nicolargo/glances/issues/1060 basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*')) + basenames.extend(glob.glob( + '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*')) basenames = sorted(set([x.split('_')[0] for x in basenames])) for base in basenames: try: - current = float(cat(base + '_input')) / 1000.0 - except (IOError, OSError) as err: + path = base + '_input' + current = float(cat(path)) / 1000.0 + path = os.path.join(os.path.dirname(base), 'name') + unit_name = cat(path, binary=False) + except (IOError, OSError, ValueError): # A lot of things can go wrong here, so let's just skip the - # whole entry. + # whole entry. Sure thing is Linux's /sys/class/hwmon really + # is a stinky broken mess. # https://github.com/giampaolo/psutil/issues/1009 # https://github.com/giampaolo/psutil/issues/1101 # https://github.com/giampaolo/psutil/issues/1129 - warnings.warn("ignoring %r" % err, RuntimeWarning) + # https://github.com/giampaolo/psutil/issues/1245 + # https://github.com/giampaolo/psutil/issues/1323 continue - unit_name = cat(os.path.join(os.path.dirname(base), 'name'), - binary=False) high = cat(base + '_max', fallback=None) critical = cat(base + '_crit', fallback=None) label = cat(base + '_label', fallback='', binary=False) if high is not None: - high = float(high) / 1000.0 + try: + high = float(high) / 1000.0 + except ValueError: + high = None if critical is not None: - critical = float(critical) / 1000.0 + try: + critical = float(critical) / 1000.0 + except ValueError: + critical = None ret[unit_name].append((label, current, high, critical)) - return ret + # Indication that no sensors were detected in /sys/class/hwmon/ + if not basenames: + basenames = glob.glob('/sys/class/thermal/thermal_zone*') + basenames = sorted(set(basenames)) + + for base in basenames: + try: + path = os.path.join(base, 'temp') + current = float(cat(path)) / 1000.0 + path = os.path.join(base, 'type') + unit_name = cat(path, binary=False) + except (IOError, OSError, ValueError) as err: + debug("ignoring %r for file %r" % (err, path)) + continue + + trip_paths = glob.glob(base + '/trip_point*') + trip_points = set(['_'.join( + os.path.basename(p).split('_')[0:3]) for p in trip_paths]) + critical = None + high = None + for trip_point in trip_points: + path = os.path.join(base, trip_point + "_type") + trip_type = cat(path, fallback='', binary=False) + if trip_type == 'critical': + critical = cat(os.path.join(base, trip_point + "_temp"), + fallback=None) + elif trip_type == 'high': + high = cat(os.path.join(base, trip_point + "_temp"), + fallback=None) + + if high is not None: + try: + high = float(high) / 1000.0 + except ValueError: + high = None + if critical is not None: + try: + critical = float(critical) / 1000.0 + except ValueError: + critical = None + + ret[unit_name].append(('', current, high, critical)) + + return dict(ret) def sensors_fans(): @@ -1217,9 +1341,13 @@ def sensors_battery(): return int(ret) if ret.isdigit() else ret return None - root = os.path.join(POWER_SUPPLY_PATH, "BAT0") - if not os.path.exists(root): + bats = [x for x in os.listdir(POWER_SUPPLY_PATH) if x.startswith('BAT')] + if not bats: return None + # Get the first available battery. Usually this is "BAT0", except + # some rare exceptions: + # https://github.com/giampaolo/psutil/issues/1238 + root = os.path.join(POWER_SUPPLY_PATH, sorted(bats)[0]) # Base metrics. energy_now = multi_cat( @@ -1366,12 +1494,10 @@ def ppid_map(): try: with open_binary("%s/%s/stat" % (procfs_path, pid)) as f: data = f.read() - except EnvironmentError as err: + except (FileNotFoundError, ProcessLookupError): # Note: we should be able to access /stat for all processes - # so we won't bump into EPERM, which is good. - if err.errno not in (errno.ENOENT, errno.ESRCH, - errno.EPERM, errno.EACCES): - raise + # aka it's unlikely we'll bump into EPERM, which is good. + pass else: rpar = data.rfind(b')') dset = data[rpar + 2:].split() @@ -1388,16 +1514,12 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - # ESRCH (no such process) can be raised on read() if - # process is gone in the meantime. - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - # ENOENT (no such file or directory) can be raised on open(). - if err.errno == errno.ENOENT and not os.path.exists("%s/%s" % ( - self._procfs_path, self.pid)): + except PermissionError: + raise AccessDenied(self.pid, self._name) + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except FileNotFoundError: + if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)): raise NoSuchProcess(self.pid, self._name) # Note: zombies will keep existing under /proc until they're # gone so there's no way to distinguish them in here. @@ -1408,7 +1530,7 @@ def wrap_exceptions(fun): class Process(object): """Linux process implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path"] + __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] def __init__(self, pid): self.pid = pid @@ -1416,13 +1538,20 @@ class Process(object): self._ppid = None self._procfs_path = get_procfs_path() + def _assert_alive(self): + """Raise NSP if the process disappeared on us.""" + # For those C function who do not raise NSP, possibly returning + # incorrect or incomplete result. + os.stat('%s/%s' % (self._procfs_path, self.pid)) + + @wrap_exceptions @memoize_when_activated def _parse_stat_file(self): - """Parse /proc/{pid}/stat file. Return a list of fields where - process name is in position 0. + """Parse /proc/{pid}/stat file and return a dict with various + process info. Using "man proc" as a reference: where "man proc" refers to - position N, always substract 2 (e.g starttime pos 22 in - 'man proc' == pos 20 in the list returned here). + position N always substract 3 (e.g ppid position 4 in + 'man proc' == position 1 in here). The return value is cached in case oneshot() ctx manager is in use. """ @@ -1433,9 +1562,24 @@ class Process(object): # the first occurrence of "(" and the last occurence of ")". rpar = data.rfind(b')') name = data[data.find(b'(') + 1:rpar] - others = data[rpar + 2:].split() - return [name] + others + fields = data[rpar + 2:].split() + ret = {} + ret['name'] = name + ret['status'] = fields[0] + ret['ppid'] = fields[1] + ret['ttynr'] = fields[4] + ret['utime'] = fields[11] + ret['stime'] = fields[12] + ret['children_utime'] = fields[13] + ret['children_stime'] = fields[14] + ret['create_time'] = fields[19] + ret['cpu_num'] = fields[36] + ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks' + + return ret + + @wrap_exceptions @memoize_when_activated def _read_status_file(self): """Read /proc/{pid}/stat file and return its content. @@ -1445,6 +1589,7 @@ class Process(object): with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f: return f.read() + @wrap_exceptions @memoize_when_activated def _read_smaps_file(self): with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid), @@ -1452,18 +1597,18 @@ class Process(object): return f.read().strip() def oneshot_enter(self): - self._parse_stat_file.cache_activate() - self._read_status_file.cache_activate() - self._read_smaps_file.cache_activate() + self._parse_stat_file.cache_activate(self) + self._read_status_file.cache_activate(self) + self._read_smaps_file.cache_activate(self) def oneshot_exit(self): - self._parse_stat_file.cache_deactivate() - self._read_status_file.cache_deactivate() - self._read_smaps_file.cache_deactivate() + self._parse_stat_file.cache_deactivate(self) + self._read_status_file.cache_deactivate(self) + self._read_smaps_file.cache_deactivate(self) @wrap_exceptions def name(self): - name = self._parse_stat_file()[0] + name = self._parse_stat_file()['name'] if PY3: name = decode(name) # XXX - gets changed later and probably needs refactoring @@ -1472,21 +1617,19 @@ class Process(object): def exe(self): try: return readlink("%s/%s/exe" % (self._procfs_path, self.pid)) - except OSError as err: - if err.errno in (errno.ENOENT, errno.ESRCH): - # no such file error; might be raised also if the - # path actually exists for system processes with - # low pids (about 0-20) - if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): - return "" + except (FileNotFoundError, ProcessLookupError): + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): + return "" + else: + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) else: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) @wrap_exceptions def cmdline(self): @@ -1505,7 +1648,13 @@ class Process(object): sep = '\x00' if data.endswith('\x00') else ' ' if data.endswith(sep): data = data[:-1] - return [x for x in data.split(sep)] + cmdline = data.split(sep) + # Sometimes last char is a null byte '\0' but the args are + # separated by spaces, see: https://github.com/giampaolo/psutil/ + # issues/1179#issuecomment-552984549 + if sep == '\x00' and len(cmdline) == 1 and ' ' in data: + cmdline = data.split(' ') + return cmdline @wrap_exceptions def environ(self): @@ -1515,13 +1664,14 @@ class Process(object): @wrap_exceptions def terminal(self): - tty_nr = int(self._parse_stat_file()[5]) + tty_nr = int(self._parse_stat_file()['ttynr']) tmap = _psposix.get_terminal_map() try: return tmap[tty_nr] except KeyError: return None + # May not be available on old kernels. if os.path.exists('/proc/%s/io' % os.getpid()): @wrap_exceptions def io_counters(self): @@ -1532,36 +1682,42 @@ class Process(object): # https://github.com/giampaolo/psutil/issues/1004 line = line.strip() if line: - name, value = line.split(b': ') - fields[name] = int(value) + try: + name, value = line.split(b': ') + except ValueError: + # https://github.com/giampaolo/psutil/issues/1004 + continue + else: + fields[name] = int(value) if not fields: raise RuntimeError("%s file was empty" % fname) - return pio( - fields[b'syscr'], # read syscalls - fields[b'syscw'], # write syscalls - fields[b'read_bytes'], # read bytes - fields[b'write_bytes'], # write bytes - fields[b'rchar'], # read chars - fields[b'wchar'], # write chars - ) - else: - def io_counters(self): - raise NotImplementedError("couldn't find /proc/%s/io (kernel " - "too old?)" % self.pid) + try: + return pio( + fields[b'syscr'], # read syscalls + fields[b'syscw'], # write syscalls + fields[b'read_bytes'], # read bytes + fields[b'write_bytes'], # write bytes + fields[b'rchar'], # read chars + fields[b'wchar'], # write chars + ) + except KeyError as err: + raise ValueError("%r field was not found in %s; found fields " + "are %r" % (err[0], fname, fields)) @wrap_exceptions def cpu_times(self): values = self._parse_stat_file() - utime = float(values[12]) / CLOCK_TICKS - stime = float(values[13]) / CLOCK_TICKS - children_utime = float(values[14]) / CLOCK_TICKS - children_stime = float(values[15]) / CLOCK_TICKS - return _common.pcputimes(utime, stime, children_utime, children_stime) + utime = float(values['utime']) / CLOCK_TICKS + stime = float(values['stime']) / CLOCK_TICKS + children_utime = float(values['children_utime']) / CLOCK_TICKS + children_stime = float(values['children_stime']) / CLOCK_TICKS + iowait = float(values['blkio_ticks']) / CLOCK_TICKS + return pcputimes(utime, stime, children_utime, children_stime, iowait) @wrap_exceptions def cpu_num(self): """What CPU the process is on.""" - return int(self._parse_stat_file()[37]) + return int(self._parse_stat_file()['cpu_num']) @wrap_exceptions def wait(self, timeout=None): @@ -1569,14 +1725,14 @@ class Process(object): @wrap_exceptions def create_time(self): - values = self._parse_stat_file() + ctime = float(self._parse_stat_file()['create_time']) # According to documentation, starttime is in field 21 and the # unit is jiffies (clock ticks). # We first divide it for clock ticks and then add uptime returning # seconds since the epoch, in UTC. # Also use cached value if available. bt = BOOT_TIME or boot_time() - return (float(values[20]) / CLOCK_TICKS) + bt + return (ctime / CLOCK_TICKS) + bt @wrap_exceptions def memory_info(self): @@ -1603,9 +1759,10 @@ class Process(object): @wrap_exceptions def memory_full_info( self, - _private_re=re.compile(br"Private.*:\s+(\d+)"), - _pss_re=re.compile(br"Pss.*:\s+(\d+)"), - _swap_re=re.compile(br"Swap.*:\s+(\d+)")): + # Gets Private_Clean, Private_Dirty, Private_Hugetlb. + _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"), + _pss_re=re.compile(br"\nPss\:\s+(\d+)"), + _swap_re=re.compile(br"\nSwap\:\s+(\d+)")): basic_mem = self.memory_info() # Note: using 3 regexes is faster than reading the file # line by line. @@ -1637,6 +1794,9 @@ class Process(object): """Return process's mapped memory regions as a list of named tuples. Fields are explained in 'man proc'; here is an updated (Apr 2012) version: http://goo.gl/fmebo + + /proc/{PID}/smaps does not exist on kernels < 2.6.14 or if + CONFIG_MMU kernel configuration option is not enabled. """ def get_blocks(lines, current_block): data = {} @@ -1684,7 +1844,7 @@ class Process(object): path = path[:-10] ls.append(( decode(addr), decode(perms), path, - data[b'Rss:'], + data.get(b'Rss:', 0), data.get(b'Size:', 0), data.get(b'Pss:', 0), data.get(b'Shared_Clean:', 0), @@ -1697,25 +1857,16 @@ class Process(object): )) return ls - else: # pragma: no cover - def memory_maps(self): - raise NotImplementedError( - "/proc/%s/smaps does not exist on kernels < 2.6.14 or " - "if CONFIG_MMU kernel configuration option is not " - "enabled." % self.pid) - @wrap_exceptions def cwd(self): try: return readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # https://github.com/giampaolo/psutil/issues/986 - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - raise + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) @wrap_exceptions def num_ctx_switches(self, @@ -1751,13 +1902,11 @@ class Process(object): try: with open_binary(fname) as f: st = f.read().strip() - except IOError as err: - if err.errno == errno.ENOENT: - # no such file or directory; it means thread - # disappeared on us - hit_enoent = True - continue - raise + except FileNotFoundError: + # no such file or directory; it means thread + # disappeared on us + hit_enoent = True + continue # ignore the first two values ("pid (exe)") st = st[st.find(b')') + 2:] values = st.split(b' ') @@ -1766,8 +1915,7 @@ class Process(object): ntuple = _common.pthread(int(thread_id), utime, stime) retlist.append(ntuple) if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (self._procfs_path, self.pid)) + self._assert_alive() return retlist @wrap_exceptions @@ -1783,41 +1931,44 @@ class Process(object): def nice_set(self, value): return cext_posix.setpriority(self.pid, value) - @wrap_exceptions - def cpu_affinity_get(self): - return cext.proc_cpu_affinity_get(self.pid) + # starting from CentOS 6. + if HAS_CPU_AFFINITY: - def _get_eligible_cpus( - self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")): - # See: https://github.com/giampaolo/psutil/issues/956 - data = self._read_status_file() - match = _re.findall(data) - if match: - return list(range(int(match[0][0]), int(match[0][1]) + 1)) - else: - return list(range(len(per_cpu_times()))) + @wrap_exceptions + def cpu_affinity_get(self): + return cext.proc_cpu_affinity_get(self.pid) - @wrap_exceptions - def cpu_affinity_set(self, cpus): - try: - cext.proc_cpu_affinity_set(self.pid, cpus) - except (OSError, ValueError) as err: - if isinstance(err, ValueError) or err.errno == errno.EINVAL: - eligible_cpus = self._get_eligible_cpus() - all_cpus = tuple(range(len(per_cpu_times()))) - for cpu in cpus: - if cpu not in all_cpus: - raise ValueError( - "invalid CPU number %r; choose between %s" % ( - cpu, eligible_cpus)) - if cpu not in eligible_cpus: - raise ValueError( - "CPU number %r is not eligible; choose " - "between %s" % (cpu, eligible_cpus)) - raise + def _get_eligible_cpus( + self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")): + # See: https://github.com/giampaolo/psutil/issues/956 + data = self._read_status_file() + match = _re.findall(data) + if match: + return list(range(int(match[0][0]), int(match[0][1]) + 1)) + else: + return list(range(len(per_cpu_times()))) + + @wrap_exceptions + def cpu_affinity_set(self, cpus): + try: + cext.proc_cpu_affinity_set(self.pid, cpus) + except (OSError, ValueError) as err: + if isinstance(err, ValueError) or err.errno == errno.EINVAL: + eligible_cpus = self._get_eligible_cpus() + all_cpus = tuple(range(len(per_cpu_times()))) + for cpu in cpus: + if cpu not in all_cpus: + raise ValueError( + "invalid CPU number %r; choose between %s" % ( + cpu, eligible_cpus)) + if cpu not in eligible_cpus: + raise ValueError( + "CPU number %r is not eligible; choose " + "between %s" % (cpu, eligible_cpus)) + raise # only starting from kernel 2.6.13 - if hasattr(cext, "proc_ioprio_get"): + if HAS_PROC_IO_PRIORITY: @wrap_exceptions def ionice_get(self): @@ -1828,38 +1979,16 @@ class Process(object): @wrap_exceptions def ionice_set(self, ioclass, value): - if value is not None: - if not PY3 and not isinstance(value, (int, long)): - msg = "value argument is not an integer (gor %r)" % value - raise TypeError(msg) - if not 0 <= value <= 7: - raise ValueError( - "value argument range expected is between 0 and 7") - - if ioclass in (IOPRIO_CLASS_NONE, None): - if value: - msg = "can't specify value with IOPRIO_CLASS_NONE " \ - "(got %r)" % value - raise ValueError(msg) - ioclass = IOPRIO_CLASS_NONE + if value is None: value = 0 - elif ioclass == IOPRIO_CLASS_IDLE: - if value: - msg = "can't specify value with IOPRIO_CLASS_IDLE " \ - "(got %r)" % value - raise ValueError(msg) - value = 0 - elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): - if value is None: - # TODO: add comment explaining why this is 4 (?) - value = 4 - else: - # otherwise we would get OSError(EVINAL) - raise ValueError("invalid ioclass argument %r" % ioclass) - + if value and ioclass in (IOPRIO_CLASS_IDLE, IOPRIO_CLASS_NONE): + raise ValueError("%r ioclass accepts no value" % ioclass) + if value < 0 or value > 7: + raise ValueError("value not in 0-7 range") return cext.proc_ioprio_set(self.pid, ioclass, value) if HAS_PRLIMIT: + @wrap_exceptions def rlimit(self, resource, limits=None): # If pid is 0 prlimit() applies to the calling process and @@ -1889,7 +2018,7 @@ class Process(object): @wrap_exceptions def status(self): - letter = self._parse_stat_file()[1] + letter = self._parse_stat_file()['status'] if PY3: letter = letter.decode() # XXX is '?' legit? (we're not supposed to return it anyway) @@ -1904,16 +2033,15 @@ class Process(object): file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) try: path = readlink(file) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime - if err.errno in (errno.ENOENT, errno.ESRCH): - hit_enoent = True - continue - elif err.errno == errno.EINVAL: + hit_enoent = True + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: # If path is not an absolute there's no way to tell # whether it's a regular file or not, so we skip it. @@ -1927,29 +2055,23 @@ class Process(object): with open_binary(file) as f: pos = int(f.readline().split()[1]) flags = int(f.readline().split()[1], 8) - except IOError as err: - if err.errno == errno.ENOENT: - # fd gone in the meantime; does not - # necessarily mean the process disappeared - # on us. - hit_enoent = True - else: - raise + except FileNotFoundError: + # fd gone in the meantime; process may + # still be alive + hit_enoent = True else: mode = file_flags_to_mode(flags) ntuple = popenfile( path, int(fd), int(pos), mode, flags) retlist.append(ntuple) if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (self._procfs_path, self.pid)) + self._assert_alive() return retlist @wrap_exceptions def connections(self, kind='inet'): ret = _connections.retrieve(kind, self.pid) - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (self._procfs_path, self.pid)) + self._assert_alive() return ret @wrap_exceptions @@ -1958,7 +2080,7 @@ class Process(object): @wrap_exceptions def ppid(self): - return int(self._parse_stat_file()[2]) + return int(self._parse_stat_file()['ppid']) @wrap_exceptions def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')): diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psosx.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psosx.py index 308756a81c00..e4296495c45b 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psosx.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psosx.py @@ -2,30 +2,29 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""OSX platform implementation.""" +"""macOS platform implementation.""" import contextlib import errno import functools import os -from socket import AF_INET from collections import namedtuple from . import _common from . import _psposix from . import _psutil_osx as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied from ._common import conn_tmap +from ._common import conn_to_ntuple from ._common import isfile_strict from ._common import memoize_when_activated +from ._common import NoSuchProcess from ._common import parse_environ_block -from ._common import sockfam_to_enum -from ._common import socktype_to_enum from ._common import usage_percent -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import ZombieProcess +from ._common import ZombieProcess +from ._compat import PermissionError +from ._compat import ProcessLookupError __extra__all__ = [] @@ -103,13 +102,6 @@ svmem = namedtuple( pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) # psutil.Process.memory_full_info() pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) -# psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple( - 'pmmap_grouped', - 'path rss private swapped dirtied ref_count shadow_depth') -# psutil.Process.memory_maps(grouped=False) -pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) # ===================================================================== @@ -119,10 +111,17 @@ pmmap_ext = namedtuple( def virtual_memory(): """System virtual memory as a namedtuple.""" - total, active, inactive, wired, free = cext.virtual_mem() + total, active, inactive, wired, free, speculative = cext.virtual_mem() + # This is how Zabbix calculate avail and used mem: + # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/ + # osx/memory.c + # Also see: https://github.com/giampaolo/psutil/issues/1277 avail = inactive + free - used = active + inactive + wired - percent = usage_percent((total - avail), total, _round=1) + used = active + wired + # This is NOT how Zabbix calculates free mem but it matches "free" + # cmdline utility. + free -= speculative + percent = usage_percent((total - avail), total, round_=1) return svmem(total, avail, percent, used, free, active, inactive, wired) @@ -130,7 +129,7 @@ def virtual_memory(): def swap_memory(): """Swap system memory as a (total, used, free, sin, sout) tuple.""" total, used, free, sin, sout = cext.swap_mem() - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sswap(total, used, free, percent, sin, sout) @@ -174,7 +173,7 @@ def cpu_stats(): def cpu_freq(): """Return CPU frequency. - On OSX per-cpu frequency is not supported. + On macOS per-cpu frequency is not supported. Also, the returned frequency never changes, see: https://arstechnica.com/civis/viewtopic.php?f=19&t=465002 """ @@ -213,8 +212,7 @@ def disk_partitions(all=False): def sensors_battery(): - """Return battery information. - """ + """Return battery information.""" try: percent, minsleft, power_plugged = cext.sensors_battery() except NotImplementedError: @@ -241,7 +239,7 @@ net_if_addrs = cext_posix.net_if_addrs def net_connections(kind='inet'): """System-wide network connections.""" - # Note: on OSX this will fail with AccessDenied unless + # Note: on macOS this will fail with AccessDenied unless # the process is owned by root. ret = [] for pid in pids(): @@ -262,12 +260,18 @@ def net_if_stats(): names = net_io_counters().keys() ret = {} for name in names: - mtu = cext_posix.net_if_mtu(name) - isup = cext_posix.net_if_flags(name) - duplex, speed = cext_posix.net_if_duplex_speed(name) - if hasattr(_common, 'NicDuplex'): - duplex = _common.NicDuplex(duplex) - ret[name] = _common.snicstats(isup, duplex, speed, mtu) + try: + mtu = cext_posix.net_if_mtu(name) + isup = cext_posix.net_if_flags(name) + duplex, speed = cext_posix.net_if_duplex_speed(name) + except OSError as err: + # https://github.com/giampaolo/psutil/issues/1279 + if err.errno != errno.ENODEV: + raise + else: + if hasattr(_common, 'NicDuplex'): + duplex = _common.NicDuplex(duplex) + ret[name] = _common.snicstats(isup, duplex, speed, mtu) return ret @@ -304,16 +308,16 @@ def users(): def pids(): ls = cext.pids() if 0 not in ls: - # On certain OSX versions pids() C doesn't return PID 0 but + # On certain macOS versions pids() C doesn't return PID 0 but # "ps" does and the process is querable via sysctl(): # https://travis-ci.org/giampaolo/psutil/jobs/309619941 try: Process(0).create_time() - ls.append(0) + ls.insert(0, 0) except NoSuchProcess: pass except AccessDenied: - ls.append(0) + ls.insert(0, 0) return ls @@ -328,12 +332,12 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError as err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except PermissionError: + raise AccessDenied(self.pid, self._name) + except cext.ZombieProcessError: + raise ZombieProcess(self.pid, self._name, self._ppid) return wrapper @@ -366,13 +370,14 @@ def catch_zombie(proc): class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid"] + __slots__ = ["pid", "_name", "_ppid", "_cache"] def __init__(self, pid): self.pid = pid self._name = None self._ppid = None + @wrap_exceptions @memoize_when_activated def _get_kinfo_proc(self): # Note: should work with all PIDs without permission issues. @@ -380,6 +385,7 @@ class Process(object): assert len(ret) == len(kinfo_proc_map) return ret + @wrap_exceptions @memoize_when_activated def _get_pidtaskinfo(self): # Note: should work for PIDs owned by user only. @@ -389,12 +395,12 @@ class Process(object): return ret def oneshot_enter(self): - self._get_kinfo_proc.cache_activate() - self._get_pidtaskinfo.cache_activate() + self._get_kinfo_proc.cache_activate(self) + self._get_pidtaskinfo.cache_activate(self) def oneshot_exit(self): - self._get_kinfo_proc.cache_deactivate() - self._get_pidtaskinfo.cache_deactivate() + self._get_kinfo_proc.cache_deactivate(self) + self._get_pidtaskinfo.cache_deactivate(self) @wrap_exceptions def name(self): @@ -516,15 +522,8 @@ class Process(object): ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item - status = TCP_STATUSES[status] - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) ret.append(nt) return ret @@ -557,15 +556,9 @@ class Process(object): @wrap_exceptions def threads(self): - with catch_zombie(self): - rawlist = cext.proc_threads(self.pid) + rawlist = cext.proc_threads(self.pid) retlist = [] for thread_id, utime, stime in rawlist: ntuple = _common.pthread(thread_id, utime, stime) retlist.append(ntuple) return retlist - - @wrap_exceptions - def memory_maps(self): - with catch_zombie(self): - return cext.proc_memory_maps(self.pid) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psposix.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psposix.py index 6bb8444d8026..88213ef8b641 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psposix.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psposix.py @@ -4,7 +4,6 @@ """Routines common to all posix systems.""" -import errno import glob import os import sys @@ -12,10 +11,15 @@ import time from ._common import memoize from ._common import sdiskusage +from ._common import TimeoutExpired from ._common import usage_percent +from ._compat import ChildProcessError +from ._compat import FileNotFoundError +from ._compat import InterruptedError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 from ._compat import unicode -from ._exceptions import TimeoutExpired __all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map'] @@ -32,19 +36,13 @@ def pid_exists(pid): return True try: os.kill(pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - # ESRCH == No such process - return False - elif err.errno == errno.EPERM: - # EPERM clearly means there's a process to deny access to - return True - else: - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) therefore we should never get - # here. If we do let's be explicit in considering this - # an error. - raise err + except ProcessLookupError: + return False + except PermissionError: + # EPERM clearly means there's a process to deny access to + return True + # According to "man 2 kill" possible error values are + # (EINVAL, EPERM, ESRCH) else: return True @@ -80,24 +78,20 @@ def wait_pid(pid, timeout=None, proc_name=None): while True: try: retpid, status = waitcall() - except OSError as err: - if err.errno == errno.EINTR: - delay = check_timeout(delay) - continue - elif err.errno == errno.ECHILD: - # This has two meanings: - # - pid is not a child of os.getpid() in which case - # we keep polling until it's gone - # - pid never existed in the first place - # In both cases we'll eventually return None as we - # can't determine its exit status code. - while True: - if pid_exists(pid): - delay = check_timeout(delay) - else: - return - else: - raise + except InterruptedError: + delay = check_timeout(delay) + except ChildProcessError: + # This has two meanings: + # - pid is not a child of os.getpid() in which case + # we keep polling until it's gone + # - pid never existed in the first place + # In both cases we'll eventually return None as we + # can't determine its exit status code. + while True: + if pid_exists(pid): + delay = check_timeout(delay) + else: + return else: if retpid == 0: # WNOHANG was used, pid is still running @@ -156,7 +150,7 @@ def disk_usage(path): # User usage percent compared to the total amount of space # the user can use. This number would be higher if compared # to root's because the user has less space (usually -5%). - usage_percent_user = usage_percent(used, total_user, _round=1) + usage_percent_user = usage_percent(used, total_user, round_=1) # NB: the percentage is -5% than what shown by df due to # reserved blocks that we are currently not considering: @@ -176,7 +170,6 @@ def get_terminal_map(): assert name not in ret, name try: ret[os.stat(name).st_rdev] = name - except OSError as err: - if err.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass return ret diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pssunos.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pssunos.py index 5471d5aa4dc0..62362b89c631 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pssunos.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pssunos.py @@ -5,6 +5,7 @@ """Sun OS Solaris platform implementation.""" import errno +import functools import os import socket import subprocess @@ -16,17 +17,22 @@ from . import _common from . import _psposix from . import _psutil_posix as cext_posix from . import _psutil_sunos as cext +from ._common import AccessDenied from ._common import AF_INET6 +from ._common import debug +from ._common import get_procfs_path from ._common import isfile_strict from ._common import memoize_when_activated +from ._common import NoSuchProcess from ._common import sockfam_to_enum from ._common import socktype_to_enum from ._common import usage_percent +from ._common import ZombieProcess from ._compat import b +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import ZombieProcess __extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"] @@ -79,7 +85,11 @@ proc_info_map = dict( nice=4, num_threads=5, status=6, - ttynr=7) + ttynr=7, + uid=8, + euid=9, + gid=10, + egid=11) # ===================================================================== @@ -105,16 +115,6 @@ pmmap_ext = namedtuple( 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) -# ===================================================================== -# --- utils -# ===================================================================== - - -def get_procfs_path(): - """Return updated psutil.PROCFS_PATH constant.""" - return sys.modules['psutil'].PROCFS_PATH - - # ===================================================================== # --- memory # ===================================================================== @@ -127,7 +127,7 @@ def virtual_memory(): # note: there's no difference on Solaris free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return svmem(total, avail, percent, used, free) @@ -159,7 +159,7 @@ def swap_memory(): total += int(int(t) * 512) free += int(int(f) * 512) used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sswap(total, used, free, percent, sin * PAGE_SIZE, sout * PAGE_SIZE) @@ -226,7 +226,12 @@ def disk_partitions(all=False): # Differently from, say, Linux, we don't have a list of # common fs types so the best we can do, AFAIK, is to # filter by filesystem having a total size > 0. - if not disk_usage(mountpoint).total: + try: + if not disk_usage(mountpoint).total: + continue + except OSError as err: + # https://github.com/giampaolo/psutil/issues/1674 + debug("skipping %r: %r" % (mountpoint, err)) continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) retlist.append(ntuple) @@ -262,6 +267,7 @@ def net_connections(kind, _pid=-1): continue if type_ not in types: continue + # TODO: refactor and use _common.conn_to_ntuple. if fam in (AF_INET, AF_INET6): if laddr: laddr = _common.addr(*laddr) @@ -337,26 +343,26 @@ def wrap_exceptions(fun): """Call callable into a try/except clause and translate ENOENT, EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. """ - + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: + except (FileNotFoundError, ProcessLookupError): + # ENOENT (no such file or directory) gets raised on open(). + # ESRCH (no such process) can get raised on read() if + # process is gone in meantime. + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) + except OSError: if self.pid == 0: if 0 in pids(): raise AccessDenied(self.pid, self._name) else: raise - # ENOENT (no such file or directory) gets raised on open(). - # ESRCH (no such process) can get raised on read() if - # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) raise return wrapper @@ -364,7 +370,7 @@ def wrap_exceptions(fun): class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid", "_procfs_path"] + __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] def __init__(self, pid): self.pid = pid @@ -372,26 +378,38 @@ class Process(object): self._ppid = None self._procfs_path = get_procfs_path() + def _assert_alive(self): + """Raise NSP if the process disappeared on us.""" + # For those C function who do not raise NSP, possibly returning + # incorrect or incomplete result. + os.stat('%s/%s' % (self._procfs_path, self.pid)) + def oneshot_enter(self): - self._proc_name_and_args.cache_activate() - self._proc_basic_info.cache_activate() - self._proc_cred.cache_activate() + self._proc_name_and_args.cache_activate(self) + self._proc_basic_info.cache_activate(self) + self._proc_cred.cache_activate(self) def oneshot_exit(self): - self._proc_name_and_args.cache_deactivate() - self._proc_basic_info.cache_deactivate() - self._proc_cred.cache_deactivate() + self._proc_name_and_args.cache_deactivate(self) + self._proc_basic_info.cache_deactivate(self) + self._proc_cred.cache_deactivate(self) + @wrap_exceptions @memoize_when_activated def _proc_name_and_args(self): return cext.proc_name_and_args(self.pid, self._procfs_path) + @wrap_exceptions @memoize_when_activated def _proc_basic_info(self): + if self.pid == 0 and not \ + os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)): + raise AccessDenied(self.pid) ret = cext.proc_basic_info(self.pid, self._procfs_path) assert len(ret) == len(proc_info_map) return ret + @wrap_exceptions @memoize_when_activated def _proc_cred(self): return cext.proc_cred(self.pid, self._procfs_path) @@ -432,27 +450,10 @@ class Process(object): @wrap_exceptions def nice_get(self): - # Note #1: for some reason getpriority(3) return ESRCH (no such - # process) for certain low-pid processes, no matter what (even - # as root). - # The process actually exists though, as it has a name, - # creation time, etc. - # The best thing we can do here appears to be raising AD. - # Note: tested on Solaris 11; on Open Solaris 5 everything is - # fine. - # - # Note #2: we also can get niceness from /proc/pid/psinfo - # but it's wrong, see: - # https://github.com/giampaolo/psutil/issues/1082 - try: - return cext_posix.getpriority(self.pid) - except EnvironmentError as err: - # 48 is 'operation not supported' but errno does not expose - # it. It occurs for low system pids. - if err.errno in (errno.ENOENT, errno.ESRCH, 48): - if pid_exists(self.pid): - raise AccessDenied(self.pid, self._name) - raise + # Note #1: getpriority(3) doesn't work for realtime processes. + # Psinfo is what ps uses, see: + # https://github.com/giampaolo/psutil/issues/1194 + return self._proc_basic_info()[proc_info_map['nice']] @wrap_exceptions def nice_set(self, value): @@ -471,12 +472,22 @@ class Process(object): @wrap_exceptions def uids(self): - real, effective, saved, _, _, _ = self._proc_cred() + try: + real, effective, saved, _, _, _ = self._proc_cred() + except AccessDenied: + real = self._proc_basic_info()[proc_info_map['uid']] + effective = self._proc_basic_info()[proc_info_map['euid']] + saved = None return _common.puids(real, effective, saved) @wrap_exceptions def gids(self): - _, _, _, real, effective, saved = self._proc_cred() + try: + _, _, _, real, effective, saved = self._proc_cred() + except AccessDenied: + real = self._proc_basic_info()[proc_info_map['gid']] + effective = self._proc_basic_info()[proc_info_map['egid']] + saved = None return _common.puids(real, effective, saved) @wrap_exceptions @@ -512,14 +523,11 @@ class Process(object): try: return os.readlink( '%s/%d/path/%d' % (procfs_path, self.pid, x)) - except OSError as err: - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (procfs_path, self.pid)) + self._assert_alive() @wrap_exceptions def cwd(self): @@ -530,11 +538,9 @@ class Process(object): procfs_path = self._procfs_path try: return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid)) - except OSError as err: - if err.errno == errno.ENOENT: - os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD - return None - raise + except FileNotFoundError: + os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD + return None @wrap_exceptions def memory_info(self): @@ -581,8 +587,7 @@ class Process(object): nt = _common.pthread(tid, utime, stime) ret.append(nt) if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (procfs_path, self.pid)) + self._assert_alive() return ret @wrap_exceptions @@ -596,18 +601,14 @@ class Process(object): if os.path.islink(path): try: file = os.readlink(path) - except OSError as err: - # ENOENT == file which is gone in the meantime - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue else: if isfile_strict(file): retlist.append(_common.popenfile(file, int(fd))) if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (procfs_path, self.pid)) + self._assert_alive() return retlist def _get_unix_sockets(self, pid): @@ -707,8 +708,7 @@ class Process(object): raise retlist.append((addr, perm, name, rss, anon, locked)) if hit_enoent: - # raise NSP if the process disappeared on us - os.stat('%s/%s' % (procfs_path, self.pid)) + self._assert_alive() return retlist @wrap_exceptions diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_psutil_windows.pyd b/third_party/python/psutil-cp27-none-win_amd64/psutil/_psutil_windows.pyd index 0d0cab4c701048f05a6309a5755e4230e563a75d..9db9a66d6ed0b4ac06fb62b3daac0eda43ae96c3 100644 GIT binary patch literal 62976 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 literal 59904 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 diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pswindows.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pswindows.py index b6c58c936cde..99d5d71499ba 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/_pswindows.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/_pswindows.py @@ -8,10 +8,37 @@ import contextlib import errno import functools import os +import signal import sys +import time from collections import namedtuple from . import _common +from ._common import AccessDenied +from ._common import conn_tmap +from ._common import conn_to_ntuple +from ._common import debug +from ._common import ENCODING +from ._common import ENCODING_ERRS +from ._common import isfile_strict +from ._common import memoize +from ._common import memoize_when_activated +from ._common import NoSuchProcess +from ._common import parse_environ_block +from ._common import TimeoutExpired +from ._common import usage_percent +from ._compat import long +from ._compat import lru_cache +from ._compat import PY3 +from ._compat import unicode +from ._compat import xrange +from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS +from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS +from ._psutil_windows import HIGH_PRIORITY_CLASS +from ._psutil_windows import IDLE_PRIORITY_CLASS +from ._psutil_windows import NORMAL_PRIORITY_CLASS +from ._psutil_windows import REALTIME_PRIORITY_CLASS + try: from . import _psutil_windows as cext except ImportError as err: @@ -21,41 +48,13 @@ except ImportError as err: # 1) we are on an old Windows version # 2) psutil was installed via pip + wheel # See: https://github.com/giampaolo/psutil/issues/811 - # It must be noted that psutil can still (kind of) work - # on outdated systems if compiled / installed from sources, - # but if we get here it means this this was a wheel (or exe). msg = "this Windows version is too old (< Windows Vista); " msg += "psutil 3.4.2 is the latest version which supports Windows " - msg += "2000, XP and 2003 server; it may be possible that psutil " - msg += "will work if compiled from sources though" + msg += "2000, XP and 2003 server" raise RuntimeError(msg) else: raise -from ._common import conn_tmap -from ._common import ENCODING -from ._common import ENCODING_ERRS -from ._common import isfile_strict -from ._common import memoize_when_activated -from ._common import parse_environ_block -from ._common import sockfam_to_enum -from ._common import socktype_to_enum -from ._common import usage_percent -from ._compat import long -from ._compat import lru_cache -from ._compat import PY3 -from ._compat import unicode -from ._compat import xrange -from ._exceptions import AccessDenied -from ._exceptions import NoSuchProcess -from ._exceptions import TimeoutExpired -from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS -from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS -from ._psutil_windows import HIGH_PRIORITY_CLASS -from ._psutil_windows import IDLE_PRIORITY_CLASS -from ._psutil_windows import NORMAL_PRIORITY_CLASS -from ._psutil_windows import REALTIME_PRIORITY_CLASS - if sys.version_info >= (3, 4): import enum else: @@ -65,11 +64,14 @@ else: # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx __extra__all__ = [ "win_service_iter", "win_service_get", + # Process priority "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", - "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", - "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS", - "CONN_DELETE_TCB", - "AF_LINK", + "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS", + "REALTIME_PRIORITY_CLASS", + # IO priority + "IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH", + # others + "CONN_DELETE_TCB", "AF_LINK", ] @@ -78,12 +80,8 @@ __extra__all__ = [ # ===================================================================== CONN_DELETE_TCB = "DELETE_TCB" -WAIT_TIMEOUT = 0x00000102 # 258 in decimal -ACCESS_DENIED_ERRSET = frozenset([errno.EPERM, errno.EACCES, - cext.ERROR_ACCESS_DENIED]) -NO_SUCH_SERVICE_ERRSET = frozenset([cext.ERROR_INVALID_NAME, - cext.ERROR_SERVICE_DOES_NOT_EXIST]) - +ERROR_PARTIAL_COPY = 299 +PYPY = '__pypy__' in sys.builtin_module_names if enum is None: AF_LINK = -1 @@ -118,6 +116,19 @@ if enum is not None: globals().update(Priority.__members__) +if enum is None: + IOPRIO_VERYLOW = 0 + IOPRIO_LOW = 1 + IOPRIO_NORMAL = 2 + IOPRIO_HIGH = 3 +else: + class IOPriority(enum.IntEnum): + IOPRIO_VERYLOW = 0 + IOPRIO_LOW = 1 + IOPRIO_NORMAL = 2 + IOPRIO_HIGH = 3 + globals().update(IOPriority.__members__) + pinfo_map = dict( num_handles=0, ctx_switches=1, @@ -187,7 +198,8 @@ def convert_dos_path(s): """ rawdrive = '\\'.join(s.split('\\')[:3]) driveletter = cext.win32_QueryDosDevice(rawdrive) - return os.path.join(driveletter, s[len(rawdrive):]) + remainder = s[len(rawdrive):] + return os.path.join(driveletter, remainder) def py2_strencode(s): @@ -200,7 +212,12 @@ def py2_strencode(s): if isinstance(s, str): return s else: - return s.encode(ENCODING, errors=ENCODING_ERRS) + return s.encode(ENCODING, ENCODING_ERRS) + + +@memoize +def getpagesize(): + return cext.getpagesize() # ===================================================================== @@ -217,7 +234,7 @@ def virtual_memory(): avail = availphys free = availphys used = total - avail - percent = usage_percent((total - avail), total, _round=1) + percent = usage_percent((total - avail), total, round_=1) return svmem(total, avail, percent, used, free) @@ -227,7 +244,7 @@ def swap_memory(): total = mem[2] free = mem[3] used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sswap(total, used, free, percent, 0, 0) @@ -247,7 +264,7 @@ def disk_usage(path): path = path.decode(ENCODING, errors="strict") total, free = cext.disk_usage(path) used = total - free - percent = usage_percent(used, total, _round=1) + percent = usage_percent(used, total, round_=1) return _common.sdiskusage(total, used, free, percent) @@ -288,7 +305,7 @@ def cpu_count_logical(): def cpu_count_physical(): - """Return the number of physical CPUs in the system.""" + """Return the number of physical CPU cores in the system.""" return cext.cpu_count_phys() @@ -309,6 +326,23 @@ def cpu_freq(): return [_common.scpufreq(float(curr), min_, float(max_))] +_loadavg_inititialized = False + + +def getloadavg(): + """Return the number of processes in the system run queue averaged + over the last 1, 5, and 15 minutes respectively as a tuple""" + global _loadavg_inititialized + + if not _loadavg_inititialized: + cext.init_loadavg_counter() + _loadavg_inititialized = True + + # Drop to 2 decimal points which is what Linux does + raw_loads = cext.getloadavg() + return tuple([round(load, 2) for load in raw_loads]) + + # ===================================================================== # --- network # ===================================================================== @@ -326,17 +360,8 @@ def net_connections(kind, _pid=-1): ret = set() for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - status = TCP_STATUSES[status] - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - if _pid == -1: - nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) - else: - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES, + pid=pid if _pid == -1 else None) ret.add(nt) return list(ret) @@ -501,14 +526,14 @@ class WindowsService(object): """ try: yield - except WindowsError as err: - if err.errno in ACCESS_DENIED_ERRSET: + except OSError as err: + if is_permission_err(err): raise AccessDenied( pid=None, name=self._name, msg="service %r is not querable (not enough privileges)" % self._name) - elif err.errno in NO_SUCH_SERVICE_ERRSET or \ - err.winerror in NO_SUCH_SERVICE_ERRSET: + elif err.winerror in (cext.ERROR_INVALID_NAME, + cext.ERROR_SERVICE_DOES_NOT_EXIST): raise NoSuchProcess( pid=None, name=self._name, msg="service %r does not exist)" % self._name) @@ -625,27 +650,68 @@ pid_exists = cext.pid_exists ppid_map = cext.ppid_map # used internally by Process.children() +def is_permission_err(exc): + """Return True if this is a permission error.""" + assert isinstance(exc, OSError), exc + # On Python 2 OSError doesn't always have 'winerror'. Sometimes + # it does, in which case the original exception was WindowsError + # (which is a subclass of OSError). + return exc.errno in (errno.EPERM, errno.EACCES) or \ + getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED, + cext.ERROR_PRIVILEGE_NOT_HELD) + + +def convert_oserror(exc, pid=None, name=None): + """Convert OSError into NoSuchProcess or AccessDenied.""" + assert isinstance(exc, OSError), exc + if is_permission_err(exc): + return AccessDenied(pid=pid, name=name) + if exc.errno == errno.ESRCH: + return NoSuchProcess(pid=pid, name=name) + raise exc + + def wrap_exceptions(fun): - """Decorator which translates bare OSError and WindowsError - exceptions into NoSuchProcess and AccessDenied. - """ + """Decorator which converts OSError into NoSuchProcess or AccessDenied.""" @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: - raise AccessDenied(self.pid, self._name) - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - raise + raise convert_oserror(err, pid=self.pid, name=self._name) + return wrapper + + +def retry_error_partial_copy(fun): + """Workaround for https://github.com/giampaolo/psutil/issues/875. + See: https://stackoverflow.com/questions/4457745#4457745 + """ + @functools.wraps(fun) + def wrapper(self, *args, **kwargs): + delay = 0.0001 + times = 33 + for x in range(times): # retries for roughly 1 second + try: + return fun(self, *args, **kwargs) + except WindowsError as _: + err = _ + if err.winerror == ERROR_PARTIAL_COPY: + time.sleep(delay) + delay = min(delay * 2, 0.04) + continue + else: + raise + else: + msg = "%s retried %s times, converted to AccessDenied as it's " \ + "still returning %r" % (fun, times, err) + raise AccessDenied(pid=self.pid, name=self._name, msg=msg) return wrapper class Process(object): """Wrapper class around underlying C implementation.""" - __slots__ = ["pid", "_name", "_ppid"] + __slots__ = ["pid", "_name", "_ppid", "_cache"] def __init__(self, pid): self.pid = pid @@ -655,13 +721,15 @@ class Process(object): # --- oneshot() stuff def oneshot_enter(self): - self.oneshot_info.cache_activate() + self._proc_info.cache_activate(self) + self.exe.cache_activate(self) def oneshot_exit(self): - self.oneshot_info.cache_deactivate() + self._proc_info.cache_deactivate(self) + self.exe.cache_deactivate(self) @memoize_when_activated - def oneshot_info(self): + def _proc_info(self): """Return multiple information about this process as a raw tuple. """ @@ -669,7 +737,6 @@ class Process(object): assert len(ret) == len(pinfo_map) return ret - @wrap_exceptions def name(self): """Return process name, which on Windows is always the final part of the executable. @@ -678,37 +745,53 @@ class Process(object): # and process-hacker. if self.pid == 0: return "System Idle Process" - elif self.pid == 4: + if self.pid == 4: return "System" - else: - try: - # Note: this will fail with AD for most PIDs owned - # by another user but it's faster. - return py2_strencode(os.path.basename(self.exe())) - except AccessDenied: - return py2_strencode(cext.proc_name(self.pid)) + return os.path.basename(self.exe()) @wrap_exceptions + @memoize_when_activated def exe(self): - # Note: os.path.exists(path) may return False even if the file - # is there, see: - # http://stackoverflow.com/questions/3112546/os-path-exists-lies - - # see https://github.com/giampaolo/psutil/issues/414 - # see https://github.com/giampaolo/psutil/issues/528 - if self.pid in (0, 4): - raise AccessDenied(self.pid, self._name) - return py2_strencode(convert_dos_path(cext.proc_exe(self.pid))) + if PYPY: + try: + exe = cext.proc_exe(self.pid) + except WindowsError as err: + # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens + # (perhaps PyPy's JIT delaying garbage collection of files?). + if err.errno == 24: + debug("%r forced into AccessDenied" % err) + raise AccessDenied(self.pid, self._name) + raise + else: + exe = cext.proc_exe(self.pid) + if not PY3: + exe = py2_strencode(exe) + if exe.startswith('\\'): + return convert_dos_path(exe) + return exe # May be "Registry", "MemCompression", ... @wrap_exceptions + @retry_error_partial_copy def cmdline(self): - ret = cext.proc_cmdline(self.pid) + if cext.WINVER >= cext.WINDOWS_8_1: + # PEB method detects cmdline changes but requires more + # privileges: https://github.com/giampaolo/psutil/pull/1398 + try: + ret = cext.proc_cmdline(self.pid, use_peb=True) + except OSError as err: + if is_permission_err(err): + ret = cext.proc_cmdline(self.pid, use_peb=False) + else: + raise + else: + ret = cext.proc_cmdline(self.pid, use_peb=True) if PY3: return ret else: return [py2_strencode(s) for s in ret] @wrap_exceptions + @retry_error_partial_copy def environ(self): ustr = cext.proc_environ(self.pid) if ustr and not PY3: @@ -725,10 +808,10 @@ class Process(object): try: return cext.proc_memory_info(self.pid) except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: + if is_permission_err(err): # TODO: the C ext can probably be refactored in order # to get this from cext.proc_info() - info = self.oneshot_info() + info = self._proc_info() return ( info[pinfo_map['num_page_faults']], info[pinfo_map['peak_wset']], @@ -757,6 +840,7 @@ class Process(object): def memory_full_info(self): basic_mem = self.memory_info() uss = cext.proc_memory_uss(self.pid) + uss *= getpagesize() return pfullmem(*basic_mem + (uss, )) def memory_maps(self): @@ -765,16 +849,11 @@ class Process(object): except OSError as err: # XXX - can't use wrap_exceptions decorator as we're # returning a generator; probably needs refactoring. - if err.errno in ACCESS_DENIED_ERRSET: - raise AccessDenied(self.pid, self._name) - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - raise + raise convert_oserror(err, self.pid, self._name) else: for addr, perm, path, rss in raw: path = convert_dos_path(path) if not PY3: - assert isinstance(path, unicode), type(path) path = py2_strencode(path) addr = hex(addr) yield (addr, perm, path, rss) @@ -785,25 +864,59 @@ class Process(object): @wrap_exceptions def send_signal(self, sig): - os.kill(self.pid, sig) + if sig == signal.SIGTERM: + cext.proc_kill(self.pid) + # py >= 2.7 + elif sig in (getattr(signal, "CTRL_C_EVENT", object()), + getattr(signal, "CTRL_BREAK_EVENT", object())): + os.kill(self.pid, sig) + else: + raise ValueError( + "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " + "are supported on Windows") @wrap_exceptions def wait(self, timeout=None): if timeout is None: cext_timeout = cext.INFINITE else: - # WaitForSingleObject() expects time in milliseconds + # WaitForSingleObject() expects time in milliseconds. cext_timeout = int(timeout * 1000) + + timer = getattr(time, 'monotonic', time.time) + stop_at = timer() + timeout if timeout is not None else None + + try: + # Exit code is supposed to come from GetExitCodeProcess(). + # May also be None if OpenProcess() failed with + # ERROR_INVALID_PARAMETER, meaning PID is already gone. + exit_code = cext.proc_wait(self.pid, cext_timeout) + except cext.TimeoutExpired: + # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise. + raise TimeoutExpired(timeout, self.pid, self._name) + except cext.TimeoutAbandoned: + # WaitForSingleObject() returned WAIT_ABANDONED, see: + # https://github.com/giampaolo/psutil/issues/1224 + # We'll just rely on the internal polling and return None + # when the PID disappears. Subprocess module does the same + # (return None): + # https://github.com/python/cpython/blob/ + # be50a7b627d0aa37e08fa8e2d5568891f19903ce/ + # Lib/subprocess.py#L1193-L1194 + exit_code = None + + # At this point WaitForSingleObject() returned WAIT_OBJECT_0, + # meaning the process is gone. Stupidly there are cases where + # its PID may still stick around so we do a further internal + # polling. + delay = 0.0001 while True: - ret = cext.proc_wait(self.pid, cext_timeout) - if ret == WAIT_TIMEOUT: - raise TimeoutExpired(timeout, self.pid, self._name) - if pid_exists(self.pid): - if timeout is None: - continue - else: - raise TimeoutExpired(timeout, self.pid, self._name) - return ret + if not pid_exists(self.pid): + return exit_code + if stop_at and timer() >= stop_at: + raise TimeoutExpired(timeout, pid=self.pid, name=self._name) + time.sleep(delay) + delay = min(delay * 2, 0.04) # incremental delay @wrap_exceptions def username(self): @@ -814,19 +927,19 @@ class Process(object): @wrap_exceptions def create_time(self): - # special case for kernel process PIDs; return system boot time - if self.pid in (0, 4): - return boot_time() + # Note: proc_times() not put under oneshot() 'cause create_time() + # is already cached by the main Process class. try: - return cext.proc_create_time(self.pid) + user, system, created = cext.proc_times(self.pid) + return created except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: - return self.oneshot_info()[pinfo_map['create_time']] + if is_permission_err(err): + return self._proc_info()[pinfo_map['create_time']] raise @wrap_exceptions def num_threads(self): - return self.oneshot_info()[pinfo_map['num_threads']] + return self._proc_info()[pinfo_map['num_threads']] @wrap_exceptions def threads(self): @@ -840,26 +953,26 @@ class Process(object): @wrap_exceptions def cpu_times(self): try: - user, system = cext.proc_cpu_times(self.pid) + user, system, created = cext.proc_times(self.pid) except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: - info = self.oneshot_info() - user = info[pinfo_map['user_time']] - system = info[pinfo_map['kernel_time']] - else: + if not is_permission_err(err): raise + info = self._proc_info() + user = info[pinfo_map['user_time']] + system = info[pinfo_map['kernel_time']] # Children user/system times are not retrievable (set to 0). return _common.pcputimes(user, system, 0.0, 0.0) @wrap_exceptions def suspend(self): - return cext.proc_suspend(self.pid) + cext.proc_suspend_or_resume(self.pid, True) @wrap_exceptions def resume(self): - return cext.proc_resume(self.pid) + cext.proc_suspend_or_resume(self.pid, False) @wrap_exceptions + @retry_error_partial_copy def cwd(self): if self.pid in (0, 4): raise AccessDenied(self.pid, self._name) @@ -902,39 +1015,38 @@ class Process(object): def nice_set(self, value): return cext.proc_priority_set(self.pid, value) - # available on Windows >= Vista - if hasattr(cext, "proc_io_priority_get"): - @wrap_exceptions - def ionice_get(self): - return cext.proc_io_priority_get(self.pid) + @wrap_exceptions + def ionice_get(self): + ret = cext.proc_io_priority_get(self.pid) + if enum is not None: + ret = IOPriority(ret) + return ret - @wrap_exceptions - def ionice_set(self, value, _): - if _: - raise TypeError("set_proc_ionice() on Windows takes only " - "1 argument (2 given)") - if value not in (2, 1, 0): - raise ValueError("value must be 2 (normal), 1 (low) or 0 " - "(very low); got %r" % value) - return cext.proc_io_priority_set(self.pid, value) + @wrap_exceptions + def ionice_set(self, ioclass, value): + if value: + raise TypeError("value argument not accepted on Windows") + if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, + IOPRIO_HIGH): + raise ValueError("%s is not a valid priority" % ioclass) + cext.proc_io_priority_set(self.pid, ioclass) @wrap_exceptions def io_counters(self): try: ret = cext.proc_io_counters(self.pid) except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: - info = self.oneshot_info() - ret = ( - info[pinfo_map['io_rcount']], - info[pinfo_map['io_wcount']], - info[pinfo_map['io_rbytes']], - info[pinfo_map['io_wbytes']], - info[pinfo_map['io_count_others']], - info[pinfo_map['io_bytes_others']], - ) - else: + if not is_permission_err(err): raise + info = self._proc_info() + ret = ( + info[pinfo_map['io_rcount']], + info[pinfo_map['io_wcount']], + info[pinfo_map['io_rbytes']], + info[pinfo_map['io_wbytes']], + info[pinfo_map['io_count_others']], + info[pinfo_map['io_bytes_others']], + ) return pio(*ret) @wrap_exceptions @@ -982,12 +1094,12 @@ class Process(object): try: return cext.proc_num_handles(self.pid) except OSError as err: - if err.errno in ACCESS_DENIED_ERRSET: - return self.oneshot_info()[pinfo_map['num_handles']] + if is_permission_err(err): + return self._proc_info()[pinfo_map['num_handles']] raise @wrap_exceptions def num_ctx_switches(self): - ctx_switches = self.oneshot_info()[pinfo_map['ctx_switches']] + ctx_switches = self._proc_info()[pinfo_map['ctx_switches']] # only voluntary ctx switches are supported return _common.pctxsw(ctx_switches, 0) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__init__.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__init__.py index 9e8d8596be35..3e4dc88066ca 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__init__.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__init__.py @@ -32,15 +32,18 @@ import traceback import warnings from socket import AF_INET from socket import AF_INET6 -from socket import SOCK_DGRAM from socket import SOCK_STREAM import psutil -from psutil import OSX +from psutil import AIX +from psutil import MACOS from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._common import supports_ipv6 +from psutil._compat import ChildProcessError +from psutil._compat import FileExistsError +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil._compat import unicode @@ -54,7 +57,9 @@ else: try: from unittest import mock # py3 except ImportError: - import mock # NOQA - requires "pip install mock" + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import mock # NOQA - requires "pip install mock" if sys.version_info >= (3, 4): import enum @@ -66,8 +71,8 @@ __all__ = [ # constants 'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'MEMORY_TOLERANCE', 'NO_RETRIES', 'PYPY', 'PYTHON_EXE', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX', - 'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'VALID_PROC_STATUSES', - 'VERBOSITY', + 'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'CIRRUS', 'CI_TESTING', + 'VALID_PROC_STATUSES', "HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS", "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", @@ -79,8 +84,7 @@ __all__ = [ 'ThreadTask' # test utils 'unittest', 'skip_on_access_denied', 'skip_on_not_implemented', - 'retry_before_failing', 'run_test_module_by_name', 'get_suite', - 'run_suite', + 'retry_on_failure', # install utils 'install_pip', 'install_test_deps', # fs utils @@ -91,7 +95,7 @@ __all__ = [ # sync primitives 'call_until', 'wait_for_pid', 'wait_for_file', # network - 'check_connection_ntuple', 'check_net_address', + 'check_net_address', 'get_free_port', 'unix_socket_path', 'bind_socket', 'bind_unix_socket', 'tcp_socketpair', 'unix_socketpair', 'create_sockets', # compat @@ -109,23 +113,20 @@ __all__ = [ TOX = os.getenv('TOX') or '' in ('1', 'true') PYPY = '__pypy__' in sys.builtin_module_names -WIN_VISTA = (6, 0, 0) if WINDOWS else None -# whether we're running this test suite on Travis (https://travis-ci.org/) +# whether we're running this test suite on a Continuous Integration service TRAVIS = bool(os.environ.get('TRAVIS')) -# whether we're running this test suite on Appveyor for Windows -# (http://www.appveyor.com/) APPVEYOR = bool(os.environ.get('APPVEYOR')) +CIRRUS = bool(os.environ.get('CIRRUS')) +CI_TESTING = TRAVIS or APPVEYOR or CIRRUS # --- configurable defaults -# how many times retry_before_failing() decorator will retry +# how many times retry_on_failure() decorator will retry NO_RETRIES = 10 # bytes tolerance for system-wide memory related tests MEMORY_TOLERANCE = 500 * 1024 # 500KB # the timeout used in functions which have to wait -GLOBAL_TIMEOUT = 3 -# test output verbosity -VERBOSITY = 1 if os.getenv('SILENT') or TOX else 2 +GLOBAL_TIMEOUT = 5 # be more tolerant if we're on travis / appveyor in order to avoid # false positives if TRAVIS or APPVEYOR: @@ -135,34 +136,49 @@ if TRAVIS or APPVEYOR: # --- files TESTFILE_PREFIX = '$testfn' +if os.name == 'java': + # Jython disallows @ in module names + TESTFILE_PREFIX = '$psutil-test-' +else: + TESTFILE_PREFIX = '@psutil-test-' TESTFN = os.path.join(os.path.realpath(os.getcwd()), TESTFILE_PREFIX) +# Disambiguate TESTFN for parallel testing, while letting it remain a valid +# module name. +TESTFN = TESTFN + str(os.getpid()) + _TESTFN = TESTFN + '-internal' TESTFN_UNICODE = TESTFN + u("-ƒőő") ASCII_FS = sys.getfilesystemencoding().lower() in ('ascii', 'us-ascii') # --- paths -ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +ROOT_DIR = os.path.realpath( + os.path.join(os.path.dirname(__file__), '..', '..')) SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts') -HERE = os.path.abspath(os.path.dirname(__file__)) +HERE = os.path.realpath(os.path.dirname(__file__)) # --- support +HAS_CONNECTIONS_UNIX = POSIX and not SUNOS HAS_CPU_AFFINITY = hasattr(psutil.Process, "cpu_affinity") HAS_CPU_FREQ = hasattr(psutil, "cpu_freq") -HAS_CONNECTIONS_UNIX = POSIX and not SUNOS +HAS_GETLOADAVG = hasattr(psutil, "getloadavg") HAS_ENVIRON = hasattr(psutil.Process, "environ") -HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters") HAS_IONICE = hasattr(psutil.Process, "ionice") -HAS_MEMORY_FULL_INFO = 'uss' in psutil.Process().memory_full_info()._fields HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps") +HAS_NET_IO_COUNTERS = hasattr(psutil, "net_io_counters") HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num") +HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters") HAS_RLIMIT = hasattr(psutil.Process, "rlimit") -HAS_THREADS = hasattr(psutil.Process, "threads") HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery") -HAS_BATTERY = HAS_SENSORS_BATTERY and psutil.sensors_battery() +try: + HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery()) +except Exception: + HAS_BATTERY = True HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans") HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures") +HAS_THREADS = hasattr(psutil.Process, "threads") +SKIP_SYSCONS = (MACOS or AIX) and os.getuid() != 0 # --- misc @@ -177,7 +193,7 @@ def _get_py_exe(): else: return exe - if OSX: + if MACOS: exe = \ attempt(sys.executable) or \ attempt(os.path.realpath(sys.executable)) or \ @@ -197,7 +213,6 @@ DEVNULL = open(os.devnull, 'r+') VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil) if x.startswith('STATUS_')] AF_UNIX = getattr(socket, "AF_UNIX", object()) -SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) _subprocesses_started = set() _pids_started = set() @@ -205,7 +220,7 @@ _testfiles_created = set() @atexit.register -def _cleanup_files(): +def cleanup_test_files(): DEVNULL.close() for name in os.listdir(u('.')): if isinstance(name, unicode): @@ -226,7 +241,7 @@ def _cleanup_files(): # this is executed first @atexit.register -def _cleanup_procs(): +def cleanup_test_procs(): reap_children(recursive=True) @@ -283,7 +298,7 @@ class ThreadTask(threading.Thread): # =================================================================== -def _cleanup_on_err(fun): +def _reap_children_on_err(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): try: @@ -294,7 +309,7 @@ def _cleanup_on_err(fun): return wrapper -@_cleanup_on_err +@_reap_children_on_err def get_test_subprocess(cmd=None, **kwds): """Creates a python subprocess which does nothing for 60 secs and return it as subprocess.Popen instance. @@ -309,8 +324,11 @@ def get_test_subprocess(cmd=None, **kwds): kwds.setdefault("cwd", os.getcwd()) kwds.setdefault("env", os.environ) if WINDOWS: - # Prevents the subprocess to open error dialogs. - kwds.setdefault("creationflags", 0x8000000) # CREATE_NO_WINDOW + # Prevents the subprocess to open error dialogs. This will also + # cause stderr to be suppressed, which is suboptimal in order + # to debug broken tests. + CREATE_NO_WINDOW = 0x8000000 + kwds.setdefault("creationflags", CREATE_NO_WINDOW) if cmd is None: safe_rmpath(_TESTFN) pyline = "from time import sleep;" \ @@ -327,7 +345,7 @@ def get_test_subprocess(cmd=None, **kwds): return sproc -@_cleanup_on_err +@_reap_children_on_err def create_proc_children_pair(): """Create a subprocess which creates another one as in: A (us) -> B (child) -> C (grandchild). @@ -343,8 +361,8 @@ def create_proc_children_pair(): s += "f.write(str(os.getpid()));" s += "f.close();" s += "time.sleep(60);" - subprocess.Popen(['%s', '-c', s]) - time.sleep(60) + p = subprocess.Popen([r'%s', '-c', s]) + p.wait() """ % (_TESTFN2, PYTHON_EXE)) # On Windows if we create a subprocess with CREATE_NO_WINDOW flag # set (which is the default) a "conhost.exe" extra process will be @@ -355,7 +373,7 @@ def create_proc_children_pair(): subp = pyrun(s) child1 = psutil.Process(subp.pid) data = wait_for_file(_TESTFN2, delete=False, empty=False) - os.remove(_TESTFN2) + safe_rmpath(_TESTFN2) child2_pid = int(data) _pids_started.add(child2_pid) child2 = psutil.Process(child2_pid) @@ -365,7 +383,7 @@ def create_proc_children_pair(): def create_zombie_proc(): """Create a zombie process and return its PID.""" assert psutil.POSIX - unix_file = tempfile.mktemp(prefix=TESTFILE_PREFIX) if OSX else TESTFN + unix_file = tempfile.mktemp(prefix=TESTFILE_PREFIX) if MACOS else TESTFN src = textwrap.dedent("""\ import os, sys, time, socket, contextlib child_pid = os.fork() @@ -385,7 +403,7 @@ def create_zombie_proc(): with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock: sock.settimeout(GLOBAL_TIMEOUT) sock.bind(unix_file) - sock.listen(1) + sock.listen(5) pyrun(src) conn, _ = sock.accept() try: @@ -399,7 +417,7 @@ def create_zombie_proc(): conn.close() -@_cleanup_on_err +@_reap_children_on_err def pyrun(src, **kwds): """Run python 'src' code string in a separate interpreter. Returns a subprocess.Popen instance. @@ -416,7 +434,7 @@ def pyrun(src, **kwds): return subp -@_cleanup_on_err +@_reap_children_on_err def sh(cmd, **kwds): """run cmd in a subprocess and return its output. raises RuntimeError on error. @@ -431,7 +449,10 @@ def sh(cmd, **kwds): kwds.setdefault("creationflags", flags) p = subprocess.Popen(cmd, **kwds) _subprocesses_started.add(p) - stdout, stderr = p.communicate() + if PY3: + stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT) + else: + stdout, stderr = p.communicate() if p.returncode != 0: raise RuntimeError(stderr) if stderr: @@ -480,7 +501,9 @@ def reap_children(recursive=False): try: subp.terminate() except OSError as err: - if err.errno != errno.ESRCH: + if WINDOWS and err.winerror == 6: # "invalid handle" + pass + elif err.errno != errno.ESRCH: raise if subp.stdout: subp.stdout.close() @@ -494,9 +517,8 @@ def reap_children(recursive=False): # Wait for the process to terminate, to avoid zombies. try: subp.wait() - except OSError as err: - if err.errno != errno.ECHILD: - raise + except ChildProcessError: + pass # Terminate started pids. while _pids_started: @@ -588,7 +610,7 @@ class retry(object): timeout=None, retries=None, interval=0.001, - logfun=lambda s: print(s, file=sys.stderr), + logfun=None, ): if timeout and retries: raise ValueError("timeout and retries args are mutually exclusive") @@ -621,7 +643,7 @@ class retry(object): for _ in self: try: return fun(*args, **kwargs) - except self.exception as _: + except self.exception as _: # NOQA exc = _ if self.logfun is not None: self.logfun(exc) @@ -659,7 +681,7 @@ def wait_for_file(fname, delete=True, empty=False): if not empty: assert data if delete: - os.remove(fname) + safe_rmpath(fname) return data @@ -681,24 +703,43 @@ def call_until(fun, expr): def safe_rmpath(path): "Convenience function for removing temporary test files or dirs" + def retry_fun(fun): + # On Windows it could happen that the file or directory has + # open handles or references preventing the delete operation + # to succeed immediately, so we retry for a while. See: + # https://bugs.python.org/issue33240 + stop_at = time.time() + 1 + while time.time() < stop_at: + try: + return fun() + except FileNotFoundError: + pass + except WindowsError as _: + err = _ + warn("ignoring %s" % (str(err))) + time.sleep(0.01) + raise err + try: st = os.stat(path) if stat.S_ISDIR(st.st_mode): - os.rmdir(path) + fun = functools.partial(shutil.rmtree, path) else: - os.remove(path) - except OSError as err: - if err.errno != errno.ENOENT: - raise + fun = functools.partial(os.remove, path) + if POSIX: + fun() + else: + retry_fun(fun) + except FileNotFoundError: + pass def safe_mkdir(dir): "Convenience function for creating a directory" try: os.mkdir(dir) - except OSError as err: - if err.errno != errno.EEXIST: - raise + except FileExistsError: + pass @contextlib.contextmanager @@ -757,9 +798,11 @@ class TestCase(unittest.TestCase): # Print a full path representation of the single unit tests # being run. def __str__(self): + fqmod = self.__class__.__module__ + if not fqmod.startswith('psutil.'): + fqmod = 'psutil.tests.' + fqmod return "%s.%s.%s" % ( - self.__class__.__module__, self.__class__.__name__, - self._testMethodName) + fqmod, self.__class__.__name__, self._testMethodName) # assertRaisesRegexp renamed to assertRaisesRegex in 3.3; # add support for the new name. @@ -771,52 +814,15 @@ class TestCase(unittest.TestCase): unittest.TestCase = TestCase -def _setup_tests(): - if 'PSUTIL_TESTING' not in os.environ: - # This won't work on Windows but set_testing() below will do it. - os.environ['PSUTIL_TESTING'] = '1' - psutil._psplatform.cext.set_testing() - - -def get_suite(): - testmods = [os.path.splitext(x)[0] for x in os.listdir(HERE) - if x.endswith('.py') and x.startswith('test_') and not - x.startswith('test_memory_leaks')] - if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ: - testmods = [x for x in testmods if not x.endswith(( - "osx", "posix", "linux"))] - suite = unittest.TestSuite() - for tm in testmods: - # ...so that the full test paths are printed on screen - tm = "psutil.tests.%s" % tm - suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm)) - return suite - - -def run_suite(): - _setup_tests() - result = unittest.TextTestRunner(verbosity=VERBOSITY).run(get_suite()) - success = result.wasSuccessful() - sys.exit(0 if success else 1) - - -def run_test_module_by_name(name): - # testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE) - # if x.endswith('.py') and x.startswith('test_')] - _setup_tests() - name = os.path.splitext(os.path.basename(name))[0] - suite = unittest.TestSuite() - suite.addTest(unittest.defaultTestLoader.loadTestsFromName(name)) - result = unittest.TextTestRunner(verbosity=VERBOSITY).run(suite) - success = result.wasSuccessful() - sys.exit(0 if success else 1) - - -def retry_before_failing(retries=NO_RETRIES): +def retry_on_failure(retries=NO_RETRIES): """Decorator which runs a test function and retries N times before actually failing. """ - return retry(exception=AssertionError, timeout=None, retries=retries) + def logfun(exc): + print("%r, retrying" % exc, file=sys.stderr) + + return retry(exception=AssertionError, timeout=None, retries=retries, + logfun=logfun) def skip_on_access_denied(only_if=None): @@ -861,7 +867,6 @@ def skip_on_not_implemented(only_if=None): def get_free_port(host='127.0.0.1'): """Return an unused TCP port.""" with contextlib.closing(socket.socket()) as sock: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, 0)) return sock.getsockname()[1] @@ -888,10 +893,11 @@ def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None): addr = ("", 0) sock = socket.socket(family, type) try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if os.name not in ('nt', 'cygwin'): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(addr) if type == socket.SOCK_STREAM: - sock.listen(10) + sock.listen(5) return sock except Exception: sock.close() @@ -906,7 +912,7 @@ def bind_unix_socket(name, type=socket.SOCK_STREAM): try: sock.bind(name) if type == socket.SOCK_STREAM: - sock.listen(10) + sock.listen(5) except Exception: sock.close() raise @@ -919,7 +925,7 @@ def tcp_socketpair(family, addr=("", 0)): """ with contextlib.closing(socket.socket(family, SOCK_STREAM)) as ll: ll.bind(addr) - ll.listen(10) + ll.listen(5) addr = ll.getsockname() c = socket.socket(family, SOCK_STREAM) try: @@ -1015,77 +1021,6 @@ def check_net_address(addr, family): raise ValueError("unknown family %r", family) -def check_connection_ntuple(conn): - """Check validity of a connection namedtuple.""" - # check ntuple - assert len(conn) in (6, 7), conn - has_pid = len(conn) == 7 - has_fd = getattr(conn, 'fd', -1) != -1 - assert conn[0] == conn.fd - assert conn[1] == conn.family - assert conn[2] == conn.type - assert conn[3] == conn.laddr - assert conn[4] == conn.raddr - assert conn[5] == conn.status - if has_pid: - assert conn[6] == conn.pid - - # check fd - if has_fd: - assert conn.fd >= 0, conn - if hasattr(socket, 'fromfd') and not WINDOWS: - try: - dupsock = socket.fromfd(conn.fd, conn.family, conn.type) - except (socket.error, OSError) as err: - if err.args[0] != errno.EBADF: - raise - else: - with contextlib.closing(dupsock): - assert dupsock.family == conn.family - assert dupsock.type == conn.type - - # check family - assert conn.family in (AF_INET, AF_INET6, AF_UNIX), repr(conn.family) - if conn.family in (AF_INET, AF_INET6): - # actually try to bind the local socket; ignore IPv6 - # sockets as their address might be represented as - # an IPv4-mapped-address (e.g. "::127.0.0.1") - # and that's rejected by bind() - if conn.family == AF_INET: - s = socket.socket(conn.family, conn.type) - with contextlib.closing(s): - try: - s.bind((conn.laddr[0], 0)) - except socket.error as err: - if err.errno != errno.EADDRNOTAVAIL: - raise - elif conn.family == AF_UNIX: - assert conn.status == psutil.CONN_NONE, conn.status - - # check type (SOCK_SEQPACKET may happen in case of AF_UNIX socks) - assert conn.type in (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET), \ - repr(conn.type) - if conn.type == SOCK_DGRAM: - assert conn.status == psutil.CONN_NONE, conn.status - - # check laddr (IP address and port sanity) - for addr in (conn.laddr, conn.raddr): - if conn.family in (AF_INET, AF_INET6): - assert isinstance(addr, tuple), addr - if not addr: - continue - assert isinstance(addr.port, int), addr.port - assert 0 <= addr.port <= 65535, addr.port - check_net_address(addr.ip, conn.family) - elif conn.family == AF_UNIX: - assert isinstance(addr, str), addr - - # check status - assert isinstance(conn.status, str), conn - valids = [getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_')] - assert conn.status in valids, conn - - # =================================================================== # --- compatibility # =================================================================== @@ -1149,11 +1084,12 @@ if POSIX: by this process, copies it in another location and loads it in memory via ctypes. Return the new absolutized path. """ + exe = 'pypy' if PYPY else 'python' ext = ".so" dst = tempfile.mktemp(prefix=dst_prefix, suffix=ext) libs = [x.path for x in psutil.Process().memory_maps() if os.path.splitext(x.path)[1] == ext and - 'python' in x.path.lower()] + exe in x.path.lower()] src = random.choice(libs) shutil.copyfile(src, dst) try: @@ -1174,9 +1110,12 @@ else: ext = ".dll" dst = tempfile.mktemp(prefix=dst_prefix, suffix=ext) libs = [x.path for x in psutil.Process().memory_maps() if - os.path.splitext(x.path)[1].lower() == ext and + x.path.lower().endswith(ext) and 'python' in os.path.basename(x.path).lower() and 'wow64' not in x.path.lower()] + if PYPY and not libs: + libs = [x.path for x in psutil.Process().memory_maps() if + 'pypy' in os.path.basename(x.path).lower()] src = random.choice(libs) shutil.copyfile(src, dst) cfile = None diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__main__.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__main__.py index 2cdf5c425eb4..d5cd02eb1c28 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__main__.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/__main__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -6,91 +6,8 @@ """ Run unit tests. This is invoked by: - $ python -m psutil.tests """ -import contextlib -import optparse -import os -import ssl -import sys -import tempfile -try: - from urllib.request import urlopen # py3 -except ImportError: - from urllib2 import urlopen - -from psutil.tests import PYTHON_EXE -from psutil.tests import run_suite - - -HERE = os.path.abspath(os.path.dirname(__file__)) -GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -TEST_DEPS = [] -if sys.version_info[:2] == (2, 6): - TEST_DEPS.extend(["ipaddress", "unittest2", "argparse", "mock==1.0.1"]) -elif sys.version_info[:2] == (2, 7) or sys.version_info[:2] <= (3, 2): - TEST_DEPS.extend(["ipaddress", "mock"]) -elif sys.version_info[:2] == (3, 3): - TEST_DEPS.extend(["ipaddress"]) - - -def install_pip(): - try: - import pip # NOQA - except ImportError: - f = tempfile.NamedTemporaryFile(suffix='.py') - with contextlib.closing(f): - print("downloading %s to %s" % (GET_PIP_URL, f.name)) - if hasattr(ssl, '_create_unverified_context'): - ctx = ssl._create_unverified_context() - else: - ctx = None - kwargs = dict(context=ctx) if ctx else {} - req = urlopen(GET_PIP_URL, **kwargs) - data = req.read() - f.write(data) - f.flush() - - print("installing pip") - code = os.system('%s %s --user' % (PYTHON_EXE, f.name)) - return code - - -def install_test_deps(deps=None): - """Install test dependencies via pip.""" - if deps is None: - deps = TEST_DEPS - deps = set(deps) - if deps: - is_venv = hasattr(sys, 'real_prefix') - opts = "--user" if not is_venv else "" - install_pip() - code = os.system('%s -m pip install %s --upgrade %s' % ( - PYTHON_EXE, opts, " ".join(deps))) - return code - - -def main(): - usage = "%s -m psutil.tests [opts]" % PYTHON_EXE - parser = optparse.OptionParser(usage=usage, description="run unit tests") - parser.add_option("-i", "--install-deps", - action="store_true", default=False, - help="don't print status messages to stdout") - - opts, args = parser.parse_args() - if opts.install_deps: - install_pip() - install_test_deps() - else: - for dep in TEST_DEPS: - try: - __import__(dep.split("==")[0]) - except ImportError: - sys.exit("%r lib is not installed; run %s -m psutil.tests " - "--install-deps" % (dep, PYTHON_EXE)) - run_suite() - - +from .runner import main main() diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/runner.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/runner.py new file mode 100644 index 000000000000..2e9264bd4281 --- /dev/null +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/runner.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Unit test runner, providing new features on top of unittest module: +- colourized output (error, skip) +- print failures/tracebacks on CTRL+C +- re-run failed tests only (make test-failed) +""" + +from __future__ import print_function +import optparse +import os +import sys +import unittest +from unittest import TestResult +from unittest import TextTestResult +from unittest import TextTestRunner +try: + import ctypes +except ImportError: + ctypes = None + +import psutil +from psutil._common import hilite +from psutil._common import print_color +from psutil._common import term_supports_colors +from psutil.tests import safe_rmpath +from psutil.tests import TOX + + +HERE = os.path.abspath(os.path.dirname(__file__)) +VERBOSITY = 1 if TOX else 2 +FAILED_TESTS_FNAME = '.failed-tests.txt' + + +# ===================================================================== +# --- unittest subclasses +# ===================================================================== + + +class ColouredResult(TextTestResult): + + def _print_color(self, s, color, bold=False): + file = sys.stderr if color == "red" else sys.stdout + print_color(s, color, bold=bold, file=file) + + def addSuccess(self, test): + TestResult.addSuccess(self, test) + self._print_color("OK", "green") + + def addError(self, test, err): + TestResult.addError(self, test, err) + self._print_color("ERROR", "red", bold=True) + + def addFailure(self, test, err): + TestResult.addFailure(self, test, err) + self._print_color("FAIL", "red") + + def addSkip(self, test, reason): + TestResult.addSkip(self, test, reason) + self._print_color("skipped: %s" % reason, "brown") + + def printErrorList(self, flavour, errors): + flavour = hilite(flavour, "red", bold=flavour == 'ERROR') + TextTestResult.printErrorList(self, flavour, errors) + + +class ColouredRunner(TextTestRunner): + resultclass = ColouredResult if term_supports_colors() else TextTestResult + + def _makeResult(self): + # Store result instance so that it can be accessed on + # KeyboardInterrupt. + self.result = TextTestRunner._makeResult(self) + return self.result + + +# ===================================================================== +# --- public API +# ===================================================================== + + +def setup_tests(): + if 'PSUTIL_TESTING' not in os.environ: + # This won't work on Windows but set_testing() below will do it. + os.environ['PSUTIL_TESTING'] = '1' + psutil._psplatform.cext.set_testing() + + +def get_suite(name=None): + suite = unittest.TestSuite() + if name is None: + testmods = [os.path.splitext(x)[0] for x in os.listdir(HERE) + if x.endswith('.py') and x.startswith('test_') and not + x.startswith('test_memory_leaks')] + if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ: + testmods = [x for x in testmods if not x.endswith(( + "osx", "posix", "linux"))] + for tm in testmods: + # ...so that the full test paths are printed on screen + tm = "psutil.tests.%s" % tm + suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm)) + else: + name = os.path.splitext(os.path.basename(name))[0] + suite.addTest(unittest.defaultTestLoader.loadTestsFromName(name)) + return suite + + +def get_suite_from_failed(): + # ...from previously failed test run + suite = unittest.TestSuite() + if not os.path.isfile(FAILED_TESTS_FNAME): + return suite + with open(FAILED_TESTS_FNAME, 'rt') as f: + names = f.read().split() + for n in names: + suite.addTest(unittest.defaultTestLoader.loadTestsFromName(n)) + return suite + + +def save_failed_tests(result): + if result.wasSuccessful(): + return safe_rmpath(FAILED_TESTS_FNAME) + with open(FAILED_TESTS_FNAME, 'wt') as f: + for t in result.errors + result.failures: + tname = str(t[0]) + unittest.defaultTestLoader.loadTestsFromName(tname) + f.write(tname + '\n') + + +def run(name=None, last_failed=False): + setup_tests() + runner = ColouredRunner(verbosity=VERBOSITY) + suite = get_suite_from_failed() if last_failed else get_suite(name) + try: + result = runner.run(suite) + except (KeyboardInterrupt, SystemExit) as err: + print("received %s" % err.__class__.__name__, file=sys.stderr) + runner.result.printErrors() + sys.exit(1) + else: + save_failed_tests(result) + success = result.wasSuccessful() + sys.exit(0 if success else 1) + + +def main(): + usage = "python3 -m psutil.tests [opts]" + parser = optparse.OptionParser(usage=usage, description="run unit tests") + parser.add_option("--last-failed", + action="store_true", default=False, + help="only run last failed tests") + opts, args = parser.parse_args() + run(last_failed=opts.last_failed) + + +if __name__ == '__main__': + main() diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_aix.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_aix.py index 7a8a4c334298..7171232e08f1 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_aix.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_aix.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola' # Copyright (c) 2017, Arnon Yaari @@ -11,7 +11,6 @@ import re from psutil import AIX -from psutil.tests import run_test_module_by_name from psutil.tests import sh from psutil.tests import unittest import psutil @@ -22,9 +21,9 @@ class AIXSpecificTestCase(unittest.TestCase): def test_virtual_memory(self): out = sh('/usr/bin/svmon -O unit=KB') - re_pattern = "memory\s*" + re_pattern = r"memory\s*" for field in ("size inuse free pin virtual available mmode").split(): - re_pattern += "(?P<%s>\S+)\s+" % (field,) + re_pattern += r"(?P<%s>\S+)\s+" % (field,) matchobj = re.search(re_pattern, out) self.assertIsNotNone( @@ -56,10 +55,10 @@ class AIXSpecificTestCase(unittest.TestCase): # we'll always have 'MB' in the result # TODO maybe try to use "swap -l" to check "used" too, but its units # are not guaranteed to be "MB" so parsing may not be consistent - matchobj = re.search("(?P\S+)\s+" - "(?P\S+)\s+" - "(?P\S+)\s+" - "(?P\d+)MB", out) + matchobj = re.search(r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\d+)MB", out) self.assertIsNotNone( matchobj, "lsps command returned unexpected output") @@ -74,11 +73,11 @@ class AIXSpecificTestCase(unittest.TestCase): def test_cpu_stats(self): out = sh('/usr/bin/mpstat -a') - re_pattern = "ALL\s*" + re_pattern = r"ALL\s*" for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq " "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd " "sysc").split(): - re_pattern += "(?P<%s>\S+)\s+" % (field,) + re_pattern += r"(?P<%s>\S+)\s+" % (field,) matchobj = re.search(re_pattern, out) self.assertIsNotNone( @@ -106,7 +105,7 @@ class AIXSpecificTestCase(unittest.TestCase): def test_cpu_count_logical(self): out = sh('/usr/bin/mpstat -a') - mpstat_lcpu = int(re.search("lcpu=(\d+)", out).group(1)) + mpstat_lcpu = int(re.search(r"lcpu=(\d+)", out).group(1)) psutil_lcpu = psutil.cpu_count(logical=True) self.assertEqual(mpstat_lcpu, psutil_lcpu) @@ -118,4 +117,5 @@ class AIXSpecificTestCase(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_bsd.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_bsd.py index d3868ada18e1..899875d076dc 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_bsd.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_bsd.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -24,8 +24,7 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import MEMORY_TOLERANCE from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import unittest from psutil.tests import which @@ -73,7 +72,7 @@ def muse(field): @unittest.skipIf(not BSD, "BSD only") -class BSDSpecificTestCase(unittest.TestCase): +class BSDTestCase(unittest.TestCase): """Generic tests common to all BSD variants.""" @classmethod @@ -149,7 +148,7 @@ class BSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not FREEBSD, "FREEBSD only") -class FreeBSDSpecificTestCase(unittest.TestCase): +class FreeBSDProcessTestCase(unittest.TestCase): @classmethod def setUpClass(cls): @@ -159,8 +158,8 @@ class FreeBSDSpecificTestCase(unittest.TestCase): def tearDownClass(cls): reap_children() - @retry_before_failing() - def test_proc_memory_maps(self): + @retry_on_failure() + def test_memory_maps(self): out = sh('procstat -v %s' % self.pid) maps = psutil.Process(self.pid).memory_maps(grouped=False) lines = out.split('\n')[1:] @@ -174,17 +173,17 @@ class FreeBSDSpecificTestCase(unittest.TestCase): if not map.path.startswith('['): self.assertEqual(fields[10], map.path) - def test_proc_exe(self): + def test_exe(self): out = sh('procstat -b %s' % self.pid) self.assertEqual(psutil.Process(self.pid).exe(), out.split('\n')[1].split()[-1]) - def test_proc_cmdline(self): + def test_cmdline(self): out = sh('procstat -c %s' % self.pid) self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), ' '.join(out.split('\n')[1].split()[2:])) - def test_proc_uids_gids(self): + def test_uids_gids(self): out = sh('procstat -s %s' % self.pid) euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] p = psutil.Process(self.pid) @@ -197,8 +196,8 @@ class FreeBSDSpecificTestCase(unittest.TestCase): self.assertEqual(gids.effective, int(egid)) self.assertEqual(gids.saved, int(sgid)) - @retry_before_failing() - def test_proc_ctx_switches(self): + @retry_on_failure() + def test_ctx_switches(self): tested = [] out = sh('procstat -r %s' % self.pid) p = psutil.Process(self.pid) @@ -217,8 +216,8 @@ class FreeBSDSpecificTestCase(unittest.TestCase): if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") - @retry_before_failing() - def test_proc_cpu_times(self): + @retry_on_failure() + def test_cpu_times(self): tested = [] out = sh('procstat -r %s' % self.pid) p = psutil.Process(self.pid) @@ -237,39 +236,76 @@ class FreeBSDSpecificTestCase(unittest.TestCase): if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") + +@unittest.skipIf(not FREEBSD, "FREEBSD only") +class FreeBSDSystemTestCase(unittest.TestCase): + + @staticmethod + def parse_swapinfo(): + # the last line is always the total + output = sh("swapinfo -k").splitlines()[-1] + parts = re.split(r'\s+', output) + + if not parts: + raise ValueError("Can't parse swapinfo: %s" % output) + + # the size is in 1k units, so multiply by 1024 + total, used, free = (int(p) * 1024 for p in parts[1:4]) + return total, used, free + + def test_cpu_frequency_against_sysctl(self): + # Currently only cpu 0 is frequency is supported in FreeBSD + # All other cores use the same frequency. + sensor = "dev.cpu.0.freq" + try: + sysctl_result = int(sysctl(sensor)) + except RuntimeError: + self.skipTest("frequencies not supported by kernel") + self.assertEqual(psutil.cpu_freq().current, sysctl_result) + + sensor = "dev.cpu.0.freq_levels" + sysctl_result = sysctl(sensor) + # sysctl returns a string of the format: + # / /... + # Ordered highest available to lowest available. + max_freq = int(sysctl_result.split()[0].split("/")[0]) + min_freq = int(sysctl_result.split()[-1].split("/")[0]) + self.assertEqual(psutil.cpu_freq().max, max_freq) + self.assertEqual(psutil.cpu_freq().min, min_freq) + # --- virtual_memory(); tests against sysctl - @retry_before_failing() + @retry_on_failure() def test_vmem_active(self): syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE self.assertAlmostEqual(psutil.virtual_memory().active, syst, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_inactive(self): syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_wired(self): syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE self.assertAlmostEqual(psutil.virtual_memory().wired, syst, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_cached(self): syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE self.assertAlmostEqual(psutil.virtual_memory().cached, syst, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_free(self): syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE self.assertAlmostEqual(psutil.virtual_memory().free, syst, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_buffers(self): syst = sysctl("vfs.bufspace") self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, @@ -283,42 +319,42 @@ class FreeBSDSpecificTestCase(unittest.TestCase): self.assertEqual(psutil.virtual_memory().total, num) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_active(self): num = muse('Active') self.assertAlmostEqual(psutil.virtual_memory().active, num, delta=MEMORY_TOLERANCE) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_inactive(self): num = muse('Inactive') self.assertAlmostEqual(psutil.virtual_memory().inactive, num, delta=MEMORY_TOLERANCE) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_wired(self): num = muse('Wired') self.assertAlmostEqual(psutil.virtual_memory().wired, num, delta=MEMORY_TOLERANCE) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_cached(self): num = muse('Cache') self.assertAlmostEqual(psutil.virtual_memory().cached, num, delta=MEMORY_TOLERANCE) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_free(self): num = muse('Free') self.assertAlmostEqual(psutil.virtual_memory().free, num, delta=MEMORY_TOLERANCE) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") - @retry_before_failing() + @retry_on_failure() def test_muse_vmem_buffers(self): num = muse('Buffer') self.assertAlmostEqual(psutil.virtual_memory().buffers, num, @@ -336,14 +372,33 @@ class FreeBSDSpecificTestCase(unittest.TestCase): self.assertAlmostEqual(psutil.cpu_stats().soft_interrupts, sysctl('vm.stats.sys.v_soft'), delta=1000) + @retry_on_failure() def test_cpu_stats_syscalls(self): + # pretty high tolerance but it looks like it's OK. self.assertAlmostEqual(psutil.cpu_stats().syscalls, - sysctl('vm.stats.sys.v_syscall'), delta=1000) + sysctl('vm.stats.sys.v_syscall'), delta=200000) # def test_cpu_stats_traps(self): # self.assertAlmostEqual(psutil.cpu_stats().traps, # sysctl('vm.stats.sys.v_trap'), delta=1000) + # --- swap memory + + def test_swapmem_free(self): + total, used, free = self.parse_swapinfo() + self.assertAlmostEqual( + psutil.swap_memory().free, free, delta=MEMORY_TOLERANCE) + + def test_swapmem_used(self): + total, used, free = self.parse_swapinfo() + self.assertAlmostEqual( + psutil.swap_memory().used, used, delta=MEMORY_TOLERANCE) + + def test_swapmem_total(self): + total, used, free = self.parse_swapinfo() + self.assertAlmostEqual( + psutil.swap_memory().total, total, delta=MEMORY_TOLERANCE) + # --- others def test_boot_time(self): @@ -397,6 +452,26 @@ class FreeBSDSpecificTestCase(unittest.TestCase): sysctl("hw.acpi.acline") self.assertIsNone(psutil.sensors_battery()) + # --- sensors_temperatures + + def test_sensors_temperatures_against_sysctl(self): + num_cpus = psutil.cpu_count(True) + for cpu in range(num_cpus): + sensor = "dev.cpu.%s.temperature" % cpu + # sysctl returns a string in the format 46.0C + try: + sysctl_result = int(float(sysctl(sensor)[:-1])) + except RuntimeError: + self.skipTest("temperatures not supported by kernel") + self.assertAlmostEqual( + psutil.sensors_temperatures()["coretemp"][cpu].current, + sysctl_result, delta=10) + + sensor = "dev.cpu.%s.coretemp.tjmax" % cpu + sysctl_result = int(float(sysctl(sensor)[:-1])) + self.assertEqual( + psutil.sensors_temperatures()["coretemp"][cpu].high, + sysctl_result) # ===================================================================== # --- OpenBSD @@ -404,7 +479,7 @@ class FreeBSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not OPENBSD, "OPENBSD only") -class OpenBSDSpecificTestCase(unittest.TestCase): +class OpenBSDTestCase(unittest.TestCase): def test_boot_time(self): s = sysctl('kern.boottime') @@ -419,11 +494,11 @@ class OpenBSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not NETBSD, "NETBSD only") -class NetBSDSpecificTestCase(unittest.TestCase): +class NetBSDTestCase(unittest.TestCase): @staticmethod def parse_meminfo(look_for): - with open('/proc/meminfo', 'rb') as f: + with open('/proc/meminfo', 'rt') as f: for line in f: if line.startswith(look_for): return int(line.split()[1]) * 1024 @@ -486,4 +561,5 @@ class NetBSDSpecificTestCase(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_connections.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_connections.py index 176e266481ab..972ac9d58ccb 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_connections.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_connections.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -6,6 +6,8 @@ """Tests for net_connections() and Process.connections() APIs.""" +import contextlib +import errno import os import socket import textwrap @@ -18,9 +20,9 @@ from socket import SOCK_STREAM import psutil from psutil import FREEBSD from psutil import LINUX +from psutil import MACOS from psutil import NETBSD from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS @@ -29,15 +31,17 @@ from psutil._compat import PY3 from psutil.tests import AF_UNIX from psutil.tests import bind_socket from psutil.tests import bind_unix_socket -from psutil.tests import check_connection_ntuple +from psutil.tests import check_net_address +from psutil.tests import CIRRUS from psutil.tests import create_sockets +from psutil.tests import enum from psutil.tests import get_free_port from psutil.tests import HAS_CONNECTIONS_UNIX from psutil.tests import pyrun from psutil.tests import reap_children -from psutil.tests import run_test_module_by_name from psutil.tests import safe_rmpath from psutil.tests import skip_on_access_denied +from psutil.tests import SKIP_SYSCONS from psutil.tests import tcp_socketpair from psutil.tests import TESTFN from psutil.tests import TRAVIS @@ -48,29 +52,147 @@ from psutil.tests import wait_for_file thisproc = psutil.Process() +SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) class Base(object): def setUp(self): - if not NETBSD: - # NetBSD opens a UNIX socket to /var/log/run. + safe_rmpath(TESTFN) + if not (NETBSD or FREEBSD): + # process opens a UNIX socket to /var/log/run. cons = thisproc.connections(kind='all') assert not cons, cons def tearDown(self): safe_rmpath(TESTFN) reap_children() - if not NETBSD: + if not (FREEBSD or NETBSD): # Make sure we closed all resources. # NetBSD opens a UNIX socket to /var/log/run. cons = thisproc.connections(kind='all') assert not cons, cons + def compare_procsys_connections(self, pid, proc_cons, kind='all'): + """Given a process PID and its list of connections compare + those against system-wide connections retrieved via + psutil.net_connections. + """ + try: + sys_cons = psutil.net_connections(kind=kind) + except psutil.AccessDenied: + # On MACOS, system-wide connections are retrieved by iterating + # over all processes + if MACOS: + return + else: + raise + # Filter for this proc PID and exlucde PIDs from the tuple. + sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] + sys_cons.sort() + proc_cons.sort() + self.assertEqual(proc_cons, sys_cons) + + def check_connection_ntuple(self, conn): + """Check validity of a connection namedtuple.""" + def check_ntuple(conn): + has_pid = len(conn) == 7 + self.assertIn(len(conn), (6, 7)) + self.assertEqual(conn[0], conn.fd) + self.assertEqual(conn[1], conn.family) + self.assertEqual(conn[2], conn.type) + self.assertEqual(conn[3], conn.laddr) + self.assertEqual(conn[4], conn.raddr) + self.assertEqual(conn[5], conn.status) + if has_pid: + self.assertEqual(conn[6], conn.pid) + + def check_family(conn): + self.assertIn(conn.family, (AF_INET, AF_INET6, AF_UNIX)) + if enum is not None: + assert isinstance(conn.family, enum.IntEnum), conn + else: + assert isinstance(conn.family, int), conn + if conn.family == AF_INET: + # actually try to bind the local socket; ignore IPv6 + # sockets as their address might be represented as + # an IPv4-mapped-address (e.g. "::127.0.0.1") + # and that's rejected by bind() + s = socket.socket(conn.family, conn.type) + with contextlib.closing(s): + try: + s.bind((conn.laddr[0], 0)) + except socket.error as err: + if err.errno != errno.EADDRNOTAVAIL: + raise + elif conn.family == AF_UNIX: + self.assertEqual(conn.status, psutil.CONN_NONE) + + def check_type(conn): + # SOCK_SEQPACKET may happen in case of AF_UNIX socks + self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) + if enum is not None: + assert isinstance(conn.type, enum.IntEnum), conn + else: + assert isinstance(conn.type, int), conn + if conn.type == SOCK_DGRAM: + self.assertEqual(conn.status, psutil.CONN_NONE) + + def check_addrs(conn): + # check IP address and port sanity + for addr in (conn.laddr, conn.raddr): + if conn.family in (AF_INET, AF_INET6): + self.assertIsInstance(addr, tuple) + if not addr: + continue + self.assertIsInstance(addr.port, int) + assert 0 <= addr.port <= 65535, addr.port + check_net_address(addr.ip, conn.family) + elif conn.family == AF_UNIX: + self.assertIsInstance(addr, str) + + def check_status(conn): + self.assertIsInstance(conn.status, str) + valids = [getattr(psutil, x) for x in dir(psutil) + if x.startswith('CONN_')] + self.assertIn(conn.status, valids) + if conn.family in (AF_INET, AF_INET6) and conn.type == SOCK_STREAM: + self.assertNotEqual(conn.status, psutil.CONN_NONE) + else: + self.assertEqual(conn.status, psutil.CONN_NONE) + + check_ntuple(conn) + check_family(conn) + check_type(conn) + check_addrs(conn) + check_status(conn) + + +class TestBase(Base, unittest.TestCase): + + @unittest.skipIf(SKIP_SYSCONS, "requires root") + def test_system(self): + with create_sockets(): + for conn in psutil.net_connections(kind='all'): + self.check_connection_ntuple(conn) + + def test_process(self): + with create_sockets(): + for conn in psutil.Process().connections(kind='all'): + self.check_connection_ntuple(conn) + + def test_invalid_kind(self): + self.assertRaises(ValueError, thisproc.connections, kind='???') + self.assertRaises(ValueError, psutil.net_connections, kind='???') + + +class TestUnconnectedSockets(Base, unittest.TestCase): + """Tests sockets which are open but not connected to anything.""" + def get_conn_from_sock(self, sock): cons = thisproc.connections(kind='all') smap = dict([(c.fd, c) for c in cons]) - if NETBSD: + if NETBSD or FREEBSD: # NetBSD opens a UNIX socket to /var/log/run # so there may be more connections. return smap[sock.fileno()] @@ -80,14 +202,13 @@ class Base(object): self.assertEqual(smap[sock.fileno()].fd, sock.fileno()) return cons[0] - def check_socket(self, sock, conn=None): + def check_socket(self, sock): """Given a socket, makes sure it matches the one obtained via psutil. It assumes this process created one connection only (the one supposed to be checked). """ - if conn is None: - conn = self.get_conn_from_sock(sock) - check_connection_ntuple(conn) + conn = self.get_conn_from_sock(sock) + self.check_connection_ntuple(conn) # fd, family, type if conn.fd != -1: @@ -113,38 +234,9 @@ class Base(object): # XXX Solaris can't retrieve system-wide UNIX sockets if sock.family == AF_UNIX and HAS_CONNECTIONS_UNIX: cons = thisproc.connections(kind='all') - self.compare_procsys_connections(os.getpid(), cons) + self.compare_procsys_connections(os.getpid(), cons, kind='all') return conn - def compare_procsys_connections(self, pid, proc_cons, kind='all'): - """Given a process PID and its list of connections compare - those against system-wide connections retrieved via - psutil.net_connections. - """ - try: - sys_cons = psutil.net_connections(kind=kind) - except psutil.AccessDenied: - # On OSX, system-wide connections are retrieved by iterating - # over all processes - if OSX: - return - else: - raise - # Filter for this proc PID and exlucde PIDs from the tuple. - sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] - sys_cons.sort() - proc_cons.sort() - self.assertEqual(proc_cons, sys_cons) - - -# ===================================================================== -# --- Test unconnected sockets -# ===================================================================== - - -class TestUnconnectedSockets(Base, unittest.TestCase): - """Tests sockets which are open but not connected to anything.""" - def test_tcp_v4(self): addr = ("127.0.0.1", get_free_port()) with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock: @@ -192,12 +284,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase): self.assertEqual(conn.status, psutil.CONN_NONE) -# ===================================================================== -# --- Test connected sockets -# ===================================================================== - - -class TestConnectedSocketPairs(Base, unittest.TestCase): +class TestConnectedSocket(Base, unittest.TestCase): """Test socket pairs which are are actually connected to each other. """ @@ -232,11 +319,14 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): cons = thisproc.connections(kind='unix') assert not (cons[0].laddr and cons[0].raddr) assert not (cons[1].laddr and cons[1].raddr) - if NETBSD: + if NETBSD or FREEBSD: # On NetBSD creating a UNIX socket will cause # a UNIX connection to /var/run/log. cons = [c for c in cons if c.raddr != '/var/run/log'] - self.assertEqual(len(cons), 2) + if CIRRUS: + cons = [c for c in cons if c.fd in + (server.fileno(), client.fileno())] + self.assertEqual(len(cons), 2, msg=cons) if LINUX or FREEBSD or SUNOS: # remote path is never set self.assertEqual(cons[0].raddr, "") @@ -257,12 +347,58 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): server.close() client.close() - @skip_on_access_denied(only_if=OSX) + +class TestFilters(Base, unittest.TestCase): + + def test_filters(self): + def check(kind, families, types): + for conn in thisproc.connections(kind=kind): + self.assertIn(conn.family, families) + self.assertIn(conn.type, types) + if not SKIP_SYSCONS: + for conn in psutil.net_connections(kind=kind): + self.assertIn(conn.family, families) + self.assertIn(conn.type, types) + + with create_sockets(): + check('all', + [AF_INET, AF_INET6, AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) + check('inet', + [AF_INET, AF_INET6], + [SOCK_STREAM, SOCK_DGRAM]) + check('inet4', + [AF_INET], + [SOCK_STREAM, SOCK_DGRAM]) + check('tcp', + [AF_INET, AF_INET6], + [SOCK_STREAM]) + check('tcp4', + [AF_INET], + [SOCK_STREAM]) + check('tcp6', + [AF_INET6], + [SOCK_STREAM]) + check('udp', + [AF_INET, AF_INET6], + [SOCK_DGRAM]) + check('udp4', + [AF_INET], + [SOCK_DGRAM]) + check('udp6', + [AF_INET6], + [SOCK_DGRAM]) + if HAS_CONNECTIONS_UNIX: + check('unix', + [AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) + + @skip_on_access_denied(only_if=MACOS) def test_combos(self): def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6") - check_connection_ntuple(conn) + self.check_connection_ntuple(conn) self.assertEqual(conn.family, family) self.assertEqual(conn.type, type) self.assertEqual(conn.laddr, laddr) @@ -284,7 +420,7 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): import socket, time s = socket.socket($family, socket.SOCK_STREAM) s.bind(('$addr', 0)) - s.listen(1) + s.listen(5) with open('$testfn', 'w') as f: f.write(str(s.getsockname()[:2])) time.sleep(60) @@ -352,13 +488,8 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): psutil.CONN_NONE, ("all", "inet", "inet6", "udp", "udp6")) - # err - self.assertRaises(ValueError, p.connections, kind='???') - - def test_multi_sockets_filtering(self): - with create_sockets() as socks: - cons = thisproc.connections(kind='all') - self.assertEqual(len(cons), len(socks)) + def test_count(self): + with create_sockets(): # tcp cons = thisproc.connections(kind='tcp') self.assertEqual(len(cons), 2 if supports_ipv6() else 1) @@ -406,8 +537,9 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): for conn in cons: self.assertEqual(conn.family, AF_INET6) self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) - # unix - if HAS_CONNECTIONS_UNIX: + # Skipped on BSD becayse by default the Python process + # creates a UNIX socket to '/var/run/log'. + if HAS_CONNECTIONS_UNIX and not (FREEBSD or NETBSD): cons = thisproc.connections(kind='unix') self.assertEqual(len(cons), 3) for conn in cons: @@ -415,23 +547,17 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) -# ===================================================================== -# --- Miscellaneous tests -# ===================================================================== - - +@unittest.skipIf(SKIP_SYSCONS, "requires root") class TestSystemWideConnections(Base, unittest.TestCase): """Tests for net_connections().""" - @skip_on_access_denied() def test_it(self): def check(cons, families, types_): - AF_UNIX = getattr(socket, 'AF_UNIX', object()) for conn in cons: self.assertIn(conn.family, families, msg=conn) if conn.family != AF_UNIX: self.assertIn(conn.type, types_, msg=conn) - check_connection_ntuple(conn) + self.check_connection_ntuple(conn) with create_sockets(): from psutil._common import conn_tmap @@ -444,18 +570,8 @@ class TestSystemWideConnections(Base, unittest.TestCase): self.assertEqual(len(cons), len(set(cons))) check(cons, families, types_) - self.assertRaises(ValueError, psutil.net_connections, kind='???') - - @skip_on_access_denied() - def test_multi_socks(self): - with create_sockets() as socks: - cons = [x for x in psutil.net_connections(kind='all') - if x.pid == os.getpid()] - self.assertEqual(len(cons), len(socks)) - - @skip_on_access_denied() # See: https://travis-ci.org/giampaolo/psutil/jobs/237566297 - @unittest.skipIf(OSX and TRAVIS, "unreliable on OSX + TRAVIS") + @unittest.skipIf(MACOS and TRAVIS, "unreliable on MACOS + TRAVIS") def test_multi_sockets_procs(self): # Creates multiple sub processes, each creating different # sockets. For each process check that proc.connections() @@ -473,7 +589,7 @@ class TestSystemWideConnections(Base, unittest.TestCase): import time, os from psutil.tests import create_sockets with create_sockets(): - with open('%s', 'w') as f: + with open(r'%s', 'w') as f: f.write(str(os.getpid())) time.sleep(60) """ % fname) @@ -495,11 +611,6 @@ class TestSystemWideConnections(Base, unittest.TestCase): self.assertEqual(len(p.connections('all')), expected) -# ===================================================================== -# --- Miscellaneous tests -# ===================================================================== - - class TestMisc(unittest.TestCase): def test_connection_constants(self): @@ -522,4 +633,5 @@ class TestMisc(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_contracts.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_contracts.py index 855b53bf9f36..312f17d9a39d 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_contracts.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_contracts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -14,35 +14,32 @@ import os import stat import time import traceback -import warnings -from contextlib import closing from psutil import AIX from psutil import BSD from psutil import FREEBSD from psutil import LINUX +from psutil import MACOS from psutil import NETBSD from psutil import OPENBSD from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS -from psutil._compat import callable from psutil._compat import long -from psutil.tests import bind_unix_socket -from psutil.tests import check_connection_ntuple +from psutil.tests import create_sockets +from psutil.tests import enum from psutil.tests import get_kernel_version -from psutil.tests import HAS_CONNECTIONS_UNIX +from psutil.tests import HAS_CPU_FREQ +from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_RLIMIT from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import is_namedtuple -from psutil.tests import run_test_module_by_name from psutil.tests import safe_rmpath -from psutil.tests import skip_on_access_denied +from psutil.tests import SKIP_SYSCONS from psutil.tests import TESTFN from psutil.tests import unittest -from psutil.tests import unix_socket_path from psutil.tests import VALID_PROC_STATUSES from psutil.tests import warn import psutil @@ -52,19 +49,10 @@ import psutil # --- APIs availability # =================================================================== +# Make sure code reflects what doc promises in terms of APIs +# availability. -class TestAvailability(unittest.TestCase): - """Make sure code reflects what doc promises in terms of APIs - availability. - """ - - def test_cpu_affinity(self): - hasit = LINUX or WINDOWS or FREEBSD - self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), hasit) - - def test_win_service(self): - self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) - self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) +class TestAvailConstantsAPIs(unittest.TestCase): def test_PROCFS_PATH(self): self.assertEqual(hasattr(psutil, "PROCFS_PATH"), @@ -79,13 +67,20 @@ class TestAvailability(unittest.TestCase): ae(hasattr(psutil, "NORMAL_PRIORITY_CLASS"), WINDOWS) ae(hasattr(psutil, "REALTIME_PRIORITY_CLASS"), WINDOWS) - def test_linux_ioprio(self): + def test_linux_ioprio_linux(self): ae = self.assertEqual ae(hasattr(psutil, "IOPRIO_CLASS_NONE"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_RT"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_BE"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_IDLE"), LINUX) + def test_linux_ioprio_windows(self): + ae = self.assertEqual + ae(hasattr(psutil, "IOPRIO_HIGH"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_NORMAL"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS) + def test_linux_rlimit(self): ae = self.assertEqual hasit = LINUX and get_kernel_version() >= (2, 6, 36) @@ -110,78 +105,77 @@ class TestAvailability(unittest.TestCase): ae(hasattr(psutil, "RLIMIT_RTTIME"), hasit) ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit) + +class TestAvailSystemAPIs(unittest.TestCase): + + def test_win_service_iter(self): + self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) + + def test_win_service_get(self): + self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) + def test_cpu_freq(self): linux = (LINUX and (os.path.exists("/sys/devices/system/cpu/cpufreq") or os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"))) - self.assertEqual(hasattr(psutil, "cpu_freq"), linux or OSX or WINDOWS) + self.assertEqual(hasattr(psutil, "cpu_freq"), + linux or MACOS or WINDOWS or FREEBSD) def test_sensors_temperatures(self): - self.assertEqual(hasattr(psutil, "sensors_temperatures"), LINUX) + self.assertEqual( + hasattr(psutil, "sensors_temperatures"), LINUX or FREEBSD) def test_sensors_fans(self): self.assertEqual(hasattr(psutil, "sensors_fans"), LINUX) def test_battery(self): self.assertEqual(hasattr(psutil, "sensors_battery"), - LINUX or WINDOWS or FREEBSD or OSX) + LINUX or WINDOWS or FREEBSD or MACOS) - def test_proc_environ(self): + +class TestAvailProcessAPIs(unittest.TestCase): + + def test_environ(self): self.assertEqual(hasattr(psutil.Process, "environ"), - LINUX or OSX or WINDOWS) + LINUX or MACOS or WINDOWS or AIX or SUNOS) - def test_proc_uids(self): + def test_uids(self): self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) - def test_proc_gids(self): + def test_gids(self): self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) - def test_proc_terminal(self): + def test_terminal(self): self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX) - def test_proc_ionice(self): + def test_ionice(self): self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS) - def test_proc_rlimit(self): + def test_rlimit(self): self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX) - def test_proc_io_counters(self): + def test_io_counters(self): hasit = hasattr(psutil.Process, "io_counters") - self.assertEqual(hasit, False if OSX or SUNOS else True) + self.assertEqual(hasit, False if MACOS or SUNOS else True) - def test_proc_num_fds(self): + def test_num_fds(self): self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX) - def test_proc_num_handles(self): + def test_num_handles(self): self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS) - def test_proc_cpu_affinity(self): + def test_cpu_affinity(self): self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), LINUX or WINDOWS or FREEBSD) - def test_proc_cpu_num(self): + def test_cpu_num(self): self.assertEqual(hasattr(psutil.Process, "cpu_num"), LINUX or FREEBSD or SUNOS) - def test_proc_memory_maps(self): + def test_memory_maps(self): hasit = hasattr(psutil.Process, "memory_maps") - self.assertEqual(hasit, False if OPENBSD or NETBSD or AIX else True) - - -# =================================================================== -# --- Test deprecations -# =================================================================== - - -class TestDeprecations(unittest.TestCase): - - def test_memory_info_ex(self): - with warnings.catch_warnings(record=True) as ws: - psutil.Process().memory_info_ex() - w = ws[0] - self.assertIsInstance(w.category(), FutureWarning) - self.assertIn("memory_info_ex() is deprecated", str(w.message)) - self.assertIn("use memory_info() instead", str(w.message)) + self.assertEqual( + hasit, False if OPENBSD or NETBSD or AIX or MACOS else True) # =================================================================== @@ -189,7 +183,7 @@ class TestDeprecations(unittest.TestCase): # =================================================================== -class TestSystem(unittest.TestCase): +class TestSystemAPITypes(unittest.TestCase): """Check the return types of system related APIs. Mainly we want to test we never return unicode on Python 2, see: https://github.com/giampaolo/psutil/issues/1039 @@ -202,18 +196,40 @@ class TestSystem(unittest.TestCase): def tearDown(self): safe_rmpath(TESTFN) - def test_cpu_times(self): - # Duplicate of test_system.py. Keep it anyway. - ret = psutil.cpu_times() - assert is_namedtuple(ret) - for n in ret: - self.assertIsInstance(n, float) - self.assertGreaterEqual(n, 0) + def assert_ntuple_of_nums(self, nt, type_=float, gezero=True): + assert is_namedtuple(nt) + for n in nt: + self.assertIsInstance(n, type_) + if gezero: + self.assertGreaterEqual(n, 0) - def test_io_counters(self): + def test_cpu_times(self): + self.assert_ntuple_of_nums(psutil.cpu_times()) + for nt in psutil.cpu_times(percpu=True): + self.assert_ntuple_of_nums(nt) + + def test_cpu_percent(self): + self.assertIsInstance(psutil.cpu_percent(interval=None), float) + self.assertIsInstance(psutil.cpu_percent(interval=0.00001), float) + + def test_cpu_times_percent(self): + self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None)) + self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001)) + + def test_cpu_count(self): + self.assertIsInstance(psutil.cpu_count(), int) + + @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + def test_cpu_freq(self): + if psutil.cpu_freq() is None: + raise self.skipTest("cpu_freq() returns None") + self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int, long)) + + def test_disk_io_counters(self): # Duplicate of test_system.py. Keep it anyway. - for k in psutil.disk_io_counters(perdisk=True): + for k, v in psutil.disk_io_counters(perdisk=True).items(): self.assertIsInstance(k, str) + self.assert_ntuple_of_nums(v, type_=(int, long)) def test_disk_partitions(self): # Duplicate of test_system.py. Keep it anyway. @@ -223,31 +239,40 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(disk.fstype, str) self.assertIsInstance(disk.opts, str) - @unittest.skipIf(not POSIX, 'POSIX only') - @unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets") - @skip_on_access_denied(only_if=OSX) + @unittest.skipIf(SKIP_SYSCONS, "requires root") def test_net_connections(self): - with unix_socket_path() as name: - with closing(bind_unix_socket(name)): - cons = psutil.net_connections(kind='unix') - assert cons - for conn in cons: - self.assertIsInstance(conn.laddr, str) + with create_sockets(): + ret = psutil.net_connections('all') + self.assertEqual(len(ret), len(set(ret))) + for conn in ret: + assert is_namedtuple(conn) def test_net_if_addrs(self): # Duplicate of test_system.py. Keep it anyway. for ifname, addrs in psutil.net_if_addrs().items(): self.assertIsInstance(ifname, str) for addr in addrs: + if enum is not None: + assert isinstance(addr.family, enum.IntEnum), addr + else: + assert isinstance(addr.family, int), addr self.assertIsInstance(addr.address, str) self.assertIsInstance(addr.netmask, (str, type(None))) self.assertIsInstance(addr.broadcast, (str, type(None))) def test_net_if_stats(self): # Duplicate of test_system.py. Keep it anyway. - for ifname, _ in psutil.net_if_stats().items(): + for ifname, info in psutil.net_if_stats().items(): self.assertIsInstance(ifname, str) + self.assertIsInstance(info.isup, bool) + if enum is not None: + self.assertIsInstance(info.duplex, enum.IntEnum) + else: + self.assertIsInstance(info.duplex, int) + self.assertIsInstance(info.speed, int) + self.assertIsInstance(info.mtu, int) + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): # Duplicate of test_system.py. Keep it anyway. for ifname, _ in psutil.net_io_counters(pernic=True).items(): @@ -260,6 +285,7 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(name, str) for unit in units: self.assertIsInstance(unit.label, str) + self.assertIsInstance(unit.current, (float, int, type(None))) @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures(self): @@ -268,6 +294,13 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(name, str) for unit in units: self.assertIsInstance(unit.label, str) + self.assertIsInstance(unit.current, (float, int, type(None))) + self.assertIsInstance(unit.high, (float, int, type(None))) + self.assertIsInstance(unit.critical, (float, int, type(None))) + + def test_boot_time(self): + # Duplicate of test_system.py. Keep it anyway. + self.assertIsInstance(psutil.boot_time(), float) def test_users(self): # Duplicate of test_system.py. Keep it anyway. @@ -288,21 +321,11 @@ class TestFetchAllProcesses(unittest.TestCase): some sanity checks against Process API's returned values. """ - def setUp(self): - if POSIX: - import pwd - import grp - users = pwd.getpwall() - groups = grp.getgrall() - self.all_uids = set([x.pw_uid for x in users]) - self.all_usernames = set([x.pw_name for x in users]) - self.all_gids = set([x.gr_gid for x in groups]) - - def test_fetch_all(self): - valid_procs = 0 + def get_attr_names(self): excluded_names = set([ 'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', - 'as_dict', 'parent', 'children', 'memory_info_ex', 'oneshot', + 'as_dict', 'parent', 'parents', 'children', 'memory_info_ex', + 'oneshot', ]) if LINUX and not HAS_RLIMIT: excluded_names.add('rlimit') @@ -313,55 +336,66 @@ class TestFetchAllProcesses(unittest.TestCase): if name in excluded_names: continue attrs.append(name) + return attrs - default = object() - failures = [] + def iter_procs(self): + attrs = self.get_attr_names() for p in psutil.process_iter(): with p.oneshot(): for name in attrs: - ret = default - try: - args = () - kwargs = {} - attr = getattr(p, name, None) - if attr is not None and callable(attr): - if name == 'rlimit': - args = (psutil.RLIMIT_NOFILE,) - elif name == 'memory_maps': - kwargs = {'grouped': False} - ret = attr(*args, **kwargs) - else: - ret = attr - valid_procs += 1 - except NotImplementedError: - msg = "%r was skipped because not implemented" % ( - self.__class__.__name__ + '.test_' + name) - warn(msg) - except (psutil.NoSuchProcess, psutil.AccessDenied) as err: - self.assertEqual(err.pid, p.pid) - if err.name: - # make sure exception's name attr is set - # with the actual process name - self.assertEqual(err.name, p.name()) - assert str(err) - assert err.msg - except Exception as err: - s = '\n' + '=' * 70 + '\n' - s += "FAIL: test_%s (proc=%s" % (name, p) - if ret != default: - s += ", ret=%s)" % repr(ret) - s += ')\n' - s += '-' * 70 - s += "\n%s" % traceback.format_exc() - s = "\n".join((" " * 4) + i for i in s.splitlines()) - s += '\n' - failures.append(s) - break - else: - if ret not in (0, 0.0, [], None, '', {}): - assert ret, ret - meth = getattr(self, name) - meth(ret, p) + yield (p, name) + + def call_meth(self, p, name): + args = () + kwargs = {} + attr = getattr(p, name, None) + if attr is not None and callable(attr): + if name == 'rlimit': + args = (psutil.RLIMIT_NOFILE,) + elif name == 'memory_maps': + kwargs = {'grouped': False} + return attr(*args, **kwargs) + else: + return attr + + def test_fetch_all(self): + valid_procs = 0 + default = object() + failures = [] + for p, name in self.iter_procs(): + ret = default + try: + ret = self.call_meth(p, name) + except NotImplementedError: + msg = "%r was skipped because not implemented" % ( + self.__class__.__name__ + '.test_' + name) + warn(msg) + except (psutil.NoSuchProcess, psutil.AccessDenied) as err: + self.assertEqual(err.pid, p.pid) + if err.name: + # make sure exception's name attr is set + # with the actual process name + self.assertEqual(err.name, p.name()) + assert str(err) + assert err.msg + except Exception: + s = '\n' + '=' * 70 + '\n' + s += "FAIL: test_%s (proc=%s" % (name, p) + if ret != default: + s += ", ret=%s)" % repr(ret) + s += ')\n' + s += '-' * 70 + s += "\n%s" % traceback.format_exc() + s = "\n".join((" " * 4) + i for i in s.splitlines()) + s += '\n' + failures.append(s) + break + else: + valid_procs += 1 + if ret not in (0, 0.0, [], None, '', {}): + assert ret, ret + meth = getattr(self, name) + meth(ret, p) if failures: self.fail(''.join(failures)) @@ -380,13 +414,15 @@ class TestFetchAllProcesses(unittest.TestCase): if not ret: self.assertEqual(ret, '') else: + if WINDOWS and not ret.endswith('.exe'): + return # May be "Registry", "MemCompression", ... assert os.path.isabs(ret), ret # Note: os.stat() may return False even if the file is there # hence we skip the test, see: # http://stackoverflow.com/questions/3112546/os-path-exists-lies if POSIX and os.path.isfile(ret): if hasattr(os, 'access') and hasattr(os, "X_OK"): - # XXX may fail on OSX + # XXX may fail on MACOS assert os.access(ret, os.X_OK) def pid(self, ret, proc): @@ -424,7 +460,6 @@ class TestFetchAllProcesses(unittest.TestCase): for uid in ret: self.assertIsInstance(uid, int) self.assertGreaterEqual(uid, 0) - self.assertIn(uid, self.all_uids) def gids(self, ret, proc): assert is_namedtuple(ret) @@ -432,15 +467,12 @@ class TestFetchAllProcesses(unittest.TestCase): # gid == 30 (nodoby); not sure why. for gid in ret: self.assertIsInstance(gid, int) - if not OSX and not NETBSD: + if not MACOS and not NETBSD: self.assertGreaterEqual(gid, 0) - self.assertIn(gid, self.all_gids) def username(self, ret, proc): self.assertIsInstance(ret, str) assert ret - if POSIX: - self.assertIn(ret, self.all_usernames) def status(self, ret, proc): self.assertIsInstance(ret, str) @@ -456,16 +488,20 @@ class TestFetchAllProcesses(unittest.TestCase): self.assertGreaterEqual(field, 0) def ionice(self, ret, proc): - if POSIX: - assert is_namedtuple(ret) - for field in ret: - self.assertIsInstance(field, int) if LINUX: + self.assertIsInstance(ret.ioclass, int) + self.assertIsInstance(ret.value, int) self.assertGreaterEqual(ret.ioclass, 0) self.assertGreaterEqual(ret.value, 0) - else: + else: # Windows, Cygwin + choices = [ + psutil.IOPRIO_VERYLOW, + psutil.IOPRIO_LOW, + psutil.IOPRIO_NORMAL, + psutil.IOPRIO_HIGH] + self.assertIsInstance(ret, int) self.assertGreaterEqual(ret, 0) - self.assertIn(ret, (0, 1, 2)) + self.assertIn(ret, choices) def num_threads(self, ret, proc): self.assertIsInstance(ret, int) @@ -506,13 +542,7 @@ class TestFetchAllProcesses(unittest.TestCase): for value in ret: self.assertIsInstance(value, (int, long)) self.assertGreaterEqual(value, 0) - if POSIX and not AIX and ret.vms != 0: - # VMS is always supposed to be the highest - for name in ret._fields: - if name != 'vms': - value = getattr(ret, name) - self.assertGreater(ret.vms, value, msg=ret) - elif WINDOWS: + if WINDOWS: self.assertGreaterEqual(ret.peak_wset, ret.wset) self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool) self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool) @@ -525,6 +555,10 @@ class TestFetchAllProcesses(unittest.TestCase): value = getattr(ret, name) self.assertIsInstance(value, (int, long)) self.assertGreaterEqual(value, 0, msg=(name, value)) + if LINUX or OSX and name in ('vms', 'data'): + # On Linux there are processes (e.g. 'goa-daemon') whose + # VMS is incredibly high for some reason. + continue self.assertLessEqual(value, total, msg=(name, value, total)) if LINUX: @@ -555,9 +589,10 @@ class TestFetchAllProcesses(unittest.TestCase): self.assertGreaterEqual(ret, 0) def connections(self, ret, proc): - self.assertEqual(len(ret), len(set(ret))) - for conn in ret: - check_connection_ntuple(conn) + with create_sockets(): + self.assertEqual(len(ret), len(set(ret))) + for conn in ret: + assert is_namedtuple(conn) def cwd(self, ret, proc): if ret: # 'ret' can be None or empty @@ -609,8 +644,11 @@ class TestFetchAllProcesses(unittest.TestCase): # commented as on Linux we might get # '/foo/bar (deleted)' # assert os.path.exists(nt.path), nt.path - elif fname in ('addr', 'perms'): - assert value + elif fname == 'addr': + assert value, repr(value) + elif fname == 'perms': + if not WINDOWS: + assert value, repr(value) else: self.assertIsInstance(value, (int, long)) self.assertGreaterEqual(value, 0) @@ -648,4 +686,5 @@ class TestFetchAllProcesses(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_linux.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_linux.py index 6ba17b254301..e51f8bd573ba 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_linux.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_linux.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -13,7 +13,6 @@ import errno import glob import io import os -import pprint import re import shutil import socket @@ -25,11 +24,14 @@ import warnings import psutil from psutil import LINUX +from psutil._compat import basestring +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil.tests import call_until from psutil.tests import HAS_BATTERY from psutil.tests import HAS_CPU_FREQ +from psutil.tests import HAS_GETLOADAVG from psutil.tests import HAS_RLIMIT from psutil.tests import MEMORY_TOLERANCE from psutil.tests import mock @@ -37,8 +39,7 @@ from psutil.tests import PYPY from psutil.tests import pyrun from psutil.tests import reap_children from psutil.tests import reload_module -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import safe_rmpath from psutil.tests import sh from psutil.tests import skip_on_not_implemented @@ -55,7 +56,7 @@ SIOCGIFCONF = 0x8912 SIOCGIFHWADDR = 0x8927 if LINUX: SECTOR_SIZE = 512 - +EMPTY_TEMPERATURES = not glob.glob('/sys/class/hwmon/hwmon*') # ===================================================================== # --- utils @@ -97,7 +98,7 @@ def free_swap(): """Parse 'free' cmd and return swap memory's s total, used and free values. """ - out = sh('free -b') + out = sh('free -b', env={"LANG": "C.UTF-8"}) lines = out.split('\n') for line in lines: if line.startswith('Swap'): @@ -116,7 +117,7 @@ def free_physmem(): # and 'cached' memory which may have different positions so we # do not return them. # https://github.com/giampaolo/psutil/issues/538#issuecomment-57059946 - out = sh('free -b') + out = sh('free -b', env={"LANG": "C.UTF-8"}) lines = out.split('\n') for line in lines: if line.startswith('Mem'): @@ -130,7 +131,7 @@ def free_physmem(): def vmstat(stat): - out = sh("vmstat -s") + out = sh("vmstat -s", env={"LANG": "C.UTF-8"}) for line in out.split("\n"): line = line.strip() if stat in line: @@ -143,6 +144,46 @@ def get_free_version_info(): return tuple(map(int, out.split()[-1].split('.'))) +@contextlib.contextmanager +def mock_open_content(for_path, content): + """Mock open() builtin and forces it to return a certain `content` + on read() if the path being opened matches `for_path`. + """ + def open_mock(name, *args, **kwargs): + if name == for_path: + if PY3: + if isinstance(content, basestring): + return io.StringIO(content) + else: + return io.BytesIO(content) + else: + return io.BytesIO(content) + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + yield m + + +@contextlib.contextmanager +def mock_open_exception(for_path, exc): + """Mock open() builtin and raises `exc` if the path being opened + matches `for_path`. + """ + def open_mock(name, *args, **kwargs): + if name == for_path: + raise exc + else: + return orig_open(name, *args, **kwargs) + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + yield m + + # ===================================================================== # --- system virtual memory # ===================================================================== @@ -165,7 +206,7 @@ class TestSystemVirtualMemory(unittest.TestCase): # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e @unittest.skipIf(LINUX and get_free_version_info() < (3, 3, 12), "old free version") - @retry_before_failing() + @retry_on_failure() def test_used(self): free = free_physmem() free_value = free.used @@ -175,18 +216,14 @@ class TestSystemVirtualMemory(unittest.TestCase): msg='%s %s \n%s' % (free_value, psutil_value, free.output)) @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - @retry_before_failing() + @retry_on_failure() def test_free(self): - # _, _, free_value, _ = free_physmem() - # psutil_value = psutil.virtual_memory().free - # self.assertAlmostEqual( - # free_value, psutil_value, delta=MEMORY_TOLERANCE) vmstat_value = vmstat('free memory') * 1024 psutil_value = psutil.virtual_memory().free self.assertAlmostEqual( vmstat_value, psutil_value, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_buffers(self): vmstat_value = vmstat('buffer memory') * 1024 psutil_value = psutil.virtual_memory().buffers @@ -195,7 +232,7 @@ class TestSystemVirtualMemory(unittest.TestCase): # https://travis-ci.org/giampaolo/psutil/jobs/226719664 @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - @retry_before_failing() + @retry_on_failure() def test_active(self): vmstat_value = vmstat('active memory') * 1024 psutil_value = psutil.virtual_memory().active @@ -204,14 +241,14 @@ class TestSystemVirtualMemory(unittest.TestCase): # https://travis-ci.org/giampaolo/psutil/jobs/227242952 @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - @retry_before_failing() + @retry_on_failure() def test_inactive(self): vmstat_value = vmstat('inactive memory') * 1024 psutil_value = psutil.virtual_memory().inactive self.assertAlmostEqual( vmstat_value, psutil_value, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_shared(self): free = free_physmem() free_value = free.shared @@ -222,7 +259,7 @@ class TestSystemVirtualMemory(unittest.TestCase): free_value, psutil_value, delta=MEMORY_TOLERANCE, msg='%s %s \n%s' % (free_value, psutil_value, free.output)) - @retry_before_failing() + @retry_on_failure() def test_available(self): # "free" output format has changed at some point: # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098 @@ -241,24 +278,18 @@ class TestSystemVirtualMemory(unittest.TestCase): # Emulate a case where /proc/meminfo provides few info. # psutil is supposed to set the missing fields to 0 and # raise a warning. - def open_mock(name, *args, **kwargs): - if name == '/proc/meminfo': - return io.BytesIO(textwrap.dedent("""\ - Active(anon): 6145416 kB - Active(file): 2950064 kB - Inactive(anon): 574764 kB - Inactive(file): 1567648 kB - MemAvailable: -1 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - SReclaimable: 346648 kB - """).encode()) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock_open_content( + '/proc/meminfo', + textwrap.dedent("""\ + Active(anon): 6145416 kB + Active(file): 2950064 kB + Inactive(anon): 574764 kB + Inactive(file): 1567648 kB + MemAvailable: -1 kB + MemFree: 2057400 kB + MemTotal: 16325648 kB + SReclaimable: 346648 kB + """).encode()) as m: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") ret = psutil.virtual_memory() @@ -280,10 +311,12 @@ class TestSystemVirtualMemory(unittest.TestCase): self.assertEqual(ret.shared, 0) self.assertEqual(ret.buffers, 0) self.assertEqual(ret.available, 0) + self.assertEqual(ret.slab, 0) + @retry_on_failure() def test_avail_old_percent(self): # Make sure that our calculation of avail mem for old kernels - # is off by max 10%. + # is off by max 15%. from psutil._pslinux import calculate_avail_vmem from psutil._pslinux import open_binary @@ -297,34 +330,28 @@ class TestSystemVirtualMemory(unittest.TestCase): if b'MemAvailable:' in mems: b = mems[b'MemAvailable:'] diff_percent = abs(a - b) / a * 100 - self.assertLess(diff_percent, 10) + self.assertLess(diff_percent, 15) def test_avail_old_comes_from_kernel(self): # Make sure "MemAvailable:" coluimn is used instead of relying # on our internal algorithm to calculate avail mem. - def open_mock(name, *args, **kwargs): - if name == "/proc/meminfo": - return io.BytesIO(textwrap.dedent("""\ - Active: 9444728 kB - Active(anon): 6145416 kB - Active(file): 2950064 kB - Buffers: 287952 kB - Cached: 4818144 kB - Inactive(file): 1578132 kB - Inactive(anon): 574764 kB - Inactive(file): 1567648 kB - MemAvailable: 6574984 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - SReclaimable: 346648 kB - """).encode()) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock_open_content( + '/proc/meminfo', + textwrap.dedent("""\ + Active: 9444728 kB + Active(anon): 6145416 kB + Active(file): 2950064 kB + Buffers: 287952 kB + Cached: 4818144 kB + Inactive(file): 1578132 kB + Inactive(anon): 574764 kB + Inactive(file): 1567648 kB + MemAvailable: 6574984 kB + MemFree: 2057400 kB + MemTotal: 16325648 kB + Shmem: 577588 kB + SReclaimable: 346648 kB + """).encode()) as m: with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called @@ -337,9 +364,9 @@ class TestSystemVirtualMemory(unittest.TestCase): # Remove Active(file), Inactive(file) and SReclaimable # from /proc/meminfo and make sure the fallback is used # (free + cached), - def open_mock(name, *args, **kwargs): - if name == "/proc/meminfo": - return io.BytesIO(textwrap.dedent("""\ + with mock_open_content( + "/proc/meminfo", + textwrap.dedent("""\ Active: 9444728 kB Active(anon): 6145416 kB Buffers: 287952 kB @@ -349,13 +376,7 @@ class TestSystemVirtualMemory(unittest.TestCase): MemFree: 2057400 kB MemTotal: 16325648 kB Shmem: 577588 kB - """).encode()) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + """).encode()) as m: with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called @@ -367,9 +388,9 @@ class TestSystemVirtualMemory(unittest.TestCase): def test_avail_old_missing_zoneinfo(self): # Remove /proc/zoneinfo file. Make sure fallback is used # (free + cached). - def open_mock(name, *args, **kwargs): - if name == "/proc/meminfo": - return io.BytesIO(textwrap.dedent("""\ + with mock_open_content( + "/proc/meminfo", + textwrap.dedent("""\ Active: 9444728 kB Active(anon): 6145416 kB Active(file): 2950064 kB @@ -382,22 +403,91 @@ class TestSystemVirtualMemory(unittest.TestCase): MemTotal: 16325648 kB Shmem: 577588 kB SReclaimable: 346648 kB + """).encode()): + with mock_open_exception( + "/proc/zoneinfo", + IOError(errno.ENOENT, 'no such file or directory')): + with warnings.catch_warnings(record=True) as ws: + ret = psutil.virtual_memory() + self.assertEqual( + ret.available, 2057400 * 1024 + 4818144 * 1024) + w = ws[0] + self.assertIn( + "inactive memory stats couldn't be determined", + str(w.message)) + + def test_virtual_memory_mocked(self): + # Emulate /proc/meminfo because neither vmstat nor free return slab. + def open_mock(name, *args, **kwargs): + if name == '/proc/meminfo': + return io.BytesIO(textwrap.dedent("""\ + MemTotal: 100 kB + MemFree: 2 kB + MemAvailable: 3 kB + Buffers: 4 kB + Cached: 5 kB + SwapCached: 6 kB + Active: 7 kB + Inactive: 8 kB + Active(anon): 9 kB + Inactive(anon): 10 kB + Active(file): 11 kB + Inactive(file): 12 kB + Unevictable: 13 kB + Mlocked: 14 kB + SwapTotal: 15 kB + SwapFree: 16 kB + Dirty: 17 kB + Writeback: 18 kB + AnonPages: 19 kB + Mapped: 20 kB + Shmem: 21 kB + Slab: 22 kB + SReclaimable: 23 kB + SUnreclaim: 24 kB + KernelStack: 25 kB + PageTables: 26 kB + NFS_Unstable: 27 kB + Bounce: 28 kB + WritebackTmp: 29 kB + CommitLimit: 30 kB + Committed_AS: 31 kB + VmallocTotal: 32 kB + VmallocUsed: 33 kB + VmallocChunk: 34 kB + HardwareCorrupted: 35 kB + AnonHugePages: 36 kB + ShmemHugePages: 37 kB + ShmemPmdMapped: 38 kB + CmaTotal: 39 kB + CmaFree: 40 kB + HugePages_Total: 41 kB + HugePages_Free: 42 kB + HugePages_Rsvd: 43 kB + HugePages_Surp: 44 kB + Hugepagesize: 45 kB + DirectMap46k: 46 kB + DirectMap47M: 47 kB + DirectMap48G: 48 kB """).encode()) - elif name == "/proc/zoneinfo": - raise IOError(errno.ENOENT, 'no such file or directory') else: return orig_open(name, *args, **kwargs) orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, create=True, side_effect=open_mock) as m: - with warnings.catch_warnings(record=True) as ws: - ret = psutil.virtual_memory() + mem = psutil.virtual_memory() assert m.called - self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024) - w = ws[0] - self.assertIn( - "inactive memory stats couldn't be determined", str(w.message)) + self.assertEqual(mem.total, 100 * 1024) + self.assertEqual(mem.free, 2 * 1024) + self.assertEqual(mem.buffers, 4 * 1024) + # cached mem also includes reclaimable memory + self.assertEqual(mem.cached, (5 + 23) * 1024) + self.assertEqual(mem.shared, 21 * 1024) + self.assertEqual(mem.active, 7 * 1024) + self.assertEqual(mem.inactive, 8 * 1024) + self.assertEqual(mem.slab, 22 * 1024) + self.assertEqual(mem.available, 3 * 1024) # ===================================================================== @@ -421,14 +511,14 @@ class TestSystemSwapMemory(unittest.TestCase): return self.assertAlmostEqual( free_value, psutil_value, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_used(self): free_value = free_swap().used psutil_value = psutil.swap_memory().used return self.assertAlmostEqual( free_value, psutil_value, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_free(self): free_value = free_swap().free psutil_value = psutil.swap_memory().free @@ -436,7 +526,7 @@ class TestSystemSwapMemory(unittest.TestCase): free_value, psutil_value, delta=MEMORY_TOLERANCE) def test_missing_sin_sout(self): - with mock.patch('psutil._pslinux.open', create=True) as m: + with mock.patch('psutil._common.open', create=True) as m: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") ret = psutil.swap_memory() @@ -452,15 +542,9 @@ class TestSystemSwapMemory(unittest.TestCase): def test_no_vmstat_mocked(self): # see https://github.com/giampaolo/psutil/issues/722 - def open_mock(name, *args, **kwargs): - if name == "/proc/vmstat": - raise IOError(errno.ENOENT, 'no such file or directory') - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock_open_exception( + "/proc/vmstat", + IOError(errno.ENOENT, 'no such file or directory')) as m: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") ret = psutil.swap_memory() @@ -489,21 +573,13 @@ class TestSystemSwapMemory(unittest.TestCase): total *= unit_multiplier free *= unit_multiplier self.assertEqual(swap.total, total) - self.assertEqual(swap.free, free) + self.assertAlmostEqual(swap.free, free, delta=MEMORY_TOLERANCE) def test_emulate_meminfo_has_no_metrics(self): # Emulate a case where /proc/meminfo provides no swap metrics # in which case sysinfo() syscall is supposed to be used # as a fallback. - def open_mock(name, *args, **kwargs): - if name == "/proc/meminfo": - return io.BytesIO(b"") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, create=True, side_effect=open_mock) as m: + with mock_open_content("/proc/meminfo", b"") as m: psutil.swap_memory() assert m.called @@ -514,10 +590,10 @@ class TestSystemSwapMemory(unittest.TestCase): @unittest.skipIf(not LINUX, "LINUX only") -class TestSystemCPU(unittest.TestCase): +class TestSystemCPUTimes(unittest.TestCase): @unittest.skipIf(TRAVIS, "unknown failure on travis") - def test_cpu_times(self): + def test_fields(self): fields = psutil.cpu_times()._fields kernel_ver = re.findall(r'\d+\.\d+\.\d+', os.uname()[2])[0] kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) @@ -534,9 +610,13 @@ class TestSystemCPU(unittest.TestCase): else: self.assertNotIn('guest_nice', fields) + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemCPUCountLogical(unittest.TestCase): + @unittest.skipIf(not 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): + def test_against_sysdev_cpu_online(self): with open("/sys/devices/system/cpu/online") as f: value = f.read().strip() if "-" in str(value): @@ -545,23 +625,23 @@ class TestSystemCPU(unittest.TestCase): @unittest.skipIf(not os.path.exists("/sys/devices/system/cpu"), "/sys/devices/system/cpu does not exist") - def test_cpu_count_logical_w_sysdev_cpu_num(self): + def test_against_sysdev_cpu_num(self): ls = os.listdir("/sys/devices/system/cpu") count = len([x for x in ls if re.search(r"cpu\d+$", x) is not None]) self.assertEqual(psutil.cpu_count(), count) @unittest.skipIf(not which("nproc"), "nproc utility not available") - def test_cpu_count_logical_w_nproc(self): + def test_against_nproc(self): num = int(sh("nproc --all")) self.assertEqual(psutil.cpu_count(logical=True), num) @unittest.skipIf(not which("lscpu"), "lscpu utility not available") - def test_cpu_count_logical_w_lscpu(self): + def test_against_lscpu(self): out = sh("lscpu -p") num = len([x for x in out.split('\n') if not x.startswith('#')]) self.assertEqual(psutil.cpu_count(logical=True), num) - def test_cpu_count_logical_mocked(self): + def test_emulate_fallbacks(self): import psutil._pslinux original = psutil._pslinux.cpu_count_logical() # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in @@ -573,7 +653,7 @@ class TestSystemCPU(unittest.TestCase): # Let's have open() return emtpy data and make sure None is # returned ('cause we mimick os.cpu_count()). - with mock.patch('psutil._pslinux.open', create=True) as m: + with mock.patch('psutil._common.open', create=True) as m: self.assertIsNone(psutil._pslinux.cpu_count_logical()) self.assertEqual(m.call_count, 2) # /proc/stat should be the last one @@ -584,63 +664,99 @@ class TestSystemCPU(unittest.TestCase): with open('/proc/cpuinfo', 'rb') as f: cpuinfo_data = f.read() fake_file = io.BytesIO(cpuinfo_data) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m: self.assertEqual(psutil._pslinux.cpu_count_logical(), original) # Finally, let's make /proc/cpuinfo return meaningless data; # this way we'll fall back on relying on /proc/stat - def open_mock(name, *args, **kwargs): - if name.startswith('/proc/cpuinfo'): - return io.BytesIO(b"") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock, create=True): + with mock_open_content('/proc/cpuinfo', b"") as m: self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + m.called - def test_cpu_count_physical_mocked(self): - # Have open() return emtpy data and make sure None is returned - # ('cause we want to mimick os.cpu_count()) - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_physical()) - assert m.called - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - def test_cpu_freq_no_result(self): - with mock.patch("psutil._pslinux.glob.glob", return_value=[]): - self.assertIsNone(psutil.cpu_freq()) +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemCPUCountPhysical(unittest.TestCase): + + @unittest.skipIf(not which("lscpu"), "lscpu utility not available") + def test_against_lscpu(self): + out = sh("lscpu -p") + core_ids = set() + for line in out.split('\n'): + if not line.startswith('#'): + fields = line.split(',') + core_ids.add(fields[1]) + self.assertEqual(psutil.cpu_count(logical=False), len(core_ids)) + + def test_emulate_none(self): + with mock.patch('glob.glob', return_value=[]) as m1: + with mock.patch('psutil._common.open', create=True) as m2: + self.assertIsNone(psutil._pslinux.cpu_count_physical()) + assert m1.called + assert m2.called + + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemCPUFrequency(unittest.TestCase): @unittest.skipIf(TRAVIS, "fails on Travis") @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - def test_cpu_freq_use_second_file(self): + def test_emulate_use_second_file(self): # https://github.com/giampaolo/psutil/issues/981 - def glob_mock(pattern): - if pattern.startswith("/sys/devices/system/cpu/cpufreq/policy"): - flags.append(None) - return [] + def path_exists_mock(path): + if path.startswith("/sys/devices/system/cpu/cpufreq/policy"): + return False else: - flags.append(None) - return orig_glob(pattern) + return orig_exists(path) - flags = [] - orig_glob = glob.glob - with mock.patch("psutil._pslinux.glob.glob", side_effect=glob_mock, + orig_exists = os.path.exists + with mock.patch("os.path.exists", side_effect=path_exists_mock, create=True): assert psutil.cpu_freq() - self.assertEqual(len(flags), 2) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - def test_cpu_freq_emulate_data(self): + def test_emulate_use_cpuinfo(self): + # Emulate a case where /sys/devices/system/cpu/cpufreq* does not + # exist and /proc/cpuinfo is used instead. + def path_exists_mock(path): + if path.startswith('/sys/devices/system/cpu/'): + return False + else: + if path == "/proc/cpuinfo": + flags.append(None) + return os_path_exists(path) + + flags = [] + os_path_exists = os.path.exists + try: + with mock.patch("os.path.exists", side_effect=path_exists_mock): + reload_module(psutil._pslinux) + ret = psutil.cpu_freq() + assert ret + assert flags + self.assertEqual(ret.max, 0.0) + self.assertEqual(ret.min, 0.0) + for freq in psutil.cpu_freq(percpu=True): + self.assertEqual(ret.max, 0.0) + self.assertEqual(ret.min, 0.0) + finally: + reload_module(psutil._pslinux) + reload_module(psutil) + + @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + def test_emulate_data(self): def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): + if (name.endswith('/scaling_cur_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"500000") - elif name.endswith('/scaling_min_freq'): + elif (name.endswith('/scaling_min_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"600000") - elif name.endswith('/scaling_max_freq'): + elif (name.endswith('/scaling_max_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"700000") + elif name == '/proc/cpuinfo': + return io.BytesIO(b"cpu MHz : 500") else: return orig_open(name, *args, **kwargs) @@ -648,81 +764,84 @@ class TestSystemCPU(unittest.TestCase): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): with mock.patch( - 'glob.glob', - return_value=['/sys/devices/system/cpu/cpufreq/policy0']): + 'os.path.exists', return_value=True): freq = psutil.cpu_freq() self.assertEqual(freq.current, 500.0) - self.assertEqual(freq.min, 600.0) - self.assertEqual(freq.max, 700.0) + # when /proc/cpuinfo is used min and max frequencies are not + # available and are set to 0. + if freq.min != 0.0: + self.assertEqual(freq.min, 600.0) + if freq.max != 0.0: + self.assertEqual(freq.max, 700.0) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - def test_cpu_freq_emulate_multi_cpu(self): + def test_emulate_multi_cpu(self): def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): + n = name + if (n.endswith('/scaling_cur_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"100000") - elif name.endswith('/scaling_min_freq'): + elif (n.endswith('/scaling_min_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"200000") - elif name.endswith('/scaling_max_freq'): + elif (n.endswith('/scaling_max_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"300000") + elif (n.endswith('/scaling_cur_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"400000") + elif (n.endswith('/scaling_min_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"500000") + elif (n.endswith('/scaling_max_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"600000") + elif name == '/proc/cpuinfo': + return io.BytesIO(b"cpu MHz : 100\n" + b"cpu MHz : 400") else: return orig_open(name, *args, **kwargs) orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' - policies = ['/sys/devices/system/cpu/cpufreq/policy0', - '/sys/devices/system/cpu/cpufreq/policy1', - '/sys/devices/system/cpu/cpufreq/policy2'] with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): - freq = psutil.cpu_freq() - self.assertEqual(freq.current, 100.0) - self.assertEqual(freq.min, 200.0) - self.assertEqual(freq.max, 300.0) + with mock.patch('os.path.exists', return_value=True): + with mock.patch('psutil._pslinux.cpu_count_logical', + return_value=2): + freq = psutil.cpu_freq(percpu=True) + self.assertEqual(freq[0].current, 100.0) + if freq[0].min != 0.0: + self.assertEqual(freq[0].min, 200.0) + if freq[0].max != 0.0: + self.assertEqual(freq[0].max, 300.0) + self.assertEqual(freq[1].current, 400.0) + if freq[1].min != 0.0: + self.assertEqual(freq[1].min, 500.0) + if freq[1].max != 0.0: + self.assertEqual(freq[1].max, 600.0) @unittest.skipIf(TRAVIS, "fails on Travis") @unittest.skipIf(not HAS_CPU_FREQ, "not supported") - def test_cpu_freq_no_scaling_cur_freq_file(self): + def test_emulate_no_scaling_cur_freq_file(self): # See: https://github.com/giampaolo/psutil/issues/1071 def open_mock(name, *args, **kwargs): if name.endswith('/scaling_cur_freq'): raise IOError(errno.ENOENT, "") elif name.endswith('/cpuinfo_cur_freq'): return io.BytesIO(b"200000") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - policies = ['/sys/devices/system/cpu/cpufreq/policy0', - '/sys/devices/system/cpu/cpufreq/policy1', - '/sys/devices/system/cpu/cpufreq/policy2'] - - with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): - freq = psutil.cpu_freq() - self.assertEqual(freq.current, 200) - - # Also test that NotImplementedError is raised in case no - # current freq file is present. - - def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): - raise IOError(errno.ENOENT, "") - elif name.endswith('/cpuinfo_cur_freq'): - raise IOError(errno.ENOENT, "") + elif name == '/proc/cpuinfo': + return io.BytesIO(b"cpu MHz : 200") else: return orig_open(name, *args, **kwargs) orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): - self.assertRaises(NotImplementedError, psutil.cpu_freq) - - -# ===================================================================== -# --- system CPU stats -# ===================================================================== + with mock.patch('os.path.exists', return_value=True): + with mock.patch('psutil._pslinux.cpu_count_logical', + return_value=1): + freq = psutil.cpu_freq() + self.assertEqual(freq.current, 200) @unittest.skipIf(not LINUX, "LINUX only") @@ -741,15 +860,29 @@ class TestSystemCPUStats(unittest.TestCase): self.assertAlmostEqual(vmstat_value, psutil_value, delta=500) +@unittest.skipIf(not LINUX, "LINUX only") +class TestLoadAvg(unittest.TestCase): + + @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + def test_getloadavg(self): + psutil_value = psutil.getloadavg() + with open("/proc/loadavg", "r") as f: + proc_value = f.read().split() + + self.assertAlmostEqual(float(proc_value[0]), psutil_value[0], delta=1) + self.assertAlmostEqual(float(proc_value[1]), psutil_value[1], delta=1) + self.assertAlmostEqual(float(proc_value[2]), psutil_value[2], delta=1) + + # ===================================================================== # --- system network # ===================================================================== @unittest.skipIf(not LINUX, "LINUX only") -class TestSystemNetwork(unittest.TestCase): +class TestSystemNetIfAddrs(unittest.TestCase): - def test_net_if_addrs_ips(self): + def test_ips(self): for name, addrs in psutil.net_if_addrs().items(): for addr in addrs: if addr.family == psutil.AF_LINK: @@ -758,7 +891,27 @@ class TestSystemNetwork(unittest.TestCase): self.assertEqual(addr.address, get_ipv4_address(name)) # TODO: test for AF_INET6 family - def test_net_if_stats(self): + # XXX - not reliable when having virtual NICs installed by Docker. + # @unittest.skipIf(not which('ip'), "'ip' utility not available") + # @unittest.skipIf(TRAVIS, "skipped on Travis") + # def test_net_if_names(self): + # out = sh("ip addr").strip() + # nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x] + # found = 0 + # for line in out.split('\n'): + # line = line.strip() + # if re.search(r"^\d+:", line): + # found += 1 + # name = line.split(':')[1].strip() + # self.assertIn(name, nics) + # self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( + # pprint.pformat(nics), out)) + + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemNetIfStats(unittest.TestCase): + + def test_against_ifconfig(self): for name, stats in psutil.net_if_stats().items(): try: out = sh("ifconfig %s" % name) @@ -770,8 +923,12 @@ class TestSystemNetwork(unittest.TestCase): self.assertEqual(stats.mtu, int(re.findall(r'(?i)MTU[: ](\d+)', out)[0])) - @retry_before_failing() - def test_net_io_counters(self): + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemNetIOCounters(unittest.TestCase): + + @retry_on_failure() + def test_against_ifconfig(self): def ifconfig(nic): ret = {} out = sh("ifconfig %s" % name) @@ -812,24 +969,13 @@ class TestSystemNetwork(unittest.TestCase): self.assertAlmostEqual( stats.dropout, ifconfig_ret['dropout'], delta=10) - @unittest.skipIf(not which('ip'), "'ip' utility not available") - @unittest.skipIf(TRAVIS, "skipped on Travis") - def test_net_if_names(self): - out = sh("ip addr").strip() - nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x] - found = 0 - for line in out.split('\n'): - line = line.strip() - if re.search(r"^\d+:", line): - found += 1 - name = line.split(':')[1].strip() - self.assertIn(name, nics) - self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( - pprint.pformat(nics), out)) + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemNetConnections(unittest.TestCase): @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError) @mock.patch('psutil._pslinux.supports_ipv6', return_value=False) - def test_net_connections_ipv6_unsupported(self, supports_ipv6, inet_ntop): + def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop): # see: https://github.com/giampaolo/psutil/issues/623 try: s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) @@ -839,36 +985,30 @@ class TestSystemNetwork(unittest.TestCase): pass psutil.net_connections(kind='inet6') - def test_net_connections_mocked(self): - def open_mock(name, *args, **kwargs): - if name == '/proc/net/unix': - return io.StringIO(textwrap.dedent(u"""\ - 0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n - 0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ - 0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O - 000000000000000000000000000000000000000000000000000000 - """)) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + def test_emulate_unix(self): + with mock_open_content( + '/proc/net/unix', + textwrap.dedent("""\ + 0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n + 0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ + 0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O + 000000000000000000000000000000000000000000000000000000 + """)) as m: psutil.net_connections(kind='unix') assert m.called # ===================================================================== -# --- system disk +# --- system disks # ===================================================================== @unittest.skipIf(not LINUX, "LINUX only") -class TestSystemDisks(unittest.TestCase): +class TestSystemDiskPartitions(unittest.TestCase): @unittest.skipIf(not hasattr(os, 'statvfs'), "os.statvfs() not available") @skip_on_not_implemented() - def test_disk_partitions_and_usage(self): + def test_against_df(self): # test psutil.disk_usage() and psutil.disk_partitions() # against "df -a" def df(path): @@ -892,7 +1032,7 @@ class TestSystemDisks(unittest.TestCase): if abs(usage.used - used) > 10 * 1024 * 1024: self.fail("psutil=%s, df=%s" % (usage.used, used)) - def test_disk_partitions_mocked(self): + def test_zfs_fs(self): # Test that ZFS partitions are returned. with open("/proc/filesystems", "r") as f: data = f.read() @@ -905,7 +1045,7 @@ class TestSystemDisks(unittest.TestCase): else: # No ZFS partitions on this system. Let's fake one. fake_file = io.StringIO(u("nodev\tzfs\n")) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m1: with mock.patch( 'psutil._pslinux.cext.disk_partitions', @@ -916,103 +1056,152 @@ class TestSystemDisks(unittest.TestCase): assert ret self.assertEqual(ret[0].fstype, 'zfs') - def test_disk_io_counters_kernel_2_4_mocked(self): + def test_emulate_realpath_fail(self): + # See: https://github.com/giampaolo/psutil/issues/1307 + try: + with mock.patch('os.path.realpath', + return_value='/non/existent') as m: + with self.assertRaises(FileNotFoundError): + psutil.disk_partitions() + assert m.called + finally: + psutil.PROCFS_PATH = "/proc" + + +@unittest.skipIf(not LINUX, "LINUX only") +class TestSystemDiskIoCounters(unittest.TestCase): + + def test_emulate_kernel_2_4(self): # Tests /proc/diskstats parsing format for 2.4 kernels, see: # https://github.com/giampaolo/psutil/issues/767 - def open_mock(name, *args, **kwargs): - if name == '/proc/partitions': - return io.StringIO(textwrap.dedent(u"""\ - major minor #blocks name + with mock_open_content( + '/proc/diskstats', + " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12"): + with mock.patch('psutil._pslinux.is_storage_device', + return_value=True): + ret = psutil.disk_io_counters(nowrap=False) + self.assertEqual(ret.read_count, 1) + self.assertEqual(ret.read_merged_count, 2) + self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) + self.assertEqual(ret.read_time, 4) + self.assertEqual(ret.write_count, 5) + self.assertEqual(ret.write_merged_count, 6) + self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) + self.assertEqual(ret.write_time, 8) + self.assertEqual(ret.busy_time, 10) - 8 0 488386584 hda - """)) - elif name == '/proc/diskstats': - return io.StringIO( - u(" 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12")) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - ret = psutil.disk_io_counters(nowrap=False) - assert m.called - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_merged_count, 2) - self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) - self.assertEqual(ret.read_time, 4) - self.assertEqual(ret.write_count, 5) - self.assertEqual(ret.write_merged_count, 6) - self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) - self.assertEqual(ret.write_time, 8) - self.assertEqual(ret.busy_time, 10) - - def test_disk_io_counters_kernel_2_6_full_mocked(self): + def test_emulate_kernel_2_6_full(self): # Tests /proc/diskstats parsing format for 2.6 kernels, # lines reporting all metrics: # https://github.com/giampaolo/psutil/issues/767 - def open_mock(name, *args, **kwargs): - if name == '/proc/partitions': - return io.StringIO(textwrap.dedent(u"""\ - major minor #blocks name + with mock_open_content( + '/proc/diskstats', + " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11"): + with mock.patch('psutil._pslinux.is_storage_device', + return_value=True): + ret = psutil.disk_io_counters(nowrap=False) + self.assertEqual(ret.read_count, 1) + self.assertEqual(ret.read_merged_count, 2) + self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) + self.assertEqual(ret.read_time, 4) + self.assertEqual(ret.write_count, 5) + self.assertEqual(ret.write_merged_count, 6) + self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) + self.assertEqual(ret.write_time, 8) + self.assertEqual(ret.busy_time, 10) - 8 0 488386584 hda - """)) - elif name == '/proc/diskstats': - return io.StringIO( - u(" 3 0 hda 1 2 3 4 5 6 7 8 9 10 11")) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - ret = psutil.disk_io_counters(nowrap=False) - assert m.called - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_merged_count, 2) - self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE) - self.assertEqual(ret.read_time, 4) - self.assertEqual(ret.write_count, 5) - self.assertEqual(ret.write_merged_count, 6) - self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE) - self.assertEqual(ret.write_time, 8) - self.assertEqual(ret.busy_time, 10) - - def test_disk_io_counters_kernel_2_6_limited_mocked(self): + def test_emulate_kernel_2_6_limited(self): # Tests /proc/diskstats parsing format for 2.6 kernels, # where one line of /proc/partitions return a limited # amount of metrics when it bumps into a partition # (instead of a disk). See: # https://github.com/giampaolo/psutil/issues/767 - def open_mock(name, *args, **kwargs): - if name == '/proc/partitions': - return io.StringIO(textwrap.dedent(u"""\ - major minor #blocks name + with mock_open_content( + '/proc/diskstats', + " 3 1 hda 1 2 3 4"): + with mock.patch('psutil._pslinux.is_storage_device', + return_value=True): + ret = psutil.disk_io_counters(nowrap=False) + self.assertEqual(ret.read_count, 1) + self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE) + self.assertEqual(ret.write_count, 3) + self.assertEqual(ret.write_bytes, 4 * SECTOR_SIZE) - 8 0 488386584 hda - """)) - elif name == '/proc/diskstats': - return io.StringIO( - u(" 3 1 hda 1 2 3 4")) - else: - return orig_open(name, *args, **kwargs) + self.assertEqual(ret.read_merged_count, 0) + self.assertEqual(ret.read_time, 0) + self.assertEqual(ret.write_merged_count, 0) + self.assertEqual(ret.write_time, 0) + self.assertEqual(ret.busy_time, 0) - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - ret = psutil.disk_io_counters(nowrap=False) - assert m.called - self.assertEqual(ret.read_count, 1) - self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE) - self.assertEqual(ret.write_count, 3) - self.assertEqual(ret.write_bytes, 4 * SECTOR_SIZE) + def test_emulate_include_partitions(self): + # Make sure that when perdisk=True disk partitions are returned, + # see: + # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 + with mock_open_content( + '/proc/diskstats', + textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """)): + with mock.patch('psutil._pslinux.is_storage_device', + return_value=False): + ret = psutil.disk_io_counters(perdisk=True, nowrap=False) + self.assertEqual(len(ret), 2) + self.assertEqual(ret['nvme0n1'].read_count, 1) + self.assertEqual(ret['nvme0n1p1'].read_count, 1) + self.assertEqual(ret['nvme0n1'].write_count, 5) + self.assertEqual(ret['nvme0n1p1'].write_count, 5) - self.assertEqual(ret.read_merged_count, 0) - self.assertEqual(ret.read_time, 0) - self.assertEqual(ret.write_merged_count, 0) - self.assertEqual(ret.write_time, 0) - self.assertEqual(ret.busy_time, 0) + def test_emulate_exclude_partitions(self): + # Make sure that when perdisk=False partitions (e.g. 'sda1', + # 'nvme0n1p1') are skipped and not included in the total count. + # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 + with mock_open_content( + '/proc/diskstats', + textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """)): + with mock.patch('psutil._pslinux.is_storage_device', + return_value=False): + ret = psutil.disk_io_counters(perdisk=False, nowrap=False) + self.assertIsNone(ret) + + # + def is_storage_device(name): + return name == 'nvme0n1' + + with mock_open_content( + '/proc/diskstats', + textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """)): + with mock.patch('psutil._pslinux.is_storage_device', + create=True, side_effect=is_storage_device): + ret = psutil.disk_io_counters(perdisk=False, nowrap=False) + self.assertEqual(ret.read_count, 1) + self.assertEqual(ret.write_count, 5) + + def test_emulate_use_sysfs(self): + def exists(path): + if path == '/proc/diskstats': + return False + return True + + wprocfs = psutil.disk_io_counters(perdisk=True) + with mock.patch('psutil._pslinux.os.path.exists', + create=True, side_effect=exists): + wsysfs = psutil.disk_io_counters(perdisk=True) + self.assertEqual(len(wprocfs), len(wsysfs)) + + def test_emulate_not_impl(self): + def exists(path): + return False + + with mock.patch('psutil._pslinux.os.path.exists', + create=True, side_effect=exists): + self.assertRaises(NotImplementedError, psutil.disk_io_counters) # ===================================================================== @@ -1028,8 +1217,7 @@ class TestMisc(unittest.TestCase): psutil_value = psutil.boot_time() self.assertEqual(int(vmstat_value), int(psutil_value)) - @mock.patch('psutil.traceback.print_exc') - def test_no_procfs_on_import(self, tb): + def test_no_procfs_on_import(self): my_procfs = tempfile.mkdtemp() with open(os.path.join(my_procfs, 'stat'), 'w') as f: @@ -1048,7 +1236,6 @@ class TestMisc(unittest.TestCase): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): reload_module(psutil) - assert tb.called self.assertRaises(IOError, psutil.cpu_times) self.assertRaises(IOError, psutil.cpu_times, percpu=True) @@ -1090,8 +1277,49 @@ class TestMisc(unittest.TestCase): self.assertEqual(psutil.PROCFS_PATH, '/proc') + def test_cpu_steal_decrease(self): + # Test cumulative cpu stats decrease. We should ignore this. + # See issue #1210. + with mock_open_content( + "/proc/stat", + textwrap.dedent("""\ + cpu 0 0 0 0 0 0 0 1 0 0 + cpu0 0 0 0 0 0 0 0 1 0 0 + cpu1 0 0 0 0 0 0 0 1 0 0 + """).encode()) as m: + # first call to "percent" functions should read the new stat file + # and compare to the "real" file read at import time - so the + # values are meaningless + psutil.cpu_percent() + assert m.called + psutil.cpu_percent(percpu=True) + psutil.cpu_times_percent() + psutil.cpu_times_percent(percpu=True) + + with mock_open_content( + "/proc/stat", + textwrap.dedent("""\ + cpu 1 0 0 0 0 0 0 0 0 0 + cpu0 1 0 0 0 0 0 0 0 0 0 + cpu1 1 0 0 0 0 0 0 0 0 0 + """).encode()) as m: + # Increase "user" while steal goes "backwards" to zero. + cpu_percent = psutil.cpu_percent() + assert m.called + cpu_percent_percpu = psutil.cpu_percent(percpu=True) + cpu_times_percent = psutil.cpu_times_percent() + cpu_times_percent_percpu = psutil.cpu_times_percent(percpu=True) + self.assertNotEqual(cpu_percent, 0) + self.assertNotEqual(sum(cpu_percent_percpu), 0) + self.assertNotEqual(sum(cpu_times_percent), 0) + self.assertNotEqual(sum(cpu_times_percent), 100.0) + self.assertNotEqual(sum(map(sum, cpu_times_percent_percpu)), 0) + self.assertNotEqual(sum(map(sum, cpu_times_percent_percpu)), 100.0) + self.assertEqual(cpu_times_percent.steal, 0) + self.assertNotEqual(cpu_times_percent.user, 0) + def test_boot_time_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: + with mock.patch('psutil._common.open', create=True) as m: self.assertRaises( RuntimeError, psutil._pslinux.boot_time) @@ -1129,32 +1357,13 @@ class TestMisc(unittest.TestCase): self.assertRaises(IOError, psutil.net_connections) self.assertRaises(IOError, psutil.net_io_counters) self.assertRaises(IOError, psutil.net_if_stats) - self.assertRaises(IOError, psutil.disk_io_counters) + # self.assertRaises(IOError, psutil.disk_io_counters) self.assertRaises(IOError, psutil.disk_partitions) self.assertRaises(psutil.NoSuchProcess, psutil.Process) finally: psutil.PROCFS_PATH = "/proc" os.rmdir(tdir) - def test_sector_size_mock(self): - # Test SECTOR_SIZE fallback in case 'hw_sector_size' file - # does not exist. - def open_mock(name, *args, **kwargs): - if PY3 and isinstance(name, bytes): - name = name.decode() - if "hw_sector_size" in name: - flag.append(None) - raise IOError(errno.ENOENT, '') - else: - return orig_open(name, *args, **kwargs) - - flag = [] - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): - psutil.disk_io_counters() - assert flag - def test_issue_687(self): # In case of thread ID: # - pid_exists() is supposed to return False @@ -1177,16 +1386,9 @@ class TestMisc(unittest.TestCase): # Internally pid_exists relies on /proc/{pid}/status. # Emulate a case where this file is empty in which case # psutil is supposed to fall back on using pids(). - def open_mock(name, *args, **kwargs): - if name == "/proc/%s/status" % os.getpid(): - return io.StringIO(u("")) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + with mock_open_content("/proc/%s/status", "") as m: assert psutil.pid_exists(os.getpid()) + assert m.called # ===================================================================== @@ -1301,103 +1503,52 @@ class TestSensorsBattery(unittest.TestCase): def test_emulate_no_base_files(self): # Emulate a case where base metrics files are not present, # in which case we're supposed to get None. - def open_mock(name, *args, **kwargs): - if name.startswith("/sys/class/power_supply/BAT0/energy_now") or \ - name.startswith("/sys/class/power_supply/BAT0/charge_now"): - raise IOError(errno.ENOENT, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - self.assertIsNone(psutil.sensors_battery()) - assert m.called + with mock_open_exception( + "/sys/class/power_supply/BAT0/energy_now", + IOError(errno.ENOENT, "")): + with mock_open_exception( + "/sys/class/power_supply/BAT0/charge_now", + IOError(errno.ENOENT, "")): + self.assertIsNone(psutil.sensors_battery()) def test_emulate_energy_full_0(self): # Emulate a case where energy_full files returns 0. - def open_mock(name, *args, **kwargs): - if name.startswith("/sys/class/power_supply/BAT0/energy_full"): - return io.BytesIO(b"0") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock_open_content( + "/sys/class/power_supply/BAT0/energy_full", b"0") as m: self.assertEqual(psutil.sensors_battery().percent, 0) assert m.called def test_emulate_energy_full_not_avail(self): # Emulate a case where energy_full file does not exist. # Expected fallback on /capacity. - def open_mock(name, *args, **kwargs): - energy_full = "/sys/class/power_supply/BAT0/energy_full" - charge_full = "/sys/class/power_supply/BAT0/charge_full" - if name.startswith(energy_full) or name.startswith(charge_full): - raise IOError(errno.ENOENT, "") - elif name.startswith("/sys/class/power_supply/BAT0/capacity"): - return io.BytesIO(b"88") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - self.assertEqual(psutil.sensors_battery().percent, 88) - assert m.called - - def test_emulate_no_ac0_online(self): - # Emulate a case where /AC0/online file does not exist. - def path_exists_mock(name): - if name.startswith("/sys/class/power_supply/AC0/online"): - return False - else: - return orig_path_exists(name) - - orig_path_exists = os.path.exists - with mock.patch("psutil._pslinux.os.path.exists", - side_effect=path_exists_mock) as m: - psutil.sensors_battery() - assert m.called + with mock_open_exception( + "/sys/class/power_supply/BAT0/energy_full", + IOError(errno.ENOENT, "")): + with mock_open_exception( + "/sys/class/power_supply/BAT0/charge_full", + IOError(errno.ENOENT, "")): + with mock_open_content( + "/sys/class/power_supply/BAT0/capacity", b"88"): + self.assertEqual(psutil.sensors_battery().percent, 88) def test_emulate_no_power(self): # Emulate a case where /AC0/online file nor /BAT0/status exist. - def open_mock(name, *args, **kwargs): - if name.startswith("/sys/class/power_supply/AC/online") or \ - name.startswith("/sys/class/power_supply/AC0/online") or \ - name.startswith("/sys/class/power_supply/BAT0/status"): - raise IOError(errno.ENOENT, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - self.assertIsNone(psutil.sensors_battery().power_plugged) - assert m.called + with mock_open_exception( + "/sys/class/power_supply/AC/online", + IOError(errno.ENOENT, "")): + with mock_open_exception( + "/sys/class/power_supply/AC0/online", + IOError(errno.ENOENT, "")): + with mock_open_exception( + "/sys/class/power_supply/BAT0/status", + IOError(errno.ENOENT, "")): + self.assertIsNone(psutil.sensors_battery().power_plugged) @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsTemperatures(unittest.TestCase): - @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - def test_emulate_eio_error(self): - def open_mock(name, *args, **kwargs): - if name.endswith("_input"): - raise OSError(errno.EIO, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - with warnings.catch_warnings(record=True) as ws: - self.assertEqual(psutil.sensors_temperatures(), {}) - assert m.called - self.assertIn("ignoring", str(ws[0].message)) - - def test_emulate_data(self): + def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): return io.StringIO(u("name")) @@ -1415,6 +1566,7 @@ class TestSensorsTemperatures(unittest.TestCase): orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): + # Test case with /sys/class/hwmon with mock.patch('glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1']): temp = psutil.sensors_temperatures()['name'][0] @@ -1423,6 +1575,41 @@ class TestSensorsTemperatures(unittest.TestCase): self.assertEqual(temp.high, 40.0) self.assertEqual(temp.critical, 50.0) + def test_emulate_class_thermal(self): + def open_mock(name, *args, **kwargs): + if name.endswith('0_temp'): + return io.BytesIO(b"50000") + elif name.endswith('temp'): + return io.BytesIO(b"30000") + elif name.endswith('0_type'): + return io.StringIO(u("critical")) + elif name.endswith('type'): + return io.StringIO(u("name")) + else: + return orig_open(name, *args, **kwargs) + + def glob_mock(path): + if path == '/sys/class/hwmon/hwmon*/temp*_*': + return [] + elif path == '/sys/class/hwmon/hwmon*/device/temp*_*': + return [] + elif path == '/sys/class/thermal/thermal_zone*': + return ['/sys/class/thermal/thermal_zone0'] + elif path == '/sys/class/thermal/thermal_zone0/trip_point*': + return ['/sys/class/thermal/thermal_zone1/trip_point_0_type', + '/sys/class/thermal/thermal_zone1/trip_point_0_temp'] + return [] + + orig_open = open + patch_point = 'builtins.open' if PY3 else '__builtin__.open' + with mock.patch(patch_point, side_effect=open_mock): + with mock.patch('glob.glob', create=True, side_effect=glob_mock): + temp = psutil.sensors_temperatures()['name'][0] + self.assertEqual(temp.label, '') + self.assertEqual(temp.current, 30.0) + self.assertEqual(temp.high, 50.0) + self.assertEqual(temp.critical, 50.0) + @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsFans(unittest.TestCase): @@ -1482,6 +1669,40 @@ class TestProcess(unittest.TestCase): self.assertAlmostEqual( mem.swap, sum([x.swap for x in maps]), delta=4096) + def test_memory_full_info_mocked(self): + # See: https://github.com/giampaolo/psutil/issues/1222 + with mock_open_content( + "/proc/%s/smaps" % os.getpid(), + textwrap.dedent("""\ + fffff0 r-xp 00000000 00:00 0 [vsyscall] + Size: 1 kB + Rss: 2 kB + Pss: 3 kB + Shared_Clean: 4 kB + Shared_Dirty: 5 kB + Private_Clean: 6 kB + Private_Dirty: 7 kB + Referenced: 8 kB + Anonymous: 9 kB + LazyFree: 10 kB + AnonHugePages: 11 kB + ShmemPmdMapped: 12 kB + Shared_Hugetlb: 13 kB + Private_Hugetlb: 14 kB + Swap: 15 kB + SwapPss: 16 kB + KernelPageSize: 17 kB + MMUPageSize: 18 kB + Locked: 19 kB + VmFlags: rd ex + """).encode()) as m: + p = psutil.Process() + mem = p.memory_full_info() + assert m.called + self.assertEqual(mem.uss, (6 + 7 + 14) * 1024) + self.assertEqual(mem.pss, 3 * 1024) + self.assertEqual(mem.swap, 15 * 1024) + # On PYPY file descriptors are not closed fast enough. @unittest.skipIf(PYPY, "unreliable on PYPY") def test_open_files_mode(self): @@ -1565,7 +1786,7 @@ class TestProcess(unittest.TestCase): # TODO: re-enable this test. # def test_num_ctx_switches_mocked(self): - # with mock.patch('psutil._pslinux.open', create=True) as m: + # with mock.patch('psutil._common.open', create=True) as m: # self.assertRaises( # NotImplementedError, # psutil._pslinux.Process(os.getpid()).num_ctx_switches) @@ -1575,12 +1796,12 @@ class TestProcess(unittest.TestCase): # see: https://github.com/giampaolo/psutil/issues/639 p = psutil.Process() fake_file = io.StringIO(u('foo\x00bar\x00')) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m: self.assertEqual(p.cmdline(), ['foo', 'bar']) assert m.called fake_file = io.StringIO(u('foo\x00bar\x00\x00')) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m: self.assertEqual(p.cmdline(), ['foo', 'bar', '']) assert m.called @@ -1589,16 +1810,26 @@ class TestProcess(unittest.TestCase): # see: https://github.com/giampaolo/psutil/issues/1179 p = psutil.Process() fake_file = io.StringIO(u('foo bar ')) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m: self.assertEqual(p.cmdline(), ['foo', 'bar']) assert m.called fake_file = io.StringIO(u('foo bar ')) - with mock.patch('psutil._pslinux.open', + with mock.patch('psutil._common.open', return_value=fake_file, create=True) as m: self.assertEqual(p.cmdline(), ['foo', 'bar', '']) assert m.called + def test_cmdline_mixed_separators(self): + # https://github.com/giampaolo/psutil/issues/ + # 1179#issuecomment-552984549 + p = psutil.Process() + fake_file = io.StringIO(u('foo\x20bar\x00')) + with mock.patch('psutil._common.open', + return_value=fake_file, create=True) as m: + self.assertEqual(p.cmdline(), ['foo', 'bar']) + assert m.called + def test_readlink_path_deleted_mocked(self): with mock.patch('psutil._pslinux.os.readlink', return_value='/home/foo (deleted)'): @@ -1658,19 +1889,12 @@ class TestProcess(unittest.TestCase): def test_issue_1014(self): # Emulates a case where smaps file does not exist. In this case # wrap_exception decorator should not raise NoSuchProcess. - def open_mock(name, *args, **kwargs): - if name.startswith('/proc/%s/smaps' % os.getpid()): - raise IOError(errno.ENOENT, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: + with mock_open_exception( + '/proc/%s/smaps' % os.getpid(), + IOError(errno.ENOENT, "")) as m: p = psutil.Process() - with self.assertRaises(IOError) as err: + with self.assertRaises(FileNotFoundError): p.memory_maps() - self.assertEqual(err.exception.errno, errno.ENOENT) assert m.called @unittest.skipIf(not HAS_RLIMIT, "not supported") @@ -1702,56 +1926,52 @@ class TestProcess(unittest.TestCase): def test_stat_file_parsing(self): from psutil._pslinux import CLOCK_TICKS - def open_mock(name, *args, **kwargs): - if name.startswith('/proc/%s/stat' % os.getpid()): - args = [ - "0", # pid - "(cat)", # name - "Z", # status - "1", # ppid - "0", # pgrp - "0", # session - "0", # tty - "0", # tpgid - "0", # flags - "0", # minflt - "0", # cminflt - "0", # majflt - "0", # cmajflt - "2", # utime - "3", # stime - "4", # cutime - "5", # cstime - "0", # priority - "0", # nice - "0", # num_threads - "0", # itrealvalue - "6", # starttime - "0", # vsize - "0", # rss - "0", # rsslim - "0", # startcode - "0", # endcode - "0", # startstack - "0", # kstkesp - "0", # kstkeip - "0", # signal - "0", # blocked - "0", # sigignore - "0", # sigcatch - "0", # wchan - "0", # nswap - "0", # cnswap - "0", # exit_signal - "6", # processor - ] - return io.BytesIO(" ".join(args).encode()) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + args = [ + "0", # pid + "(cat)", # name + "Z", # status + "1", # ppid + "0", # pgrp + "0", # session + "0", # tty + "0", # tpgid + "0", # flags + "0", # minflt + "0", # cminflt + "0", # majflt + "0", # cmajflt + "2", # utime + "3", # stime + "4", # cutime + "5", # cstime + "0", # priority + "0", # nice + "0", # num_threads + "0", # itrealvalue + "6", # starttime + "0", # vsize + "0", # rss + "0", # rsslim + "0", # startcode + "0", # endcode + "0", # startstack + "0", # kstkesp + "0", # kstkeip + "0", # signal + "0", # blocked + "0", # sigignore + "0", # sigcatch + "0", # wchan + "0", # nswap + "0", # cnswap + "0", # exit_signal + "6", # processor + "0", # rt priority + "0", # policy + "7", # delayacct_blkio_ticks + ] + content = " ".join(args).encode() + with mock_open_content('/proc/%s/stat' % os.getpid(), content): p = psutil.Process() self.assertEqual(p.name(), 'cat') self.assertEqual(p.status(), psutil.STATUS_ZOMBIE) @@ -1763,25 +1983,20 @@ class TestProcess(unittest.TestCase): self.assertEqual(cpu.system, 3 / CLOCK_TICKS) self.assertEqual(cpu.children_user, 4 / CLOCK_TICKS) self.assertEqual(cpu.children_system, 5 / CLOCK_TICKS) + self.assertEqual(cpu.iowait, 7 / CLOCK_TICKS) self.assertEqual(p.cpu_num(), 6) def test_status_file_parsing(self): - def open_mock(name, *args, **kwargs): - if name.startswith('/proc/%s/status' % os.getpid()): - return io.BytesIO(textwrap.dedent("""\ - Uid:\t1000\t1001\t1002\t1003 - Gid:\t1004\t1005\t1006\t1007 - Threads:\t66 - Cpus_allowed:\tf - Cpus_allowed_list:\t0-7 - voluntary_ctxt_switches:\t12 - nonvoluntary_ctxt_switches:\t13""").encode()) - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): + with mock_open_content( + '/proc/%s/status' % os.getpid(), + textwrap.dedent("""\ + Uid:\t1000\t1001\t1002\t1003 + Gid:\t1004\t1005\t1006\t1007 + Threads:\t66 + Cpus_allowed:\tf + Cpus_allowed_list:\t0-7 + voluntary_ctxt_switches:\t12 + nonvoluntary_ctxt_switches:\t13""").encode()): p = psutil.Process() self.assertEqual(p.num_ctx_switches().voluntary, 12) self.assertEqual(p.num_ctx_switches().involuntary, 13) @@ -1851,7 +2066,7 @@ class TestProcessAgainstStatus(unittest.TestCase): value = tuple(map(int, value.split()[1:4])) self.assertEqual(self.proc.gids(), value) - @retry_before_failing() + @retry_on_failure() def test_num_ctx_switches(self): value = self.read_status_file("voluntary_ctxt_switches:") self.assertEqual(self.proc.num_ctx_switches().voluntary, value) @@ -1883,14 +2098,6 @@ class TestProcessAgainstStatus(unittest.TestCase): @unittest.skipIf(not LINUX, "LINUX only") class TestUtils(unittest.TestCase): - def test_open_text(self): - with psutil._psplatform.open_text(__file__) as f: - self.assertEqual(f.mode, 'rt') - - def test_open_binary(self): - with psutil._psplatform.open_binary(__file__) as f: - self.assertEqual(f.mode, 'rb') - def test_readlink(self): with mock.patch("os.readlink", return_value="foo (deleted)") as m: self.assertEqual(psutil._psplatform.readlink("bar"), "foo") @@ -1907,4 +2114,5 @@ class TestUtils(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_memory_leaks.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_memory_leaks.py index 680fe78036c8..f9cad70fd33e 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_memory_leaks.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_memory_leaks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,10 +11,11 @@ checking whether process memory usage keeps increasing between calls or over time. Note that this may produce false positives (especially on Windows for some reason). +PyPy appears to be completely unstable for this framework, probably +because of how its JIT handles memory, so tests are skipped. """ from __future__ import print_function -import errno import functools import gc import os @@ -24,13 +25,17 @@ import time import psutil import psutil._common +from psutil import FREEBSD from psutil import LINUX +from psutil import MACOS from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS +from psutil._common import bytes2human +from psutil._compat import ProcessLookupError from psutil._compat import xrange +from psutil.tests import CIRRUS from psutil.tests import create_sockets from psutil.tests import get_test_subprocess from psutil.tests import HAS_CPU_AFFINITY @@ -38,14 +43,15 @@ from psutil.tests import HAS_CPU_FREQ from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE from psutil.tests import HAS_MEMORY_MAPS +from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_PROC_CPU_NUM from psutil.tests import HAS_PROC_IO_COUNTERS from psutil.tests import HAS_RLIMIT from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES +from psutil.tests import PYPY from psutil.tests import reap_children -from psutil.tests import run_test_module_by_name from psutil.tests import safe_rmpath from psutil.tests import skip_on_access_denied from psutil.tests import TESTFN @@ -53,14 +59,14 @@ from psutil.tests import TRAVIS from psutil.tests import unittest +# configurable opts LOOPS = 1000 MEMORY_TOLERANCE = 4096 RETRY_FOR = 3 +SKIP_PYTHON_IMPL = True -SKIP_PYTHON_IMPL = True if TRAVIS else False cext = psutil._psplatform.cext thisproc = psutil.Process() -SKIP_PYTHON_IMPL = True if TRAVIS else False # =================================================================== @@ -73,25 +79,7 @@ def skip_if_linux(): "worthless on LINUX (pure python)") -def bytes2human(n): - """ - http://code.activestate.com/recipes/578019 - >>> bytes2human(10000) - '9.8K' - >>> bytes2human(100001221) - '95.4M' - """ - symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') - prefix = {} - for i, s in enumerate(symbols): - prefix[s] = 1 << (i + 1) * 10 - for s in reversed(symbols): - if n >= prefix[s]: - value = float(n) / prefix[s] - return '%.2f%s' % (value, s) - return "%sB" % n - - +@unittest.skipIf(PYPY, "unreliable on PYPY") class TestMemLeak(unittest.TestCase): """Base framework class which calls a function many times and produces a failure if process memory usage keeps increasing @@ -176,7 +164,7 @@ class TestMemLeak(unittest.TestCase): def _get_mem(): # By using USS memory it seems it's less likely to bump # into false positives. - if LINUX or WINDOWS or OSX: + if LINUX or WINDOWS or MACOS: return thisproc.memory_full_info().uss else: return thisproc.memory_info().rss @@ -200,8 +188,8 @@ class TestProcessObjectLeaks(TestMemLeak): skip = set(( "pid", "as_dict", "children", "cpu_affinity", "cpu_percent", "ionice", "is_running", "kill", "memory_info_ex", "memory_percent", - "nice", "oneshot", "parent", "rlimit", "send_signal", "suspend", - "terminate", "wait")) + "nice", "oneshot", "parent", "parents", "rlimit", "send_signal", + "suspend", "terminate", "wait")) for name in dir(psutil.Process): if name.startswith('_'): continue @@ -344,8 +332,6 @@ class TestProcessObjectLeaks(TestMemLeak): with open(TESTFN, 'w'): self.execute(self.proc.open_files) - # OSX implementation is unbelievably slow - @unittest.skipIf(OSX, "too slow on OSX") @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") @skip_if_linux() def test_memory_maps(self): @@ -384,6 +370,16 @@ class TestProcessObjectLeaks(TestMemLeak): self.execute(cext.proc_info, os.getpid()) +@unittest.skipIf(not WINDOWS, "WINDOWS only") +class TestProcessDualImplementation(TestMemLeak): + + def test_cmdline_peb_true(self): + self.execute(cext.proc_cmdline, os.getpid(), use_peb=True) + + def test_cmdline_peb_false(self): + self.execute(cext.proc_cmdline, os.getpid(), use_peb=False) + + class TestTerminatedProcessLeaks(TestProcessObjectLeaks): """Repeat the tests above looking for leaks occurring when dealing with terminated processes raising NoSuchProcess exception. @@ -432,9 +428,8 @@ class TestTerminatedProcessLeaks(TestProcessObjectLeaks): def call(): try: return cext.proc_info(self.proc.pid) - except OSError as err: - if err.errno != errno.ESRCH: - raise + except ProcessLookupError: + pass self.execute(call) @@ -476,6 +471,7 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_per_cpu_times(self): self.execute(psutil.cpu_times, percpu=True) + @skip_if_linux() def test_cpu_stats(self): self.execute(psutil.cpu_stats) @@ -484,14 +480,17 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_cpu_freq(self): self.execute(psutil.cpu_freq) + @unittest.skipIf(not WINDOWS, "WINDOWS only") + def test_getloadavg(self): + self.execute(psutil.getloadavg) + # --- mem def test_virtual_memory(self): self.execute(psutil.virtual_memory) # TODO: remove this skip when this gets fixed - @unittest.skipIf(SUNOS, - "worthless on SUNOS (uses a subprocess)") + @unittest.skipIf(SUNOS, "worthless on SUNOS (uses a subprocess)") def test_swap_memory(self): self.execute(psutil.swap_memory) @@ -524,13 +523,15 @@ class TestModuleFunctionsLeaks(TestMemLeak): # --- net + @unittest.skipIf(TRAVIS and MACOS, "false positive on TRAVIS + MACOS") + @unittest.skipIf(CIRRUS and FREEBSD, "false positive on CIRRUS + FREEBSD") @skip_if_linux() + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): self.execute(psutil.net_io_counters, nowrap=False) - @unittest.skipIf(LINUX, - "worthless on Linux (pure python)") - @unittest.skipIf(OSX and os.getuid() != 0, "need root access") + @skip_if_linux() + @unittest.skipIf(MACOS and os.getuid() != 0, "need root access") def test_net_connections(self): with create_sockets(): self.execute(psutil.net_connections) @@ -567,7 +568,6 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_boot_time(self): self.execute(psutil.boot_time) - # XXX - on Windows this produces a false positive @unittest.skipIf(WINDOWS, "XXX produces a false positive on Windows") def test_users(self): self.execute(psutil.users) @@ -596,4 +596,5 @@ class TestModuleFunctionsLeaks(TestMemLeak): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_misc.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_misc.py index f67c0e4cd7a2..c20cd9413b6a 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_misc.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_misc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -19,19 +19,24 @@ import pickle import socket import stat +from psutil import FREEBSD from psutil import LINUX +from psutil import NETBSD from psutil import POSIX from psutil import WINDOWS from psutil._common import memoize from psutil._common import memoize_when_activated from psutil._common import supports_ipv6 from psutil._common import wrap_numbers +from psutil._common import open_text +from psutil._common import open_binary from psutil._compat import PY3 from psutil.tests import APPVEYOR from psutil.tests import bind_socket from psutil.tests import bind_unix_socket from psutil.tests import call_until from psutil.tests import chdir +from psutil.tests import CI_TESTING from psutil.tests import create_proc_children_pair from psutil.tests import create_sockets from psutil.tests import create_zombie_proc @@ -40,8 +45,8 @@ from psutil.tests import get_free_port from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import HAS_CONNECTIONS_UNIX -from psutil.tests import HAS_MEMORY_FULL_INFO from psutil.tests import HAS_MEMORY_MAPS +from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES @@ -53,7 +58,7 @@ from psutil.tests import reap_children from psutil.tests import reload_module from psutil.tests import retry from psutil.tests import ROOT_DIR -from psutil.tests import run_test_module_by_name +from psutil.tests import safe_mkdir from psutil.tests import safe_rmpath from psutil.tests import SCRIPTS_DIR from psutil.tests import sh @@ -177,7 +182,8 @@ class TestMisc(unittest.TestCase): for name in dir_psutil: if name in ('callable', 'error', 'namedtuple', 'tests', 'long', 'test', 'NUM_CPUS', 'BOOT_TIME', - 'TOTAL_PHYMEM'): + 'TOTAL_PHYMEM', 'PermissionError', + 'ProcessLookupError'): continue if not name.startswith('_'): try: @@ -258,14 +264,14 @@ class TestMisc(unittest.TestCase): # activate calls = [] - f.foo.cache_activate() + f.foo.cache_activate(f) f.foo() f.foo() self.assertEqual(len(calls), 1) # deactivate calls = [] - f.foo.cache_deactivate() + f.foo.cache_deactivate(f) f.foo() f.foo() self.assertEqual(len(calls), 2) @@ -617,10 +623,10 @@ class TestWrapNumbers(unittest.TestCase): wrap_numbers.cache_clear('disk_io') wrap_numbers.cache_clear('?!?') - @unittest.skipIf( - not psutil.disk_io_counters() or not psutil.net_io_counters(), - "no disks or NICs available") + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_cache_clear_public_apis(self): + if not psutil.disk_io_counters() or not psutil.net_io_counters(): + return self.skipTest("no disks or NICs available") psutil.disk_io_counters() psutil.net_io_counters() caches = wrap_numbers.cache_info() @@ -708,9 +714,7 @@ class TestScripts(unittest.TestCase): def test_procinfo(self): self.assert_stdout('procinfo.py', str(os.getpid())) - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") + @unittest.skipIf(CI_TESTING and not psutil.users(), "no users") def test_who(self): self.assert_stdout('who.py') @@ -732,8 +736,9 @@ class TestScripts(unittest.TestCase): def test_pmap(self): self.assert_stdout('pmap.py', str(os.getpid())) - @unittest.skipIf(not HAS_MEMORY_FULL_INFO, "not supported") def test_procsmem(self): + if 'uss' not in psutil.Process().memory_full_info()._fields: + raise self.skipTest("not supported") self.assert_stdout('procsmem.py', stderr=DEVNULL) def test_killall(self): @@ -762,11 +767,15 @@ class TestScripts(unittest.TestCase): @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") def test_temperatures(self): + if not psutil.sensors_temperatures(): + self.skipTest("no temperatures") self.assert_stdout('temperatures.py') @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") def test_fans(self): + if not psutil.sensors_fans(): + self.skipTest("no fans") self.assert_stdout('fans.py') @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") @@ -895,6 +904,20 @@ class TestFSTestUtils(unittest.TestCase): tearDown = setUp + def test_open_text(self): + with open_text(__file__) as f: + self.assertEqual(f.mode, 'rt') + + def test_open_binary(self): + with open_binary(__file__) as f: + self.assertEqual(f.mode, 'rb') + + def test_safe_mkdir(self): + safe_mkdir(TESTFN) + assert os.path.isdir(TESTFN) + safe_mkdir(TESTFN) + assert os.path.isdir(TESTFN) + def test_safe_rmpath(self): # test file is removed open(TESTFN, 'w').close() @@ -994,6 +1017,8 @@ class TestNetUtils(unittest.TestCase): self.assertNotEqual(client.getsockname(), addr) @unittest.skipIf(not POSIX, "POSIX only") + @unittest.skipIf(NETBSD or FREEBSD, + "/var/run/log UNIX socket opened by default") def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() @@ -1036,4 +1061,5 @@ class TestOtherUtils(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_osx.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_osx.py index bcb2ba4e1a50..e4e77f93537e 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_osx.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_osx.py @@ -1,29 +1,28 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""OSX specific tests.""" +"""MACOS specific tests.""" import os import re import time import psutil -from psutil import OSX +from psutil import MACOS from psutil.tests import create_zombie_proc from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import MEMORY_TOLERANCE from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import unittest -PAGESIZE = os.sysconf("SC_PAGE_SIZE") if OSX else None +PAGESIZE = os.sysconf("SC_PAGE_SIZE") if MACOS else None def sysctl(cmdline): @@ -76,7 +75,7 @@ def human2bytes(s): return int(num * prefix[letter]) -@unittest.skipIf(not OSX, "OSX only") +@unittest.skipIf(not MACOS, "MACOS only") class TestProcess(unittest.TestCase): @classmethod @@ -101,7 +100,7 @@ class TestProcess(unittest.TestCase): time.strftime("%Y", time.localtime(start_psutil))) -@unittest.skipIf(not OSX, "OSX only") +@unittest.skipIf(not MACOS, "MACOS only") class TestZombieProcessAPIs(unittest.TestCase): @classmethod @@ -158,11 +157,8 @@ class TestZombieProcessAPIs(unittest.TestCase): self.assertRaises((psutil.ZombieProcess, psutil.AccessDenied), self.p.threads) - def test_memory_maps(self): - self.assertRaises(psutil.ZombieProcess, self.p.memory_maps) - -@unittest.skipIf(not OSX, "OSX only") +@unittest.skipIf(not MACOS, "MACOS only") class TestSystemAPIs(unittest.TestCase): # --- disk @@ -219,31 +215,25 @@ class TestSystemAPIs(unittest.TestCase): sysctl_hwphymem = sysctl('sysctl hw.memsize') self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) - @retry_before_failing() + @retry_on_failure() def test_vmem_free(self): vmstat_val = vm_stat("free") psutil_val = psutil.virtual_memory().free self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE) - @retry_before_failing() - def test_vmem_available(self): - vmstat_val = vm_stat("inactive") + vm_stat("free") - psutil_val = psutil.virtual_memory().available - self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE) - - @retry_before_failing() + @retry_on_failure() def test_vmem_active(self): vmstat_val = vm_stat("active") psutil_val = psutil.virtual_memory().active self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_inactive(self): vmstat_val = vm_stat("inactive") psutil_val = psutil.virtual_memory().inactive self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE) - @retry_before_failing() + @retry_on_failure() def test_vmem_wired(self): vmstat_val = vm_stat("wired") psutil_val = psutil.virtual_memory().wired @@ -251,13 +241,13 @@ class TestSystemAPIs(unittest.TestCase): # --- swap mem - @retry_before_failing() + @retry_on_failure() def test_swapmem_sin(self): vmstat_val = vm_stat("Pageins") psutil_val = psutil.swap_memory().sin self.assertEqual(psutil_val, vmstat_val) - @retry_before_failing() + @retry_on_failure() def test_swapmem_sout(self): vmstat_val = vm_stat("Pageout") psutil_val = psutil.swap_memory().sout @@ -291,7 +281,7 @@ class TestSystemAPIs(unittest.TestCase): @unittest.skipIf(not HAS_BATTERY, "no battery") def test_sensors_battery(self): out = sh("pmset -g batt") - percent = re.search("(\d+)%", out).group(1) + percent = re.search(r"(\d+)%", out).group(1) drawing_from = re.search("Now drawing from '([^']+)'", out).group(1) power_plugged = drawing_from == "AC Power" psutil_result = psutil.sensors_battery() @@ -300,4 +290,5 @@ class TestSystemAPIs(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_posix.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_posix.py index c59f9a1c75cc..a96b310ffe7f 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_posix.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_posix.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -12,27 +12,24 @@ import errno import os import re import subprocess -import sys import time import psutil from psutil import AIX from psutil import BSD from psutil import LINUX +from psutil import MACOS from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import SUNOS -from psutil._compat import callable -from psutil._compat import PY3 -from psutil.tests import APPVEYOR +from psutil.tests import CI_TESTING from psutil.tests import get_kernel_version from psutil.tests import get_test_subprocess +from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import mock from psutil.tests import PYTHON_EXE from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import skip_on_access_denied from psutil.tests import TRAVIS @@ -41,23 +38,53 @@ from psutil.tests import wait_for_pid from psutil.tests import which -def ps(cmd): - """Expects a ps command with a -o argument and parse the result - returning only the value of interest. +def ps(fmt, pid=None): """ - if not LINUX: - cmd = cmd.replace(" --no-headers ", " ") + Wrapper for calling the ps command with a little bit of cross-platform + support for a narrow range of features. + """ + + cmd = ['ps'] + + if LINUX: + cmd.append('--no-headers') + + if pid is not None: + cmd.extend(['-p', str(pid)]) + else: + if SUNOS or AIX: + cmd.append('-A') + else: + cmd.append('ax') + if SUNOS: - cmd = cmd.replace("-o start", "-o stime") - if AIX: - cmd = cmd.replace("-o rss", "-o rssize") + fmt_map = set(('command', 'comm', 'start', 'stime')) + fmt = fmt_map.get(fmt, fmt) + + cmd.extend(['-o', fmt]) + output = sh(cmd) - if not LINUX: - output = output.split('\n')[1].strip() - try: - return int(output) - except ValueError: - return output + + if LINUX: + output = output.splitlines() + else: + output = output.splitlines()[1:] + + all_output = [] + for line in output: + line = line.strip() + + try: + line = int(line) + except ValueError: + pass + + all_output.append(line) + + if pid is None: + return all_output + else: + return all_output[0] # ps "-o" field names differ wildly between platforms. # "comm" means "only executable name" but is not available on BSD platforms. @@ -75,14 +102,28 @@ def ps_name(pid): field = "command" if SUNOS: field = "comm" - return ps("ps --no-headers -o %s -p %s" % (field, pid)).split(' ')[0] + return ps(field, pid).split()[0] def ps_args(pid): field = "command" if AIX or SUNOS: field = "args" - return ps("ps --no-headers -o %s -p %s" % (field, pid)) + return ps(field, pid) + + +def ps_rss(pid): + field = "rss" + if AIX: + field = "rssize" + return ps(field, pid) + + +def ps_vsz(pid): + field = "vsz" + if AIX: + field = "vsize" + return ps(field, pid) @unittest.skipIf(not POSIX, "POSIX only") @@ -100,22 +141,22 @@ class TestProcess(unittest.TestCase): reap_children() def test_ppid(self): - ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) + ppid_ps = ps('ppid', self.pid) ppid_psutil = psutil.Process(self.pid).ppid() self.assertEqual(ppid_ps, ppid_psutil) def test_uid(self): - uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) + uid_ps = ps('uid', self.pid) uid_psutil = psutil.Process(self.pid).uids().real self.assertEqual(uid_ps, uid_psutil) def test_gid(self): - gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) + gid_ps = ps('rgid', self.pid) gid_psutil = psutil.Process(self.pid).gids().real self.assertEqual(gid_ps, gid_psutil) def test_username(self): - username_ps = ps("ps --no-headers -o user -p %s" % self.pid) + username_ps = ps('user', self.pid) username_psutil = psutil.Process(self.pid).username() self.assertEqual(username_ps, username_psutil) @@ -129,22 +170,22 @@ class TestProcess(unittest.TestCase): assert fun.called @skip_on_access_denied() - @retry_before_failing() + @retry_on_failure() def test_rss_memory(self): # give python interpreter some time to properly initialize # so that the results are the same time.sleep(0.1) - rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) + rss_ps = ps_rss(self.pid) rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 self.assertEqual(rss_ps, rss_psutil) @skip_on_access_denied() - @retry_before_failing() + @retry_on_failure() def test_vsz_memory(self): # give python interpreter some time to properly initialize # so that the results are the same time.sleep(0.1) - vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) + vsz_ps = ps_vsz(self.pid) vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 self.assertEqual(vsz_ps, vsz_psutil) @@ -153,10 +194,13 @@ class TestProcess(unittest.TestCase): # remove path if there is any, from the command name_ps = os.path.basename(name_ps).lower() name_psutil = psutil.Process(self.pid).name().lower() - # ...because of how we calculate PYTHON_EXE; on OSX this may + # ...because of how we calculate PYTHON_EXE; on MACOS this may # be "pythonX.Y". name_ps = re.sub(r"\d.\d", "", name_ps) name_psutil = re.sub(r"\d.\d", "", name_psutil) + # ...may also be "python.X" + name_ps = re.sub(r"\d", "", name_ps) + name_psutil = re.sub(r"\d", "", name_psutil) self.assertEqual(name_ps, name_psutil) def test_name_long(self): @@ -195,9 +239,9 @@ class TestProcess(unittest.TestCase): p = psutil.Process() self.assertRaises(psutil.NoSuchProcess, p.name) - @unittest.skipIf(OSX or BSD, 'ps -o start not available') + @unittest.skipIf(MACOS or BSD, 'ps -o start not available') def test_create_time(self): - time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] + time_ps = ps('start', self.pid) time_psutil = psutil.Process(self.pid).create_time() time_psutil_tstamp = datetime.datetime.fromtimestamp( time_psutil).strftime("%H:%M:%S") @@ -236,7 +280,7 @@ class TestProcess(unittest.TestCase): @unittest.skipIf(SUNOS, "not reliable on SUNOS") @unittest.skipIf(AIX, "not reliable on AIX") def test_nice(self): - ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid) + ps_nice = ps('nice', self.pid) psutil_nice = psutil.Process().nice() self.assertEqual(ps_nice, psutil_nice) @@ -257,7 +301,7 @@ class TestProcess(unittest.TestCase): failures = [] ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', 'send_signal', 'wait', 'children', 'as_dict', - 'memory_info_ex'] + 'memory_info_ex', 'parent', 'parents'] if LINUX and get_kernel_version() < (2, 6, 36): ignored_names.append('rlimit') if LINUX and get_kernel_version() < (2, 6, 23): @@ -286,40 +330,29 @@ class TestProcess(unittest.TestCase): class TestSystemAPIs(unittest.TestCase): """Test some system APIs.""" - @retry_before_failing() + @retry_on_failure() def test_pids(self): # Note: this test might fail if the OS is starting/killing # other processes in the meantime - if SUNOS or AIX: - cmd = ["ps", "-A", "-o", "pid"] - else: - cmd = ["ps", "ax", "-o", "pid"] - p = get_test_subprocess(cmd, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - assert p.poll() == 0 - if PY3: - output = str(output, sys.stdout.encoding) - pids_ps = [] - for line in output.split('\n')[1:]: - if line: - pid = int(line.split()[0].strip()) - pids_ps.append(pid) - # remove ps subprocess pid which is supposed to be dead in meantime - pids_ps.remove(p.pid) + pids_ps = sorted(ps("pid")) pids_psutil = psutil.pids() - pids_ps.sort() - pids_psutil.sort() - # on OSX and OPENBSD ps doesn't show pid 0 - if OSX or OPENBSD and 0 not in pids_ps: + # on MACOS and OPENBSD ps doesn't show pid 0 + if MACOS or OPENBSD and 0 not in pids_ps: pids_ps.insert(0, 0) - self.assertEqual(pids_ps, pids_psutil) + + # There will often be one more process in pids_ps for ps itself + if len(pids_ps) - len(pids_psutil) > 1: + difference = [x for x in pids_psutil if x not in pids_ps] + \ + [x for x in pids_ps if x not in pids_psutil] + self.fail("difference: " + str(difference)) # for some reason ifconfig -a does not report all interfaces # returned by psutil @unittest.skipIf(SUNOS, "unreliable on SUNOS") @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") @unittest.skipIf(not which('ifconfig'), "no ifconfig cmd") + @unittest.skipIf(not HAS_NET_IO_COUNTERS, "not supported") def test_nic_names(self): output = sh("ifconfig -a") for nic in psutil.net_io_counters(pernic=True).keys(): @@ -331,13 +364,13 @@ class TestSystemAPIs(unittest.TestCase): "couldn't find %s nic in 'ifconfig -a' output\n%s" % ( nic, output)) - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") - @retry_before_failing() + @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") + @retry_on_failure() def test_users(self): out = sh("who") lines = out.split('\n') + if not lines: + raise self.skipTest("no users on this system") users = [x.split()[0] for x in lines] terminals = [x.split()[1] for x in lines] self.assertEqual(len(users), len(psutil.users())) @@ -416,4 +449,5 @@ class TestSystemAPIs(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_process.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_process.py index a629cae524de..987bdf38bb69 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_process.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_process.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -25,23 +25,25 @@ import psutil from psutil import AIX from psutil import BSD from psutil import LINUX +from psutil import MACOS from psutil import NETBSD from psutil import OPENBSD from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS +from psutil._common import open_text from psutil._compat import long from psutil._compat import PY3 from psutil.tests import APPVEYOR from psutil.tests import call_until +from psutil.tests import CIRRUS from psutil.tests import copyload_shared_lib from psutil.tests import create_exe from psutil.tests import create_proc_children_pair from psutil.tests import create_zombie_proc from psutil.tests import enum from psutil.tests import get_test_subprocess -from psutil.tests import get_winver from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE @@ -54,8 +56,7 @@ from psutil.tests import mock from psutil.tests import PYPY from psutil.tests import PYTHON_EXE from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import safe_rmpath from psutil.tests import sh from psutil.tests import skip_on_access_denied @@ -66,7 +67,6 @@ from psutil.tests import ThreadTask from psutil.tests import TRAVIS from psutil.tests import unittest from psutil.tests import wait_for_pid -from psutil.tests import WIN_VISTA # =================================================================== @@ -238,10 +238,6 @@ class TestProcess(unittest.TestCase): percent = p.cpu_percent(interval=None) self.assertIsInstance(percent, float) self.assertGreaterEqual(percent, 0.0) - if not POSIX: - self.assertLessEqual(percent, 100.0) - else: - self.assertGreaterEqual(percent, 0.0) with self.assertRaises(ValueError): p.cpu_percent(interval=-1) @@ -256,6 +252,8 @@ class TestProcess(unittest.TestCase): assert (times.user > 0.0) or (times.system > 0.0), times assert (times.children_user >= 0.0), times assert (times.children_system >= 0.0), times + if LINUX: + assert times.iowait >= 0.0, times # make sure returned values can be pretty printed with strftime for name in times._fields: time.strftime("%H:%M:%S", time.localtime(getattr(times, name))) @@ -300,7 +298,7 @@ class TestProcess(unittest.TestCase): time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time())) @unittest.skipIf(not POSIX, 'POSIX only') - @unittest.skipIf(TRAVIS, 'not reliable on TRAVIS') + @unittest.skipIf(TRAVIS or CIRRUS, 'not reliable on TRAVIS/CIRRUS') def test_terminal(self): terminal = psutil.Process().terminal() if sys.stdin.isatty() or sys.stdout.isatty(): @@ -313,7 +311,6 @@ class TestProcess(unittest.TestCase): @skip_on_not_implemented(only_if=LINUX) def test_io_counters(self): p = psutil.Process() - # test reads io1 = p.io_counters() with open(PYTHON_EXE, 'rb') as f: @@ -354,74 +351,77 @@ class TestProcess(unittest.TestCase): self.assertGreaterEqual(io2[i], 0) @unittest.skipIf(not HAS_IONICE, "not supported") - @unittest.skipIf(WINDOWS and get_winver() < WIN_VISTA, 'not supported') - def test_ionice(self): - if LINUX: - from psutil import (IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, - IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE) - self.assertEqual(IOPRIO_CLASS_NONE, 0) - self.assertEqual(IOPRIO_CLASS_RT, 1) - self.assertEqual(IOPRIO_CLASS_BE, 2) - self.assertEqual(IOPRIO_CLASS_IDLE, 3) - p = psutil.Process() - try: - p.ionice(2) - ioclass, value = p.ionice() - if enum is not None: - self.assertIsInstance(ioclass, enum.IntEnum) - self.assertEqual(ioclass, 2) - self.assertEqual(value, 4) - # - p.ionice(3) - ioclass, value = p.ionice() - self.assertEqual(ioclass, 3) - self.assertEqual(value, 0) - # - p.ionice(2, 0) - ioclass, value = p.ionice() - self.assertEqual(ioclass, 2) - self.assertEqual(value, 0) - p.ionice(2, 7) - ioclass, value = p.ionice() - self.assertEqual(ioclass, 2) - self.assertEqual(value, 7) - finally: - p.ionice(IOPRIO_CLASS_NONE) - else: - p = psutil.Process() - original = p.ionice() - self.assertIsInstance(original, int) - try: - value = 0 # very low - if original == value: - value = 1 # low - p.ionice(value) - self.assertEqual(p.ionice(), value) - finally: - p.ionice(original) - - @unittest.skipIf(not HAS_IONICE, "not supported") - @unittest.skipIf(WINDOWS and get_winver() < WIN_VISTA, 'not supported') - def test_ionice_errs(self): - sproc = get_test_subprocess() - p = psutil.Process(sproc.pid) - if LINUX: - self.assertRaises(ValueError, p.ionice, 2, 10) - self.assertRaises(ValueError, p.ionice, 2, -1) - self.assertRaises(ValueError, p.ionice, 4) - self.assertRaises(TypeError, p.ionice, 2, "foo") + @unittest.skipIf(not LINUX, "linux only") + def test_ionice_linux(self): + p = psutil.Process() + self.assertEqual(p.ionice()[0], psutil.IOPRIO_CLASS_NONE) + self.assertEqual(psutil.IOPRIO_CLASS_NONE, 0) + self.assertEqual(psutil.IOPRIO_CLASS_RT, 1) # high + self.assertEqual(psutil.IOPRIO_CLASS_BE, 2) # normal + self.assertEqual(psutil.IOPRIO_CLASS_IDLE, 3) # low + try: + # low + p.ionice(psutil.IOPRIO_CLASS_IDLE) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_IDLE, 0)) + with self.assertRaises(ValueError): # accepts no value + p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7) + # normal + p.ionice(psutil.IOPRIO_CLASS_BE) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 0)) + p.ionice(psutil.IOPRIO_CLASS_BE, value=7) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 7)) + with self.assertRaises(ValueError): + p.ionice(psutil.IOPRIO_CLASS_BE, value=8) + # high + if os.getuid() == 0: # root + p.ionice(psutil.IOPRIO_CLASS_RT) + self.assertEqual(tuple(p.ionice()), + (psutil.IOPRIO_CLASS_RT, 0)) + p.ionice(psutil.IOPRIO_CLASS_RT, value=7) + self.assertEqual(tuple(p.ionice()), + (psutil.IOPRIO_CLASS_RT, 7)) + with self.assertRaises(ValueError): + p.ionice(psutil.IOPRIO_CLASS_IDLE, value=8) + # errs self.assertRaisesRegex( - ValueError, "can't specify value with IOPRIO_CLASS_NONE", + ValueError, "ioclass accepts no value", p.ionice, psutil.IOPRIO_CLASS_NONE, 1) self.assertRaisesRegex( - ValueError, "can't specify value with IOPRIO_CLASS_IDLE", + ValueError, "ioclass accepts no value", p.ionice, psutil.IOPRIO_CLASS_IDLE, 1) self.assertRaisesRegex( ValueError, "'ioclass' argument must be specified", p.ionice, value=1) - else: - self.assertRaises(ValueError, p.ionice, 3) - self.assertRaises(TypeError, p.ionice, 2, 1) + finally: + p.ionice(psutil.IOPRIO_CLASS_BE) + + @unittest.skipIf(not HAS_IONICE, "not supported") + @unittest.skipIf(not WINDOWS, 'not supported on this win version') + def test_ionice_win(self): + p = psutil.Process() + self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL) + try: + # base + p.ionice(psutil.IOPRIO_VERYLOW) + self.assertEqual(p.ionice(), psutil.IOPRIO_VERYLOW) + p.ionice(psutil.IOPRIO_LOW) + self.assertEqual(p.ionice(), psutil.IOPRIO_LOW) + try: + p.ionice(psutil.IOPRIO_HIGH) + except psutil.AccessDenied: + pass + else: + self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH) + # errs + self.assertRaisesRegex( + TypeError, "value argument not accepted on Windows", + p.ionice, psutil.IOPRIO_NORMAL, value=1) + self.assertRaisesRegex( + ValueError, "is not a valid priority", + p.ionice, psutil.IOPRIO_HIGH + 1) + finally: + p.ionice(psutil.IOPRIO_NORMAL) + self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL) @unittest.skipIf(not HAS_RLIMIT, "not supported") def test_rlimit_get(self): @@ -508,7 +508,7 @@ class TestProcess(unittest.TestCase): def test_num_threads(self): # on certain platforms such as Linux we might test for exact # thread number, since we always have with 1 thread per process, - # but this does not apply across all platforms (OSX, Windows) + # but this does not apply across all platforms (MACOS, Windows) p = psutil.Process() if OPENBSD: try: @@ -542,17 +542,14 @@ class TestProcess(unittest.TestCase): with ThreadTask(): step2 = p.threads() self.assertEqual(len(step2), len(step1) + 1) - # on Linux, first thread id is supposed to be this process - if LINUX: - self.assertEqual(step2[0].id, os.getpid()) athread = step2[0] # test named tuple self.assertEqual(athread.id, athread[0]) self.assertEqual(athread.user_time, athread[1]) self.assertEqual(athread.system_time, athread[2]) - @retry_before_failing() - @skip_on_access_denied(only_if=OSX) + @retry_on_failure() + @skip_on_access_denied(only_if=MACOS) @unittest.skipIf(not HAS_THREADS, 'not supported') def test_threads_2(self): sproc = get_test_subprocess() @@ -606,8 +603,10 @@ class TestProcess(unittest.TestCase): for name in mem._fields: value = getattr(mem, name) self.assertGreaterEqual(value, 0, msg=(name, value)) + if name == 'vms' and OSX or LINUX: + continue self.assertLessEqual(value, total, msg=(name, value, total)) - if LINUX or WINDOWS or OSX: + if LINUX or WINDOWS or MACOS: self.assertGreaterEqual(mem.uss, 0) if LINUX: self.assertGreaterEqual(mem.pss, 0) @@ -633,7 +632,7 @@ class TestProcess(unittest.TestCase): raise else: # https://github.com/giampaolo/psutil/issues/759 - with open('/proc/self/smaps') as f: + with open_text('/proc/self/smaps') as f: data = f.read() if "%s (deleted)" % nt.path not in data: raise @@ -666,16 +665,10 @@ class TestProcess(unittest.TestCase): def test_memory_percent(self): p = psutil.Process() - ret = p.memory_percent() - assert 0 <= ret <= 100, ret - ret = p.memory_percent(memtype='vms') - assert 0 <= ret <= 100, ret - assert 0 <= ret <= 100, ret + p.memory_percent() self.assertRaises(ValueError, p.memory_percent, memtype="?!?") - if LINUX or OSX or WINDOWS: - ret = p.memory_percent(memtype='uss') - assert 0 <= ret <= 100, ret - assert 0 <= ret <= 100, ret + if LINUX or MACOS or WINDOWS: + p.memory_percent(memtype='uss') def test_is_running(self): sproc = get_test_subprocess() @@ -709,7 +702,7 @@ class TestProcess(unittest.TestCase): self.assertEqual(exe.replace(ver, ''), PYTHON_EXE.replace(ver, '')) except AssertionError: - # Tipically OSX. Really not sure what to do here. + # Tipically MACOS. Really not sure what to do here. pass out = sh([exe, "-c", "import os; print('hey')"]) @@ -733,15 +726,34 @@ class TestProcess(unittest.TestCase): else: raise + @unittest.skipIf(PYPY, "broken on PYPY") + def test_long_cmdline(self): + create_exe(TESTFN) + self.addCleanup(safe_rmpath, TESTFN) + cmdline = [TESTFN] + (["0123456789"] * 20) + sproc = get_test_subprocess(cmdline) + p = psutil.Process(sproc.pid) + self.assertEqual(p.cmdline(), cmdline) + def test_name(self): sproc = get_test_subprocess(PYTHON_EXE) name = psutil.Process(sproc.pid).name().lower() pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) + @unittest.skipIf(PYPY, "unreliable on PYPY") + def test_long_name(self): + long_name = TESTFN + ("0123456789" * 2) + create_exe(long_name) + self.addCleanup(safe_rmpath, long_name) + sproc = get_test_subprocess(long_name) + p = psutil.Process(sproc.pid) + self.assertEqual(p.name(), os.path.basename(long_name)) + # XXX @unittest.skipIf(SUNOS, "broken on SUNOS") @unittest.skipIf(AIX, "broken on AIX") + @unittest.skipIf(PYPY, "broken on PYPY") def test_prog_w_funky_name(self): # Test that name(), exe() and cmdline() correctly handle programs # with funky chars such as spaces and ")", see: @@ -829,8 +841,8 @@ class TestProcess(unittest.TestCase): self.assertEqual( os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice()) # XXX - going back to previous nice value raises - # AccessDenied on OSX - if not OSX: + # AccessDenied on MACOS + if not MACOS: p.nice(0) self.assertEqual(p.nice(), 0) except psutil.AccessDenied: @@ -908,8 +920,9 @@ class TestProcess(unittest.TestCase): self.assertRaises(TypeError, p.cpu_affinity, 1) p.cpu_affinity(initial) # it should work with all iterables, not only lists - p.cpu_affinity(set(all_cpus)) - p.cpu_affinity(tuple(all_cpus)) + if not TRAVIS: + p.cpu_affinity(set(all_cpus)) + p.cpu_affinity(tuple(all_cpus)) @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported') def test_cpu_affinity_errs(self): @@ -929,6 +942,8 @@ class TestProcess(unittest.TestCase): self.addCleanup(p.cpu_affinity, initial) # All possible CPU set combinations. + if len(initial) > 12: + initial = initial[:12] # ...otherwise it will take forever combos = [] for l in range(0, len(initial) + 1): for subset in itertools.combinations(initial, l): @@ -953,13 +968,12 @@ class TestProcess(unittest.TestCase): f.flush() # give the kernel some time to see the new file files = call_until(p.open_files, "len(ret) != %i" % len(files)) - for file in files: - if file.path == TESTFN: - if LINUX: + filenames = [os.path.normcase(x.path) for x in files] + self.assertIn(os.path.normcase(TESTFN), filenames) + if LINUX: + for file in files: + if file.path == TESTFN: self.assertEqual(file.position, 1024) - break - else: - self.fail("no file found; files=%s" % repr(files)) for file in files: assert os.path.isfile(file.path), file @@ -969,12 +983,12 @@ class TestProcess(unittest.TestCase): p = psutil.Process(sproc.pid) for x in range(100): - filenames = [x.path for x in p.open_files()] + filenames = [os.path.normcase(x.path) for x in p.open_files()] if TESTFN in filenames: break time.sleep(.01) else: - self.assertIn(TESTFN, filenames) + self.assertIn(os.path.normcase(TESTFN), filenames) for file in filenames: assert os.path.isfile(file), file @@ -984,14 +998,16 @@ class TestProcess(unittest.TestCase): @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") def test_open_files_2(self): # test fd and path fields + normcase = os.path.normcase with open(TESTFN, 'w') as fileobj: p = psutil.Process() for file in p.open_files(): - if file.path == fileobj.name or file.fd == fileobj.fileno(): + if normcase(file.path) == normcase(fileobj.name) or \ + file.fd == fileobj.fileno(): break else: self.fail("no file found; files=%s" % repr(p.open_files())) - self.assertEqual(file.path, fileobj.name) + self.assertEqual(normcase(file.path), normcase(fileobj.name)) if WINDOWS: self.assertEqual(file.fd, -1) else: @@ -1054,6 +1070,14 @@ class TestProcess(unittest.TestCase): p = psutil.Process(sproc.pid) self.assertEqual(p.parent().pid, this_parent) + lowest_pid = psutil.pids()[0] + self.assertIsNone(psutil.Process(lowest_pid).parent()) + + def test_parent_multi(self): + p1, p2 = create_proc_children_pair() + self.assertEqual(p2.parent(), p1) + self.assertEqual(p1.parent(), psutil.Process()) + def test_parent_disappeared(self): # Emulate a case where the parent process disappeared. sproc = get_test_subprocess() @@ -1062,7 +1086,16 @@ class TestProcess(unittest.TestCase): side_effect=psutil.NoSuchProcess(0, 'foo')): self.assertIsNone(p.parent()) + @retry_on_failure() + def test_parents(self): + assert psutil.Process().parents() + p1, p2 = create_proc_children_pair() + self.assertEqual(p1.parents()[0], psutil.Process()) + self.assertEqual(p2.parents()[0], p1) + self.assertEqual(p2.parents()[1], psutil.Process()) + def test_children(self): + reap_children(recursive=True) p = psutil.Process() self.assertEqual(p.children(), []) self.assertEqual(p.children(recursive=True), []) @@ -1108,6 +1141,19 @@ class TestProcess(unittest.TestCase): else: self.assertEqual(len(c), len(set(c))) + def test_parents_and_children(self): + p1, p2 = create_proc_children_pair() + me = psutil.Process() + # forward + children = me.children(recursive=True) + self.assertEqual(len(children), 2) + self.assertEqual(children[0], p1) + self.assertEqual(children[1], p2) + # backward + parents = p2.parents() + self.assertEqual(parents[0], p1) + self.assertEqual(parents[1], me) + def test_suspend_resume(self): sproc = get_test_subprocess() p = psutil.Process(sproc.pid) @@ -1202,13 +1248,28 @@ class TestProcess(unittest.TestCase): p.cpu_times() self.assertEqual(m.call_count, 2) + def test_oneshot_cache(self): + # Make sure oneshot() cache is nonglobal. Instead it's + # supposed to be bound to the Process instance, see: + # https://github.com/giampaolo/psutil/issues/1373 + p1, p2 = create_proc_children_pair() + p1_ppid = p1.ppid() + p2_ppid = p2.ppid() + self.assertNotEqual(p1_ppid, p2_ppid) + with p1.oneshot(): + self.assertEqual(p1.ppid(), p1_ppid) + self.assertEqual(p2.ppid(), p2_ppid) + with p2.oneshot(): + self.assertEqual(p1.ppid(), p1_ppid) + self.assertEqual(p2.ppid(), p2_ppid) + def test_halfway_terminated_process(self): # Test that NoSuchProcess exception gets raised in case the # process dies after we create the Process object. # Example: - # >>> proc = Process(1234) + # >>> proc = Process(1234) # >>> time.sleep(2) # time-consuming task, process dies in meantime - # >>> proc.name() + # >>> proc.name() # Refers to Issue #15 sproc = get_test_subprocess() p = psutil.Process(sproc.pid) @@ -1216,9 +1277,13 @@ class TestProcess(unittest.TestCase): p.wait() if WINDOWS: call_until(psutil.pids, "%s not in ret" % p.pid) - self.assertFalse(p.is_running()) - # self.assertFalse(p.pid in psutil.pids(), msg="retcode = %s" % - # retcode) + assert not p.is_running() + + if WINDOWS: + with self.assertRaises(psutil.NoSuchProcess): + p.send_signal(signal.CTRL_C_EVENT) + with self.assertRaises(psutil.NoSuchProcess): + p.send_signal(signal.CTRL_BREAK_EVENT) excluded_names = ['pid', 'is_running', 'wait', 'create_time', 'oneshot', 'memory_info_ex'] @@ -1262,6 +1327,16 @@ class TestProcess(unittest.TestCase): except NotImplementedError: pass else: + # NtQuerySystemInformation succeeds if process is gone. + if WINDOWS and name in ('exe', 'name'): + normcase = os.path.normcase + if name == 'exe': + self.assertEqual(normcase(ret), normcase(PYTHON_EXE)) + else: + self.assertEqual( + normcase(ret), + normcase(os.path.basename(PYTHON_EXE))) + continue self.fail( "NoSuchProcess exception not raised for %r, retval=%s" % ( name, ret)) @@ -1320,7 +1395,7 @@ class TestProcess(unittest.TestCase): succeed_or_zombie_p_exc(zproc.kill) # ...its parent should 'see' it - # edit: not true on BSD and OSX + # edit: not true on BSD and MACOS # descendants = [x.pid for x in psutil.Process().children( # recursive=True)] # self.assertIn(zpid, descendants) @@ -1330,9 +1405,9 @@ class TestProcess(unittest.TestCase): # self.assertEqual(zpid.ppid(), os.getpid()) # ...and all other APIs should be able to deal with it self.assertTrue(psutil.pid_exists(zpid)) - if not TRAVIS and OSX: + if not TRAVIS and MACOS: # For some reason this started failing all of the sudden. - # Maybe they upgraded OSX version? + # Maybe they upgraded MACOS version? # https://travis-ci.org/giampaolo/psutil/jobs/310896404 self.assertIn(zpid, psutil.pids()) self.assertIn(zpid, [x.pid for x in psutil.process_iter()]) @@ -1405,12 +1480,14 @@ class TestProcess(unittest.TestCase): d.pop("PSUTIL_TESTING", None) d.pop("PLAT", None) d.pop("HOME", None) - if OSX: + if MACOS: d.pop("__CF_USER_TEXT_ENCODING", None) d.pop("VERSIONER_PYTHON_PREFER_32_BIT", None) d.pop("VERSIONER_PYTHON_VERSION", None) return dict( - [(k.rstrip("\r\n"), v.rstrip("\r\n")) for k, v in d.items()]) + [(k.replace("\r", "").replace("\n", ""), + v.replace("\r", "").replace("\n", "")) + for k, v in d.items()]) self.maxDiff = None p = psutil.Process() @@ -1562,4 +1639,5 @@ class TestPopen(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_sunos.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_sunos.py index ea9afcde0ea9..e3beb625bf53 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_sunos.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_sunos.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -10,7 +10,6 @@ import os import psutil from psutil import SUNOS -from psutil.tests import run_test_module_by_name from psutil.tests import sh from psutil.tests import unittest @@ -42,4 +41,5 @@ class SunOSSpecificTestCase(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_system.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_system.py index 20b132a91dc0..3834209fcb36 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_system.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_system.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -23,28 +23,31 @@ from psutil import AIX from psutil import BSD from psutil import FREEBSD from psutil import LINUX +from psutil import MACOS from psutil import NETBSD from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS +from psutil._compat import FileNotFoundError from psutil._compat import long -from psutil.tests import APPVEYOR from psutil.tests import ASCII_FS from psutil.tests import check_net_address +from psutil.tests import CI_TESTING from psutil.tests import DEVNULL from psutil.tests import enum from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import HAS_CPU_FREQ +from psutil.tests import HAS_GETLOADAVG +from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import mock +from psutil.tests import PYPY from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import safe_rmpath from psutil.tests import TESTFN from psutil.tests import TESTFN_UNICODE @@ -57,8 +60,7 @@ from psutil.tests import unittest # =================================================================== -class TestSystemAPIs(unittest.TestCase): - """Tests for system-related APIs.""" +class TestProcessAPIs(unittest.TestCase): def setUp(self): safe_rmpath(TESTFN) @@ -83,7 +85,7 @@ class TestSystemAPIs(unittest.TestCase): with self.assertRaises(psutil.AccessDenied): list(psutil.process_iter()) - def test_prcess_iter_w_params(self): + def test_prcess_iter_w_attrs(self): for p in psutil.process_iter(attrs=['pid']): self.assertEqual(list(p.info.keys()), ['pid']) with self.assertRaises(ValueError): @@ -103,6 +105,8 @@ class TestSystemAPIs(unittest.TestCase): self.assertGreaterEqual(p.info['pid'], 0) assert m.called + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs(self): def callback(p): pids.append(p.pid) @@ -124,7 +128,7 @@ class TestSystemAPIs(unittest.TestCase): for p in alive: self.assertFalse(hasattr(p, 'returncode')) - @retry_before_failing(30) + @retry_on_failure(30) def test(procs, callback): gone, alive = psutil.wait_procs(procs, timeout=0.03, callback=callback) @@ -143,7 +147,7 @@ class TestSystemAPIs(unittest.TestCase): for p in alive: self.assertFalse(hasattr(p, 'returncode')) - @retry_before_failing(30) + @retry_on_failure(30) def test(procs, callback): gone, alive = psutil.wait_procs(procs, timeout=0.03, callback=callback) @@ -158,6 +162,8 @@ class TestSystemAPIs(unittest.TestCase): for p in gone: self.assertTrue(hasattr(p, 'returncode')) + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs_no_timeout(self): sproc1 = get_test_subprocess() sproc2 = get_test_subprocess() @@ -167,12 +173,67 @@ class TestSystemAPIs(unittest.TestCase): p.terminate() gone, alive = psutil.wait_procs(procs) + def test_pid_exists(self): + sproc = get_test_subprocess() + self.assertTrue(psutil.pid_exists(sproc.pid)) + p = psutil.Process(sproc.pid) + p.kill() + p.wait() + self.assertFalse(psutil.pid_exists(sproc.pid)) + self.assertFalse(psutil.pid_exists(-1)) + self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) + + def test_pid_exists_2(self): + reap_children() + pids = psutil.pids() + for pid in pids: + try: + assert psutil.pid_exists(pid) + except AssertionError: + # in case the process disappeared in meantime fail only + # if it is no longer in psutil.pids() + time.sleep(.1) + if pid in psutil.pids(): + self.fail(pid) + pids = range(max(pids) + 5000, max(pids) + 6000) + for pid in pids: + self.assertFalse(psutil.pid_exists(pid), msg=pid) + + def test_pids(self): + pidslist = psutil.pids() + procslist = [x.pid for x in psutil.process_iter()] + # make sure every pid is unique + self.assertEqual(sorted(set(pidslist)), pidslist) + self.assertEqual(pidslist, procslist) + + +class TestMiscAPIs(unittest.TestCase): + def test_boot_time(self): bt = psutil.boot_time() self.assertIsInstance(bt, float) self.assertGreater(bt, 0) self.assertLess(bt, time.time()) + @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") + def test_users(self): + users = psutil.users() + self.assertNotEqual(users, []) + for user in users: + assert user.name, user + self.assertIsInstance(user.name, str) + self.assertIsInstance(user.terminal, (str, type(None))) + if user.host is not None: + self.assertIsInstance(user.host, (str, type(None))) + user.terminal + user.host + assert user.started > 0.0, user + datetime.datetime.fromtimestamp(user.started) + if WINDOWS or OPENBSD: + self.assertIsNone(user.pid) + else: + psutil.Process(user.pid) + @unittest.skipIf(not POSIX, 'POSIX only') def test_PAGESIZE(self): # pagesize is used internally to perform different calculations @@ -181,6 +242,55 @@ class TestSystemAPIs(unittest.TestCase): import resource self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize()) + def test_test(self): + # test for psutil.test() function + stdout = sys.stdout + sys.stdout = DEVNULL + try: + psutil.test() + finally: + sys.stdout = stdout + + def test_os_constants(self): + names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD", + "NETBSD", "BSD", "SUNOS"] + for name in names: + self.assertIsInstance(getattr(psutil, name), bool, msg=name) + + if os.name == 'posix': + assert psutil.POSIX + assert not psutil.WINDOWS + names.remove("POSIX") + if "linux" in sys.platform.lower(): + assert psutil.LINUX + names.remove("LINUX") + elif "bsd" in sys.platform.lower(): + assert psutil.BSD + self.assertEqual([psutil.FREEBSD, psutil.OPENBSD, + psutil.NETBSD].count(True), 1) + names.remove("BSD") + names.remove("FREEBSD") + names.remove("OPENBSD") + names.remove("NETBSD") + elif "sunos" in sys.platform.lower() or \ + "solaris" in sys.platform.lower(): + assert psutil.SUNOS + names.remove("SUNOS") + elif "darwin" in sys.platform.lower(): + assert psutil.MACOS + names.remove("MACOS") + else: + assert psutil.WINDOWS + assert not psutil.POSIX + names.remove("WINDOWS") + + # assert all other constants are set to False + for name in names: + self.assertIs(getattr(psutil, name), False, msg=name) + + +class TestMemoryAPIs(unittest.TestCase): + def test_virtual_memory(self): mem = psutil.virtual_memory() assert mem.total > 0, mem @@ -215,50 +325,12 @@ class TestSystemAPIs(unittest.TestCase): assert mem.sin >= 0, mem assert mem.sout >= 0, mem - def test_pid_exists(self): - sproc = get_test_subprocess() - self.assertTrue(psutil.pid_exists(sproc.pid)) - p = psutil.Process(sproc.pid) - p.kill() - p.wait() - self.assertFalse(psutil.pid_exists(sproc.pid)) - self.assertFalse(psutil.pid_exists(-1)) - self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) - def test_pid_exists_2(self): - reap_children() - pids = psutil.pids() - for pid in pids: - try: - assert psutil.pid_exists(pid) - except AssertionError: - # in case the process disappeared in meantime fail only - # if it is no longer in psutil.pids() - time.sleep(.1) - if pid in psutil.pids(): - self.fail(pid) - pids = range(max(pids) + 5000, max(pids) + 6000) - for pid in pids: - self.assertFalse(psutil.pid_exists(pid), msg=pid) +class TestCpuAPIs(unittest.TestCase): - def test_pids(self): - plist = [x.pid for x in psutil.process_iter()] - pidlist = psutil.pids() - self.assertEqual(plist.sort(), pidlist.sort()) - # make sure every pid is unique - self.assertEqual(len(pidlist), len(set(pidlist))) - - def test_test(self): - # test for psutil.test() function - stdout = sys.stdout - sys.stdout = DEVNULL - try: - psutil.test() - finally: - sys.stdout = stdout - - def test_cpu_count(self): + def test_cpu_count_logical(self): logical = psutil.cpu_count() + self.assertIsNotNone(logical) self.assertEqual(logical, len(psutil.cpu_times(percpu=True))) self.assertGreaterEqual(logical, 1) # @@ -267,9 +339,17 @@ class TestSystemAPIs(unittest.TestCase): cpuinfo_data = fd.read() if "physical id" not in cpuinfo_data: raise unittest.SkipTest("cpuinfo doesn't include physical id") + + def test_cpu_count_physical(self): + logical = psutil.cpu_count() physical = psutil.cpu_count(logical=False) - self.assertGreaterEqual(physical, 1) - self.assertGreaterEqual(logical, physical) + if physical is None: + raise self.skipTest("physical cpu_count() is None") + if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista + self.assertIsNone(physical) + else: + self.assertGreaterEqual(physical, 1) + self.assertGreaterEqual(logical, physical) def test_cpu_count_none(self): # https://github.com/giampaolo/psutil/issues/1085 @@ -315,11 +395,12 @@ class TestSystemAPIs(unittest.TestCase): def test_cpu_times_time_increases(self): # Make sure time increases between calls. t1 = sum(psutil.cpu_times()) - time.sleep(0.1) - t2 = sum(psutil.cpu_times()) - difference = t2 - t1 - if not difference >= 0.05: - self.fail("difference %s" % difference) + stop_at = time.time() + 1 + while time.time() < stop_at: + t2 = sum(psutil.cpu_times()) + if t2 > t1: + return + self.fail("time remained the same") def test_per_cpu_times(self): # Check type, value >= 0, str(). @@ -358,17 +439,16 @@ class TestSystemAPIs(unittest.TestCase): # Simulate some work load then make sure time have increased # between calls. tot1 = psutil.cpu_times(percpu=True) - stop_at = time.time() + 0.1 + giveup_at = time.time() + 1 while True: - if time.time() >= stop_at: - break - tot2 = psutil.cpu_times(percpu=True) - for t1, t2 in zip(tot1, tot2): - t1, t2 = sum(t1), sum(t2) - difference = t2 - t1 - if difference >= 0.05: - return - self.fail() + if time.time() >= giveup_at: + return self.fail("timeout") + tot2 = psutil.cpu_times(percpu=True) + for t1, t2 in zip(tot1, tot2): + t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2) + difference = t2 - t1 + if difference >= 0.05: + return def test_cpu_times_comparison(self): # Make sure the sum of all per cpu times is almost equal to @@ -440,6 +520,55 @@ class TestSystemAPIs(unittest.TestCase): for percent in cpu: self._test_cpu_percent(percent, None, None) + def test_cpu_stats(self): + # Tested more extensively in per-platform test modules. + infos = psutil.cpu_stats() + self.assertEqual( + infos._fields, + ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls')) + for name in infos._fields: + value = getattr(infos, name) + self.assertGreaterEqual(value, 0) + # on AIX, ctx_switches is always 0 + if not AIX and name in ('ctx_switches', 'interrupts'): + self.assertGreater(value, 0) + + @unittest.skipIf(not HAS_CPU_FREQ, "not suported") + def test_cpu_freq(self): + def check_ls(ls): + for nt in ls: + self.assertEqual(nt._fields, ('current', 'min', 'max')) + if nt.max != 0.0: + self.assertLessEqual(nt.current, nt.max) + for name in nt._fields: + value = getattr(nt, name) + self.assertIsInstance(value, (int, long, float)) + self.assertGreaterEqual(value, 0) + + ls = psutil.cpu_freq(percpu=True) + if TRAVIS and not ls: + raise self.skipTest("skipped on Travis") + if FREEBSD and not ls: + raise self.skipTest("returns empty list on FreeBSD") + + assert ls, ls + check_ls([psutil.cpu_freq(percpu=False)]) + + if LINUX: + self.assertEqual(len(ls), psutil.cpu_count()) + + @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + def test_getloadavg(self): + loadavg = psutil.getloadavg() + assert len(loadavg) == 3 + + for load in loadavg: + self.assertIsInstance(load, float) + self.assertGreaterEqual(load, 0.0) + + +class TestDiskAPIs(unittest.TestCase): + def test_disk_usage(self): usage = psutil.disk_usage(os.getcwd()) self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent')) @@ -463,9 +592,8 @@ class TestSystemAPIs(unittest.TestCase): # if path does not exist OSError ENOENT is expected across # all platforms fname = tempfile.mktemp() - with self.assertRaises(OSError) as exc: + with self.assertRaises(FileNotFoundError): psutil.disk_usage(fname) - self.assertEqual(exc.exception.errno, errno.ENOENT) def test_disk_usage_unicode(self): # See: https://github.com/giampaolo/psutil/issues/416 @@ -496,33 +624,26 @@ class TestSystemAPIs(unittest.TestCase): # we cannot make any assumption about this, see: # http://goo.gl/p9c43 disk.device - if SUNOS or TRAVIS: - # on solaris apparently mount points can also be files - assert os.path.exists(disk.mountpoint), disk - else: - assert os.path.isdir(disk.mountpoint), disk + # on modern systems mount points can also be files + assert os.path.exists(disk.mountpoint), disk assert disk.fstype, disk # all = True ls = psutil.disk_partitions(all=True) self.assertTrue(ls, msg=ls) for disk in psutil.disk_partitions(all=True): - if not WINDOWS: + if not WINDOWS and disk.mountpoint: try: os.stat(disk.mountpoint) except OSError as err: - if TRAVIS and OSX and err.errno == errno.EIO: + if TRAVIS and MACOS and err.errno == errno.EIO: continue # http://mail.python.org/pipermail/python-dev/ # 2012-June/120787.html if err.errno not in (errno.EPERM, errno.EACCES): raise else: - if SUNOS or TRAVIS: - # on solaris apparently mount points can also be files - assert os.path.exists(disk.mountpoint), disk - else: - assert os.path.isdir(disk.mountpoint), disk + assert os.path.exists(disk.mountpoint), disk self.assertIsInstance(disk.fstype, str) self.assertIsInstance(disk.opts, str) @@ -534,10 +655,55 @@ class TestSystemAPIs(unittest.TestCase): mount = find_mount_point(__file__) mounts = [x.mountpoint.lower() for x in - psutil.disk_partitions(all=True)] + psutil.disk_partitions(all=True) if x.mountpoint] self.assertIn(mount, mounts) psutil.disk_usage(mount) + @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), + '/proc/diskstats not available on this linux version') + @unittest.skipIf(CI_TESTING and not psutil.disk_io_counters(), + "unreliable on CI") # no visible disks + def test_disk_io_counters(self): + def check_ntuple(nt): + self.assertEqual(nt[0], nt.read_count) + self.assertEqual(nt[1], nt.write_count) + self.assertEqual(nt[2], nt.read_bytes) + self.assertEqual(nt[3], nt.write_bytes) + if not (OPENBSD or NETBSD): + self.assertEqual(nt[4], nt.read_time) + self.assertEqual(nt[5], nt.write_time) + if LINUX: + self.assertEqual(nt[6], nt.read_merged_count) + self.assertEqual(nt[7], nt.write_merged_count) + self.assertEqual(nt[8], nt.busy_time) + elif FREEBSD: + self.assertEqual(nt[6], nt.busy_time) + for name in nt._fields: + assert getattr(nt, name) >= 0, nt + + ret = psutil.disk_io_counters(perdisk=False) + assert ret is not None, "no disks on this system?" + check_ntuple(ret) + ret = psutil.disk_io_counters(perdisk=True) + # make sure there are no duplicates + self.assertEqual(len(ret), len(set(ret))) + for key in ret: + assert key, key + check_ntuple(ret[key]) + + def test_disk_io_counters_no_disks(self): + # Emulate a case where no disks are installed, see: + # https://github.com/giampaolo/psutil/issues/1062 + with mock.patch('psutil._psplatform.disk_io_counters', + return_value={}) as m: + self.assertIsNone(psutil.disk_io_counters(perdisk=False)) + self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) + assert m.called + + +class TestNetAPIs(unittest.TestCase): + + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): def check_ntuple(nt): self.assertEqual(nt[0], nt.bytes_sent) @@ -566,6 +732,7 @@ class TestSystemAPIs(unittest.TestCase): self.assertIsInstance(key, str) check_ntuple(ret[key]) + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters_no_nics(self): # Emulate a case where no NICs are installed, see: # https://github.com/giampaolo/psutil/issues/1062 @@ -627,7 +794,7 @@ class TestSystemAPIs(unittest.TestCase): elif addr.ptp: self.assertIsNone(addr.broadcast) - if BSD or OSX or SUNOS: + if BSD or MACOS or SUNOS: if hasattr(socket, "AF_LINK"): self.assertEqual(psutil.AF_LINK, socket.AF_LINK) elif LINUX: @@ -668,144 +835,18 @@ class TestSystemAPIs(unittest.TestCase): self.assertGreaterEqual(speed, 0) self.assertGreaterEqual(mtu, 0) - @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this linux version') - @unittest.skipIf(APPVEYOR and psutil.disk_io_counters() is None, - "unreliable on APPVEYOR") # no visible disks - def test_disk_io_counters(self): - def check_ntuple(nt): - self.assertEqual(nt[0], nt.read_count) - self.assertEqual(nt[1], nt.write_count) - self.assertEqual(nt[2], nt.read_bytes) - self.assertEqual(nt[3], nt.write_bytes) - if not (OPENBSD or NETBSD): - self.assertEqual(nt[4], nt.read_time) - self.assertEqual(nt[5], nt.write_time) - if LINUX: - self.assertEqual(nt[6], nt.read_merged_count) - self.assertEqual(nt[7], nt.write_merged_count) - self.assertEqual(nt[8], nt.busy_time) - elif FREEBSD: - self.assertEqual(nt[6], nt.busy_time) - for name in nt._fields: - assert getattr(nt, name) >= 0, nt - - ret = psutil.disk_io_counters(perdisk=False) - assert ret is not None, "no disks on this system?" - check_ntuple(ret) - ret = psutil.disk_io_counters(perdisk=True) - # make sure there are no duplicates - self.assertEqual(len(ret), len(set(ret))) - for key in ret: - assert key, key - check_ntuple(ret[key]) - if LINUX and key[-1].isdigit(): - # if 'sda1' is listed 'sda' shouldn't, see: - # https://github.com/giampaolo/psutil/issues/338 - while key[-1].isdigit(): - key = key[:-1] - self.assertNotIn(key, ret.keys()) - - def test_disk_io_counters_no_disks(self): - # Emulate a case where no disks are installed, see: - # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch('psutil._psplatform.disk_io_counters', - return_value={}) as m: - self.assertIsNone(psutil.disk_io_counters(perdisk=False)) - self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) + @unittest.skipIf(not (LINUX or BSD or MACOS), + "LINUX or BSD or MACOS specific") + def test_net_if_stats_enodev(self): + # See: https://github.com/giampaolo/psutil/issues/1279 + with mock.patch('psutil._psutil_posix.net_if_mtu', + side_effect=OSError(errno.ENODEV, "")) as m: + ret = psutil.net_if_stats() + self.assertEqual(ret, {}) assert m.called - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") - def test_users(self): - users = psutil.users() - self.assertNotEqual(users, []) - for user in users: - assert user.name, user - self.assertIsInstance(user.name, str) - self.assertIsInstance(user.terminal, (str, type(None))) - if user.host is not None: - self.assertIsInstance(user.host, (str, type(None))) - user.terminal - user.host - assert user.started > 0.0, user - datetime.datetime.fromtimestamp(user.started) - if WINDOWS or OPENBSD: - self.assertIsNone(user.pid) - else: - psutil.Process(user.pid) - def test_cpu_stats(self): - # Tested more extensively in per-platform test modules. - infos = psutil.cpu_stats() - self.assertEqual( - infos._fields, - ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls')) - for name in infos._fields: - value = getattr(infos, name) - self.assertGreaterEqual(value, 0) - # on AIX, ctx_switches is always 0 - if not AIX and name in ('ctx_switches', 'interrupts'): - self.assertGreater(value, 0) - - @unittest.skipIf(not HAS_CPU_FREQ, "not suported") - def test_cpu_freq(self): - def check_ls(ls): - for nt in ls: - self.assertEqual(nt._fields, ('current', 'min', 'max')) - self.assertLessEqual(nt.current, nt.max) - for name in nt._fields: - value = getattr(nt, name) - self.assertIsInstance(value, (int, long, float)) - self.assertGreaterEqual(value, 0) - - ls = psutil.cpu_freq(percpu=True) - if TRAVIS and not ls: - return - - 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"] - for name in names: - self.assertIsInstance(getattr(psutil, name), bool, msg=name) - - if os.name == 'posix': - assert psutil.POSIX - assert not psutil.WINDOWS - names.remove("POSIX") - if "linux" in sys.platform.lower(): - assert psutil.LINUX - names.remove("LINUX") - elif "bsd" in sys.platform.lower(): - assert psutil.BSD - self.assertEqual([psutil.FREEBSD, psutil.OPENBSD, - psutil.NETBSD].count(True), 1) - names.remove("BSD") - names.remove("FREEBSD") - names.remove("OPENBSD") - names.remove("NETBSD") - elif "sunos" in sys.platform.lower() or \ - "solaris" in sys.platform.lower(): - assert psutil.SUNOS - names.remove("SUNOS") - elif "darwin" in sys.platform.lower(): - assert psutil.OSX - names.remove("OSX") - else: - assert psutil.WINDOWS - assert not psutil.POSIX - names.remove("WINDOWS") - - # assert all other constants are set to False - for name in names: - self.assertIs(getattr(psutil, name), False, msg=name) +class TestSensorsAPIs(unittest.TestCase): @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures(self): @@ -859,4 +900,5 @@ class TestSystemAPIs(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_unicode.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_unicode.py index c2a2f8479f66..ac2d4f69e0aa 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_unicode.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_unicode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -9,7 +9,32 @@ Notes about unicode handling in psutil ====================================== -In psutil these are the APIs returning or dealing with a string +Starting from version 5.3.0 psutil adds unicode support, see: +https://github.com/giampaolo/psutil/issues/1040 +The notes below apply to *any* API returning a string such as +process exe(), cwd() or username(): + +* all strings are encoded by using the OS filesystem encoding + (sys.getfilesystemencoding()) which varies depending on the platform + (e.g. "UTF-8" on macOS, "mbcs" on Win) +* no API call is supposed to crash with UnicodeDecodeError +* instead, in case of badly encoded data returned by the OS, the + following error handlers are used to replace the corrupted characters in + the string: + * Python 3: sys.getfilesystemencodeerrors() (PY 3.6+) or + "surrogatescape" on POSIX and "replace" on Windows + * Python 2: "replace" +* on Python 2 all APIs return bytes (str type), never unicode +* on Python 2, you can go back to unicode by doing: + + >>> unicode(p.exe(), sys.getdefaultencoding(), errors="replace") + +For a detailed explanation of how psutil handles unicode see #1040. + +Tests +===== + +List of APIs returning or dealing with a string: ('not tested' means they are not tested to deal with non-ASCII strings): * Process.cmdline() @@ -46,10 +71,6 @@ etc.) and make sure that: * psutil never crashes with UnicodeDecodeError * the returned path matches - -For a detailed explanation of how psutil handles unicode see: -- https://github.com/giampaolo/psutil/issues/1040 -- http://psutil.readthedocs.io/#unicode """ import os @@ -58,13 +79,14 @@ import warnings from contextlib import closing from psutil import BSD +from psutil import MACOS from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import WINDOWS from psutil._compat import PY3 from psutil._compat import u from psutil.tests import APPVEYOR +from psutil.tests import CIRRUS from psutil.tests import ASCII_FS from psutil.tests import bind_unix_socket from psutil.tests import chdir @@ -74,9 +96,8 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_CONNECTIONS_UNIX from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import mock +from psutil.tests import PYPY from psutil.tests import reap_children -from psutil.tests import run_test_module_by_name from psutil.tests import safe_mkdir from psutil.tests import safe_rmpath as _safe_rmpath from psutil.tests import skip_on_access_denied @@ -87,7 +108,6 @@ from psutil.tests import TRAVIS from psutil.tests import unittest from psutil.tests import unix_socket_path import psutil -import psutil.tests def safe_rmpath(path): @@ -166,20 +186,12 @@ class _BaseFSAPIsTests(object): exe = p.exe() self.assertIsInstance(exe, str) if self.expect_exact_path_match(): - self.assertEqual(exe, self.funky_name) + self.assertEqual(os.path.normcase(exe), + os.path.normcase(self.funky_name)) def test_proc_name(self): subp = get_test_subprocess(cmd=[self.funky_name]) - if WINDOWS: - # On Windows name() is determined from exe() first, because - # it's faster; we want to overcome the internal optimization - # and test name() instead of exe(). - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as m: - name = psutil.Process(subp.pid).name() - assert m.called - else: - name = psutil.Process(subp.pid).name() + name = psutil.Process(subp.pid).name() self.assertIsInstance(name, str) if self.expect_exact_path_match(): self.assertEqual(name, os.path.basename(self.funky_name)) @@ -204,6 +216,7 @@ class _BaseFSAPIsTests(object): if self.expect_exact_path_match(): self.assertEqual(cwd, dname) + @unittest.skipIf(PYPY and WINDOWS, "fails on PYPY + WINDOWS") def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) @@ -233,7 +246,7 @@ class _BaseFSAPIsTests(object): conn = psutil.Process().connections('unix')[0] self.assertIsInstance(conn.laddr, str) # AF_UNIX addr not set on OpenBSD - if not OPENBSD: + if not OPENBSD and not CIRRUS: # XXX self.assertEqual(conn.laddr, name) @unittest.skipIf(not POSIX, "POSIX only") @@ -271,6 +284,8 @@ class _BaseFSAPIsTests(object): @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") @unittest.skipIf(not PY3, "ctypes does not support unicode on PY2") + @unittest.skipIf(PYPY and WINDOWS, + "copyload_shared_lib() unsupported on PYPY + WINDOWS") def test_memory_maps(self): # XXX: on Python 2, using ctypes.CDLL with a unicode path # opens a message box which blocks the test run. @@ -286,7 +301,9 @@ class _BaseFSAPIsTests(object): self.assertIsInstance(path, str) -@unittest.skipIf(OSX and TRAVIS, "unreliable on TRAVIS") # TODO +# https://travis-ci.org/giampaolo/psutil/jobs/440073249 +@unittest.skipIf(PYPY and TRAVIS, "unreliable on PYPY + TRAVIS") +@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO @unittest.skipIf(ASCII_FS, "ASCII fs") @unittest.skipIf(not subprocess_supports_unicode(TESTFN_UNICODE), "subprocess can't deal with unicode") @@ -298,16 +315,15 @@ class TestFSAPIs(_BaseFSAPIsTests, unittest.TestCase): def expect_exact_path_match(cls): # Do not expect psutil to correctly handle unicode paths on # Python 2 if os.listdir() is not able either. - if PY3: - return True - else: - here = '.' if isinstance(cls.funky_name, str) else u('.') - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return cls.funky_name in os.listdir(here) + here = '.' if isinstance(cls.funky_name, str) else u('.') + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return cls.funky_name in os.listdir(here) -@unittest.skipIf(OSX and TRAVIS, "unreliable on TRAVIS") # TODO +@unittest.skipIf(PYPY and TRAVIS, "unreliable on PYPY + TRAVIS") +@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO +@unittest.skipIf(PYPY, "unreliable on PYPY") @unittest.skipIf(not subprocess_supports_unicode(INVALID_NAME), "subprocess can't deal with invalid unicode") class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase): @@ -320,19 +336,6 @@ class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase): return True -@unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestWinProcessName(unittest.TestCase): - - def test_name_type(self): - # On Windows name() is determined from exe() first, because - # it's faster; we want to overcome the internal optimization - # and test name() instead of exe(). - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as m: - self.assertIsInstance(psutil.Process().name(), str) - assert m.called - - # =================================================================== # Non fs APIs # =================================================================== @@ -345,6 +348,7 @@ class TestNonFSAPIS(unittest.TestCase): reap_children() @unittest.skipIf(not HAS_ENVIRON, "not supported") + @unittest.skipIf(PYPY and WINDOWS, "segfaults on PYPY + WINDOWS") def test_proc_environ(self): # Note: differently from others, this test does not deal # with fs paths. On Python 2 subprocess module is broken as @@ -364,4 +368,5 @@ class TestNonFSAPIS(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_windows.py b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_windows.py index e4a719ea4ce7..f68885d0ca3d 100644 --- a/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_windows.py +++ b/third_party/python/psutil-cp27-none-win_amd64/psutil/tests/test_windows.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: UTF-8 -* # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -21,27 +21,26 @@ import warnings import psutil from psutil import WINDOWS -from psutil._compat import callable +from psutil._compat import FileNotFoundError from psutil.tests import APPVEYOR from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import mock +from psutil.tests import PY3 +from psutil.tests import PYPY from psutil.tests import reap_children -from psutil.tests import retry_before_failing -from psutil.tests import run_test_module_by_name +from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import unittest -with warnings.catch_warnings(): - warnings.simplefilter("ignore") - try: + +if WINDOWS and not PYPY: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") import win32api # requires "pip install pypiwin32" import win32con import win32process import wmi # requires "pip install wmi" / "make setup-dev-env" - except ImportError: - if os.name == 'nt': - raise cext = psutil._psplatform.cext @@ -64,13 +63,57 @@ def wrap_exceptions(fun): return wrapper +@unittest.skipIf(PYPY, "pywin32 not available on PYPY") # skip whole module +class TestCase(unittest.TestCase): + pass + + # =================================================================== # System APIs # =================================================================== @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSystemAPIs(unittest.TestCase): +class TestCpuAPIs(TestCase): + + @unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ, + 'NUMBER_OF_PROCESSORS env var is not available') + def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self): + # Will likely fail on many-cores systems: + # https://stackoverflow.com/questions/31209256 + num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) + self.assertEqual(num_cpus, psutil.cpu_count()) + + def test_cpu_count_vs_GetSystemInfo(self): + # Will likely fail on many-cores systems: + # https://stackoverflow.com/questions/31209256 + sys_value = win32api.GetSystemInfo()[5] + psutil_value = psutil.cpu_count() + self.assertEqual(sys_value, psutil_value) + + def test_cpu_count_logical_vs_wmi(self): + w = wmi.WMI() + proc = w.Win32_Processor()[0] + self.assertEqual(psutil.cpu_count(), proc.NumberOfLogicalProcessors) + + def test_cpu_count_phys_vs_wmi(self): + w = wmi.WMI() + proc = w.Win32_Processor()[0] + self.assertEqual(psutil.cpu_count(logical=False), proc.NumberOfCores) + + def test_cpu_count_vs_cpu_times(self): + self.assertEqual(psutil.cpu_count(), + len(psutil.cpu_times(percpu=True))) + + 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) + + +@unittest.skipIf(not WINDOWS, "WINDOWS only") +class TestSystemAPIs(TestCase): def test_nic_names(self): out = sh('ipconfig /all') @@ -82,23 +125,6 @@ class TestSystemAPIs(unittest.TestCase): self.fail( "%r nic wasn't found in 'ipconfig /all' output" % nic) - @unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ, - 'NUMBER_OF_PROCESSORS env var is not available') - def test_cpu_count(self): - num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - self.assertEqual(num_cpus, psutil.cpu_count()) - - def test_cpu_count_2(self): - sys_value = win32api.GetSystemInfo()[5] - psutil_value = psutil.cpu_count() - self.assertEqual(sys_value, psutil_value) - - 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), @@ -117,7 +143,7 @@ class TestSystemAPIs(unittest.TestCase): # Note: this test is not very reliable @unittest.skipIf(APPVEYOR, "test not relieable on appveyor") - @retry_before_failing() + @retry_on_failure() def test_pids(self): # Note: this test might fail if the OS is starting/killing # other processes in the meantime @@ -126,7 +152,7 @@ class TestSystemAPIs(unittest.TestCase): psutil_pids = set(psutil.pids()) self.assertEqual(wmi_pids, psutil_pids) - @retry_before_failing() + @retry_on_failure() def test_disks(self): ps_parts = psutil.disk_partitions(all=True) wmi_parts = wmi.WMI().Win32_LogicalDisk() @@ -136,14 +162,13 @@ class TestSystemAPIs(unittest.TestCase): if not ps_part.mountpoint: # this is usually a CD-ROM with no disk inserted break + if 'cdrom' in ps_part.opts: + break try: usage = psutil.disk_usage(ps_part.mountpoint) - except OSError as err: - if err.errno == errno.ENOENT: - # usually this is the floppy - break - else: - raise + except FileNotFoundError: + # usually this is the floppy + break self.assertEqual(usage.total, int(wmi_part.Size)) wmi_free = int(wmi_part.FreeSpace) self.assertEqual(usage.free, wmi_free) @@ -157,6 +182,8 @@ class TestSystemAPIs(unittest.TestCase): def test_disk_usage(self): for disk in psutil.disk_partitions(): + if 'cdrom' in disk.opts: + continue sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint) psutil_value = psutil.disk_usage(disk.mountpoint) self.assertAlmostEqual(sys_value[0], psutil_value.free, @@ -190,7 +217,6 @@ class TestSystemAPIs(unittest.TestCase): wmi_btime_str, "%Y%m%d%H%M%S") psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time()) diff = abs((wmi_btime_dt - psutil_dt).total_seconds()) - # Wmic time is 2-3 secs lower for some reason; that's OK. self.assertLessEqual(diff, 3) def test_boot_time_fluctuation(self): @@ -211,7 +237,7 @@ class TestSystemAPIs(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSensorsBattery(unittest.TestCase): +class TestSensorsBattery(TestCase): def test_has_battery(self): if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: @@ -272,7 +298,7 @@ class TestSensorsBattery(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcess(unittest.TestCase): +class TestProcess(TestCase): @classmethod def setUpClass(cls): @@ -307,13 +333,6 @@ class TestProcess(unittest.TestCase): p = psutil.Process(self.pid) self.assertRaises(ValueError, p.send_signal, signal.SIGINT) - def test_exe(self): - for p in psutil.process_iter(): - try: - self.assertEqual(os.path.basename(p.exe()), p.name()) - except psutil.Error: - pass - def test_num_handles_increment(self): p = psutil.Process(os.getpid()) before = p.num_handles() @@ -360,15 +379,6 @@ class TestProcess(unittest.TestCase): if failures: self.fail('\n' + '\n'.join(failures)) - def test_name_always_available(self): - # On Windows name() is never supposed to raise AccessDenied, - # see https://github.com/giampaolo/psutil/issues/627 - for p in psutil.process_iter(): - try: - p.name() - except psutil.NoSuchProcess: - pass - @unittest.skipIf(not sys.version_info >= (2, 7), "CTRL_* signals not supported") def test_ctrl_signals(self): @@ -382,16 +392,6 @@ class TestProcess(unittest.TestCase): self.assertRaises(psutil.NoSuchProcess, p.send_signal, signal.CTRL_BREAK_EVENT) - def test_compare_name_exe(self): - for p in psutil.process_iter(): - try: - a = os.path.basename(p.exe()) - b = p.name() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - else: - self.assertEqual(a, b) - def test_username(self): self.assertEqual(psutil.Process().username(), win32api.GetUserNameEx(win32con.NameSamCompatible)) @@ -497,19 +497,36 @@ class TestProcess(unittest.TestCase): import ctypes.wintypes PROCESS_QUERY_INFORMATION = 0x400 handle = ctypes.windll.kernel32.OpenProcess( - PROCESS_QUERY_INFORMATION, 0, os.getpid()) + PROCESS_QUERY_INFORMATION, 0, self.pid) self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle) + hndcnt = ctypes.wintypes.DWORD() ctypes.windll.kernel32.GetProcessHandleCount( handle, ctypes.byref(hndcnt)) sys_value = hndcnt.value - psutil_value = psutil.Process().num_handles() - ctypes.windll.kernel32.CloseHandle(handle) - self.assertEqual(psutil_value, sys_value + 1) + psutil_value = psutil.Process(self.pid).num_handles() + self.assertEqual(psutil_value, sys_value) + + def test_error_partial_copy(self): + # https://github.com/giampaolo/psutil/issues/875 + exc = WindowsError() + exc.winerror = 299 + with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc): + with mock.patch("time.sleep") as m: + p = psutil.Process() + self.assertRaises(psutil.AccessDenied, p.cwd) + self.assertGreaterEqual(m.call_count, 5) + + def test_exe(self): + # NtQuerySystemInformation succeeds if process is gone. Make sure + # it raises NSP for a non existent pid. + pid = psutil.pids()[-1] + 99999 + proc = psutil._psplatform.Process(pid) + self.assertRaises(psutil.NoSuchProcess, proc.exe) @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcessWMI(unittest.TestCase): +class TestProcessWMI(TestCase): """Compare Process API results with WMI.""" @classmethod @@ -575,7 +592,7 @@ class TestProcessWMI(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestDualProcessImplementation(unittest.TestCase): +class TestDualProcessImplementation(TestCase): """ Certain APIs on Windows have 2 internal implementations, one based on documented Windows APIs, another one based @@ -593,16 +610,6 @@ class TestDualProcessImplementation(unittest.TestCase): @classmethod def tearDownClass(cls): reap_children() - # --- - # same tests as above but mimicks the AccessDenied failure of - # the first (fast) method failing with AD. - - def test_name(self): - name = psutil.Process(self.pid).name() - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as fun: - self.assertEqual(psutil.Process(self.pid).name(), name) - assert fun.called def test_memory_info(self): mem_1 = psutil.Process(self.pid).memory_info() @@ -618,14 +625,14 @@ class TestDualProcessImplementation(unittest.TestCase): def test_create_time(self): ctime = psutil.Process(self.pid).create_time() - with mock.patch("psutil._psplatform.cext.proc_create_time", + with mock.patch("psutil._psplatform.cext.proc_times", side_effect=OSError(errno.EPERM, "msg")) as fun: self.assertEqual(psutil.Process(self.pid).create_time(), ctime) assert fun.called def test_cpu_times(self): cpu_times_1 = psutil.Process(self.pid).cpu_times() - with mock.patch("psutil._psplatform.cext.proc_cpu_times", + with mock.patch("psutil._psplatform.cext.proc_times", side_effect=OSError(errno.EPERM, "msg")) as fun: cpu_times_2 = psutil.Process(self.pid).cpu_times() assert fun.called @@ -652,9 +659,23 @@ class TestDualProcessImplementation(unittest.TestCase): num_handles) assert fun.called + def test_cmdline(self): + from psutil._pswindows import convert_oserror + for pid in psutil.pids(): + try: + a = cext.proc_cmdline(pid, use_peb=True) + b = cext.proc_cmdline(pid, use_peb=False) + except OSError as err: + err = convert_oserror(err) + if not isinstance(err, (psutil.AccessDenied, + psutil.NoSuchProcess)): + raise + else: + self.assertEqual(a, b) + @unittest.skipIf(not WINDOWS, "WINDOWS only") -class RemoteProcessTestCase(unittest.TestCase): +class RemoteProcessTestCase(TestCase): """Certain functions require calling ReadProcessMemory. This trivially works when called on the current process. Check that this works on other processes, especially when they @@ -738,9 +759,10 @@ class RemoteProcessTestCase(unittest.TestCase): def test_environ_64(self): p = psutil.Process(self.proc64.pid) - e = p.environ() - self.assertIn("THINK_OF_A_NUMBER", e) - self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid())) + try: + p.environ() + except psutil.AccessDenied: + pass # =================================================================== @@ -749,7 +771,7 @@ class RemoteProcessTestCase(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestServices(unittest.TestCase): +class TestServices(TestCase): def test_win_service_iter(self): valid_statuses = set([ @@ -800,16 +822,22 @@ class TestServices(unittest.TestCase): self.assertEqual(serv, s) def test_win_service_get(self): - name = next(psutil.win_service_iter()).name() + ERROR_SERVICE_DOES_NOT_EXIST = \ + psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST + ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED + name = next(psutil.win_service_iter()).name() with self.assertRaises(psutil.NoSuchProcess) as cm: psutil.win_service_get(name + '???') self.assertEqual(cm.exception.name, name + '???') # test NoSuchProcess service = psutil.win_service_get(name) - exc = WindowsError( - psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST, "") + if PY3: + args = (0, "msg", 0, ERROR_SERVICE_DOES_NOT_EXIST) + else: + args = (ERROR_SERVICE_DOES_NOT_EXIST, "msg") + exc = WindowsError(*args) with mock.patch("psutil._psplatform.cext.winservice_query_status", side_effect=exc): self.assertRaises(psutil.NoSuchProcess, service.status) @@ -818,8 +846,11 @@ class TestServices(unittest.TestCase): self.assertRaises(psutil.NoSuchProcess, service.username) # test AccessDenied - exc = WindowsError( - psutil._psplatform.cext.ERROR_ACCESS_DENIED, "") + if PY3: + args = (0, "msg", 0, ERROR_ACCESS_DENIED) + else: + args = (ERROR_ACCESS_DENIED, "msg") + exc = WindowsError(*args) with mock.patch("psutil._psplatform.cext.winservice_query_status", side_effect=exc): self.assertRaises(psutil.AccessDenied, service.status) @@ -835,4 +866,5 @@ class TestServices(unittest.TestCase): if __name__ == '__main__': - run_test_module_by_name(__file__) + from psutil.tests.runner import run + run(__file__) diff --git a/third_party/python/psutil/.cirrus.yml b/third_party/python/psutil/.cirrus.yml new file mode 100644 index 000000000000..41c58a93d305 --- /dev/null +++ b/third_party/python/psutil/.cirrus.yml @@ -0,0 +1,31 @@ +freebsd_12_1_py3_task: + freebsd_instance: + image: freebsd-12-1-release-amd64 + env: + CIRRUS: 1 + install_script: + - pkg install -y python3 gcc py37-pip + script: + - python3 -m pip install --user setuptools + - make clean + - make install + - make test + - make test-memleaks + - PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_access_denied.py + - PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_api_speed.py + +freebsd_12_1_py2_task: + freebsd_instance: + image: freebsd-12-1-release-amd64 + env: + CIRRUS: 1 + install_script: + - pkg install -y python gcc py27-pip + script: + - python2.7 -m pip install --user setuptools ipaddress mock + - make clean + - make install + - make test + - make test-memleaks + - PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_access_denied.py + - PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_api_speed.py diff --git a/third_party/python/psutil/CREDITS b/third_party/python/psutil/CREDITS index 5469b0b1c91c..716de944c23c 100644 --- a/third_party/python/psutil/CREDITS +++ b/third_party/python/psutil/CREDITS @@ -57,7 +57,7 @@ N: Arnon Yaari (wiggin15) W: https://github.com/wiggin15 D: AIX implementation, expert on multiple fronts I: 517, 607, 610, 1131, 1123, 1130, 1154, 1164, 1174, 1177, 1210, 1214, 1408, - 1329, 1276, 1494. + 1329, 1276, 1494, 1528. N: Alex Manuskin W: https://github.com/amanusk @@ -621,4 +621,46 @@ I: 1491 N: Kamil Rytarowski W: https://github.com/krytarowski C: Poland -I: 1526 +I: 1526, 1530 + +N: Athos Ribeiro +W: https://github.com/athos-ribeiro +I: 1585 + +N: Erwan Le Pape +W: https://github.com/erwan-le-pape +I: 1570 + +N: Étienne Servais +W: https://github.com/vser1 +I: 1607, 1637 + +N: Bernát Gábor +W: https://github.com/gaborbernat +I: 1565 + +N: Nathan Houghton +W: https://github.com/n1000 +I: 1619 + +N: Riccardo Schirone +W: https://github.com/ret2libc +C: Milano, Italy +I: 1616 + +N: Po-Chuan Hsieh +W: https://github.com/sunpoet +C: Taiwan +I: 1646 + +N: Javad Karabi +W: https://github.com/karabijavad +I: 1648 + +N: Mike Hommey +W: https://github.com/glandium +I: 1665 + +N: Anselm Kruis +W: https://github.com/akruis +I: 1695 diff --git a/third_party/python/psutil/HISTORY.rst b/third_party/python/psutil/HISTORY.rst index f8147bfed144..9b39fa051785 100644 --- a/third_party/python/psutil/HISTORY.rst +++ b/third_party/python/psutil/HISTORY.rst @@ -1,5 +1,116 @@ *Bug tracker at https://github.com/giampaolo/psutil/issues* +5.7.0 +===== + +2020-12-18 + +**Enhancements** + +- 1637_: [SunOS] add partial support for old SunOS 5.10 Update 0 to 3. +- 1648_: [Linux] sensors_temperatures() looks into an additional /sys/device/ + directory for additional data. (patch by Javad Karabi) +- 1652_: [Windows] dropped support for Windows XP and Windows Server 2003. + Minimum supported Windows version now is Windows Vista. +- 1671_: [FreeBSD] add CI testing/service for FreeBSD (Cirrus CI). +- 1677_: [Windows] process exe() will succeed for all process PIDs (instead of + raising AccessDenied). +- 1679_: [Windows] net_connections() and Process.connections() are 10% faster. +- 1682_: [PyPy] added CI / test integration for PyPy via Travis. +- 1686_: [Windows] added support for PyPy on Windows. +- 1693_: [Windows] boot_time(), Process.create_time() and users()'s login time + now have 1 micro second precision (before the precision was of 1 second). + +**Bug fixes** + +- 1538_: [NetBSD] process cwd() may return ENOENT instead of NoSuchProcess. +- 1627_: [Linux] Process.memory_maps() can raise KeyError. +- 1642_: [SunOS] querying basic info for PID 0 results in FileNotFoundError. +- 1646_: [FreeBSD] many Process methods may cause a segfault on FreeBSD 12.0 + due to a backward incompatible change in a C type introduced in 12.0. +- 1656_: [Windows] Process.memory_full_info() raises AccessDenied even for the + current user and os.getpid(). +- 1660_: [Windows] Process.open_files() complete rewrite + check of errors. +- 1662_: [Windows] process exe() may raise WinError 0. +- 1665_: [Linux] disk_io_counters() does not take into account extra fields + added to recent kernels. (patch by Mike Hommey) +- 1672_: use the right C type when dealing with PIDs (int or long). Thus far + (long) was almost always assumed, which is wrong on most platforms. +- 1673_: [OpenBSD] Process connections(), num_fds() and threads() returned + improper exception if process is gone. +- 1674_: [SunOS] disk_partitions() may raise OSError. +- 1684_: [Linux] disk_io_counters() may raise ValueError on systems not + having /proc/diskstats. +- 1695_: [Linux] could not compile on kernels <= 2.6.13 due to + PSUTIL_HAVE_IOPRIO not being defined. (patch by Anselm Kruis) + +5.6.7 +===== + +2019-11-26 + +**Bug fixes** + +- 1630_: [Windows] can't compile source distribution due to C syntax error. + +5.6.6 +===== + +2019-11-25 + +**Bug fixes** + +- 1179_: [Linux] Process cmdline() now takes into account misbehaving processes + renaming the command line and using inappropriate chars to separate args. +- 1616_: use of Py_DECREF instead of Py_CLEAR will result in double free and + segfault + (`CVE-2019-18874 `__). + (patch by Riccardo Schirone) +- 1619_: [OpenBSD] compilation fails due to C syntax error. (patch by Nathan + Houghton) + +5.6.5 +===== + +2019-11-06 + +**Bug fixes** + +- 1615_: remove pyproject.toml as it was causing installation issues. + +5.6.4 +===== + +2019-11-04 + +**Enhancements** + +- 1527_: [Linux] added Process.cpu_times().iowait counter, which is the time + spent waiting for blocking I/O to complete. +- 1565_: add PEP 517/8 build backend and requirements specification for better + pip integration. (patch by Bernát Gábor) + +**Bug fixes** + +- 875_: [Windows] Process' cmdline(), environ() or cwd() may occasionally fail + with ERROR_PARTIAL_COPY which now gets translated to AccessDenied. +- 1126_: [Linux] cpu_affinity() segfaults on CentOS 5 / manylinux. + cpu_affinity() support for CentOS 5 was removed. +- 1528_: [AIX] compilation error on AIX 7.2 due to 32 vs 64 bit differences. + (patch by Arnon Yaari) +- 1535_: 'type' and 'family' fields returned by net_connections() are not + always turned into enums. +- 1536_: [NetBSD] process cmdline() erroneously raise ZombieProcess error if + cmdline has non encodable chars. +- 1546_: usage percent may be rounded to 0 on Python 2. +- 1552_: [Windows] getloadavg() math for calculating 5 and 15 mins values is + incorrect. +- 1568_: [Linux] use CC compiler env var if defined. +- 1570_: [Windows] `NtWow64*` syscalls fail to raise the proper error code +- 1585_: [OSX] calling close() (in C) on possible negative integers. (patch + by Athos Ribeiro) +- 1606_: [SunOS] compilation fails on SunOS 5.10. (patch by vser1) + 5.6.3 ===== @@ -3528,3 +3639,503 @@ DeprecationWarning. .. _1498: https://github.com/giampaolo/psutil/issues/1498 .. _1499: https://github.com/giampaolo/psutil/issues/1499 .. _1500: https://github.com/giampaolo/psutil/issues/1500 +.. _1501: https://github.com/giampaolo/psutil/issues/1501 +.. _1502: https://github.com/giampaolo/psutil/issues/1502 +.. _1503: https://github.com/giampaolo/psutil/issues/1503 +.. _1504: https://github.com/giampaolo/psutil/issues/1504 +.. _1505: https://github.com/giampaolo/psutil/issues/1505 +.. _1506: https://github.com/giampaolo/psutil/issues/1506 +.. _1507: https://github.com/giampaolo/psutil/issues/1507 +.. _1508: https://github.com/giampaolo/psutil/issues/1508 +.. _1509: https://github.com/giampaolo/psutil/issues/1509 +.. _1510: https://github.com/giampaolo/psutil/issues/1510 +.. _1511: https://github.com/giampaolo/psutil/issues/1511 +.. _1512: https://github.com/giampaolo/psutil/issues/1512 +.. _1513: https://github.com/giampaolo/psutil/issues/1513 +.. _1514: https://github.com/giampaolo/psutil/issues/1514 +.. _1515: https://github.com/giampaolo/psutil/issues/1515 +.. _1516: https://github.com/giampaolo/psutil/issues/1516 +.. _1517: https://github.com/giampaolo/psutil/issues/1517 +.. _1518: https://github.com/giampaolo/psutil/issues/1518 +.. _1519: https://github.com/giampaolo/psutil/issues/1519 +.. _1520: https://github.com/giampaolo/psutil/issues/1520 +.. _1521: https://github.com/giampaolo/psutil/issues/1521 +.. _1522: https://github.com/giampaolo/psutil/issues/1522 +.. _1523: https://github.com/giampaolo/psutil/issues/1523 +.. _1524: https://github.com/giampaolo/psutil/issues/1524 +.. _1525: https://github.com/giampaolo/psutil/issues/1525 +.. _1526: https://github.com/giampaolo/psutil/issues/1526 +.. _1527: https://github.com/giampaolo/psutil/issues/1527 +.. _1528: https://github.com/giampaolo/psutil/issues/1528 +.. _1529: https://github.com/giampaolo/psutil/issues/1529 +.. _1530: https://github.com/giampaolo/psutil/issues/1530 +.. _1531: https://github.com/giampaolo/psutil/issues/1531 +.. _1532: https://github.com/giampaolo/psutil/issues/1532 +.. _1533: https://github.com/giampaolo/psutil/issues/1533 +.. _1534: https://github.com/giampaolo/psutil/issues/1534 +.. _1535: https://github.com/giampaolo/psutil/issues/1535 +.. _1536: https://github.com/giampaolo/psutil/issues/1536 +.. _1537: https://github.com/giampaolo/psutil/issues/1537 +.. _1538: https://github.com/giampaolo/psutil/issues/1538 +.. _1539: https://github.com/giampaolo/psutil/issues/1539 +.. _1540: https://github.com/giampaolo/psutil/issues/1540 +.. _1541: https://github.com/giampaolo/psutil/issues/1541 +.. _1542: https://github.com/giampaolo/psutil/issues/1542 +.. _1543: https://github.com/giampaolo/psutil/issues/1543 +.. _1544: https://github.com/giampaolo/psutil/issues/1544 +.. _1545: https://github.com/giampaolo/psutil/issues/1545 +.. _1546: https://github.com/giampaolo/psutil/issues/1546 +.. _1547: https://github.com/giampaolo/psutil/issues/1547 +.. _1548: https://github.com/giampaolo/psutil/issues/1548 +.. _1549: https://github.com/giampaolo/psutil/issues/1549 +.. _1550: https://github.com/giampaolo/psutil/issues/1550 +.. _1551: https://github.com/giampaolo/psutil/issues/1551 +.. _1552: https://github.com/giampaolo/psutil/issues/1552 +.. _1553: https://github.com/giampaolo/psutil/issues/1553 +.. _1554: https://github.com/giampaolo/psutil/issues/1554 +.. _1555: https://github.com/giampaolo/psutil/issues/1555 +.. _1556: https://github.com/giampaolo/psutil/issues/1556 +.. _1557: https://github.com/giampaolo/psutil/issues/1557 +.. _1558: https://github.com/giampaolo/psutil/issues/1558 +.. _1559: https://github.com/giampaolo/psutil/issues/1559 +.. _1560: https://github.com/giampaolo/psutil/issues/1560 +.. _1561: https://github.com/giampaolo/psutil/issues/1561 +.. _1562: https://github.com/giampaolo/psutil/issues/1562 +.. _1563: https://github.com/giampaolo/psutil/issues/1563 +.. _1564: https://github.com/giampaolo/psutil/issues/1564 +.. _1565: https://github.com/giampaolo/psutil/issues/1565 +.. _1566: https://github.com/giampaolo/psutil/issues/1566 +.. _1567: https://github.com/giampaolo/psutil/issues/1567 +.. _1568: https://github.com/giampaolo/psutil/issues/1568 +.. _1569: https://github.com/giampaolo/psutil/issues/1569 +.. _1570: https://github.com/giampaolo/psutil/issues/1570 +.. _1571: https://github.com/giampaolo/psutil/issues/1571 +.. _1572: https://github.com/giampaolo/psutil/issues/1572 +.. _1573: https://github.com/giampaolo/psutil/issues/1573 +.. _1574: https://github.com/giampaolo/psutil/issues/1574 +.. _1575: https://github.com/giampaolo/psutil/issues/1575 +.. _1576: https://github.com/giampaolo/psutil/issues/1576 +.. _1577: https://github.com/giampaolo/psutil/issues/1577 +.. _1578: https://github.com/giampaolo/psutil/issues/1578 +.. _1579: https://github.com/giampaolo/psutil/issues/1579 +.. _1580: https://github.com/giampaolo/psutil/issues/1580 +.. _1581: https://github.com/giampaolo/psutil/issues/1581 +.. _1582: https://github.com/giampaolo/psutil/issues/1582 +.. _1583: https://github.com/giampaolo/psutil/issues/1583 +.. _1584: https://github.com/giampaolo/psutil/issues/1584 +.. _1585: https://github.com/giampaolo/psutil/issues/1585 +.. _1586: https://github.com/giampaolo/psutil/issues/1586 +.. _1587: https://github.com/giampaolo/psutil/issues/1587 +.. _1588: https://github.com/giampaolo/psutil/issues/1588 +.. _1589: https://github.com/giampaolo/psutil/issues/1589 +.. _1590: https://github.com/giampaolo/psutil/issues/1590 +.. _1591: https://github.com/giampaolo/psutil/issues/1591 +.. _1592: https://github.com/giampaolo/psutil/issues/1592 +.. _1593: https://github.com/giampaolo/psutil/issues/1593 +.. _1594: https://github.com/giampaolo/psutil/issues/1594 +.. _1595: https://github.com/giampaolo/psutil/issues/1595 +.. _1596: https://github.com/giampaolo/psutil/issues/1596 +.. _1597: https://github.com/giampaolo/psutil/issues/1597 +.. _1598: https://github.com/giampaolo/psutil/issues/1598 +.. _1599: https://github.com/giampaolo/psutil/issues/1599 +.. _1600: https://github.com/giampaolo/psutil/issues/1600 +.. _1601: https://github.com/giampaolo/psutil/issues/1601 +.. _1602: https://github.com/giampaolo/psutil/issues/1602 +.. _1603: https://github.com/giampaolo/psutil/issues/1603 +.. _1604: https://github.com/giampaolo/psutil/issues/1604 +.. _1605: https://github.com/giampaolo/psutil/issues/1605 +.. _1606: https://github.com/giampaolo/psutil/issues/1606 +.. _1607: https://github.com/giampaolo/psutil/issues/1607 +.. _1608: https://github.com/giampaolo/psutil/issues/1608 +.. _1609: https://github.com/giampaolo/psutil/issues/1609 +.. _1610: https://github.com/giampaolo/psutil/issues/1610 +.. _1611: https://github.com/giampaolo/psutil/issues/1611 +.. _1612: https://github.com/giampaolo/psutil/issues/1612 +.. _1613: https://github.com/giampaolo/psutil/issues/1613 +.. _1614: https://github.com/giampaolo/psutil/issues/1614 +.. _1615: https://github.com/giampaolo/psutil/issues/1615 +.. _1616: https://github.com/giampaolo/psutil/issues/1616 +.. _1617: https://github.com/giampaolo/psutil/issues/1617 +.. _1618: https://github.com/giampaolo/psutil/issues/1618 +.. _1619: https://github.com/giampaolo/psutil/issues/1619 +.. _1620: https://github.com/giampaolo/psutil/issues/1620 +.. _1621: https://github.com/giampaolo/psutil/issues/1621 +.. _1622: https://github.com/giampaolo/psutil/issues/1622 +.. _1623: https://github.com/giampaolo/psutil/issues/1623 +.. _1624: https://github.com/giampaolo/psutil/issues/1624 +.. _1625: https://github.com/giampaolo/psutil/issues/1625 +.. _1626: https://github.com/giampaolo/psutil/issues/1626 +.. _1627: https://github.com/giampaolo/psutil/issues/1627 +.. _1628: https://github.com/giampaolo/psutil/issues/1628 +.. _1629: https://github.com/giampaolo/psutil/issues/1629 +.. _1630: https://github.com/giampaolo/psutil/issues/1630 +.. _1631: https://github.com/giampaolo/psutil/issues/1631 +.. _1632: https://github.com/giampaolo/psutil/issues/1632 +.. _1633: https://github.com/giampaolo/psutil/issues/1633 +.. _1634: https://github.com/giampaolo/psutil/issues/1634 +.. _1635: https://github.com/giampaolo/psutil/issues/1635 +.. _1636: https://github.com/giampaolo/psutil/issues/1636 +.. _1637: https://github.com/giampaolo/psutil/issues/1637 +.. _1638: https://github.com/giampaolo/psutil/issues/1638 +.. _1639: https://github.com/giampaolo/psutil/issues/1639 +.. _1640: https://github.com/giampaolo/psutil/issues/1640 +.. _1641: https://github.com/giampaolo/psutil/issues/1641 +.. _1642: https://github.com/giampaolo/psutil/issues/1642 +.. _1643: https://github.com/giampaolo/psutil/issues/1643 +.. _1644: https://github.com/giampaolo/psutil/issues/1644 +.. _1645: https://github.com/giampaolo/psutil/issues/1645 +.. _1646: https://github.com/giampaolo/psutil/issues/1646 +.. _1647: https://github.com/giampaolo/psutil/issues/1647 +.. _1648: https://github.com/giampaolo/psutil/issues/1648 +.. _1649: https://github.com/giampaolo/psutil/issues/1649 +.. _1650: https://github.com/giampaolo/psutil/issues/1650 +.. _1651: https://github.com/giampaolo/psutil/issues/1651 +.. _1652: https://github.com/giampaolo/psutil/issues/1652 +.. _1653: https://github.com/giampaolo/psutil/issues/1653 +.. _1654: https://github.com/giampaolo/psutil/issues/1654 +.. _1655: https://github.com/giampaolo/psutil/issues/1655 +.. _1656: https://github.com/giampaolo/psutil/issues/1656 +.. _1657: https://github.com/giampaolo/psutil/issues/1657 +.. _1658: https://github.com/giampaolo/psutil/issues/1658 +.. _1659: https://github.com/giampaolo/psutil/issues/1659 +.. _1660: https://github.com/giampaolo/psutil/issues/1660 +.. _1661: https://github.com/giampaolo/psutil/issues/1661 +.. _1662: https://github.com/giampaolo/psutil/issues/1662 +.. _1663: https://github.com/giampaolo/psutil/issues/1663 +.. _1664: https://github.com/giampaolo/psutil/issues/1664 +.. _1665: https://github.com/giampaolo/psutil/issues/1665 +.. _1666: https://github.com/giampaolo/psutil/issues/1666 +.. _1667: https://github.com/giampaolo/psutil/issues/1667 +.. _1668: https://github.com/giampaolo/psutil/issues/1668 +.. _1669: https://github.com/giampaolo/psutil/issues/1669 +.. _1670: https://github.com/giampaolo/psutil/issues/1670 +.. _1671: https://github.com/giampaolo/psutil/issues/1671 +.. _1672: https://github.com/giampaolo/psutil/issues/1672 +.. _1673: https://github.com/giampaolo/psutil/issues/1673 +.. _1674: https://github.com/giampaolo/psutil/issues/1674 +.. _1675: https://github.com/giampaolo/psutil/issues/1675 +.. _1676: https://github.com/giampaolo/psutil/issues/1676 +.. _1677: https://github.com/giampaolo/psutil/issues/1677 +.. _1678: https://github.com/giampaolo/psutil/issues/1678 +.. _1679: https://github.com/giampaolo/psutil/issues/1679 +.. _1680: https://github.com/giampaolo/psutil/issues/1680 +.. _1681: https://github.com/giampaolo/psutil/issues/1681 +.. _1682: https://github.com/giampaolo/psutil/issues/1682 +.. _1683: https://github.com/giampaolo/psutil/issues/1683 +.. _1684: https://github.com/giampaolo/psutil/issues/1684 +.. _1685: https://github.com/giampaolo/psutil/issues/1685 +.. _1686: https://github.com/giampaolo/psutil/issues/1686 +.. _1687: https://github.com/giampaolo/psutil/issues/1687 +.. _1688: https://github.com/giampaolo/psutil/issues/1688 +.. _1689: https://github.com/giampaolo/psutil/issues/1689 +.. _1690: https://github.com/giampaolo/psutil/issues/1690 +.. _1691: https://github.com/giampaolo/psutil/issues/1691 +.. _1692: https://github.com/giampaolo/psutil/issues/1692 +.. _1693: https://github.com/giampaolo/psutil/issues/1693 +.. _1694: https://github.com/giampaolo/psutil/issues/1694 +.. _1695: https://github.com/giampaolo/psutil/issues/1695 +.. _1696: https://github.com/giampaolo/psutil/issues/1696 +.. _1697: https://github.com/giampaolo/psutil/issues/1697 +.. _1698: https://github.com/giampaolo/psutil/issues/1698 +.. _1699: https://github.com/giampaolo/psutil/issues/1699 +.. _1700: https://github.com/giampaolo/psutil/issues/1700 +.. _1701: https://github.com/giampaolo/psutil/issues/1701 +.. _1702: https://github.com/giampaolo/psutil/issues/1702 +.. _1703: https://github.com/giampaolo/psutil/issues/1703 +.. _1704: https://github.com/giampaolo/psutil/issues/1704 +.. _1705: https://github.com/giampaolo/psutil/issues/1705 +.. _1706: https://github.com/giampaolo/psutil/issues/1706 +.. _1707: https://github.com/giampaolo/psutil/issues/1707 +.. _1708: https://github.com/giampaolo/psutil/issues/1708 +.. _1709: https://github.com/giampaolo/psutil/issues/1709 +.. _1710: https://github.com/giampaolo/psutil/issues/1710 +.. _1711: https://github.com/giampaolo/psutil/issues/1711 +.. _1712: https://github.com/giampaolo/psutil/issues/1712 +.. _1713: https://github.com/giampaolo/psutil/issues/1713 +.. _1714: https://github.com/giampaolo/psutil/issues/1714 +.. _1715: https://github.com/giampaolo/psutil/issues/1715 +.. _1716: https://github.com/giampaolo/psutil/issues/1716 +.. _1717: https://github.com/giampaolo/psutil/issues/1717 +.. _1718: https://github.com/giampaolo/psutil/issues/1718 +.. _1719: https://github.com/giampaolo/psutil/issues/1719 +.. _1720: https://github.com/giampaolo/psutil/issues/1720 +.. _1721: https://github.com/giampaolo/psutil/issues/1721 +.. _1722: https://github.com/giampaolo/psutil/issues/1722 +.. _1723: https://github.com/giampaolo/psutil/issues/1723 +.. _1724: https://github.com/giampaolo/psutil/issues/1724 +.. _1725: https://github.com/giampaolo/psutil/issues/1725 +.. _1726: https://github.com/giampaolo/psutil/issues/1726 +.. _1727: https://github.com/giampaolo/psutil/issues/1727 +.. _1728: https://github.com/giampaolo/psutil/issues/1728 +.. _1729: https://github.com/giampaolo/psutil/issues/1729 +.. _1730: https://github.com/giampaolo/psutil/issues/1730 +.. _1731: https://github.com/giampaolo/psutil/issues/1731 +.. _1732: https://github.com/giampaolo/psutil/issues/1732 +.. _1733: https://github.com/giampaolo/psutil/issues/1733 +.. _1734: https://github.com/giampaolo/psutil/issues/1734 +.. _1735: https://github.com/giampaolo/psutil/issues/1735 +.. _1736: https://github.com/giampaolo/psutil/issues/1736 +.. _1737: https://github.com/giampaolo/psutil/issues/1737 +.. _1738: https://github.com/giampaolo/psutil/issues/1738 +.. _1739: https://github.com/giampaolo/psutil/issues/1739 +.. _1740: https://github.com/giampaolo/psutil/issues/1740 +.. _1741: https://github.com/giampaolo/psutil/issues/1741 +.. _1742: https://github.com/giampaolo/psutil/issues/1742 +.. _1743: https://github.com/giampaolo/psutil/issues/1743 +.. _1744: https://github.com/giampaolo/psutil/issues/1744 +.. _1745: https://github.com/giampaolo/psutil/issues/1745 +.. _1746: https://github.com/giampaolo/psutil/issues/1746 +.. _1747: https://github.com/giampaolo/psutil/issues/1747 +.. _1748: https://github.com/giampaolo/psutil/issues/1748 +.. _1749: https://github.com/giampaolo/psutil/issues/1749 +.. _1750: https://github.com/giampaolo/psutil/issues/1750 +.. _1751: https://github.com/giampaolo/psutil/issues/1751 +.. _1752: https://github.com/giampaolo/psutil/issues/1752 +.. _1753: https://github.com/giampaolo/psutil/issues/1753 +.. _1754: https://github.com/giampaolo/psutil/issues/1754 +.. _1755: https://github.com/giampaolo/psutil/issues/1755 +.. _1756: https://github.com/giampaolo/psutil/issues/1756 +.. _1757: https://github.com/giampaolo/psutil/issues/1757 +.. _1758: https://github.com/giampaolo/psutil/issues/1758 +.. _1759: https://github.com/giampaolo/psutil/issues/1759 +.. _1760: https://github.com/giampaolo/psutil/issues/1760 +.. _1761: https://github.com/giampaolo/psutil/issues/1761 +.. _1762: https://github.com/giampaolo/psutil/issues/1762 +.. _1763: https://github.com/giampaolo/psutil/issues/1763 +.. _1764: https://github.com/giampaolo/psutil/issues/1764 +.. _1765: https://github.com/giampaolo/psutil/issues/1765 +.. _1766: https://github.com/giampaolo/psutil/issues/1766 +.. _1767: https://github.com/giampaolo/psutil/issues/1767 +.. _1768: https://github.com/giampaolo/psutil/issues/1768 +.. _1769: https://github.com/giampaolo/psutil/issues/1769 +.. _1770: https://github.com/giampaolo/psutil/issues/1770 +.. _1771: https://github.com/giampaolo/psutil/issues/1771 +.. _1772: https://github.com/giampaolo/psutil/issues/1772 +.. _1773: https://github.com/giampaolo/psutil/issues/1773 +.. _1774: https://github.com/giampaolo/psutil/issues/1774 +.. _1775: https://github.com/giampaolo/psutil/issues/1775 +.. _1776: https://github.com/giampaolo/psutil/issues/1776 +.. _1777: https://github.com/giampaolo/psutil/issues/1777 +.. _1778: https://github.com/giampaolo/psutil/issues/1778 +.. _1779: https://github.com/giampaolo/psutil/issues/1779 +.. _1780: https://github.com/giampaolo/psutil/issues/1780 +.. _1781: https://github.com/giampaolo/psutil/issues/1781 +.. _1782: https://github.com/giampaolo/psutil/issues/1782 +.. _1783: https://github.com/giampaolo/psutil/issues/1783 +.. _1784: https://github.com/giampaolo/psutil/issues/1784 +.. _1785: https://github.com/giampaolo/psutil/issues/1785 +.. _1786: https://github.com/giampaolo/psutil/issues/1786 +.. _1787: https://github.com/giampaolo/psutil/issues/1787 +.. _1788: https://github.com/giampaolo/psutil/issues/1788 +.. _1789: https://github.com/giampaolo/psutil/issues/1789 +.. _1790: https://github.com/giampaolo/psutil/issues/1790 +.. _1791: https://github.com/giampaolo/psutil/issues/1791 +.. _1792: https://github.com/giampaolo/psutil/issues/1792 +.. _1793: https://github.com/giampaolo/psutil/issues/1793 +.. _1794: https://github.com/giampaolo/psutil/issues/1794 +.. _1795: https://github.com/giampaolo/psutil/issues/1795 +.. _1796: https://github.com/giampaolo/psutil/issues/1796 +.. _1797: https://github.com/giampaolo/psutil/issues/1797 +.. _1798: https://github.com/giampaolo/psutil/issues/1798 +.. _1799: https://github.com/giampaolo/psutil/issues/1799 +.. _1800: https://github.com/giampaolo/psutil/issues/1800 +.. _1801: https://github.com/giampaolo/psutil/issues/1801 +.. _1802: https://github.com/giampaolo/psutil/issues/1802 +.. _1803: https://github.com/giampaolo/psutil/issues/1803 +.. _1804: https://github.com/giampaolo/psutil/issues/1804 +.. _1805: https://github.com/giampaolo/psutil/issues/1805 +.. _1806: https://github.com/giampaolo/psutil/issues/1806 +.. _1807: https://github.com/giampaolo/psutil/issues/1807 +.. _1808: https://github.com/giampaolo/psutil/issues/1808 +.. _1809: https://github.com/giampaolo/psutil/issues/1809 +.. _1810: https://github.com/giampaolo/psutil/issues/1810 +.. _1811: https://github.com/giampaolo/psutil/issues/1811 +.. _1812: https://github.com/giampaolo/psutil/issues/1812 +.. _1813: https://github.com/giampaolo/psutil/issues/1813 +.. _1814: https://github.com/giampaolo/psutil/issues/1814 +.. _1815: https://github.com/giampaolo/psutil/issues/1815 +.. _1816: https://github.com/giampaolo/psutil/issues/1816 +.. _1817: https://github.com/giampaolo/psutil/issues/1817 +.. _1818: https://github.com/giampaolo/psutil/issues/1818 +.. _1819: https://github.com/giampaolo/psutil/issues/1819 +.. _1820: https://github.com/giampaolo/psutil/issues/1820 +.. _1821: https://github.com/giampaolo/psutil/issues/1821 +.. _1822: https://github.com/giampaolo/psutil/issues/1822 +.. _1823: https://github.com/giampaolo/psutil/issues/1823 +.. _1824: https://github.com/giampaolo/psutil/issues/1824 +.. _1825: https://github.com/giampaolo/psutil/issues/1825 +.. _1826: https://github.com/giampaolo/psutil/issues/1826 +.. _1827: https://github.com/giampaolo/psutil/issues/1827 +.. _1828: https://github.com/giampaolo/psutil/issues/1828 +.. _1829: https://github.com/giampaolo/psutil/issues/1829 +.. _1830: https://github.com/giampaolo/psutil/issues/1830 +.. _1831: https://github.com/giampaolo/psutil/issues/1831 +.. _1832: https://github.com/giampaolo/psutil/issues/1832 +.. _1833: https://github.com/giampaolo/psutil/issues/1833 +.. _1834: https://github.com/giampaolo/psutil/issues/1834 +.. _1835: https://github.com/giampaolo/psutil/issues/1835 +.. _1836: https://github.com/giampaolo/psutil/issues/1836 +.. _1837: https://github.com/giampaolo/psutil/issues/1837 +.. _1838: https://github.com/giampaolo/psutil/issues/1838 +.. _1839: https://github.com/giampaolo/psutil/issues/1839 +.. _1840: https://github.com/giampaolo/psutil/issues/1840 +.. _1841: https://github.com/giampaolo/psutil/issues/1841 +.. _1842: https://github.com/giampaolo/psutil/issues/1842 +.. _1843: https://github.com/giampaolo/psutil/issues/1843 +.. _1844: https://github.com/giampaolo/psutil/issues/1844 +.. _1845: https://github.com/giampaolo/psutil/issues/1845 +.. _1846: https://github.com/giampaolo/psutil/issues/1846 +.. _1847: https://github.com/giampaolo/psutil/issues/1847 +.. _1848: https://github.com/giampaolo/psutil/issues/1848 +.. _1849: https://github.com/giampaolo/psutil/issues/1849 +.. _1850: https://github.com/giampaolo/psutil/issues/1850 +.. _1851: https://github.com/giampaolo/psutil/issues/1851 +.. _1852: https://github.com/giampaolo/psutil/issues/1852 +.. _1853: https://github.com/giampaolo/psutil/issues/1853 +.. _1854: https://github.com/giampaolo/psutil/issues/1854 +.. _1855: https://github.com/giampaolo/psutil/issues/1855 +.. _1856: https://github.com/giampaolo/psutil/issues/1856 +.. _1857: https://github.com/giampaolo/psutil/issues/1857 +.. _1858: https://github.com/giampaolo/psutil/issues/1858 +.. _1859: https://github.com/giampaolo/psutil/issues/1859 +.. _1860: https://github.com/giampaolo/psutil/issues/1860 +.. _1861: https://github.com/giampaolo/psutil/issues/1861 +.. _1862: https://github.com/giampaolo/psutil/issues/1862 +.. _1863: https://github.com/giampaolo/psutil/issues/1863 +.. _1864: https://github.com/giampaolo/psutil/issues/1864 +.. _1865: https://github.com/giampaolo/psutil/issues/1865 +.. _1866: https://github.com/giampaolo/psutil/issues/1866 +.. _1867: https://github.com/giampaolo/psutil/issues/1867 +.. _1868: https://github.com/giampaolo/psutil/issues/1868 +.. _1869: https://github.com/giampaolo/psutil/issues/1869 +.. _1870: https://github.com/giampaolo/psutil/issues/1870 +.. _1871: https://github.com/giampaolo/psutil/issues/1871 +.. _1872: https://github.com/giampaolo/psutil/issues/1872 +.. _1873: https://github.com/giampaolo/psutil/issues/1873 +.. _1874: https://github.com/giampaolo/psutil/issues/1874 +.. _1875: https://github.com/giampaolo/psutil/issues/1875 +.. _1876: https://github.com/giampaolo/psutil/issues/1876 +.. _1877: https://github.com/giampaolo/psutil/issues/1877 +.. _1878: https://github.com/giampaolo/psutil/issues/1878 +.. _1879: https://github.com/giampaolo/psutil/issues/1879 +.. _1880: https://github.com/giampaolo/psutil/issues/1880 +.. _1881: https://github.com/giampaolo/psutil/issues/1881 +.. _1882: https://github.com/giampaolo/psutil/issues/1882 +.. _1883: https://github.com/giampaolo/psutil/issues/1883 +.. _1884: https://github.com/giampaolo/psutil/issues/1884 +.. _1885: https://github.com/giampaolo/psutil/issues/1885 +.. _1886: https://github.com/giampaolo/psutil/issues/1886 +.. _1887: https://github.com/giampaolo/psutil/issues/1887 +.. _1888: https://github.com/giampaolo/psutil/issues/1888 +.. _1889: https://github.com/giampaolo/psutil/issues/1889 +.. _1890: https://github.com/giampaolo/psutil/issues/1890 +.. _1891: https://github.com/giampaolo/psutil/issues/1891 +.. _1892: https://github.com/giampaolo/psutil/issues/1892 +.. _1893: https://github.com/giampaolo/psutil/issues/1893 +.. _1894: https://github.com/giampaolo/psutil/issues/1894 +.. _1895: https://github.com/giampaolo/psutil/issues/1895 +.. _1896: https://github.com/giampaolo/psutil/issues/1896 +.. _1897: https://github.com/giampaolo/psutil/issues/1897 +.. _1898: https://github.com/giampaolo/psutil/issues/1898 +.. _1899: https://github.com/giampaolo/psutil/issues/1899 +.. _1900: https://github.com/giampaolo/psutil/issues/1900 +.. _1901: https://github.com/giampaolo/psutil/issues/1901 +.. _1902: https://github.com/giampaolo/psutil/issues/1902 +.. _1903: https://github.com/giampaolo/psutil/issues/1903 +.. _1904: https://github.com/giampaolo/psutil/issues/1904 +.. _1905: https://github.com/giampaolo/psutil/issues/1905 +.. _1906: https://github.com/giampaolo/psutil/issues/1906 +.. _1907: https://github.com/giampaolo/psutil/issues/1907 +.. _1908: https://github.com/giampaolo/psutil/issues/1908 +.. _1909: https://github.com/giampaolo/psutil/issues/1909 +.. _1910: https://github.com/giampaolo/psutil/issues/1910 +.. _1911: https://github.com/giampaolo/psutil/issues/1911 +.. _1912: https://github.com/giampaolo/psutil/issues/1912 +.. _1913: https://github.com/giampaolo/psutil/issues/1913 +.. _1914: https://github.com/giampaolo/psutil/issues/1914 +.. _1915: https://github.com/giampaolo/psutil/issues/1915 +.. _1916: https://github.com/giampaolo/psutil/issues/1916 +.. _1917: https://github.com/giampaolo/psutil/issues/1917 +.. _1918: https://github.com/giampaolo/psutil/issues/1918 +.. _1919: https://github.com/giampaolo/psutil/issues/1919 +.. _1920: https://github.com/giampaolo/psutil/issues/1920 +.. _1921: https://github.com/giampaolo/psutil/issues/1921 +.. _1922: https://github.com/giampaolo/psutil/issues/1922 +.. _1923: https://github.com/giampaolo/psutil/issues/1923 +.. _1924: https://github.com/giampaolo/psutil/issues/1924 +.. _1925: https://github.com/giampaolo/psutil/issues/1925 +.. _1926: https://github.com/giampaolo/psutil/issues/1926 +.. _1927: https://github.com/giampaolo/psutil/issues/1927 +.. _1928: https://github.com/giampaolo/psutil/issues/1928 +.. _1929: https://github.com/giampaolo/psutil/issues/1929 +.. _1930: https://github.com/giampaolo/psutil/issues/1930 +.. _1931: https://github.com/giampaolo/psutil/issues/1931 +.. _1932: https://github.com/giampaolo/psutil/issues/1932 +.. _1933: https://github.com/giampaolo/psutil/issues/1933 +.. _1934: https://github.com/giampaolo/psutil/issues/1934 +.. _1935: https://github.com/giampaolo/psutil/issues/1935 +.. _1936: https://github.com/giampaolo/psutil/issues/1936 +.. _1937: https://github.com/giampaolo/psutil/issues/1937 +.. _1938: https://github.com/giampaolo/psutil/issues/1938 +.. _1939: https://github.com/giampaolo/psutil/issues/1939 +.. _1940: https://github.com/giampaolo/psutil/issues/1940 +.. _1941: https://github.com/giampaolo/psutil/issues/1941 +.. _1942: https://github.com/giampaolo/psutil/issues/1942 +.. _1943: https://github.com/giampaolo/psutil/issues/1943 +.. _1944: https://github.com/giampaolo/psutil/issues/1944 +.. _1945: https://github.com/giampaolo/psutil/issues/1945 +.. _1946: https://github.com/giampaolo/psutil/issues/1946 +.. _1947: https://github.com/giampaolo/psutil/issues/1947 +.. _1948: https://github.com/giampaolo/psutil/issues/1948 +.. _1949: https://github.com/giampaolo/psutil/issues/1949 +.. _1950: https://github.com/giampaolo/psutil/issues/1950 +.. _1951: https://github.com/giampaolo/psutil/issues/1951 +.. _1952: https://github.com/giampaolo/psutil/issues/1952 +.. _1953: https://github.com/giampaolo/psutil/issues/1953 +.. _1954: https://github.com/giampaolo/psutil/issues/1954 +.. _1955: https://github.com/giampaolo/psutil/issues/1955 +.. _1956: https://github.com/giampaolo/psutil/issues/1956 +.. _1957: https://github.com/giampaolo/psutil/issues/1957 +.. _1958: https://github.com/giampaolo/psutil/issues/1958 +.. _1959: https://github.com/giampaolo/psutil/issues/1959 +.. _1960: https://github.com/giampaolo/psutil/issues/1960 +.. _1961: https://github.com/giampaolo/psutil/issues/1961 +.. _1962: https://github.com/giampaolo/psutil/issues/1962 +.. _1963: https://github.com/giampaolo/psutil/issues/1963 +.. _1964: https://github.com/giampaolo/psutil/issues/1964 +.. _1965: https://github.com/giampaolo/psutil/issues/1965 +.. _1966: https://github.com/giampaolo/psutil/issues/1966 +.. _1967: https://github.com/giampaolo/psutil/issues/1967 +.. _1968: https://github.com/giampaolo/psutil/issues/1968 +.. _1969: https://github.com/giampaolo/psutil/issues/1969 +.. _1970: https://github.com/giampaolo/psutil/issues/1970 +.. _1971: https://github.com/giampaolo/psutil/issues/1971 +.. _1972: https://github.com/giampaolo/psutil/issues/1972 +.. _1973: https://github.com/giampaolo/psutil/issues/1973 +.. _1974: https://github.com/giampaolo/psutil/issues/1974 +.. _1975: https://github.com/giampaolo/psutil/issues/1975 +.. _1976: https://github.com/giampaolo/psutil/issues/1976 +.. _1977: https://github.com/giampaolo/psutil/issues/1977 +.. _1978: https://github.com/giampaolo/psutil/issues/1978 +.. _1979: https://github.com/giampaolo/psutil/issues/1979 +.. _1980: https://github.com/giampaolo/psutil/issues/1980 +.. _1981: https://github.com/giampaolo/psutil/issues/1981 +.. _1982: https://github.com/giampaolo/psutil/issues/1982 +.. _1983: https://github.com/giampaolo/psutil/issues/1983 +.. _1984: https://github.com/giampaolo/psutil/issues/1984 +.. _1985: https://github.com/giampaolo/psutil/issues/1985 +.. _1986: https://github.com/giampaolo/psutil/issues/1986 +.. _1987: https://github.com/giampaolo/psutil/issues/1987 +.. _1988: https://github.com/giampaolo/psutil/issues/1988 +.. _1989: https://github.com/giampaolo/psutil/issues/1989 +.. _1990: https://github.com/giampaolo/psutil/issues/1990 +.. _1991: https://github.com/giampaolo/psutil/issues/1991 +.. _1992: https://github.com/giampaolo/psutil/issues/1992 +.. _1993: https://github.com/giampaolo/psutil/issues/1993 +.. _1994: https://github.com/giampaolo/psutil/issues/1994 +.. _1995: https://github.com/giampaolo/psutil/issues/1995 +.. _1996: https://github.com/giampaolo/psutil/issues/1996 +.. _1997: https://github.com/giampaolo/psutil/issues/1997 +.. _1998: https://github.com/giampaolo/psutil/issues/1998 +.. _1999: https://github.com/giampaolo/psutil/issues/1999 +.. _2000: https://github.com/giampaolo/psutil/issues/2000 diff --git a/third_party/python/psutil/INSTALL.rst b/third_party/python/psutil/INSTALL.rst index 2b1ea3292a4a..c3b9e91c0c9a 100644 --- a/third_party/python/psutil/INSTALL.rst +++ b/third_party/python/psutil/INSTALL.rst @@ -93,7 +93,7 @@ OpenBSD :: export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/ - pkg_add -v python3 gcc + pkg_add -v python gcc python3 -m pip install psutil NetBSD diff --git a/third_party/python/psutil/MANIFEST.in b/third_party/python/psutil/MANIFEST.in index 027e4e94bfda..801139b2c131 100644 --- a/third_party/python/psutil/MANIFEST.in +++ b/third_party/python/psutil/MANIFEST.in @@ -1,3 +1,4 @@ +include .cirrus.yml include .coveragerc include .gitignore include CREDITS @@ -64,19 +65,25 @@ include psutil/arch/solaris/environ.c include psutil/arch/solaris/environ.h include psutil/arch/solaris/v10/ifaddrs.c include psutil/arch/solaris/v10/ifaddrs.h -include psutil/arch/windows/global.c -include psutil/arch/windows/global.h -include psutil/arch/windows/inet_ntop.c -include psutil/arch/windows/inet_ntop.h +include psutil/arch/windows/cpu.c +include psutil/arch/windows/cpu.h +include psutil/arch/windows/disk.c +include psutil/arch/windows/disk.h +include psutil/arch/windows/net.c +include psutil/arch/windows/net.h include psutil/arch/windows/ntextapi.h include psutil/arch/windows/process_handles.c include psutil/arch/windows/process_handles.h include psutil/arch/windows/process_info.c include psutil/arch/windows/process_info.h +include psutil/arch/windows/process_utils.c +include psutil/arch/windows/process_utils.h include psutil/arch/windows/security.c include psutil/arch/windows/security.h include psutil/arch/windows/services.c include psutil/arch/windows/services.h +include psutil/arch/windows/socks.c +include psutil/arch/windows/socks.h include psutil/arch/windows/wmi.c include psutil/arch/windows/wmi.h include psutil/tests/README.rst @@ -108,14 +115,15 @@ include scripts/internal/README include scripts/internal/bench_oneshot.py include scripts/internal/bench_oneshot_2.py include scripts/internal/check_broken_links.py -include scripts/internal/download_exes.py +include scripts/internal/clinter.py +include scripts/internal/fix_flake8.py include scripts/internal/generate_manifest.py include scripts/internal/print_access_denied.py include scripts/internal/print_announce.py include scripts/internal/print_api_speed.py include scripts/internal/print_timeline.py include scripts/internal/purge_installation.py -include scripts/internal/scriptutils.py +include scripts/internal/win_download_wheels.py include scripts/internal/winmake.py include scripts/iotop.py include scripts/killall.py diff --git a/third_party/python/psutil/Makefile b/third_party/python/psutil/Makefile index e91ae3424445..b15aa5d95723 100644 --- a/third_party/python/psutil/Makefile +++ b/third_party/python/psutil/Makefile @@ -3,7 +3,7 @@ # You can set the variables below from the command line. PYTHON = python3 -TSCRIPT = psutil/tests/__main__.py +TSCRIPT = psutil/tests/runner.py ARGS = # List of nice-to-have dev libs. DEPS = \ @@ -11,20 +11,22 @@ DEPS = \ check-manifest \ coverage \ flake8 \ + pyperf \ + requests \ + setuptools \ + twine \ + virtualenv \ + wheel +PY2_DEPS = \ futures \ ipaddress \ mock==1.0.1 \ - perf \ - requests \ - setuptools \ - sphinx \ - twine \ - unittest2 \ - virtualenv \ - wheel - + unittest2 +DEPS += `$(PYTHON) -c \ + "import sys; print('$(PY2_DEPS)' if sys.version_info[0] == 2 else '')"` # In not in a virtualenv, add --user options for install commands. -INSTALL_OPTS = `$(PYTHON) -c "import sys; print('' if hasattr(sys, 'real_prefix') else '--user')"` +INSTALL_OPTS = `$(PYTHON) -c \ + "import sys; print('' if hasattr(sys, 'real_prefix') else '--user')"` TEST_PREFIX = PYTHONWARNINGS=all PSUTIL_TESTING=1 PSUTIL_DEBUG=1 all: test @@ -54,8 +56,7 @@ clean: ## Remove all build files. build/ \ dist/ \ docs/_build/ \ - htmlcov/ \ - tmp/ + htmlcov/ _: @@ -67,13 +68,11 @@ build: _ ## Compile without installing. @# "import psutil" when using the interactive interpreter from within @# this directory. PYTHONWARNINGS=all $(PYTHON) setup.py build_ext -i - rm -rf tmp $(PYTHON) -c "import psutil" # make sure it actually worked install: ## Install this package as current user in "edit" mode. ${MAKE} build PYTHONWARNINGS=all $(PYTHON) setup.py develop $(INSTALL_OPTS) - rm -rf tmp uninstall: ## Uninstall this package via pip. cd ..; $(PYTHON) -m pip uninstall -y -v psutil || true @@ -101,8 +100,8 @@ install-pip: ## Install pip (no-op if already installed). setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them). ${MAKE} install-git-hooks ${MAKE} install-pip - $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade pip - $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade $(DEPS) + $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade --trusted-host files.pythonhosted.org pip + $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade --trusted-host files.pythonhosted.org $(DEPS) # =================================================================== # Tests @@ -154,7 +153,7 @@ test-by-name: ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSyste test-failed: ## Re-run tests which failed on last run ${MAKE} install - $(TEST_PREFIX) $(PYTHON) -c "import psutil.tests.runner as r; r.run(last_failed=True)" + $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) --last-failed test-coverage: ## Run test coverage. ${MAKE} install @@ -170,8 +169,18 @@ test-coverage: ## Run test coverage. # Linters # =================================================================== -flake8: ## flake8 linter. - @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 +lint-py: ## Run Python (flake8) linter. + @git ls-files '*.py' | xargs $(PYTHON) -m flake8 + +lint-c: ## Run C linter. + @git ls-files '*.c' '*.h' | xargs $(PYTHON) scripts/internal/clinter.py + +lint: ## Run Python (flake8) and C linters. + ${MAKE} lint-py + ${MAKE} lint-c + +fix-lint: ## Attempt to automatically fix some Python lint issues. + @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 --exit-zero | $(PYTHON) scripts/internal/fix_flake8.py # =================================================================== # GIT @@ -197,7 +206,7 @@ wheel: ## Generate wheel. $(PYTHON) setup.py bdist_wheel win-download-wheels: ## Download wheels hosted on appveyor. - $(TEST_PREFIX) $(PYTHON) scripts/internal/download_exes.py --user giampaolo --project psutil + $(TEST_PREFIX) $(PYTHON) scripts/internal/win_download_wheels.py --user giampaolo --project psutil upload-src: ## Upload source tarball on https://pypi.org/project/psutil/ ${MAKE} sdist @@ -254,10 +263,12 @@ print-timeline: ## Print releases' timeline. @$(PYTHON) scripts/internal/print_timeline.py print-access-denied: ## Print AD exceptions - @$(PYTHON) scripts/internal/print_access_denied.py + ${MAKE} install + @$(TEST_PREFIX) $(PYTHON) scripts/internal/print_access_denied.py print-api-speed: ## Benchmark all API calls - @$(PYTHON) scripts/internal/print_api_speed.py $(ARGS) + ${MAKE} install + @$(TEST_PREFIX) $(PYTHON) scripts/internal/print_api_speed.py $(ARGS) # =================================================================== # Misc diff --git a/third_party/python/psutil/PKG-INFO b/third_party/python/psutil/PKG-INFO index d7c9d17ab3a2..bc21bff7fb25 100644 --- a/third_party/python/psutil/PKG-INFO +++ b/third_party/python/psutil/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: psutil -Version: 5.6.3 +Version: 5.7.0 Summary: Cross-platform lib for process and system monitoring in Python. Home-page: https://github.com/giampaolo/psutil Author: Giampaolo Rodola @@ -8,7 +8,7 @@ Author-email: g.rodola@gmail.com License: BSD Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| | |version| |py-versions| |packages| |license| - | |travis| |appveyor| |doc| |twitter| |tidelift| + | |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift| .. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg :target: https://pepy.tech/project/psutil @@ -30,14 +30,18 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| :target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade :alt: Code quality - .. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=linux%20/%20osx + .. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy :target: https://travis-ci.org/giampaolo/psutil :alt: Linux tests (Travis) - .. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=windows + .. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows :target: https://ci.appveyor.com/project/giampaolo/psutil :alt: Windows tests (Appveyor) + .. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD + :target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci + :alt: FreeBSD tests (Cirrus-Ci) + .. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage :target: https://coveralls.io/github/giampaolo/psutil?branch=master :alt: Test coverage (coverall.io) @@ -104,30 +108,36 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| - **Sun Solaris** - **AIX** - ...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy `__ is also known to work. + ...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 `__ is also known to work. - Professional support - ==================== + psutil for enterprise + ===================== .. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png - :width: 100 + :width: 150 :alt: Tidelift :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme .. list-table:: - :widths: 10 100 + :widths: 10 150 * - |tideliftlogo| - - Professional support for psutil is available as part of the - `Tidelift Subscription`_. - Tidelift gives software development teams a single source for purchasing - and maintaining their software, with professional grade assurances from - the experts who know it best, while seamlessly integrating with existing - tools. - By subscribing you will help me (`Giampaolo Rodola`_) support psutil - future development. Alternatively consider making a small `donation`_. + - The maintainer of psutil and thousands of other packages are working + with Tidelift to deliver commercial support and maintenance for the open + source dependencies you use to build your applications. Save time, + reduce risk, and improve code health, while paying the maintainers of + the exact dependencies you use. + `Learn more `__. - .. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support + psutil future development. Alternatively consider making a small + `donation`_. + + Security + ======== + + To report a security vulnerability, please use the `Tidelift security + contact`_. Tidelift will coordinate the fix and disclosure. Example applications ==================== @@ -170,9 +180,7 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| - Go: https://github.com/shirou/gopsutil - C: https://github.com/hamon-in/cpslib - - Node: https://github.com/christkv/node-psutil - Rust: https://github.com/borntyping/rust-psutil - - Ruby: https://github.com/spacewander/posixpsutil - Nim: https://github.com/johnscillieri/psutil-nim @@ -367,7 +375,7 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| pgids(real=1000, effective=1000, saved=1000) >>> >>> p.cpu_times() - pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1) + pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0) >>> p.cpu_percent(interval=1.0) 12.1 >>> p.cpu_affinity() @@ -393,8 +401,8 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543) >>> >>> 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/monit.py', fd=3, position=0, mode='r', flags=32768), + popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)] >>> >>> p.connections() [pconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'), @@ -436,6 +444,7 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| >>> p.resume() >>> >>> p.terminate() + >>> p.kill() >>> p.wait(timeout=3) 0 >>> @@ -455,7 +464,7 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| .. code-block:: python >>> import psutil - >>> for proc in psutil.process_iter(attrs=['pid', 'name']): + >>> for proc in psutil.process_iter(['pid', 'name']): ... print(proc.info) ... {'pid': 1, 'name': 'systemd'} @@ -515,6 +524,9 @@ Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality| .. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html .. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 + .. _Tidelift security contact: https://tidelift.com/security + .. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability Platform: Platform Independent @@ -526,9 +538,16 @@ Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 +Classifier: Operating System :: Microsoft :: Windows :: Windows 10 +Classifier: Operating System :: Microsoft :: Windows :: Windows 7 +Classifier: Operating System :: Microsoft :: Windows :: Windows 8 +Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1 +Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003 +Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008 +Classifier: Operating System :: Microsoft :: Windows :: Windows Vista Classifier: Operating System :: Microsoft Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX :: AIX Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: BSD :: NetBSD Classifier: Operating System :: POSIX :: BSD :: OpenBSD @@ -547,8 +566,10 @@ Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Benchmark +Classifier: Topic :: System :: Hardware :: Hardware Drivers Classifier: Topic :: System :: Hardware Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog Classifier: Topic :: System :: Networking :: Monitoring Classifier: Topic :: System :: Networking Classifier: Topic :: System :: Operating System diff --git a/third_party/python/psutil/README.rst b/third_party/python/psutil/README.rst index 4f1a71c34832..56b4017e7d95 100644 --- a/third_party/python/psutil/README.rst +++ b/third_party/python/psutil/README.rst @@ -1,6 +1,6 @@ | |downloads| |stars| |forks| |contributors| |coverage| |quality| | |version| |py-versions| |packages| |license| -| |travis| |appveyor| |doc| |twitter| |tidelift| +| |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift| .. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg :target: https://pepy.tech/project/psutil @@ -22,14 +22,18 @@ :target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade :alt: Code quality -.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=linux%20/%20osx +.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy :target: https://travis-ci.org/giampaolo/psutil :alt: Linux tests (Travis) -.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=windows +.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows :target: https://ci.appveyor.com/project/giampaolo/psutil :alt: Windows tests (Appveyor) +.. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD + :target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci + :alt: FreeBSD tests (Cirrus-Ci) + .. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage :target: https://coveralls.io/github/giampaolo/psutil?branch=master :alt: Test coverage (coverall.io) @@ -96,30 +100,36 @@ psutil currently supports the following platforms: - **Sun Solaris** - **AIX** -...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy `__ is also known to work. +...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 `__ is also known to work. -Professional support -==================== +psutil for enterprise +===================== .. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png - :width: 100 + :width: 150 :alt: Tidelift :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme .. list-table:: - :widths: 10 100 + :widths: 10 150 * - |tideliftlogo| - - Professional support for psutil is available as part of the - `Tidelift Subscription`_. - Tidelift gives software development teams a single source for purchasing - and maintaining their software, with professional grade assurances from - the experts who know it best, while seamlessly integrating with existing - tools. - By subscribing you will help me (`Giampaolo Rodola`_) support psutil - future development. Alternatively consider making a small `donation`_. + - The maintainer of psutil and thousands of other packages are working + with Tidelift to deliver commercial support and maintenance for the open + source dependencies you use to build your applications. Save time, + reduce risk, and improve code health, while paying the maintainers of + the exact dependencies you use. + `Learn more `__. -.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support + psutil future development. Alternatively consider making a small + `donation`_. + +Security +======== + +To report a security vulnerability, please use the `Tidelift security +contact`_. Tidelift will coordinate the fix and disclosure. Example applications ==================== @@ -162,9 +172,7 @@ Portings - Go: https://github.com/shirou/gopsutil - C: https://github.com/hamon-in/cpslib -- Node: https://github.com/christkv/node-psutil - Rust: https://github.com/borntyping/rust-psutil -- Ruby: https://github.com/spacewander/posixpsutil - Nim: https://github.com/johnscillieri/psutil-nim @@ -359,7 +367,7 @@ Process management pgids(real=1000, effective=1000, saved=1000) >>> >>> p.cpu_times() - pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1) + pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0) >>> p.cpu_percent(interval=1.0) 12.1 >>> p.cpu_affinity() @@ -385,8 +393,8 @@ Process management pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543) >>> >>> 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/monit.py', fd=3, position=0, mode='r', flags=32768), + popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)] >>> >>> p.connections() [pconn(fd=115, family=, type=, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'), @@ -428,6 +436,7 @@ Process management >>> p.resume() >>> >>> p.terminate() + >>> p.kill() >>> p.wait(timeout=3) 0 >>> @@ -447,7 +456,7 @@ Further process APIs .. code-block:: python >>> import psutil - >>> for proc in psutil.process_iter(attrs=['pid', 'name']): + >>> for proc in psutil.process_iter(['pid', 'name']): ... print(proc.info) ... {'pid': 1, 'name': 'systemd'} @@ -507,3 +516,6 @@ Windows services .. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html .. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 +.. _Tidelift security contact: https://tidelift.com/security +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme + diff --git a/third_party/python/psutil/docs/DEVGUIDE.rst b/third_party/python/psutil/docs/DEVGUIDE.rst index d9eb6d7ab02b..170a40a9d56b 100644 --- a/third_party/python/psutil/docs/DEVGUIDE.rst +++ b/third_party/python/psutil/docs/DEVGUIDE.rst @@ -1,24 +1,13 @@ -Setup and running tests -======================= +Build, setup and running tests +=============================== -If you plan on hacking on psutil this is what you're supposed to do first: - -- clone the GIT repository: - -.. code-block:: bash - - $ git clone git@github.com:giampaolo/psutil.git - -- install test deps and GIT hooks: +Make sure to `install `__ +a C compiler first, then: .. code-block:: bash + git clone git@github.com:giampaolo/psutil.git make setup-dev-env - -- run tests: - -.. code-block:: bash - make test - bear in mind that ``make``(see `Makefile`_) is the designated tool to run @@ -47,7 +36,7 @@ Some useful make commands: make test # run unit tests make test-memleaks # run memory leak tests make test-coverage # run test coverage - make flake8 # run PEP8 linter + make lint # run Python (PEP8) and C linters There are some differences between ``make`` on UNIX and Windows. For instance, to run a specific Python version. On UNIX: @@ -60,13 +49,7 @@ On Windows: .. code-block:: bat - set PYTHON=C:\python35\python.exe && make test - -...or: - -.. code-block:: bat - - make -p 35 test + make -p C:\python35\python.exe test If you want to modify psutil and run a script on the fly which uses it do (on UNIX): @@ -121,6 +104,7 @@ Make a pull request - commit your changes: ``git commit -am 'add some feature'`` - push to the branch: ``git push origin new-feature`` - create a new pull request by via github web interface +- remember to update `HISTORY.rst`_ and `CREDITS`_ files. Continuous integration ====================== @@ -141,14 +125,18 @@ Both services run psutil test suite against all supported python version (2.6 - 3.6). Two icons in the home page (README) always show the build status: -.. image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux%20/%20macOS +.. image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy :target: https://travis-ci.org/giampaolo/psutil - :alt: Linux and macOS tests (Travis) + :alt: Linux, macOS and PyPy3 tests (Travis) .. image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows :target: https://ci.appveyor.com/project/giampaolo/psutil :alt: Windows tests (Appveyor) +.. image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD + :target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci + :alt: FreeBSD tests (Cirrus-CI) + BSD, AIX and Solaris are currently tested manually. Test coverage @@ -200,3 +188,5 @@ These are notes for myself (Giampaolo): .. _`RsT syntax`: http://docutils.sourceforge.net/docs/user/rst/quickref.htm .. _`sphinx`: http://sphinx-doc.org .. _`Travis`: https://travis-ci.org/giampaolo/psuti +.. _`HISTORY.rst`: https://github.com/giampaolo/psutil/blob/master/HISTORY.rst +.. _`CREDITS`: https://github.com/giampaolo/psutil/blob/master/CREDITS diff --git a/third_party/python/psutil/docs/DEVNOTES b/third_party/python/psutil/docs/DEVNOTES index abd2e3688dca..7fe14f7d0b66 100644 --- a/third_party/python/psutil/docs/DEVNOTES +++ b/third_party/python/psutil/docs/DEVNOTES @@ -158,10 +158,11 @@ BUGFIXES - #600: windows / open_files(): support network file handles. -REJECTED -======== +REJECTED IDEAS +============== - #550: threads per core +- #1667: process_iter(new_only=True) INCONSISTENCIES =============== diff --git a/third_party/python/psutil/docs/Makefile b/third_party/python/psutil/docs/Makefile index 0c4bdf48a2f1..cca5435fa20b 100644 --- a/third_party/python/psutil/docs/Makefile +++ b/third_party/python/psutil/docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -PYTHON = python +PYTHON = python3 SPHINXOPTS = SPHINXBUILD = $(PYTHON) -m sphinx PAPER = @@ -15,6 +15,9 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +DEPS = sphinx + + .PHONY: help help: @echo "Please use \`make ' where is one of" @@ -224,3 +227,7 @@ dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." + +.PHONY: setup-dev-env +setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them). + $(PYTHON) -m pip install --user --upgrade --trusted-host files.pythonhosted.org $(DEPS) diff --git a/third_party/python/psutil/docs/_static/css/custom.css b/third_party/python/psutil/docs/_static/css/custom.css index b76f442af83e..c5c201e4f13a 100644 --- a/third_party/python/psutil/docs/_static/css/custom.css +++ b/third_party/python/psutil/docs/_static/css/custom.css @@ -15,10 +15,30 @@ border-right:10px !important; } -.local-toc li ul li{ +.local-toc li ul li { padding-left: 20px !important; } +.rst-content ul p { + margin-bottom: 0px !important; +} + +.document td { + padding-bottom: 0px !important; +} + +.document th { + padding-top: 0px !important; + padding-bottom: 0px !important; +} + +.document th p { + margin-bottom: 0px !important; +} + +.document th p { +} + .function .descclassname { font-weight: normal !important; } diff --git a/third_party/python/psutil/docs/index.rst b/third_party/python/psutil/docs/index.rst index 7a2348726c35..c6b9a8cca1af 100644 --- a/third_party/python/psutil/docs/index.rst +++ b/third_party/python/psutil/docs/index.rst @@ -13,7 +13,7 @@ Quick links - `Blog `__ - `Forum `__ - `Download `__ -- `Development guide `_ +- `Development guide `_ - `What's new `__ About @@ -42,21 +42,45 @@ Supported Python versions are **2.6**, **2.7** and **3.4+**. The psutil documentation you're reading is distributed as a single HTML page. + +Professional support +-------------------- + +.. image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png + :width: 80px + :align: left + +Professional support for psutil is available as part of the `Tidelift Subscription`_. +Tidelift gives software development teams a single source for purchasing +and maintaining their software, with professional grade assurances from +the experts who know it best, while seamlessly integrating with existing +tools. +By subscribing you will help me (`Giampaolo Rodola`_) support psutil +future development. Alternatively consider making a small `donation`_. +To report a security vulnerability, please use the `Tidelift security +contact`_. Tidelift will coordinate the fix and disclosure. + Install ------- -The easiest way to install psutil is via ``pip``:: +Linux Ubuntu / Debian:: - pip install psutil + sudo apt-get install gcc python3-dev + sudo pip3 install psutil -On UNIX this requires a C compiler (e.g. gcc) installed. On Windows pip will -automatically retrieve a pre-compiled wheel version from -`PyPI repository `__. -Alternatively, see more detailed +Linux Redhat:: + + sudo yum install gcc python3-devel + sudo pip3 install psutil + +Windows:: + + pip3 install psutil + +For other platforms see more detailed `install `_ instructions. - System related functions ======================== @@ -78,7 +102,8 @@ CPU - **nice** *(UNIX)*: time spent by niced (prioritized) processes executing in user mode; on Linux this also includes **guest_nice** time - - **iowait** *(Linux)*: time spent waiting for I/O to complete + - **iowait** *(Linux)*: time spent waiting for I/O to complete. This is *not* + accounted in **idle** time counter. - **irq** *(Linux, BSD)*: time spent for servicing hardware interrupts - **softirq** *(Linux)*: time spent for servicing software interrupts - **steal** *(Linux 2.6.11+)*: time spent by other operating systems running @@ -161,8 +186,10 @@ CPU Return the number of logical CPUs in the system (same as `os.cpu_count`_ in Python 3.4) or ``None`` if undetermined. - If *logical* is ``False`` return the number of physical cores only (hyper - thread CPUs are excluded) or ``None`` if undetermined. + *logical* cores means the number of physical cores multiplied by the number + of threads that can run on each core (this is known as Hyper Threading). + If *logical* is ``False`` return the number of physical cores only (Hyper + Thread CPUs are excluded) or ``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: @@ -241,19 +268,27 @@ CPU .. function:: getloadavg() Return the average system load over the last 1, 5 and 15 minutes as a tuple. - The load represents how many processes are waiting to be run by the - operating system. - On UNIX systems this relies on `os.getloadavg`_. On Windows this is - emulated by using a Windows API that spawns a thread which updates the - average every 5 seconds, mimicking the UNIX behavior. Thus, the first time - this is called and for the next 5 seconds it will return a meaningless - ``(0.0, 0.0, 0.0)`` tuple. Example: + The load represents the processes which are in a runnable state, either + using the CPU or waiting to use the CPU (e.g. waiting for disk I/O). + On UNIX systems this relies on `os.getloadavg`_. On Windows this is emulated + by using a Windows API that spawns a thread which keeps running in + background and updates the load average every 5 seconds, mimicking the UNIX + behavior. Thus, the first time this is called and for the next 5 seconds + it will return a meaningless ``(0.0, 0.0, 0.0)`` tuple. + The numbers returned only make sense if related to the number of CPU cores + installed on the system. So, for instance, `3.14` on a system with 10 CPU + cores means that the system load was 31.4% percent over the last N minutes. .. code-block:: python >>> import psutil >>> psutil.getloadavg() (3.14, 3.89, 4.67) + >>> psutil.cpu_count() + 10 + >>> # percentage representation + >>> [x / psutil.cpu_count() * 100 for x in psutil.getloadavg()] + [31.4, 38.9, 46.7] Availability: Unix, Windows @@ -267,7 +302,7 @@ Memory Return statistics about system memory usage as a named tuple including the following fields, expressed in bytes. Main metrics: - - **total**: total physical memory. + - **total**: total physical memory (exclusive swap). - **available**: the memory that can be given instantly to processes without the system going into swap. This is calculated by summing different memory values depending on the @@ -353,7 +388,7 @@ Disks mount point and filesystem type, similarly to "df" command on UNIX. If *all* parameter is ``False`` it tries to distinguish and return physical devices only (e.g. hard disks, cd-rom drives, USB keys) and ignore all others - (e.g. memory partitions such as /dev/shm). + (e.g. pseudo, memory, duplicate, inaccessible filesystems). Note that this may not be fully reliable on all systems (e.g. on BSD this parameter is ignored). Named tuple's **fstype** field is a string which varies depending on the @@ -513,7 +548,8 @@ Network to obtain a usable socket object. On Windows and SunOS this is always set to ``-1``. - **family**: the address family, either `AF_INET`_, `AF_INET6`_ or `AF_UNIX`_. - - **type**: the address type, either `SOCK_STREAM`_ or `SOCK_DGRAM`_. + - **type**: the address type, either `SOCK_STREAM`_, `SOCK_DGRAM`_ or + `SOCK_SEQPACKET`_. - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path`` in case of AF_UNIX sockets. For UNIX sockets see notes below. - **raddr**: the remote address as a ``(ip, port)`` named tuple or an @@ -825,38 +861,24 @@ Functions Return an iterator yielding a :class:`Process` class instance for all running processes on the local machine. - Every instance is only created once and then cached into an internal table - which is updated every time an element is yielded. - Cached :class:`Process` instances are checked for identity so that you're - safe in case a PID has been reused by another process, in which case the - cached instance is updated. - This is preferred over :func:`psutil.pids()` for iterating over processes. - Sorting order in which processes are returned is based on their PID. + This should be preferred over :func:`psutil.pids()` to iterate over processes + as it's safe from race condition. + + Every :class:`Process` instance is only created once, and then cached for the + next time :func:`psutil.process_iter()` is called (if PID is still alive). + Also it makes sure process PIDs are not reused. + *attrs* and *ad_value* have the same meaning as in :meth:`Process.as_dict()`. - If *attrs* is specified :meth:`Process.as_dict()` is called internally and - the resulting dict is stored as a ``info`` attribute which is attached to the - returned :class:`Process` instances. + If *attrs* is specified :meth:`Process.as_dict()` result will be stored as a + ``info`` attribute attached to the returned :class:`Process` instances. If *attrs* is an empty list it will retrieve all process info (slow). - Example usage:: + + Sorting order in which processes are returned is based on their PID. + + Example:: >>> import psutil - >>> for proc in psutil.process_iter(): - ... try: - ... pinfo = proc.as_dict(attrs=['pid', 'name', 'username']) - ... except psutil.NoSuchProcess: - ... pass - ... else: - ... print(pinfo) - ... - {'name': 'systemd', 'pid': 1, 'username': 'root'} - {'name': 'kthreadd', 'pid': 2, 'username': 'root'} - {'name': 'ksoftirqd/0', 'pid': 3, 'username': 'root'} - ... - - More compact version using *attrs* parameter:: - - >>> import psutil - >>> for proc in psutil.process_iter(attrs=['pid', 'name', 'username']): + >>> for proc in psutil.process_iter(['pid', 'name', 'username']): ... print(proc.info) ... {'name': 'systemd', 'pid': 1, 'username': 'root'} @@ -864,27 +886,16 @@ Functions {'name': 'ksoftirqd/0', 'pid': 3, 'username': 'root'} ... - Example of a dict comprehensions to create a ``{pid: info, ...}`` data - structure:: + A dict comprehensions to create a ``{pid: info, ...}`` data structure:: >>> import psutil - >>> procs = {p.pid: p.info for p in psutil.process_iter(attrs=['name', 'username'])} + >>> procs = {p.pid: p.info for p in psutil.process_iter(['name', 'username'])} >>> procs {1: {'name': 'systemd', 'username': 'root'}, 2: {'name': 'kthreadd', 'username': 'root'}, 3: {'name': 'ksoftirqd/0', 'username': 'root'}, ...} - Example showing how to filter processes by name:: - - >>> import psutil - >>> [p.info for p in psutil.process_iter(attrs=['pid', 'name']) if 'python' in p.info['name']] - [{'name': 'python3', 'pid': 21947}, - {'name': 'python', 'pid': 23835}] - - See also `process filtering <#filtering-and-sorting-processes>`__ section for - more examples. - .. versionchanged:: 5.3.0 added "attrs" and "ad_value" parameters. @@ -898,11 +909,11 @@ Functions Convenience function which waits for a list of :class:`Process` instances to terminate. Return a ``(gone, alive)`` tuple indicating which processes are gone and which ones are still alive. The *gone* ones will have a new - *returncode* attribute indicating process exit status (will be ``None`` for - processes which are not our children). + *returncode* attribute indicating process exit status as returned by + :meth:`Process.wait`. ``callback`` is a function which gets called when one of the processes being waited on is terminated and a :class:`Process` instance is passed as callback - argument). + argument (the instance will also have a *returncode* attribute set). This function will return as soon as all processes terminate or when *timeout* (seconds) occurs. Differently from :meth:`Process.wait` it will not raise @@ -1069,9 +1080,9 @@ Process class +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+ | :meth:`gids` | | :meth:`name` | :meth:`num_ctx_switches` | :meth:`terminal` | :meth:`terminal` | +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+ - | :meth:`num_ctx_switches` | | :meth:`ppid` | :meth:`ppid` | | | + | :meth:`num_ctx_switches` | :meth:`exe` | :meth:`ppid` | :meth:`ppid` | | | +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+ - | :meth:`num_threads` | | :meth:`status` | :meth:`status` | :meth:`gids` | :meth:`gids` | + | :meth:`num_threads` | :meth:`name` | :meth:`status` | :meth:`status` | :meth:`gids` | :meth:`gids` | +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+ | :meth:`uids` | | :meth:`terminal` | :meth:`terminal` | :meth:`uids` | :meth:`uids` | +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+ @@ -1205,6 +1216,8 @@ Process class The process current working directory as an absolute path. + .. versionchanged:: 5.6.4 added support for NetBSD + .. method:: username() The name of the user that owns the process. On UNIX this is calculated by @@ -1287,13 +1300,15 @@ Process class Here's an example on how to set the highest I/O priority depending on what platform you're on:: - import psutil - p = psutil.Process() - if psutil.LINUX - p.ionice(psutil.IOPRIO_CLASS_RT, value=7) - else: # Windows - p.ionice(psutil.IOPRIO_HIGH) - p.ionice() # get + >>> import psutil + >>> p = psutil.Process() + >>> if psutil.LINUX: + ... p.ionice(psutil.IOPRIO_CLASS_RT, value=7) + ... else: + ... p.ionice(psutil.IOPRIO_HIGH) + ... + >>> p.ionice() # get + pionice(ioclass=, value=7) Availability: Linux, Windows Vista+ @@ -1401,16 +1416,33 @@ Process class .. method:: cpu_times() - Return a `(user, system, children_user, children_system)` named tuple - representing the accumulated process time, in seconds (see - `explanation `__). - On Windows and macOS only *user* and *system* are filled, the others are - set to ``0``. + Return a named tuple representing the accumulated process times, in seconds + (see `explanation `__). This is similar to `os.times`_ but can be used for any process PID. + - **user**: time spent in user mode. + - **system**: time spent in kernel mode. + - **children_user**: user time of all child processes (always ``0`` on + Windows and macOS). + - **system_user**: user time of all child processes (always ``0`` on + Windows and macOS). + - **iowait**: (Linux) time spent waiting for blocking I/O to complete. + This value is excluded from `user` and `system` times count (because the + CPU is not doing any work). + + >>> import psutil + >>> p = psutil.Process() + >>> p.cpu_times() + pcputimes(user=0.03, system=0.67, children_user=0.0, children_system=0.0, iowait=0.08) + >>> sum(p.cpu_times()[:2]) # cumulative, excluding children and iowait + 0.70 + .. versionchanged:: 4.1.0 return two extra fields: *children_user* and *children_system*. + .. versionchanged:: + 5.6.4 added *iowait* on Linux. + .. method:: cpu_percent(interval=None) Return a float representing the process CPU utilization as a percentage @@ -1752,13 +1784,12 @@ Process class on Windows this method is not reliable due to some limitations of the underlying Windows API which may hang when retrieving certain file handles. - In order to work around that psutil spawns a thread for each handle and - kills it if it's not responding after 100ms. + In order to work around that psutil spawns a thread to determine the file + handle name and kills it if it's not responding after 100ms. That implies that this method on Windows is not guaranteed to enumerate all regular file handles (see `issue 597 `_). - Also, it will only list files living in the C:\\ drive (see - `issue 1020 `_). + Tools like ProcessHacker has the same limitation. .. warning:: on BSD this method can return files with a null path ("") due to a @@ -1782,7 +1813,8 @@ Process class always set to ``-1``. - **family**: the address family, either `AF_INET`_, `AF_INET6`_ or `AF_UNIX`_. - - **type**: the address type, either `SOCK_STREAM`_ or `SOCK_DGRAM`_. + - **type**: the address type, either `SOCK_STREAM`_, `SOCK_DGRAM`_ or + `SOCK_SEQPACKET`_. . - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path`` in case of AF_UNIX sockets. For UNIX sockets see notes below. - **raddr**: the remote address as a ``(ip, port)`` named tuple or an @@ -2189,7 +2221,7 @@ Process priority constants .. data:: IOPRIO_NORMAL .. data:: IOPRIO_HIGH - A set of integers representing the I/O priority of a process on Linux. + A set of integers representing the I/O priority of a process on Windows. They can be used in conjunction with :meth:`psutil.Process.ionice()` to get or set process I/O priority. @@ -2291,46 +2323,6 @@ Hardware constants >>> if psutil.version_info >= (4, 5): ... pass ----- - -Unicode -======= - -Starting from version 5.3.0 psutil adds unicode support, see `issue #1040`_. -The notes below apply to *any* API returning a string such as -:meth:`Process.exe` or :meth:`Process.cwd`, including non-filesystem related -methods such as :meth:`Process.username` or :meth:`WindowsService.description`: - -* all strings are encoded by using the OS filesystem encoding - (``sys.getfilesystemencoding()``) which varies depending on the platform - (e.g. "UTF-8" on macOS, "mbcs" on Win) -* no API call is supposed to crash with ``UnicodeDecodeError`` -* instead, in case of badly encoded data returned by the OS, the following error handlers are used to replace the corrupted characters in the string: - * Python 3: ``sys.getfilesystemencodeerrors()`` (PY 3.6+) or - ``"surrogatescape"`` on POSIX and ``"replace"`` on Windows - * Python 2: ``"replace"`` -* on Python 2 all APIs return bytes (``str`` type), never ``unicode`` -* on Python 2, you can go back to ``unicode`` by doing: - -.. code-block:: python - - >>> unicode(p.exe(), sys.getdefaultencoding(), errors="replace") - -Example which filters processes with a funky name working with both Python 2 -and 3:: - - # -*- coding: utf-8 -*- - import psutil, sys - - PY3 = sys.version_info[0] == 2 - LOOKFOR = u"ƒőő" - for proc in psutil.process_iter(attrs=['name']): - name = proc.info['name'] - if not PY3: - name = unicode(name, sys.getdefaultencoding(), errors="replace") - if LOOKFOR == name: - print("process %s found" % p) - Recipes ======= @@ -2346,7 +2338,7 @@ Check string against :meth:`Process.name()`: def find_procs_by_name(name): "Return a list of processes matching 'name'." ls = [] - for p in psutil.process_iter(attrs=['name']): + for p in psutil.process_iter(['name']): if p.info['name'] == name: ls.append(p) return ls @@ -2362,7 +2354,7 @@ A bit more advanced, check string against :meth:`Process.name()`, def find_procs_by_name(name): "Return a list of processes matching 'name'." ls = [] - for p in psutil.process_iter(attrs=["name", "exe", "cmdline"]): + for p in psutil.process_iter(["name", "exe", "cmdline"]): if name == p.info['name'] or \ p.info['exe'] and os.path.basename(p.info['exe']) == name or \ p.info['cmdline'] and p.info['cmdline'][0] == name: @@ -2396,83 +2388,34 @@ Kill process tree callback=on_terminate) return (gone, alive) -Terminate my children ---------------------- - -This may be useful in unit tests whenever sub-processes are started. -This will help ensure that no extra children (zombies) stick around to hog -resources. - -:: - - import psutil - - def reap_children(timeout=3): - "Tries hard to terminate and ultimately kill all the children of this process." - def on_terminate(proc): - print("process {} terminated with exit code {}".format(proc, proc.returncode)) - - procs = psutil.Process().children() - # send SIGTERM - for p in procs: - try: - p.terminate() - except psutil.NoSuchProcess: - pass - gone, alive = psutil.wait_procs(procs, timeout=timeout, callback=on_terminate) - if alive: - # send SIGKILL - for p in alive: - print("process {} survived SIGTERM; trying SIGKILL" % p) - try: - p.kill() - except psutil.NoSuchProcess: - pass - gone, alive = psutil.wait_procs(alive, timeout=timeout, callback=on_terminate) - if alive: - # give up - for p in alive: - print("process {} survived SIGKILL; giving up" % p) - Filtering and sorting processes ------------------------------- -This is a collection of one-liners showing how to use :func:`process_iter()` in -order to filter for processes and sort them. - -Setup:: +A collection of code samples showing how to use :func:`process_iter()` to filter processes and sort them. Setup:: >>> import psutil >>> from pprint import pprint as pp -Processes having "python" in their name:: - - >>> pp([p.info for p in psutil.process_iter(attrs=['pid', 'name']) if 'python' in p.info['name']]) - [{'name': 'python3', 'pid': 21947}, - {'name': 'python', 'pid': 23835}] - Processes owned by user:: >>> import getpass - >>> pp([(p.pid, p.info['name']) for p in psutil.process_iter(attrs=['name', 'username']) if p.info['username'] == getpass.getuser()]) + >>> pp([(p.pid, p.info['name']) for p in psutil.process_iter(['name', 'username']) if p.info['username'] == getpass.getuser()]) (16832, 'bash'), (19772, 'ssh'), (20492, 'python')] Processes actively running:: - >>> pp([(p.pid, p.info) for p in psutil.process_iter(attrs=['name', 'status']) if p.info['status'] == psutil.STATUS_RUNNING]) + >>> pp([(p.pid, p.info) for p in psutil.process_iter(['name', 'status']) if p.info['status'] == psutil.STATUS_RUNNING]) [(1150, {'name': 'Xorg', 'status': 'running'}), (1776, {'name': 'unity-panel-service', 'status': 'running'}), (20492, {'name': 'python', 'status': 'running'})] Processes using log files:: - >>> import os - >>> import psutil - >>> for p in psutil.process_iter(attrs=['name', 'open_files']): + >>> for p in psutil.process_iter(['name', 'open_files']): ... for file in p.info['open_files'] or []: - ... if os.path.splitext(file.path)[1] == '.log': + ... if file.path.endswith('.log'): ... print("%-5s %-10s %s" % (p.pid, p.info['name'][:10], file.path)) ... 1510 upstart /home/giampaolo/.cache/upstart/unity-settings-daemon.log @@ -2481,39 +2424,18 @@ Processes using log files:: Processes consuming more than 500M of memory:: - >>> pp([(p.pid, p.info['name'], p.info['memory_info'].rss) for p in psutil.process_iter(attrs=['name', 'memory_info']) if p.info['memory_info'].rss > 500 * 1024 * 1024]) + >>> pp([(p.pid, p.info['name'], p.info['memory_info'].rss) for p in psutil.process_iter(['name', 'memory_info']) if p.info['memory_info'].rss > 500 * 1024 * 1024]) [(2650, 'chrome', 532324352), (3038, 'chrome', 1120088064), (21915, 'sublime_text', 615407616)] -Top 3 most memory consuming processes:: - - >>> pp([(p.pid, p.info) for p in sorted(psutil.process_iter(attrs=['name', 'memory_percent']), key=lambda p: p.info['memory_percent'])][-3:]) - [(21915, {'memory_percent': 3.6815453247662737, 'name': 'sublime_text'}), - (3038, {'memory_percent': 6.732935429979187, 'name': 'chrome'}), - (3249, {'memory_percent': 8.994554843376399, 'name': 'chrome'})] - Top 3 processes which consumed the most CPU time:: - >>> pp([(p.pid, p.info['name'], sum(p.info['cpu_times'])) for p in sorted(psutil.process_iter(attrs=['name', 'cpu_times']), key=lambda p: sum(p.info['cpu_times'][:2]))][-3:]) + >>> pp([(p.pid, p.info['name'], sum(p.info['cpu_times'])) for p in sorted(psutil.process_iter(['name', 'cpu_times']), key=lambda p: sum(p.info['cpu_times'][:2]))][-3:]) [(2721, 'chrome', 10219.73), (1150, 'Xorg', 11116.989999999998), (2650, 'chrome', 18451.97)] -Top 3 processes which caused the most I/O:: - - >>> pp([(p.pid, p.info['name']) for p in sorted(psutil.process_iter(attrs=['name', 'io_counters']), key=lambda p: p.info['io_counters'] and p.info['io_counters'][:2])][-3:]) - [(21915, 'sublime_text'), - (1871, 'pulseaudio'), - (1510, 'upstart')] - -Top 3 processes opening more file descriptors:: - - >>> pp([(p.pid, p.info) for p in sorted(psutil.process_iter(attrs=['name', 'num_fds']), key=lambda p: p.info['num_fds'])][-3:]) - [(21915, {'name': 'sublime_text', 'num_fds': 105}), - (2721, {'name': 'chrome', 'num_fds': 185}), - (2650, {'name': 'chrome', 'num_fds': 354})] - Bytes conversion ---------------- @@ -2546,28 +2468,6 @@ Bytes conversion 100399730688 93.5G -Supported platforms -=================== - -These are the platforms I develop and test on: - -* Linux Ubuntu 16.04 -* MacOS 10.11 El Captain -* Windows 10 -* Solaris 10 -* FreeBSD 11 -* OpenBSD 6.4 -* NetBSD 8.0 -* AIX 6.1 TL8 (maintainer `Arnon Yaari `__) - -Earlier versions are supposed to work but are not tested. -For Linux, Windows and MacOS we have continuos integration. Other platforms -are tested manually from time to time. -Oldest supported Windows version is Windows XP, which can be compiled from -sources. Latest wheel supporting Windows XP is -`psutil 2.1.3 `__. -Supported Python versions are 3.4+, 2.7 and 2.6. - FAQs ==== @@ -2576,7 +2476,7 @@ FAQs especially on macOS (see `issue #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 + On Unix you may run 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 @@ -2585,25 +2485,58 @@ FAQs Running tests ============= -There are two ways of running tests. If psutil is already installed use:: +:: - $ python -m psutil.tests - -You can use this method as a quick way to make sure psutil fully works on your -platform. If you have a copy of the source code you can also use:: - - $ make test + $ python3 -m psutil.tests Development guide ================= -If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug) +If you want to hacking on psutil (e.g. want to add a new feature or fix a bug) take a look at the `development guide`_. +Platforms support history +========================= + +* psutil 5.7.0 (2020-02): drop Windows XP & Server 2003 support +* psutil 5.7.0 (2020-02): **PyPy** on Windows +* psutil 5.4.0 (2017-11): **AIX** +* psutil 3.4.1 (2016-01): **NetBSD** +* psutil 3.3.0 (2015-11): **OpenBSD** +* psutil 1.0.0 (2013-07): **Solaris** +* psutil 0.1.1 (2009-03): **FreeBSD** +* psutil 0.1.0 (2009-01): **Linux, Windows, macOS** + +Supported Python versions are 2.6, 2.7, 3.4+ and PyPy3. + Timeline ======== -- 2019-0426: +- 2020-02-18: + `5.7.0 `__ - + `what's new `__ - + `diff `__ +- 2019-11-26: + `5.6.7 `__ - + `what's new `__ - + `diff `__ +- 2019-11-25: + `5.6.6 `__ - + `what's new `__ - + `diff `__ +- 2019-11-06: + `5.6.5 `__ - + `what's new `__ - + `diff `__ +- 2019-11-04: + `5.6.4 `__ - + `what's new `__ - + `diff `__ +- 2019-06-11: + `5.6.3 `__ - + `what's new `__ - + `diff `__ +- 2019-04-26: `5.6.2 `__ - `what's new `__ - `diff `__ @@ -2902,13 +2835,15 @@ Timeline .. _`BPO-6973`: https://bugs.python.org/issue6973 .. _`CPU affinity`: https://www.linuxjournal.com/article/6799?page=0,0 .. _`cpu_distribution.py`: https://github.com/giampaolo/psutil/blob/master/scripts/cpu_distribution.py -.. _`development guide`: https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst +.. _`development guide`: https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst .. _`disk_usage.py`: https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py +.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8 .. _`enums`: https://docs.python.org/3/library/enum.html#module-enum .. _`fans.py`: https://github.com/giampaolo/psutil/blob/master/scripts/fans.py .. _`GetDriveType`: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getdrivetypea .. _`getfsstat`: http://www.manpagez.com/man/2/getfsstat/ .. _`GetPriorityClass`: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass +.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html .. _`hash`: https://docs.python.org/3/library/functions.html#hash .. _`ifconfig.py`: https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py .. _`ioprio_get`: https://linux.die.net/man/2/ioprio_get @@ -2919,7 +2854,7 @@ Timeline .. _`issue #883`: https://github.com/giampaolo/psutil/issues/883 .. _`man prlimit`: https://linux.die.net/man/2/prlimit .. _`meminfo.py`: https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py -.. _`netstat.py`: https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py. +.. _`netstat.py`: https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py .. _`nettop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py .. _`open`: https://docs.python.org/3/library/functions.html#open .. _`os.cpu_count`: https://docs.python.org/3/library/os.html#os.cpu_count @@ -2944,8 +2879,11 @@ Timeline .. _`shutil.disk_usage`: https://docs.python.org/3/library/shutil.html#shutil.disk_usage. .. _`signal module`: https://docs.python.org//library/signal.html .. _`SOCK_DGRAM`: https://docs.python.org/3/library/socket.html#socket.SOCK_DGRAM +.. _`SOCK_SEQPACKET`: https://docs.python.org/3/library/socket.html#socket.SOCK_SEQPACKET .. _`SOCK_STREAM`: https://docs.python.org/3/library/socket.html#socket.SOCK_STREAM .. _`socket.fromfd`: https://docs.python.org/3/library/socket.html#socket.fromfd .. _`subprocess.Popen`: https://docs.python.org/3/library/subprocess.html#subprocess.Popen .. _`temperatures.py`: https://github.com/giampaolo/psutil/blob/master/scripts/temperatures.py .. _`TerminateProcess`: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-terminateprocess +.. _Tidelift security contact: https://tidelift.com/security +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme diff --git a/third_party/python/psutil/make.bat b/third_party/python/psutil/make.bat index d47eaecc78ab..8e60811ccfac 100644 --- a/third_party/python/psutil/make.bat +++ b/third_party/python/psutil/make.bat @@ -20,15 +20,15 @@ rem set PYTHON=C:\Python34\python.exe & set TSCRIPT=foo.py & make.bat test rem ========================================================================== if "%PYTHON%" == "" ( - if exist "C:\Python37\python.exe" ( - set PYTHON=C:\Python37\python.exe + if exist "C:\Python38-64\python.exe" ( + set PYTHON=C:\Python38-64\python.exe ) else ( set PYTHON=C:\Python27\python.exe ) ) if "%TSCRIPT%" == "" ( - set TSCRIPT=psutil\tests\__main__.py + set TSCRIPT=psutil\tests\runner.py ) rem Needed to locate the .pypirc file and upload exes on PyPI. diff --git a/third_party/python/psutil/psutil/__init__.py b/third_party/python/psutil/psutil/__init__.py index b400ec852384..22bb46f3f9bd 100644 --- a/third_party/python/psutil/psutil/__init__.py +++ b/third_party/python/psutil/psutil/__init__.py @@ -25,7 +25,6 @@ from __future__ import division import collections import contextlib import datetime -import errno import functools import os import signal @@ -39,11 +38,18 @@ except ImportError: pwd = None from . import _common +from ._common import AccessDenied from ._common import deprecated_method +from ._common import Error from ._common import memoize from ._common import memoize_when_activated +from ._common import NoSuchProcess +from ._common import TimeoutExpired from ._common import wrap_numbers as _wrap_numbers +from ._common import ZombieProcess from ._compat import long +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 as _PY3 from ._common import STATUS_DEAD @@ -221,7 +227,7 @@ __all__ = [ __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "5.6.3" +__version__ = "5.7.0" version_info = tuple([int(num) for num in __version__.split('.')]) _timer = getattr(time, 'monotonic', time.time) @@ -253,112 +259,6 @@ if (int(__version__.replace('.', '')) != raise ImportError(msg) -# ===================================================================== -# --- Exceptions -# ===================================================================== - - -class Error(Exception): - """Base exception class. All other psutil exceptions inherit - from this one. - """ - - def __init__(self, msg=""): - Exception.__init__(self, msg) - self.msg = msg - - def __repr__(self): - ret = "psutil.%s %s" % (self.__class__.__name__, self.msg) - return ret.strip() - - __str__ = __repr__ - - -class NoSuchProcess(Error): - """Exception raised when a process with a certain PID doesn't - or no longer exists. - """ - - def __init__(self, pid, name=None, msg=None): - Error.__init__(self, msg) - self.pid = pid - self.name = name - self.msg = msg - if msg is None: - if name: - details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) - else: - details = "(pid=%s)" % self.pid - self.msg = "process no longer exists " + details - - -class ZombieProcess(NoSuchProcess): - """Exception raised when querying a zombie process. This is - raised on macOS, BSD and Solaris only, and not always: depending - on the query the OS may be able to succeed anyway. - On Linux all zombie processes are querable (hence this is never - raised). Windows doesn't have zombie processes. - """ - - def __init__(self, pid, name=None, ppid=None, msg=None): - NoSuchProcess.__init__(self, msg) - self.pid = pid - self.ppid = ppid - self.name = name - self.msg = msg - if msg is None: - args = ["pid=%s" % pid] - if name: - args.append("name=%s" % repr(self.name)) - if ppid: - args.append("ppid=%s" % self.ppid) - details = "(%s)" % ", ".join(args) - self.msg = "process still exists but it's a zombie " + details - - -class AccessDenied(Error): - """Exception raised when permission to perform an action is denied.""" - - def __init__(self, pid=None, name=None, msg=None): - Error.__init__(self, msg) - self.pid = pid - self.name = name - self.msg = msg - if msg is None: - if (pid is not None) and (name is not None): - self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) - elif (pid is not None): - self.msg = "(pid=%s)" % self.pid - else: - self.msg = "" - - -class TimeoutExpired(Error): - """Raised on Process.wait(timeout) if timeout expires and process - is still alive. - """ - - def __init__(self, seconds, pid=None, name=None): - Error.__init__(self, "timeout after %s seconds" % seconds) - self.seconds = seconds - self.pid = pid - self.name = name - if (pid is not None) and (name is not None): - self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) - elif (pid is not None): - self.msg += " (pid=%s)" % self.pid - - -# Push exception classes into platform specific module namespace. -_psplatform.NoSuchProcess = NoSuchProcess -_psplatform.ZombieProcess = ZombieProcess -_psplatform.AccessDenied = AccessDenied -_psplatform.TimeoutExpired = TimeoutExpired -if POSIX: - from . import _psposix - _psposix.TimeoutExpired = TimeoutExpired - - # ===================================================================== # --- Utils # ===================================================================== @@ -875,7 +775,7 @@ class Process(object): """ return self._proc.io_counters() - # Linux and Windows >= Vista only + # Linux and Windows if hasattr(_psplatform.Process, "ionice_get"): def ionice(self, ioclass=None, value=None): @@ -1290,18 +1190,16 @@ class Process(object): "calling process (os.getpid()) instead of PID 0") try: os.kill(self.pid, sig) - except OSError as err: - if err.errno == errno.ESRCH: - if OPENBSD and pid_exists(self.pid): - # We do this because os.kill() lies in case of - # zombie processes. - raise ZombieProcess(self.pid, self._name, self._ppid) - else: - self._gone = True - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + except ProcessLookupError: + if OPENBSD and pid_exists(self.pid): + # We do this because os.kill() lies in case of + # zombie processes. + raise ZombieProcess(self.pid, self._name, self._ppid) + else: + self._gone = True + raise NoSuchProcess(self.pid, self._name) + except PermissionError: + raise AccessDenied(self.pid, self._name) @_assert_pid_not_reused def send_signal(self, sig): @@ -1313,16 +1211,7 @@ class Process(object): if POSIX: self._send_signal(sig) else: # pragma: no cover - if sig == signal.SIGTERM: - self._proc.kill() - # py >= 2.7 - elif sig in (getattr(signal, "CTRL_C_EVENT", object()), - getattr(signal, "CTRL_BREAK_EVENT", object())): - self._proc.send_signal(sig) - else: - raise ValueError( - "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " - "are supported on Windows") + self._proc.send_signal(sig) @_assert_pid_not_reused def suspend(self): @@ -1370,6 +1259,8 @@ class Process(object): def wait(self, timeout=None): """Wait for process to terminate and, if process is a children of os.getpid(), also return its exit code, else None. + On Windows there's no such limitation (exit code is always + returned). If the process is already terminated immediately return None instead of raising NoSuchProcess. @@ -1634,6 +1525,7 @@ def wait_procs(procs, timeout=None, callback=None): pass else: if returncode is not None or not proc.is_running(): + # Set new Process instance attribute. proc.returncode = returncode gone.add(proc) if callback is not None: diff --git a/third_party/python/psutil/psutil/_common.py b/third_party/python/psutil/psutil/_common.py index e3b45417ae48..17b6eeb3bf85 100644 --- a/third_party/python/psutil/psutil/_common.py +++ b/third_party/python/psutil/psutil/_common.py @@ -7,7 +7,7 @@ # Note: this module is imported by setup.py so it should not import # psutil or third-party modules. -from __future__ import division +from __future__ import division, print_function import contextlib import errno @@ -23,6 +23,7 @@ from collections import namedtuple from socket import AF_INET from socket import SOCK_DGRAM from socket import SOCK_STREAM + try: from socket import AF_INET6 except ImportError: @@ -37,14 +38,14 @@ if sys.version_info >= (3, 4): else: enum = None + # can't take it from _common.py as this script is imported by setup.py PY3 = sys.version_info[0] == 3 __all__ = [ - # constants + # OS constants 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', 'SUNOS', 'WINDOWS', - 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', # connection constants 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', @@ -56,6 +57,8 @@ __all__ = [ 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED', 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL', 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED', + # other constants + 'ENCODING', 'ENCODING_ERRS', 'AF_INET6', # named tuples 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile', 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart', @@ -64,7 +67,9 @@ __all__ = [ 'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize', 'parse_environ_block', 'path_exists_strict', 'usage_percent', 'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers", - 'bytes2human', + 'bytes2human', 'conn_to_ntuple', 'debug', + # shell utils + 'hilite', 'term_supports_colors', 'print_color', ] @@ -257,7 +262,109 @@ if AF_UNIX is not None: "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), }) -del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM + +# ===================================================================== +# --- Exceptions +# ===================================================================== + + +class Error(Exception): + """Base exception class. All other psutil exceptions inherit + from this one. + """ + __module__ = 'psutil' + + def __init__(self, msg=""): + Exception.__init__(self, msg) + self.msg = msg + + def __repr__(self): + ret = "psutil.%s %s" % (self.__class__.__name__, self.msg) + return ret.strip() + + __str__ = __repr__ + + +class NoSuchProcess(Error): + """Exception raised when a process with a certain PID doesn't + or no longer exists. + """ + __module__ = 'psutil' + + def __init__(self, pid, name=None, msg=None): + Error.__init__(self, msg) + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process no longer exists " + details + + def __path__(self): + return 'xxx' + + +class ZombieProcess(NoSuchProcess): + """Exception raised when querying a zombie process. This is + raised on macOS, BSD and Solaris only, and not always: depending + on the query the OS may be able to succeed anyway. + On Linux all zombie processes are querable (hence this is never + raised). Windows doesn't have zombie processes. + """ + __module__ = 'psutil' + + def __init__(self, pid, name=None, ppid=None, msg=None): + NoSuchProcess.__init__(self, msg) + self.pid = pid + self.ppid = ppid + self.name = name + self.msg = msg + if msg is None: + args = ["pid=%s" % pid] + if name: + args.append("name=%s" % repr(self.name)) + if ppid: + args.append("ppid=%s" % self.ppid) + details = "(%s)" % ", ".join(args) + self.msg = "process still exists but it's a zombie " + details + + +class AccessDenied(Error): + """Exception raised when permission to perform an action is denied.""" + __module__ = 'psutil' + + def __init__(self, pid=None, name=None, msg=None): + Error.__init__(self, msg) + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + +class TimeoutExpired(Error): + """Raised on Process.wait(timeout) if timeout expires and process + is still alive. + """ + __module__ = 'psutil' + + def __init__(self, seconds, pid=None, name=None): + Error.__init__(self, "timeout after %s seconds" % seconds) + self.seconds = seconds + self.pid = pid + self.name = name + if (pid is not None) and (name is not None): + self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg += " (pid=%s)" % self.pid # =================================================================== @@ -268,12 +375,12 @@ del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM def usage_percent(used, total, round_=None): """Calculate percentage usage of 'used' against 'total'.""" try: - ret = (used / total) * 100 + ret = (float(used) / total) * 100 except ZeroDivisionError: - ret = 0.0 if isinstance(used, float) or isinstance(total, float) else 0 - if round_ is not None: - return round(ret, round_) + return 0.0 else: + if round_ is not None: + ret = round(ret, round_) return ret @@ -447,7 +554,7 @@ def sockfam_to_enum(num): else: # pragma: no cover try: return socket.AddressFamily(num) - except (ValueError, AttributeError): + except ValueError: return num @@ -459,11 +566,30 @@ def socktype_to_enum(num): return num else: # pragma: no cover try: - return socket.AddressType(num) - except (ValueError, AttributeError): + return socket.SocketKind(num) + except ValueError: return num +def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None): + """Convert a raw connection tuple to a proper ntuple.""" + if fam in (socket.AF_INET, AF_INET6): + if laddr: + laddr = addr(*laddr) + if raddr: + raddr = addr(*raddr) + if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6): + status = status_map.get(status, CONN_NONE) + else: + status = CONN_NONE # ignore whatever C returned to us + fam = sockfam_to_enum(fam) + type_ = socktype_to_enum(type_) + if pid is None: + return pconn(fd, fam, type_, laddr, raddr, status) + else: + return sconn(fd, fam, type_, laddr, raddr, status, pid) + + def deprecated_method(replacement): """A decorator which can be used to mark a method as deprecated 'replcement' is the method name which will be called instead. @@ -632,3 +758,89 @@ if PY3: else: def decode(s): return s + + +# ===================================================================== +# --- shell utils +# ===================================================================== + + +@memoize +def term_supports_colors(file=sys.stdout): + if os.name == 'nt': + return True + try: + import curses + assert file.isatty() + curses.setupterm() + assert curses.tigetnum("colors") > 0 + except Exception: + return False + else: + return True + + +def hilite(s, color="green", bold=False): + """Return an highlighted version of 'string'.""" + if not term_supports_colors(): + return s + attr = [] + colors = dict(green='32', red='91', brown='33') + colors[None] = '29' + try: + color = colors[color] + except KeyError: + raise ValueError("invalid color %r; choose between %s" % ( + list(colors.keys()))) + attr.append(color) + if bold: + attr.append('1') + return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) + + +def print_color(s, color="green", bold=False, file=sys.stdout): + """Print a colorized version of string.""" + if not term_supports_colors(): + print(s, file=file) + elif POSIX: + print(hilite(s, color, bold), file=file) + else: + import ctypes + + DEFAULT_COLOR = 7 + GetStdHandle = ctypes.windll.Kernel32.GetStdHandle + SetConsoleTextAttribute = \ + ctypes.windll.Kernel32.SetConsoleTextAttribute + + colors = dict(green=2, red=4, brown=6) + colors[None] = DEFAULT_COLOR + try: + color = colors[color] + except KeyError: + raise ValueError("invalid color %r; choose between %r" % ( + color, list(colors.keys()))) + if bold and color <= 7: + color += 8 + + handle_id = -12 if file is sys.stderr else -11 + GetStdHandle.restype = ctypes.c_ulong + handle = GetStdHandle(handle_id) + SetConsoleTextAttribute(handle, color) + try: + print(s, file=file) + finally: + SetConsoleTextAttribute(handle, DEFAULT_COLOR) + + +if bool(os.getenv('PSUTIL_DEBUG', 0)): + import inspect + + def debug(msg): + """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" + fname, lineno, func_name, lines, index = inspect.getframeinfo( + inspect.currentframe().f_back) + print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), + file=sys.stderr) +else: + def debug(msg): + pass diff --git a/third_party/python/psutil/psutil/_compat.py b/third_party/python/psutil/psutil/_compat.py index c772f61d601d..a9371382bd6e 100644 --- a/third_party/python/psutil/psutil/_compat.py +++ b/third_party/python/psutil/psutil/_compat.py @@ -5,12 +5,15 @@ """Module which provides compatibility with older Python versions.""" import collections +import errno import functools import os import sys __all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b", - "lru_cache", "which", "get_terminal_size"] + "lru_cache", "which", "get_terminal_size", + "FileNotFoundError", "PermissionError", "ProcessLookupError", + "InterruptedError", "ChildProcessError", "FileExistsError"] PY3 = sys.version_info[0] == 3 @@ -38,6 +41,86 @@ else: return s +# --- exceptions + + +if PY3: + FileNotFoundError = FileNotFoundError # NOQA + PermissionError = PermissionError # NOQA + ProcessLookupError = ProcessLookupError # NOQA + InterruptedError = InterruptedError # NOQA + ChildProcessError = ChildProcessError # NOQA + FileExistsError = FileExistsError # NOQA +else: + # https://github.com/PythonCharmers/python-future/blob/exceptions/ + # src/future/types/exceptions/pep3151.py + import platform + + _singleton = object() + + def instance_checking_exception(base_exception=Exception): + def wrapped(instance_checker): + class TemporaryClass(base_exception): + + def __init__(self, *args, **kwargs): + if len(args) == 1 and isinstance(args[0], TemporaryClass): + unwrap_me = args[0] + for attr in dir(unwrap_me): + if not attr.startswith('__'): + setattr(self, attr, getattr(unwrap_me, attr)) + else: + super(TemporaryClass, self).__init__(*args, **kwargs) + + class __metaclass__(type): + def __instancecheck__(cls, inst): + return instance_checker(inst) + + def __subclasscheck__(cls, classinfo): + value = sys.exc_info()[1] + return isinstance(value, cls) + + TemporaryClass.__name__ = instance_checker.__name__ + TemporaryClass.__doc__ = instance_checker.__doc__ + return TemporaryClass + + return wrapped + + @instance_checking_exception(EnvironmentError) + def FileNotFoundError(inst): + return getattr(inst, 'errno', _singleton) == errno.ENOENT + + @instance_checking_exception(EnvironmentError) + def ProcessLookupError(inst): + return getattr(inst, 'errno', _singleton) == errno.ESRCH + + @instance_checking_exception(EnvironmentError) + def PermissionError(inst): + return getattr(inst, 'errno', _singleton) in ( + errno.EACCES, errno.EPERM) + + @instance_checking_exception(EnvironmentError) + def InterruptedError(inst): + return getattr(inst, 'errno', _singleton) == errno.EINTR + + @instance_checking_exception(EnvironmentError) + def ChildProcessError(inst): + return getattr(inst, 'errno', _singleton) == errno.ECHILD + + @instance_checking_exception(EnvironmentError) + def FileExistsError(inst): + return getattr(inst, 'errno', _singleton) == errno.EEXIST + + if platform.python_implementation() != "CPython": + try: + raise OSError(errno.EEXIST, "perm") + except FileExistsError: + pass + except OSError: + raise RuntimeError( + "broken / incompatible Python implementation, see: " + "https://github.com/giampaolo/psutil/issues/1659") + + # --- stdlib additions diff --git a/third_party/python/psutil/psutil/_psaix.py b/third_party/python/psutil/psutil/_psaix.py index b24325d19aad..994366aaa1b9 100644 --- a/third_party/python/psutil/psutil/_psaix.py +++ b/third_party/python/psutil/psutil/_psaix.py @@ -6,7 +6,6 @@ """AIX platform implementation.""" -import errno import functools import glob import os @@ -14,21 +13,24 @@ import re import subprocess import sys from collections import namedtuple -from socket import AF_INET from . import _common from . import _psposix from . import _psutil_aix as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied +from ._common import conn_to_ntuple from ._common import get_procfs_path from ._common import memoize_when_activated from ._common import NIC_DUPLEX_FULL from ._common import NIC_DUPLEX_HALF from ._common import NIC_DUPLEX_UNKNOWN -from ._common import sockfam_to_enum -from ._common import socktype_to_enum +from ._common import NoSuchProcess from ._common import usage_percent +from ._common import ZombieProcess +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 @@ -80,13 +82,6 @@ proc_info_map = dict( status=6, ttynr=7) -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - # ===================================================================== # --- named tuples @@ -220,27 +215,17 @@ def net_connections(kind, _pid=-1): % (kind, ', '.join([repr(x) for x in cmap]))) families, types = _common.conn_tmap[kind] rawlist = cext.net_connections(_pid) - ret = set() + ret = [] for item in rawlist: fd, fam, type_, laddr, raddr, status, pid = item if fam not in families: continue if type_ not in types: continue - status = TCP_STATUSES[status] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type_ = socktype_to_enum(type_) - if _pid == -1: - nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid) - else: - nt = _common.pconn(fd, fam, type_, laddr, raddr, status) - ret.add(nt) - return list(ret) + nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status, + TCP_STATUSES, pid=pid if _pid == -1 else None) + ret.append(nt) + return ret def net_if_stats(): @@ -327,22 +312,16 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: - # support for private module import - if (NoSuchProcess is None or AccessDenied is None or - ZombieProcess is None): - raise + except (FileNotFoundError, ProcessLookupError): # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) return wrapper @@ -501,11 +480,9 @@ class Process(object): try: result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid)) return result.rstrip('/') - except OSError as err: - if err.errno == errno.ENOENT: - os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD - return None - raise + except FileNotFoundError: + os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD + return None @wrap_exceptions def memory_info(self): diff --git a/third_party/python/psutil/psutil/_psbsd.py b/third_party/python/psutil/psutil/_psbsd.py index 3d9dfdab69e6..49ad1e995cc1 100644 --- a/third_party/python/psutil/psutil/_psbsd.py +++ b/third_party/python/psutil/psutil/_psbsd.py @@ -10,25 +10,29 @@ import functools import os import xml.etree.ElementTree as ET from collections import namedtuple -from socket import AF_INET from collections import defaultdict from . import _common from . import _psposix from . import _psutil_bsd as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied from ._common import conn_tmap +from ._common import conn_to_ntuple from ._common import FREEBSD from ._common import memoize from ._common import memoize_when_activated from ._common import NETBSD +from ._common import NoSuchProcess from ._common import OPENBSD -from ._common import sockfam_to_enum -from ._common import socktype_to_enum from ._common import usage_percent +from ._common import ZombieProcess +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import which + __extra__all__ = [] @@ -134,13 +138,6 @@ kinfo_proc_map = dict( name=24, ) -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - # ===================================================================== # --- named tuples @@ -399,22 +396,8 @@ def net_connections(kind): fd, fam, type, laddr, raddr, status, pid = item # TODO: apply filter at C level if fam in families and type in types: - try: - status = TCP_STATUSES[status] - except KeyError: - # XXX: Not sure why this happens. I saw this occurring - # with IPv6 sockets opened by 'vim'. Those sockets - # have a very short lifetime so maybe the kernel - # can't initialize their status? - status = TCP_STATUSES[cext.PSUTIL_CONN_NONE] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES, pid) ret.add(nt) return list(ret) @@ -551,6 +534,14 @@ else: pid_exists = _psposix.pid_exists +def is_zombie(pid): + try: + st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']] + return st == cext.SZOMB + except Exception: + return False + + def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. @@ -559,19 +550,19 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError as err: + except ProcessLookupError: + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) + except OSError: if self.pid == 0: if 0 in pids(): raise AccessDenied(self.pid, self._name) else: raise - if err.errno == errno.ESRCH: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) raise return wrapper @@ -581,18 +572,16 @@ def wrap_exceptions_procfs(inst): """Same as above, for routines relying on reading /proc fs.""" try: yield - except EnvironmentError as err: + except (ProcessLookupError, FileNotFoundError): # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(inst.pid): - raise NoSuchProcess(inst.pid, inst._name) - else: - raise ZombieProcess(inst.pid, inst._name, inst._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(inst.pid, inst._name) - raise + if not pid_exists(inst.pid): + raise NoSuchProcess(inst.pid, inst._name) + else: + raise ZombieProcess(inst.pid, inst._name, inst._ppid) + except PermissionError: + raise AccessDenied(inst.pid, inst._name) class Process(object): @@ -633,6 +622,8 @@ class Process(object): @wrap_exceptions def exe(self): if FREEBSD: + if self.pid == 0: + return '' # else NSP return cext.proc_exe(self.pid) elif NETBSD: if self.pid == 0: @@ -648,7 +639,7 @@ class Process(object): # cmdline arg (may return None). cmdline = self.cmdline() if cmdline: - return which(cmdline[0]) + return which(cmdline[0]) or "" else: return "" @@ -665,10 +656,14 @@ class Process(object): return cext.proc_cmdline(self.pid) except OSError as err: if err.errno == errno.EINVAL: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: + if is_zombie(self.pid): raise ZombieProcess(self.pid, self._name, self._ppid) + elif not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name, self._ppid) + else: + # XXX: this happens with unicode tests. It means the C + # routine is unable to decode invalid unicode chars. + return [] else: raise else: @@ -769,25 +764,15 @@ class Process(object): if NETBSD: families, types = conn_tmap[kind] - ret = set() + ret = [] rawlist = cext.net_connections(self.pid) for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item assert pid == self.pid if fam in families and type in types: - try: - status = TCP_STATUSES[status] - except KeyError: - status = TCP_STATUSES[cext.PSUTIL_CONN_NONE] - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - nt = _common.pconn(fd, fam, type, laddr, raddr, status) - ret.add(nt) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) + ret.append(nt) self._assert_alive() return list(ret) @@ -796,18 +781,13 @@ class Process(object): ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - status = TCP_STATUSES[status] - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) ret.append(nt) + if OPENBSD: self._assert_alive() + return ret @wrap_exceptions @@ -844,10 +824,7 @@ class Process(object): # it into None if OPENBSD and self.pid == 0: return None # ...else it would raise EINVAL - elif NETBSD: - with wrap_exceptions_procfs(self): - return os.readlink("/proc/%s/cwd" % self.pid) - elif HAS_PROC_OPEN_FILES: + elif NETBSD or HAS_PROC_OPEN_FILES: # FreeBSD < 8 does not support functions based on # kinfo_getfile() and kinfo_getvmmap() return cext.proc_cwd(self.pid) or None diff --git a/third_party/python/psutil/psutil/_pslinux.py b/third_party/python/psutil/psutil/_pslinux.py index e4bc7d75c128..9e32f25e7bf9 100644 --- a/third_party/python/psutil/psutil/_pslinux.py +++ b/third_party/python/psutil/psutil/_pslinux.py @@ -25,6 +25,8 @@ from . import _common from . import _psposix from . import _psutil_linux as cext from . import _psutil_posix as cext_posix +from ._common import AccessDenied +from ._common import debug from ._common import decode from ._common import get_procfs_path from ._common import isfile_strict @@ -33,14 +35,19 @@ from ._common import memoize_when_activated from ._common import NIC_DUPLEX_FULL from ._common import NIC_DUPLEX_HALF from ._common import NIC_DUPLEX_UNKNOWN +from ._common import NoSuchProcess from ._common import open_binary from ._common import open_text from ._common import parse_environ_block from ._common import path_exists_strict from ._common import supports_ipv6 from ._common import usage_percent +from ._common import ZombieProcess from ._compat import b from ._compat import basestring +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 if sys.version_info >= (3, 4): @@ -70,6 +77,7 @@ POWER_SUPPLY_PATH = "/sys/class/power_supply" HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid()) HAS_PRLIMIT = hasattr(cext, "linux_prlimit") HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_ioprio_get") +HAS_CPU_AFFINITY = hasattr(cext, "proc_cpu_affinity_get") _DEFAULT = object() # RLIMIT_* constants, not guaranteed to be present on all kernels @@ -157,13 +165,6 @@ TCP_STATUSES = { "0B": _common.CONN_CLOSING } -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - # ===================================================================== # --- named tuples @@ -200,6 +201,10 @@ pmmap_ext = namedtuple( pio = namedtuple('pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes', 'read_chars', 'write_chars']) +# psutil.Process.cpu_times() +pcputimes = namedtuple('pcputimes', + ['user', 'system', 'children_user', 'children_system', + 'iowait']) # ===================================================================== @@ -747,6 +752,8 @@ class Connections: """ def __init__(self): + # The string represents the basename of the corresponding + # /proc/net/{proto_name} file. tcp4 = ("tcp", socket.AF_INET, socket.SOCK_STREAM) tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM) udp4 = ("udp", socket.AF_INET, socket.SOCK_DGRAM) @@ -772,17 +779,16 @@ class Connections: for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)): try: inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime; # os.stat('/proc/%s' % self.pid) will be done later # to force NSP (if it's the case) - if err.errno in (errno.ENOENT, errno.ESRCH): - continue - elif err.errno == errno.EINVAL: + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: if inode.startswith('socket:['): # the process is using a socket @@ -795,7 +801,7 @@ class Connections: for pid in pids(): try: inodes.update(self.get_proc_inodes(pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError, PermissionError): # os.listdir() is gonna raise a lot of access denied # exceptions in case of unprivileged user; that's fine # as we'll just end up returning a connection with PID @@ -803,9 +809,7 @@ class Connections: # Both netstat -an and lsof does the same so it's # unlikely we can do any better. # ENOENT just means a PID disappeared on us. - if err.errno not in ( - errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES): - raise + continue return inodes @staticmethod @@ -933,7 +937,7 @@ class Connections: path = tokens[-1] else: path = "" - type_ = int(type_) + type_ = _common.socktype_to_enum(int(type_)) # XXX: determining the remote endpoint of a # UNIX socket on Linux is not possible, see: # https://serverfault.com/questions/252723/ @@ -954,15 +958,14 @@ class Connections: else: inodes = self.get_all_inodes() ret = set() - for f, family, type_ in self.tmap[kind]: + for proto_name, family, type_ in self.tmap[kind]: + path = "%s/net/%s" % (self._procfs_path, proto_name) if family in (socket.AF_INET, socket.AF_INET6): ls = self.process_inet( - "%s/net/%s" % (self._procfs_path, f), - family, type_, inodes, filter_pid=pid) + path, family, type_, inodes, filter_pid=pid) else: ls = self.process_unix( - "%s/net/%s" % (self._procfs_path, f), - family, inodes, filter_pid=pid) + path, family, inodes, filter_pid=pid) for fd, family, type_, laddr, raddr, status, bound_pid in ls: if pid: conn = _common.pconn(fd, family, type_, laddr, raddr, @@ -1065,6 +1068,7 @@ def disk_io_counters(perdisk=False): # "3 1 hda1 8 8 8 8" # 4.18+ has 4 fields added: # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0" + # 5.5 has 2 more fields. # See: # https://www.kernel.org/doc/Documentation/iostats.txt # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats @@ -1079,7 +1083,7 @@ def disk_io_counters(perdisk=False): reads = int(fields[2]) (reads_merged, rbytes, rtime, writes, writes_merged, wbytes, wtime, _, busy_time, _) = map(int, fields[4:14]) - elif flen == 14 or flen == 18: + elif flen == 14 or flen >= 18: # Linux 2.6+, line referring to a disk name = fields[2] (reads, reads_merged, rbytes, rtime, writes, writes_merged, @@ -1103,7 +1107,7 @@ def disk_io_counters(perdisk=False): fields = f.read().strip().split() name = os.path.basename(root) (reads, reads_merged, rbytes, rtime, writes, writes_merged, - wbytes, wtime, _, busy_time, _) = map(int, fields) + wbytes, wtime, _, busy_time) = map(int, fields[:10]) yield (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, writes_merged, busy_time) @@ -1174,6 +1178,7 @@ def disk_partitions(all=False): continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) retlist.append(ntuple) + return retlist @@ -1201,6 +1206,8 @@ def sensors_temperatures(): # https://github.com/giampaolo/psutil/issues/971 # https://github.com/nicolargo/glances/issues/1060 basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*')) + basenames.extend(glob.glob( + '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*')) basenames = sorted(set([x.split('_')[0] for x in basenames])) for base in basenames: @@ -1209,7 +1216,7 @@ def sensors_temperatures(): current = float(cat(path)) / 1000.0 path = os.path.join(os.path.dirname(base), 'name') unit_name = cat(path, binary=False) - except (IOError, OSError, ValueError) as err: + except (IOError, OSError, ValueError): # A lot of things can go wrong here, so let's just skip the # whole entry. Sure thing is Linux's /sys/class/hwmon really # is a stinky broken mess. @@ -1218,8 +1225,6 @@ def sensors_temperatures(): # https://github.com/giampaolo/psutil/issues/1129 # https://github.com/giampaolo/psutil/issues/1245 # https://github.com/giampaolo/psutil/issues/1323 - warnings.warn("ignoring %r for file %r" % (err, path), - RuntimeWarning) continue high = cat(base + '_max', fallback=None) @@ -1251,8 +1256,7 @@ def sensors_temperatures(): path = os.path.join(base, 'type') unit_name = cat(path, binary=False) except (IOError, OSError, ValueError) as err: - warnings.warn("ignoring %r for file %r" % (err, path), - RuntimeWarning) + debug("ignoring %r for file %r" % (err, path)) continue trip_paths = glob.glob(base + '/trip_point*') @@ -1490,11 +1494,10 @@ def ppid_map(): try: with open_binary("%s/%s/stat" % (procfs_path, pid)) as f: data = f.read() - except EnvironmentError as err: + except (FileNotFoundError, ProcessLookupError): # Note: we should be able to access /stat for all processes # aka it's unlikely we'll bump into EPERM, which is good. - if err.errno not in (errno.ENOENT, errno.ESRCH): - raise + pass else: rpar = data.rfind(b')') dset = data[rpar + 2:].split() @@ -1511,16 +1514,12 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - # ESRCH (no such process) can be raised on read() if - # process is gone in the meantime. - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - # ENOENT (no such file or directory) can be raised on open(). - if err.errno == errno.ENOENT and not os.path.exists("%s/%s" % ( - self._procfs_path, self.pid)): + except PermissionError: + raise AccessDenied(self.pid, self._name) + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except FileNotFoundError: + if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)): raise NoSuchProcess(self.pid, self._name) # Note: zombies will keep existing under /proc until they're # gone so there's no way to distinguish them in here. @@ -1576,6 +1575,7 @@ class Process(object): ret['children_stime'] = fields[14] ret['create_time'] = fields[19] ret['cpu_num'] = fields[36] + ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks' return ret @@ -1617,21 +1617,19 @@ class Process(object): def exe(self): try: return readlink("%s/%s/exe" % (self._procfs_path, self.pid)) - except OSError as err: - if err.errno in (errno.ENOENT, errno.ESRCH): - # no such file error; might be raised also if the - # path actually exists for system processes with - # low pids (about 0-20) - if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): - return "" + except (FileNotFoundError, ProcessLookupError): + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): + return "" + else: + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) else: - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) @wrap_exceptions def cmdline(self): @@ -1650,7 +1648,13 @@ class Process(object): sep = '\x00' if data.endswith('\x00') else ' ' if data.endswith(sep): data = data[:-1] - return data.split(sep) + cmdline = data.split(sep) + # Sometimes last char is a null byte '\0' but the args are + # separated by spaces, see: https://github.com/giampaolo/psutil/ + # issues/1179#issuecomment-552984549 + if sep == '\x00' and len(cmdline) == 1 and ' ' in data: + cmdline = data.split(' ') + return cmdline @wrap_exceptions def environ(self): @@ -1707,7 +1711,8 @@ class Process(object): stime = float(values['stime']) / CLOCK_TICKS children_utime = float(values['children_utime']) / CLOCK_TICKS children_stime = float(values['children_stime']) / CLOCK_TICKS - return _common.pcputimes(utime, stime, children_utime, children_stime) + iowait = float(values['blkio_ticks']) / CLOCK_TICKS + return pcputimes(utime, stime, children_utime, children_stime, iowait) @wrap_exceptions def cpu_num(self): @@ -1839,7 +1844,7 @@ class Process(object): path = path[:-10] ls.append(( decode(addr), decode(perms), path, - data[b'Rss:'], + data.get(b'Rss:', 0), data.get(b'Size:', 0), data.get(b'Pss:', 0), data.get(b'Shared_Clean:', 0), @@ -1856,14 +1861,12 @@ class Process(object): def cwd(self): try: return readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # https://github.com/giampaolo/psutil/issues/986 - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - raise + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) @wrap_exceptions def num_ctx_switches(self, @@ -1899,13 +1902,11 @@ class Process(object): try: with open_binary(fname) as f: st = f.read().strip() - except IOError as err: - if err.errno == errno.ENOENT: - # no such file or directory; it means thread - # disappeared on us - hit_enoent = True - continue - raise + except FileNotFoundError: + # no such file or directory; it means thread + # disappeared on us + hit_enoent = True + continue # ignore the first two values ("pid (exe)") st = st[st.find(b')') + 2:] values = st.split(b' ') @@ -1930,38 +1931,41 @@ class Process(object): def nice_set(self, value): return cext_posix.setpriority(self.pid, value) - @wrap_exceptions - def cpu_affinity_get(self): - return cext.proc_cpu_affinity_get(self.pid) + # starting from CentOS 6. + if HAS_CPU_AFFINITY: - def _get_eligible_cpus( - self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")): - # See: https://github.com/giampaolo/psutil/issues/956 - data = self._read_status_file() - match = _re.findall(data) - if match: - return list(range(int(match[0][0]), int(match[0][1]) + 1)) - else: - return list(range(len(per_cpu_times()))) + @wrap_exceptions + def cpu_affinity_get(self): + return cext.proc_cpu_affinity_get(self.pid) - @wrap_exceptions - def cpu_affinity_set(self, cpus): - try: - cext.proc_cpu_affinity_set(self.pid, cpus) - except (OSError, ValueError) as err: - if isinstance(err, ValueError) or err.errno == errno.EINVAL: - eligible_cpus = self._get_eligible_cpus() - all_cpus = tuple(range(len(per_cpu_times()))) - for cpu in cpus: - if cpu not in all_cpus: - raise ValueError( - "invalid CPU number %r; choose between %s" % ( - cpu, eligible_cpus)) - if cpu not in eligible_cpus: - raise ValueError( - "CPU number %r is not eligible; choose " - "between %s" % (cpu, eligible_cpus)) - raise + def _get_eligible_cpus( + self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")): + # See: https://github.com/giampaolo/psutil/issues/956 + data = self._read_status_file() + match = _re.findall(data) + if match: + return list(range(int(match[0][0]), int(match[0][1]) + 1)) + else: + return list(range(len(per_cpu_times()))) + + @wrap_exceptions + def cpu_affinity_set(self, cpus): + try: + cext.proc_cpu_affinity_set(self.pid, cpus) + except (OSError, ValueError) as err: + if isinstance(err, ValueError) or err.errno == errno.EINVAL: + eligible_cpus = self._get_eligible_cpus() + all_cpus = tuple(range(len(per_cpu_times()))) + for cpu in cpus: + if cpu not in all_cpus: + raise ValueError( + "invalid CPU number %r; choose between %s" % ( + cpu, eligible_cpus)) + if cpu not in eligible_cpus: + raise ValueError( + "CPU number %r is not eligible; choose " + "between %s" % (cpu, eligible_cpus)) + raise # only starting from kernel 2.6.13 if HAS_PROC_IO_PRIORITY: @@ -2029,16 +2033,15 @@ class Process(object): file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) try: path = readlink(file) - except OSError as err: + except (FileNotFoundError, ProcessLookupError): # ENOENT == file which is gone in the meantime - if err.errno in (errno.ENOENT, errno.ESRCH): - hit_enoent = True - continue - elif err.errno == errno.EINVAL: + hit_enoent = True + continue + except OSError as err: + if err.errno == errno.EINVAL: # not a link continue - else: - raise + raise else: # If path is not an absolute there's no way to tell # whether it's a regular file or not, so we skip it. @@ -2052,13 +2055,10 @@ class Process(object): with open_binary(file) as f: pos = int(f.readline().split()[1]) flags = int(f.readline().split()[1], 8) - except IOError as err: - if err.errno == errno.ENOENT: - # fd gone in the meantime; process may - # still be alive - hit_enoent = True - else: - raise + except FileNotFoundError: + # fd gone in the meantime; process may + # still be alive + hit_enoent = True else: mode = file_flags_to_mode(flags) ntuple = popenfile( diff --git a/third_party/python/psutil/psutil/_psosx.py b/third_party/python/psutil/psutil/_psosx.py index 7459a0f3d8d4..e4296495c45b 100644 --- a/third_party/python/psutil/psutil/_psosx.py +++ b/third_party/python/psutil/psutil/_psosx.py @@ -8,21 +8,23 @@ import contextlib import errno import functools import os -from socket import AF_INET from collections import namedtuple from . import _common from . import _psposix from . import _psutil_osx as cext from . import _psutil_posix as cext_posix -from ._common import AF_INET6 +from ._common import AccessDenied from ._common import conn_tmap +from ._common import conn_to_ntuple from ._common import isfile_strict from ._common import memoize_when_activated +from ._common import NoSuchProcess from ._common import parse_environ_block -from ._common import sockfam_to_enum -from ._common import socktype_to_enum from ._common import usage_percent +from ._common import ZombieProcess +from ._compat import PermissionError +from ._compat import ProcessLookupError __extra__all__ = [] @@ -84,13 +86,6 @@ pidtaskinfo_map = dict( volctxsw=7, ) -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - # ===================================================================== # --- named tuples @@ -337,12 +332,10 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError as err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid, self._name) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) - raise + except ProcessLookupError: + raise NoSuchProcess(self.pid, self._name) + except PermissionError: + raise AccessDenied(self.pid, self._name) except cext.ZombieProcessError: raise ZombieProcess(self.pid, self._name, self._ppid) return wrapper @@ -529,15 +522,8 @@ class Process(object): ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item - status = TCP_STATUSES[status] - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - if fam in (AF_INET, AF_INET6): - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, + TCP_STATUSES) ret.append(nt) return ret diff --git a/third_party/python/psutil/psutil/_psposix.py b/third_party/python/psutil/psutil/_psposix.py index d362143f69bc..88213ef8b641 100644 --- a/third_party/python/psutil/psutil/_psposix.py +++ b/third_party/python/psutil/psutil/_psposix.py @@ -4,7 +4,6 @@ """Routines common to all posix systems.""" -import errno import glob import os import sys @@ -12,7 +11,13 @@ import time from ._common import memoize from ._common import sdiskusage +from ._common import TimeoutExpired from ._common import usage_percent +from ._compat import ChildProcessError +from ._compat import FileNotFoundError +from ._compat import InterruptedError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 from ._compat import unicode @@ -20,11 +25,6 @@ from ._compat import unicode __all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map'] -# This object gets set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -TimeoutExpired = None - - def pid_exists(pid): """Check whether pid exists in the current process table.""" if pid == 0: @@ -36,19 +36,13 @@ def pid_exists(pid): return True try: os.kill(pid, 0) - except OSError as err: - if err.errno == errno.ESRCH: - # ESRCH == No such process - return False - elif err.errno == errno.EPERM: - # EPERM clearly means there's a process to deny access to - return True - else: - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) therefore we should never get - # here. If we do let's be explicit in considering this - # an error. - raise err + except ProcessLookupError: + return False + except PermissionError: + # EPERM clearly means there's a process to deny access to + return True + # According to "man 2 kill" possible error values are + # (EINVAL, EPERM, ESRCH) else: return True @@ -84,24 +78,20 @@ def wait_pid(pid, timeout=None, proc_name=None): while True: try: retpid, status = waitcall() - except OSError as err: - if err.errno == errno.EINTR: - delay = check_timeout(delay) - continue - elif err.errno == errno.ECHILD: - # This has two meanings: - # - pid is not a child of os.getpid() in which case - # we keep polling until it's gone - # - pid never existed in the first place - # In both cases we'll eventually return None as we - # can't determine its exit status code. - while True: - if pid_exists(pid): - delay = check_timeout(delay) - else: - return - else: - raise + except InterruptedError: + delay = check_timeout(delay) + except ChildProcessError: + # This has two meanings: + # - pid is not a child of os.getpid() in which case + # we keep polling until it's gone + # - pid never existed in the first place + # In both cases we'll eventually return None as we + # can't determine its exit status code. + while True: + if pid_exists(pid): + delay = check_timeout(delay) + else: + return else: if retpid == 0: # WNOHANG was used, pid is still running @@ -180,7 +170,6 @@ def get_terminal_map(): assert name not in ret, name try: ret[os.stat(name).st_rdev] = name - except OSError as err: - if err.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass return ret diff --git a/third_party/python/psutil/psutil/_pssunos.py b/third_party/python/psutil/psutil/_pssunos.py index 6d7fda855563..62362b89c631 100644 --- a/third_party/python/psutil/psutil/_pssunos.py +++ b/third_party/python/psutil/psutil/_pssunos.py @@ -17,14 +17,21 @@ from . import _common from . import _psposix from . import _psutil_posix as cext_posix from . import _psutil_sunos as cext +from ._common import AccessDenied from ._common import AF_INET6 +from ._common import debug from ._common import get_procfs_path from ._common import isfile_strict from ._common import memoize_when_activated +from ._common import NoSuchProcess from ._common import sockfam_to_enum from ._common import socktype_to_enum from ._common import usage_percent +from ._common import ZombieProcess from ._compat import b +from ._compat import FileNotFoundError +from ._compat import PermissionError +from ._compat import ProcessLookupError from ._compat import PY3 @@ -84,13 +91,6 @@ proc_info_map = dict( gid=10, egid=11) -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - # ===================================================================== # --- named tuples @@ -226,7 +226,12 @@ def disk_partitions(all=False): # Differently from, say, Linux, we don't have a list of # common fs types so the best we can do, AFAIK, is to # filter by filesystem having a total size > 0. - if not disk_usage(mountpoint).total: + try: + if not disk_usage(mountpoint).total: + continue + except OSError as err: + # https://github.com/giampaolo/psutil/issues/1674 + debug("skipping %r: %r" % (mountpoint, err)) continue ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) retlist.append(ntuple) @@ -262,6 +267,7 @@ def net_connections(kind, _pid=-1): continue if type_ not in types: continue + # TODO: refactor and use _common.conn_to_ntuple. if fam in (AF_INET, AF_INET6): if laddr: laddr = _common.addr(*laddr) @@ -341,22 +347,22 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError as err: + except (FileNotFoundError, ProcessLookupError): + # ENOENT (no such file or directory) gets raised on open(). + # ESRCH (no such process) can get raised on read() if + # process is gone in meantime. + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._name) + else: + raise ZombieProcess(self.pid, self._name, self._ppid) + except PermissionError: + raise AccessDenied(self.pid, self._name) + except OSError: if self.pid == 0: if 0 in pids(): raise AccessDenied(self.pid, self._name) else: raise - # ENOENT (no such file or directory) gets raised on open(). - # ESRCH (no such process) can get raised on read() if - # process is gone in meantime. - if err.errno in (errno.ENOENT, errno.ESRCH): - if not pid_exists(self.pid): - raise NoSuchProcess(self.pid, self._name) - else: - raise ZombieProcess(self.pid, self._name, self._ppid) - if err.errno in (errno.EPERM, errno.EACCES): - raise AccessDenied(self.pid, self._name) raise return wrapper @@ -396,6 +402,9 @@ class Process(object): @wrap_exceptions @memoize_when_activated def _proc_basic_info(self): + if self.pid == 0 and not \ + os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)): + raise AccessDenied(self.pid) ret = cext.proc_basic_info(self.pid, self._procfs_path) assert len(ret) == len(proc_info_map) return ret @@ -514,11 +523,9 @@ class Process(object): try: return os.readlink( '%s/%d/path/%d' % (procfs_path, self.pid, x)) - except OSError as err: - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue if hit_enoent: self._assert_alive() @@ -531,11 +538,9 @@ class Process(object): procfs_path = self._procfs_path try: return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid)) - except OSError as err: - if err.errno == errno.ENOENT: - os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD - return None - raise + except FileNotFoundError: + os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD + return None @wrap_exceptions def memory_info(self): @@ -596,12 +601,9 @@ class Process(object): if os.path.islink(path): try: file = os.readlink(path) - except OSError as err: - # ENOENT == file which is gone in the meantime - if err.errno == errno.ENOENT: - hit_enoent = True - continue - raise + except FileNotFoundError: + hit_enoent = True + continue else: if isfile_strict(file): retlist.append(_common.popenfile(file, int(fd))) diff --git a/third_party/python/psutil/psutil/_psutil_aix.c b/third_party/python/psutil/psutil/_psutil_aix.c index 723d159d4ed9..cf79d307d907 100644 --- a/third_party/python/psutil/psutil/_psutil_aix.c +++ b/third_party/python/psutil/psutil/_psutil_aix.c @@ -15,7 +15,7 @@ * - psutil.Process.io_counters read count is always 0 * - psutil.Process.io_counters may not be available on older AIX versions * - psutil.Process.threads may not be available on older AIX versions - # - psutil.net_io_counters may not be available on older AIX versions + * - psutil.net_io_counters may not be available on older AIX versions * - reading basic process info may fail or return incorrect values when * process is starting (see IBM APAR IV58499 - fixed in newer AIX versions) * - sockets and pipes may not be counted in num_fds (fixed in newer AIX @@ -29,7 +29,6 @@ */ #include - #include #include #include @@ -51,11 +50,11 @@ #include #include +#include "_psutil_common.h" +#include "_psutil_posix.h" #include "arch/aix/ifaddrs.h" #include "arch/aix/net_connections.h" #include "arch/aix/common.h" -#include "_psutil_common.h" -#include "_psutil_posix.h" #define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec) @@ -180,7 +179,7 @@ psutil_proc_args(PyObject *self, PyObject *args) { } procbuf.pi_pid = pid; - ret = getargs(&procbuf, sizeof(struct procinfo), argbuf, ARG_MAX); + ret = getargs(&procbuf, sizeof(procbuf), argbuf, ARG_MAX); if (ret == -1) { PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -241,7 +240,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) { } procbuf.pi_pid = pid; - ret = getevars(&procbuf, sizeof(struct procinfo), envbuf, ARG_MAX); + ret = getevars(&procbuf, sizeof(procbuf), envbuf, ARG_MAX); if (ret == -1) { PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -265,8 +264,8 @@ psutil_proc_environ(PyObject *self, PyObject *args) { goto error; if (PyDict_SetItem(py_retdict, py_key, py_val)) goto error; - Py_DECREF(py_key); - Py_DECREF(py_val); + Py_CLEAR(py_key); + Py_CLEAR(py_val); } curvar = strchr(curvar, '\0') + 1; } @@ -462,7 +461,7 @@ psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) { /* finished iteration without finding requested pid */ free(processes); - return NoSuchProcess(""); + return NoSuchProcess("psutil_read_process_table (no PID found)"); } @@ -510,10 +509,10 @@ psutil_users(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); } endutxent(); @@ -570,9 +569,9 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_dev); - Py_DECREF(py_mountp); - Py_DECREF(py_tuple); + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); mt = getmntent(file); } endmntent(file); diff --git a/third_party/python/psutil/psutil/_psutil_bsd.c b/third_party/python/psutil/psutil/_psutil_bsd.c index 0f899ef51b8c..953fcd083c28 100644 --- a/third_party/python/psutil/psutil/_psutil_bsd.c +++ b/third_party/python/psutil/psutil/_psutil_bsd.c @@ -31,7 +31,7 @@ #include #include #if !defined(__NetBSD__) -#include + #include #endif #include #include @@ -51,13 +51,10 @@ #include // for struct xtcpcb #include // for TCP connection states #include // for inet_ntop() - #include - #include // net io counters #include #include - #include // process open files/connections #include @@ -99,7 +96,6 @@ #endif - // convert a timeval struct to a double #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) @@ -129,32 +125,22 @@ psutil_pids(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - // TODO: RuntimeError is inappropriate here; we could return the - // original error instead. - if (psutil_get_proc_list(&proclist, &num_processes) != 0) { - if (errno != 0) { - PyErr_SetFromErrno(PyExc_OSError); - } - else { - PyErr_SetString(PyExc_RuntimeError, - "failed to retrieve process list"); - } + if (psutil_get_proc_list(&proclist, &num_processes) != 0) goto error; - } 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 PSUTIL_FREEBSD - py_pid = Py_BuildValue("i", proclist->ki_pid); + py_pid = PyLong_FromPid(proclist->ki_pid); #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) - py_pid = Py_BuildValue("i", proclist->p_pid); + py_pid = PyLong_FromPid(proclist->p_pid); #endif if (!py_pid) goto error; if (PyList_Append(py_retlist, py_pid)) goto error; - Py_DECREF(py_pid); + Py_CLEAR(py_pid); proclist++; } free(orig_address); @@ -194,7 +180,7 @@ psutil_boot_time(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_oneshot_info(PyObject *self, PyObject *args) { - long pid; + pid_t pid; long rss; long vms; long memtext; @@ -205,9 +191,10 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { long pagesize = sysconf(_SC_PAGESIZE); char str[1000]; PyObject *py_name; + PyObject *py_ppid; PyObject *py_retlist; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; @@ -269,12 +256,25 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { oncpu = -1; #endif +#ifdef PSUTIL_FREEBSD + py_ppid = PyLong_FromPid(kp.ki_ppid); +#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) + py_ppid = PyLong_FromPid(kp.p_ppid); +#else + py_ppid = Py_BuildfValue(-1); +#endif + if (! py_ppid) + return NULL; + // Return a single big tuple with all process info. py_retlist = Py_BuildValue( - "(lillllllidllllddddlllllbO)", +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031 + "(OillllllLdllllddddlllllbO)", +#else + "(OillllllidllllddddlllllbO)", +#endif #ifdef PSUTIL_FREEBSD - // - (long)kp.ki_ppid, // (long) ppid + py_ppid, // (pid_t) ppid (int)kp.ki_stat, // (int) status // UIDs (long)kp.ki_ruid, // (long) real uid @@ -285,7 +285,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { (long)kp.ki_groups[0], // (long) effective gid (long)kp.ki_svuid, // (long) saved gid // - kp.ki_tdev, // (int) tty nr + kp.ki_tdev, // (int or long long) tty nr PSUTIL_TV2DOUBLE(kp.ki_start), // (double) create time // ctx switches kp.ki_rusage.ru_nvcsw, // (long) ctx switches (voluntary) @@ -307,8 +307,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { // others oncpu, // (int) the CPU we are on #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) - // - (long)kp.p_ppid, // (long) ppid + py_ppid, // (pid_t) ppid (int)kp.p_stat, // (int) status // UIDs (long)kp.p_ruid, // (long) real uid @@ -347,6 +346,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { ); Py_DECREF(py_name); + Py_DECREF(py_ppid); return py_retlist; } @@ -356,11 +356,11 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_name(PyObject *self, PyObject *args) { - long pid; + pid_t pid; kinfo_proc kp; char str[1000]; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; @@ -379,10 +379,10 @@ psutil_proc_name(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_cmdline(PyObject *self, PyObject *args) { - long pid; + pid_t pid; PyObject *py_retlist = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; py_retlist = psutil_get_cmdline(pid); if (py_retlist == NULL) @@ -452,7 +452,7 @@ psutil_cpu_times(PyObject *self, PyObject *args) { #if (defined(__FreeBSD_version) && __FreeBSD_version >= 800000) || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD) static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int i; int cnt; int regular; @@ -467,7 +467,7 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; if (psutil_kinfo_proc(pid, &kipp) == -1) goto error; @@ -475,7 +475,9 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { errno = 0; freep = kinfo_getfile(pid, &cnt); if (freep == NULL) { +#if !defined(PSUTIL_OPENBSD) psutil_raise_for_pid(pid, "kinfo_getfile()"); +#endif goto error; } @@ -507,8 +509,8 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_path); - Py_DECREF(py_tuple); + Py_CLEAR(py_path); + Py_CLEAR(py_tuple); } } free(freep); @@ -670,9 +672,9 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_dev); - Py_DECREF(py_mountp); - Py_DECREF(py_tuple); + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); } free(fs); @@ -765,7 +767,7 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { goto error; if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info)) goto error; - Py_DECREF(py_ifc_info); + Py_CLEAR(py_ifc_info); } else { continue; @@ -794,6 +796,7 @@ psutil_users(PyObject *self, PyObject *args) { PyObject *py_tty = NULL; PyObject *py_hostname = NULL; PyObject *py_tuple = NULL; + PyObject *py_pid = NULL; if (py_retlist == NULL) return NULL; @@ -802,7 +805,9 @@ psutil_users(PyObject *self, PyObject *args) { struct utmp ut; FILE *fp; + Py_BEGIN_ALLOW_THREADS fp = fopen(_PATH_UTMP, "r"); + Py_END_ALLOW_THREADS if (fp == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_OSError, _PATH_UTMP); goto error; @@ -829,7 +834,7 @@ psutil_users(PyObject *self, PyObject *args) { #ifdef PSUTIL_OPENBSD -1 // process id (set to None later) #else - ut.ut_pid // process id + ut.ut_pid // TODO: use PyLong_FromPid #endif ); if (!py_tuple) { @@ -840,10 +845,10 @@ psutil_users(PyObject *self, PyObject *args) { fclose(fp); goto error; } - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); } fclose(fp); @@ -862,17 +867,21 @@ psutil_users(PyObject *self, PyObject *args) { py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host); if (! py_hostname) goto error; +#ifdef PSUTIL_OPENBSD + py_pid = Py_BuildValue("i", -1); // set to None later +#else + py_pid = PyLong_FromPid(utx->ut_pid); +#endif + if (! py_pid) + goto error; + py_tuple = Py_BuildValue( - "(OOOfi)", + "(OOOfO)", py_username, // username py_tty, // tty py_hostname, // hostname (float)utx->ut_tv.tv_sec, // start time -#ifdef PSUTIL_OPENBSD - -1 // process id (set to None later) -#else - utx->ut_pid // process id -#endif + py_pid // process id ); if (!py_tuple) { @@ -883,10 +892,11 @@ psutil_users(PyObject *self, PyObject *args) { endutxent(); goto error; } - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); + Py_CLEAR(py_pid); } endutxent(); @@ -898,6 +908,7 @@ error: Py_XDECREF(py_tty); Py_XDECREF(py_hostname); Py_XDECREF(py_tuple); + Py_XDECREF(py_pid); Py_DECREF(py_retlist); return NULL; } @@ -906,8 +917,7 @@ error: /* * define the psutil C module methods and initialize the module. */ -static PyMethodDef -PsutilMethods[] = { +static PyMethodDef mod_methods[] = { // --- per-process functions {"proc_oneshot_info", psutil_proc_oneshot_info, METH_VARARGS, @@ -921,9 +931,9 @@ PsutilMethods[] = { #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) {"proc_connections", psutil_proc_connections, METH_VARARGS, "Return connections opened by process"}, +#endif {"proc_cwd", psutil_proc_cwd, METH_VARARGS, "Return process current working directory."}, -#endif #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"}, @@ -994,109 +1004,99 @@ PsutilMethods[] = { {NULL, NULL, 0, NULL} }; -struct module_state { - PyObject *error; -}; - #if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif + #define INITERR return NULL -#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_bsd", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL + }; -static int -psutil_bsd_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} + PyObject *PyInit__psutil_bsd(void) +#else /* PY_MAJOR_VERSION */ + #define INITERR return -static int -psutil_bsd_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - -static struct PyModuleDef - moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_bsd", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_bsd_traverse, - psutil_bsd_clear, - NULL -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_bsd(void) - -#else -#define INITERROR return - -void init_psutil_bsd(void) -#endif + void init_psutil_bsd(void) +#endif /* PY_MAJOR_VERSION */ { + PyObject *v; #if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); + PyObject *mod = PyModule_Create(&moduledef); #else - PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); + PyObject *mod = Py_InitModule("_psutil_bsd", mod_methods); #endif - PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + if (mod == NULL) + INITERR; + + if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR; // process status constants #ifdef PSUTIL_FREEBSD - PyModule_AddIntConstant(module, "SIDL", SIDL); - PyModule_AddIntConstant(module, "SRUN", SRUN); - PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); - PyModule_AddIntConstant(module, "SSTOP", SSTOP); - PyModule_AddIntConstant(module, "SZOMB", SZOMB); - PyModule_AddIntConstant(module, "SWAIT", SWAIT); - PyModule_AddIntConstant(module, "SLOCK", SLOCK); + if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR; + if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) INITERR; + if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP)) INITERR; + if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) INITERR; + if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR; + if (PyModule_AddIntConstant(mod, "SWAIT", SWAIT)) INITERR; + if (PyModule_AddIntConstant(mod, "SLOCK", SLOCK)) INITERR; #elif PSUTIL_OPENBSD - PyModule_AddIntConstant(module, "SIDL", SIDL); - PyModule_AddIntConstant(module, "SRUN", SRUN); - PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); - PyModule_AddIntConstant(module, "SSTOP", SSTOP); - PyModule_AddIntConstant(module, "SZOMB", SZOMB); // unused - PyModule_AddIntConstant(module, "SDEAD", SDEAD); - PyModule_AddIntConstant(module, "SONPROC", SONPROC); + if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR; + if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) INITERR; + if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP)) INITERR; + if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) INITERR; + if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR; // unused + if (PyModule_AddIntConstant(mod, "SDEAD", SDEAD)) INITERR; + if (PyModule_AddIntConstant(mod, "SONPROC", SONPROC)) INITERR; #elif defined(PSUTIL_NETBSD) - PyModule_AddIntConstant(module, "SIDL", LSIDL); - PyModule_AddIntConstant(module, "SRUN", LSRUN); - PyModule_AddIntConstant(module, "SSLEEP", LSSLEEP); - PyModule_AddIntConstant(module, "SSTOP", LSSTOP); - PyModule_AddIntConstant(module, "SZOMB", LSZOMB); - PyModule_AddIntConstant(module, "SDEAD", LSDEAD); - PyModule_AddIntConstant(module, "SONPROC", LSONPROC); + if (PyModule_AddIntConstant(mod, "SIDL", LSIDL)) INITERR; + if (PyModule_AddIntConstant(mod, "SRUN", LSRUN)) INITERR; + if (PyModule_AddIntConstant(mod, "SSLEEP", LSSLEEP)) INITERR; + if (PyModule_AddIntConstant(mod, "SSTOP", LSSTOP)) INITERR; + if (PyModule_AddIntConstant(mod, "SZOMB", LSZOMB)) INITERR; + if (PyModule_AddIntConstant(mod, "SDEAD", LSDEAD)) INITERR; + if (PyModule_AddIntConstant(mod, "SONPROC", LSONPROC)) INITERR; // unique to NetBSD - PyModule_AddIntConstant(module, "SSUSPENDED", LSSUSPENDED); + if (PyModule_AddIntConstant(mod, "SSUSPENDED", LSSUSPENDED)) INITERR; #endif // connection status constants - PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED); - PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING); - PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT); - PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN); - PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED); - PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT); - PyModule_AddIntConstant(module, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED); - PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1); - PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2); - PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK); - PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT); + if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT)) + INITERR; // PSUTIL_CONN_NONE - PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", 128); + if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", 128)) INITERR; psutil_setup(); - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; #if PY_MAJOR_VERSION >= 3 - return module; + return mod; #endif } diff --git a/third_party/python/psutil/psutil/_psutil_common.c b/third_party/python/psutil/psutil/_psutil_common.c index c6e37bc22ed9..07578edaa277 100644 --- a/third_party/python/psutil/psutil/_psutil_common.c +++ b/third_party/python/psutil/psutil/_psutil_common.c @@ -7,52 +7,64 @@ */ #include -#ifdef _WIN32 -#include -#endif - #include "_psutil_common.h" -// Global vars. +// ==================================================================== +// --- Global vars +// ==================================================================== + int PSUTIL_DEBUG = 0; int PSUTIL_TESTING = 0; +// PSUTIL_CONN_NONE -/* - * Backport of unicode FS APIs from Python 3. - * On Python 2 we just return a plain byte string - * which is never supposed to raise decoding errors. - * See: https://github.com/giampaolo/psutil/issues/1040 - */ -#if PY_MAJOR_VERSION < 3 +// ==================================================================== +// --- Backward compatibility with missing Python.h APIs +// ==================================================================== + +// PyPy on Windows +#if defined(PSUTIL_WINDOWS) && \ + defined(PYPY_VERSION) && \ + !defined(PyErr_SetFromWindowsErrWithFilename) PyObject * -PyUnicode_DecodeFSDefault(char *s) { - return PyString_FromString(s); -} +PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) { + PyObject *py_exc = NULL; + PyObject *py_winerr = NULL; + if (winerr == 0) + winerr = GetLastError(); + if (filename == NULL) { + py_exc = PyObject_CallFunction(PyExc_OSError, "(is)", winerr, + strerror(winerr)); + } + else { + py_exc = PyObject_CallFunction(PyExc_OSError, "(iss)", winerr, + strerror(winerr), filename); + } + if (py_exc == NULL) + return NULL; -PyObject * -PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) { - return PyString_FromStringAndSize(s, size); -} -#endif + py_winerr = Py_BuildValue("i", winerr); + if (py_winerr == NULL) + goto error; + if (PyObject_SetAttrString(py_exc, "winerror", py_winerr) != 0) + goto error; + PyErr_SetObject(PyExc_OSError, py_exc); + Py_XDECREF(py_exc); + return NULL; - -/* - * Set OSError(errno=ESRCH, strerror="No such process") Python exception. - * If msg != "" the exception message will change in accordance. - */ -PyObject * -NoSuchProcess(const char *msg) { - PyObject *exc; - exc = PyObject_CallFunction( - PyExc_OSError, "(is)", ESRCH, strlen(msg) ? msg : strerror(ESRCH)); - PyErr_SetObject(PyExc_OSError, exc); - Py_XDECREF(exc); +error: + Py_XDECREF(py_exc); + Py_XDECREF(py_winerr); return NULL; } +#endif // PYPY on Windows +// ==================================================================== +// --- Custom exceptions +// ==================================================================== + /* * Same as PyErr_SetFromErrno(0) but adds the syscall to the exception * message. @@ -61,7 +73,7 @@ PyObject * PyErr_SetFromOSErrnoWithSyscall(const char *syscall) { char fullmsg[1024]; -#ifdef _WIN32 +#ifdef PSUTIL_WINDOWS sprintf(fullmsg, "(originated from %s)", syscall); PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg); #else @@ -76,20 +88,43 @@ PyErr_SetFromOSErrnoWithSyscall(const char *syscall) { /* - * Set OSError(errno=EACCES, strerror="Permission denied") Python exception. - * If msg != "" the exception message will change in accordance. + * Set OSError(errno=ESRCH, strerror="No such process (originated from") + * Python exception. */ PyObject * -AccessDenied(const char *msg) { +NoSuchProcess(const char *syscall) { PyObject *exc; - exc = PyObject_CallFunction( - PyExc_OSError, "(is)", EACCES, strlen(msg) ? msg : strerror(EACCES)); + char msg[1024]; + + sprintf(msg, "No such process (originated from %s)", syscall); + exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg); PyErr_SetObject(PyExc_OSError, exc); Py_XDECREF(exc); return NULL; } +/* + * Set OSError(errno=EACCES, strerror="Permission denied" (originated from ...) + * Python exception. + */ +PyObject * +AccessDenied(const char *syscall) { + PyObject *exc; + char msg[1024]; + + sprintf(msg, "Access denied (originated from %s)", syscall); + exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg); + PyErr_SetObject(PyExc_OSError, exc); + Py_XDECREF(exc); + return NULL; +} + + +// ==================================================================== +// --- Global utils +// ==================================================================== + /* * Enable testing mode. This has the same effect as setting PSUTIL_TESTING * env var. This dual method exists because updating os.environ on @@ -130,3 +165,239 @@ psutil_setup(void) { PSUTIL_TESTING = 1; return 0; } + + +// ==================================================================== +// --- Windows +// ==================================================================== + +#ifdef PSUTIL_WINDOWS +#include + +// Needed to make these globally visible. +int PSUTIL_WINVER; +SYSTEM_INFO PSUTIL_SYSTEM_INFO; +CRITICAL_SECTION PSUTIL_CRITICAL_SECTION; + +#define NT_FACILITY_MASK 0xfff +#define NT_FACILITY_SHIFT 16 +#define NT_FACILITY(Status) \ + ((((ULONG)(Status)) >> NT_FACILITY_SHIFT) & NT_FACILITY_MASK) +#define NT_NTWIN32(status) (NT_FACILITY(Status) == FACILITY_WIN32) +#define WIN32_FROM_NTSTATUS(Status) (((ULONG)(Status)) & 0xffff) + + +// A wrapper around GetModuleHandle and GetProcAddress. +PVOID +psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) { + HMODULE mod; + FARPROC addr; + + if ((mod = GetModuleHandleA(libname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, libname); + return NULL; + } + if ((addr = GetProcAddress(mod, procname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, procname); + return NULL; + } + return addr; +} + + +// A wrapper around LoadLibrary and GetProcAddress. +PVOID +psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) { + HMODULE mod; + FARPROC addr; + + Py_BEGIN_ALLOW_THREADS + mod = LoadLibraryA(libname); + Py_END_ALLOW_THREADS + if (mod == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, libname); + return NULL; + } + if ((addr = GetProcAddress(mod, procname)) == NULL) { + PyErr_SetFromWindowsErrWithFilename(0, procname); + FreeLibrary(mod); + return NULL; + } + // Causes crash. + // FreeLibrary(mod); + return addr; +} + + +/* + * Convert a NTSTATUS value to a Win32 error code and set the proper + * Python exception. + */ +PVOID +psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) { + ULONG err; + char fullmsg[1024]; + + if (NT_NTWIN32(Status)) + err = WIN32_FROM_NTSTATUS(Status); + else + err = RtlNtStatusToDosErrorNoTeb(Status); + // if (GetLastError() != 0) + // err = GetLastError(); + sprintf(fullmsg, "(originated from %s)", syscall); + return PyErr_SetFromWindowsErrWithFilename(err, fullmsg); +} + + +static int +psutil_loadlibs() { + // --- Mandatory + NtQuerySystemInformation = psutil_GetProcAddressFromLib( + "ntdll.dll", "NtQuerySystemInformation"); + if (! NtQuerySystemInformation) + return 1; + NtQueryInformationProcess = psutil_GetProcAddress( + "ntdll.dll", "NtQueryInformationProcess"); + if (! NtQueryInformationProcess) + return 1; + NtSetInformationProcess = psutil_GetProcAddress( + "ntdll.dll", "NtSetInformationProcess"); + if (! NtSetInformationProcess) + return 1; + WinStationQueryInformationW = psutil_GetProcAddressFromLib( + "winsta.dll", "WinStationQueryInformationW"); + if (! WinStationQueryInformationW) + return 1; + NtQueryObject = psutil_GetProcAddressFromLib( + "ntdll.dll", "NtQueryObject"); + if (! NtQueryObject) + return 1; + RtlIpv4AddressToStringA = psutil_GetProcAddressFromLib( + "ntdll.dll", "RtlIpv4AddressToStringA"); + if (! RtlIpv4AddressToStringA) + return 1; + GetExtendedTcpTable = psutil_GetProcAddressFromLib( + "iphlpapi.dll", "GetExtendedTcpTable"); + if (! GetExtendedTcpTable) + return 1; + GetExtendedUdpTable = psutil_GetProcAddressFromLib( + "iphlpapi.dll", "GetExtendedUdpTable"); + if (! GetExtendedUdpTable) + return 1; + RtlGetVersion = psutil_GetProcAddressFromLib( + "ntdll.dll", "RtlGetVersion"); + if (! RtlGetVersion) + return 1; + NtSuspendProcess = psutil_GetProcAddressFromLib( + "ntdll", "NtSuspendProcess"); + if (! NtSuspendProcess) + return 1; + NtResumeProcess = psutil_GetProcAddressFromLib( + "ntdll", "NtResumeProcess"); + if (! NtResumeProcess) + return 1; + NtQueryVirtualMemory = psutil_GetProcAddressFromLib( + "ntdll", "NtQueryVirtualMemory"); + if (! NtQueryVirtualMemory) + return 1; + RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib( + "ntdll", "RtlNtStatusToDosErrorNoTeb"); + if (! RtlNtStatusToDosErrorNoTeb) + return 1; + GetTickCount64 = psutil_GetProcAddress( + "kernel32", "GetTickCount64"); + if (! GetTickCount64) + return 1; + RtlIpv6AddressToStringA = psutil_GetProcAddressFromLib( + "ntdll.dll", "RtlIpv6AddressToStringA"); + if (! RtlIpv6AddressToStringA) + return 1; + + // --- Optional + // minimum requirement: Win 7 + GetActiveProcessorCount = psutil_GetProcAddress( + "kernel32", "GetActiveProcessorCount"); + // minumum requirement: Win 7 + GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib( + "kernel32", "GetLogicalProcessorInformationEx"); + + PyErr_Clear(); + return 0; +} + + +static int +psutil_set_winver() { + RTL_OSVERSIONINFOEXW versionInfo; + ULONG maj; + ULONG min; + + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW)); + RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo); + maj = versionInfo.dwMajorVersion; + min = versionInfo.dwMinorVersion; + if (maj == 6 && min == 0) + PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA; // or Server 2008 + else if (maj == 6 && min == 1) + PSUTIL_WINVER = PSUTIL_WINDOWS_7; + else if (maj == 6 && min == 2) + PSUTIL_WINVER = PSUTIL_WINDOWS_8; + else if (maj == 6 && min == 3) + PSUTIL_WINVER = PSUTIL_WINDOWS_8_1; + else if (maj == 10 && min == 0) + PSUTIL_WINVER = PSUTIL_WINDOWS_10; + else + PSUTIL_WINVER = PSUTIL_WINDOWS_NEW; + return 0; +} + + +int +psutil_load_globals() { + if (psutil_loadlibs() != 0) + return 1; + if (psutil_set_winver() != 0) + return 1; + GetSystemInfo(&PSUTIL_SYSTEM_INFO); + InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION); + return 0; +} + + +/* + * Convert the hi and lo parts of a FILETIME structure or a LARGE_INTEGER + * to a UNIX time. + * A FILETIME contains a 64-bit value representing the number of + * 100-nanosecond intervals since January 1, 1601 (UTC). + * A UNIX time is the number of seconds that have elapsed since the + * UNIX epoch, that is the time 00:00:00 UTC on 1 January 1970. + */ +static double +_to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) { + ULONGLONG ret; + + // 100 nanosecond intervals since January 1, 1601. + ret = hiPart << 32; + ret += loPart; + // Change starting time to the Epoch (00:00:00 UTC, January 1, 1970). + ret -= 116444736000000000ull; + // Convert nano secs to secs. + return (double) ret / 10000000ull; +} + + +double +psutil_FiletimeToUnixTime(FILETIME ft) { + return _to_unix_time((ULONGLONG)ft.dwHighDateTime, + (ULONGLONG)ft.dwLowDateTime); + +} + + +double +psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) { + return _to_unix_time((ULONGLONG)li.HighPart, + (ULONGLONG)li.LowPart); +} +#endif // PSUTIL_WINDOWS diff --git a/third_party/python/psutil/psutil/_psutil_common.h b/third_party/python/psutil/psutil/_psutil_common.h index 7f58ad173827..34c428c0581a 100644 --- a/third_party/python/psutil/psutil/_psutil_common.h +++ b/third_party/python/psutil/psutil/_psutil_common.h @@ -4,28 +4,136 @@ * found in the LICENSE file. */ -#ifndef PSUTIL_PSUTIL_COMMON_H -#define PSUTIL_PSUTIL_COMMON_H - #include +// ==================================================================== +// --- Global vars / constants +// ==================================================================== + extern int PSUTIL_TESTING; extern int PSUTIL_DEBUG; - // a signaler for connections without an actual status static const int PSUTIL_CONN_NONE = 128; +// ==================================================================== +// --- Backward compatibility with missing Python.h APIs +// ==================================================================== + #if PY_MAJOR_VERSION < 3 -PyObject* PyUnicode_DecodeFSDefault(char *s); -PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size); + // On Python 2 we just return a plain byte string, which is never + // supposed to raise decoding errors, see: + // https://github.com/giampaolo/psutil/issues/1040 + #define PyUnicode_DecodeFSDefault PyString_FromString + #define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize #endif +#if defined(PSUTIL_WINDOWS) && \ + defined(PYPY_VERSION) && \ + !defined(PyErr_SetFromWindowsErrWithFilename) + PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, + const char *filename); +#endif + +// --- _Py_PARSE_PID + +// SIZEOF_INT|LONG is missing on Linux + PyPy (only?). +// SIZEOF_PID_T is missing on Windows + Python2. +// In this case we guess it from setup.py. It's not 100% bullet proof, +// If wrong we'll probably get compiler warnings. +// FWIW on all UNIX platforms I've seen pid_t is defined as an int. +// _getpid() on Windows also returns an int. +#if !defined(SIZEOF_INT) + #define SIZEOF_INT 4 +#endif +#if !defined(SIZEOF_LONG) + #define SIZEOF_LONG 8 +#endif +#if !defined(SIZEOF_PID_T) + #define SIZEOF_PID_T PSUTIL_SIZEOF_PID_T // set as a macro in setup.py +#endif + +// _Py_PARSE_PID is Python 3 only, but since it's private make sure it's +// always present. +#ifndef _Py_PARSE_PID + #if SIZEOF_PID_T == SIZEOF_INT + #define _Py_PARSE_PID "i" + #elif SIZEOF_PID_T == SIZEOF_LONG + #define _Py_PARSE_PID "l" + #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG + #define _Py_PARSE_PID "L" + #else + #error "_Py_PARSE_PID: sizeof(pid_t) is neither sizeof(int), " + "sizeof(long) or sizeof(long long)" + #endif +#endif + +// Python 2 or PyPy on Windows +#ifndef PyLong_FromPid + #if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG)) + #if PY_MAJOR_VERSION >= 3 + #define PyLong_FromPid PyLong_FromLong + #else + #define PyLong_FromPid PyInt_FromLong + #endif + #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG + #define PyLong_FromPid PyLong_FromLongLong + #else + #error "PyLong_FromPid: sizeof(pid_t) is neither sizeof(int), " + "sizeof(long) or sizeof(long long)" + #endif +#endif + +// ==================================================================== +// --- Custom exceptions +// ==================================================================== + PyObject* AccessDenied(const char *msg); PyObject* NoSuchProcess(const char *msg); PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall); +// ==================================================================== +// --- Global utils +// ==================================================================== + PyObject* psutil_set_testing(PyObject *self, PyObject *args); void psutil_debug(const char* format, ...); int psutil_setup(void); -#endif // PSUTIL_PSUTIL_COMMON_H +// ==================================================================== +// --- Windows +// ==================================================================== + +#ifdef PSUTIL_WINDOWS + #include + // make it available to any file which includes this module + #include "arch/windows/ntextapi.h" + + extern int PSUTIL_WINVER; + extern SYSTEM_INFO PSUTIL_SYSTEM_INFO; + extern CRITICAL_SECTION PSUTIL_CRITICAL_SECTION; + + #define PSUTIL_WINDOWS_VISTA 60 + #define PSUTIL_WINDOWS_7 61 + #define PSUTIL_WINDOWS_8 62 + #define PSUTIL_WINDOWS_8_1 63 + #define PSUTIL_WINDOWS_10 100 + #define PSUTIL_WINDOWS_NEW MAXLONG + + #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) + #define MALLOC_ZERO(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x)) + #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) + + #define LO_T 1e-7 + #define HI_T 429.4967296 + + #ifndef AF_INET6 + #define AF_INET6 23 + #endif + + int psutil_load_globals(); + PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); + PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); + PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall); + double psutil_FiletimeToUnixTime(FILETIME ft); + double psutil_LargeIntegerToUnixTime(LARGE_INTEGER li); +#endif diff --git a/third_party/python/psutil/psutil/_psutil_linux.c b/third_party/python/psutil/psutil/_psutil_linux.c index 4bf53b8579c4..915ab9b4868c 100644 --- a/third_party/python/psutil/psutil/_psutil_linux.c +++ b/third_party/python/psutil/psutil/_psutil_linux.c @@ -54,6 +54,11 @@ static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT; #include #endif +// Should exist starting from CentOS 6 (year 2011). +#ifdef CPU_ALLOC + #define PSUTIL_HAVE_CPU_AFFINITY +#endif + #include "_psutil_common.h" #include "_psutil_posix.h" @@ -92,9 +97,9 @@ ioprio_set(int which, int who, int ioprio) { */ static PyObject * psutil_proc_ioprio_get(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int ioprio, ioclass, iodata; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); if (ioprio == -1) @@ -112,12 +117,14 @@ psutil_proc_ioprio_get(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_ioprio_set(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int ioprio, ioclass, iodata; int retval; - if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) + if (! PyArg_ParseTuple( + args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) { return NULL; + } ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata); retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); if (retval == -1) @@ -135,15 +142,17 @@ psutil_proc_ioprio_set(PyObject *self, PyObject *args) { */ static PyObject * psutil_linux_prlimit(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int ret, resource; struct rlimit old, new; struct rlimit *newp = NULL; PyObject *py_soft = NULL; PyObject *py_hard = NULL; - if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &py_soft, &py_hard)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i|OO", &pid, &resource, + &py_soft, &py_hard)) { return NULL; + } // get if (py_soft == NULL && py_hard == NULL) { @@ -195,7 +204,7 @@ static PyObject * psutil_disk_partitions(PyObject *self, PyObject *args) { FILE *file = NULL; struct mntent *entry; - const char *mtab_path; + char *mtab_path; PyObject *py_dev = NULL; PyObject *py_mountp = NULL; PyObject *py_tuple = NULL; @@ -236,9 +245,9 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_dev); - Py_DECREF(py_mountp); - Py_DECREF(py_tuple); + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); } endmntent(file); return py_retlist; @@ -279,21 +288,18 @@ psutil_linux_sysinfo(PyObject *self, PyObject *args) { /* * Return process CPU affinity as a Python list - * The dual implementation exists because of: - * https://github.com/giampaolo/psutil/issues/536 */ - -#ifdef CPU_ALLOC +#ifdef PSUTIL_HAVE_CPU_AFFINITY static PyObject * psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { int cpu, ncpus, count, cpucount_s; - long pid; + pid_t pid; size_t setsize; cpu_set_t *mask = NULL; PyObject *py_list = NULL; - if (!PyArg_ParseTuple(args, "l", &pid)) + if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; ncpus = NCPUS_START; while (1) { @@ -347,50 +353,8 @@ error: Py_XDECREF(py_list); return NULL; } -#else -/* - * Alternative implementation in case CPU_ALLOC is not defined. - */ -static PyObject * -psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { - cpu_set_t cpuset; - unsigned int len = sizeof(cpu_set_t); - long pid; - int i; - PyObject* py_retlist = NULL; - PyObject *py_cpu_num = NULL; - - if (!PyArg_ParseTuple(args, "l", &pid)) - return NULL; - CPU_ZERO(&cpuset); - if (sched_getaffinity(pid, len, &cpuset) < 0) - return PyErr_SetFromErrno(PyExc_OSError); - - py_retlist = PyList_New(0); - if (py_retlist == NULL) - goto error; - for (i = 0; i < CPU_SETSIZE; ++i) { - if (CPU_ISSET(i, &cpuset)) { - py_cpu_num = Py_BuildValue("i", i); - if (py_cpu_num == NULL) - goto error; - if (PyList_Append(py_retlist, py_cpu_num)) - goto error; - Py_DECREF(py_cpu_num); - } - } - - return py_retlist; - -error: - Py_XDECREF(py_cpu_num); - Py_XDECREF(py_retlist); - return NULL; -} -#endif - /* * Set process CPU affinity; expects a bitmask */ @@ -398,12 +362,12 @@ static PyObject * psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { cpu_set_t cpu_set; size_t len; - long pid; + pid_t pid; int i, seq_len; PyObject *py_cpu_set; PyObject *py_cpu_seq = NULL; - if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set)) return NULL; if (!PySequence_Check(py_cpu_set)) { @@ -432,7 +396,6 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { CPU_SET(value, &cpu_set); } - len = sizeof(cpu_set); if (sched_setaffinity(pid, len, &cpu_set)) { PyErr_SetFromErrno(PyExc_OSError); @@ -447,6 +410,7 @@ error: Py_DECREF(py_cpu_seq); return NULL; } +#endif /* PSUTIL_HAVE_CPU_AFFINITY */ /* @@ -481,8 +445,9 @@ psutil_users(PyObject *self, PyObject *args) { py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); if (! py_hostname) goto error; + py_tuple = Py_BuildValue( - "(OOOfOi)", + "OOOfO" _Py_PARSE_PID, py_username, // username py_tty, // tty py_hostname, // hostname @@ -494,10 +459,10 @@ psutil_users(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); } endutent(); return py_retlist; @@ -577,10 +542,10 @@ error: /* - * Define the psutil C module methods and initialize the module. + * Module init. */ -static PyMethodDef -PsutilMethods[] = { + +static PyMethodDef mod_methods[] = { // --- per-process functions #if PSUTIL_HAVE_IOPRIO @@ -589,10 +554,12 @@ PsutilMethods[] = { {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS, "Set process I/O priority"}, #endif +#ifdef PSUTIL_HAVE_CPU_AFFINITY {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, "Return process CPU affinity as a Python long (the bitmask)."}, {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, "Set process CPU affinity; expects a bitmask."}, +#endif // --- system related functions @@ -612,7 +579,6 @@ PsutilMethods[] = { {"linux_prlimit", psutil_linux_prlimit, METH_VARARGS, "Get or set process resource limits."}, #endif - // --- others {"set_testing", psutil_set_testing, METH_NOARGS, "Set psutil in testing mode"}, @@ -620,75 +586,51 @@ PsutilMethods[] = { {NULL, NULL, 0, NULL} }; -struct module_state { - PyObject *error; -}; #if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif + #define INITERR return NULL -#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_linux", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL + }; -static int -psutil_linux_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} + PyObject *PyInit__psutil_linux(void) +#else /* PY_MAJOR_VERSION */ + #define INITERR return -static int -psutil_linux_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - -static struct PyModuleDef - moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_linux", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_linux_traverse, - psutil_linux_clear, - NULL -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_linux(void) - -#else -#define INITERROR return - -void init_psutil_linux(void) -#endif + void init_psutil_linux(void) +#endif /* PY_MAJOR_VERSION */ { PyObject *v; #if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); + PyObject *mod = PyModule_Create(&moduledef); #else - PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods); + PyObject *mod = Py_InitModule("_psutil_linux", mod_methods); #endif - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; - PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR; #if PSUTIL_HAVE_PRLIMIT - PyModule_AddIntConstant(module, "RLIMIT_AS", RLIMIT_AS); - PyModule_AddIntConstant(module, "RLIMIT_CORE", RLIMIT_CORE); - PyModule_AddIntConstant(module, "RLIMIT_CPU", RLIMIT_CPU); - PyModule_AddIntConstant(module, "RLIMIT_DATA", RLIMIT_DATA); - PyModule_AddIntConstant(module, "RLIMIT_FSIZE", RLIMIT_FSIZE); - PyModule_AddIntConstant(module, "RLIMIT_LOCKS", RLIMIT_LOCKS); - PyModule_AddIntConstant(module, "RLIMIT_MEMLOCK", RLIMIT_MEMLOCK); - PyModule_AddIntConstant(module, "RLIMIT_NOFILE", RLIMIT_NOFILE); - PyModule_AddIntConstant(module, "RLIMIT_NPROC", RLIMIT_NPROC); - PyModule_AddIntConstant(module, "RLIMIT_RSS", RLIMIT_RSS); - PyModule_AddIntConstant(module, "RLIMIT_STACK", RLIMIT_STACK); + if (PyModule_AddIntConstant(mod, "RLIMIT_AS", RLIMIT_AS)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_CORE", RLIMIT_CORE)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_CPU", RLIMIT_CPU)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_DATA", RLIMIT_DATA)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_FSIZE", RLIMIT_FSIZE)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", RLIMIT_LOCKS)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", RLIMIT_MEMLOCK)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", RLIMIT_NOFILE)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_NPROC", RLIMIT_NPROC)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_RSS", RLIMIT_RSS)) INITERR; + if (PyModule_AddIntConstant(mod, "RLIMIT_STACK", RLIMIT_STACK)) INITERR; #if defined(HAVE_LONG_LONG) if (sizeof(RLIM_INFINITY) > sizeof(long)) { @@ -699,32 +641,33 @@ void init_psutil_linux(void) v = PyLong_FromLong((long) RLIM_INFINITY); } if (v) { - PyModule_AddObject(module, "RLIM_INFINITY", v); + PyModule_AddObject(mod, "RLIM_INFINITY", v); } #ifdef RLIMIT_MSGQUEUE - PyModule_AddIntConstant(module, "RLIMIT_MSGQUEUE", RLIMIT_MSGQUEUE); + if (PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", RLIMIT_MSGQUEUE)) INITERR; #endif #ifdef RLIMIT_NICE - PyModule_AddIntConstant(module, "RLIMIT_NICE", RLIMIT_NICE); + if (PyModule_AddIntConstant(mod, "RLIMIT_NICE", RLIMIT_NICE)) INITERR; #endif #ifdef RLIMIT_RTPRIO - PyModule_AddIntConstant(module, "RLIMIT_RTPRIO", RLIMIT_RTPRIO); + if (PyModule_AddIntConstant(mod, "RLIMIT_RTPRIO", RLIMIT_RTPRIO)) INITERR; #endif #ifdef RLIMIT_RTTIME - PyModule_AddIntConstant(module, "RLIMIT_RTTIME", RLIMIT_RTTIME); + if (PyModule_AddIntConstant(mod, "RLIMIT_RTTIME", RLIMIT_RTTIME)) INITERR; #endif #ifdef RLIMIT_SIGPENDING - PyModule_AddIntConstant(module, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING); + if (PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING)) + INITERR; #endif #endif - PyModule_AddIntConstant(module, "DUPLEX_HALF", DUPLEX_HALF); - PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL); - PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN); + if (PyModule_AddIntConstant(mod, "DUPLEX_HALF", DUPLEX_HALF)) INITERR; + if (PyModule_AddIntConstant(mod, "DUPLEX_FULL", DUPLEX_FULL)) INITERR; + if (PyModule_AddIntConstant(mod, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN)) INITERR; - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; #if PY_MAJOR_VERSION >= 3 - return module; + return mod; #endif } diff --git a/third_party/python/psutil/psutil/_psutil_osx.c b/third_party/python/psutil/psutil/_psutil_osx.c index eaf4e514a11f..c51c1c788b11 100644 --- a/third_party/python/psutil/psutil/_psutil_osx.c +++ b/third_party/python/psutil/psutil/_psutil_osx.c @@ -88,15 +88,15 @@ psutil_sys_vminfo(vm_statistics_data_t *vmstat) { * https://github.com/giampaolo/psutil/issues/1291#issuecomment-396062519 */ int -psutil_task_for_pid(long pid, mach_port_t *task) +psutil_task_for_pid(pid_t pid, mach_port_t *task) { // See: https://github.com/giampaolo/psutil/issues/1181 kern_return_t err = KERN_SUCCESS; - err = task_for_pid(mach_task_self(), (pid_t)pid, task); + err = task_for_pid(mach_task_self(), pid, task); if (err != KERN_SUCCESS) { if (psutil_pid_exists(pid) == 0) - NoSuchProcess("task_for_pid() failed"); + NoSuchProcess("task_for_pid"); else if (psutil_is_zombie(pid) == 1) PyErr_SetString(ZombieProcessError, "task_for_pid() failed"); else { @@ -104,7 +104,7 @@ psutil_task_for_pid(long pid, mach_port_t *task) "task_for_pid() failed (pid=%ld, err=%i, errno=%i, msg='%s'); " "setting AccessDenied()", pid, err, errno, mach_error_string(err)); - AccessDenied("task_for_pid() failed"); + AccessDenied("task_for_pid"); } return 1; } @@ -133,12 +133,12 @@ psutil_pids(PyObject *self, PyObject *args) { // save the address of proclist so we can free it later orig_address = proclist; for (idx = 0; idx < num_processes; idx++) { - py_pid = Py_BuildValue("i", proclist->kp_proc.p_pid); + py_pid = PyLong_FromPid(proclist->kp_proc.p_pid); if (! py_pid) goto error; if (PyList_Append(py_retlist, py_pid)) goto error; - Py_DECREF(py_pid); + Py_CLEAR(py_pid); proclist++; } free(orig_address); @@ -164,12 +164,12 @@ error: */ static PyObject * psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { - long pid; + pid_t pid; struct kinfo_proc kp; PyObject *py_name; PyObject *py_retlist; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; @@ -183,8 +183,8 @@ psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { } py_retlist = Py_BuildValue( - "lllllllidiO", - (long)kp.kp_eproc.e_ppid, // (long) ppid + _Py_PARSE_PID "llllllidiO", + kp.kp_eproc.e_ppid, // (pid_t) ppid (long)kp.kp_eproc.e_pcred.p_ruid, // (long) real uid (long)kp.kp_eproc.e_ucred.cr_uid, // (long) effective uid (long)kp.kp_eproc.e_pcred.p_svuid, // (long) saved uid @@ -215,10 +215,10 @@ psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) { - long pid; + pid_t pid; struct proc_taskinfo pti; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) <= 0) return NULL; @@ -250,10 +250,10 @@ psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_name(PyObject *self, PyObject *args) { - long pid; + pid_t pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_get_kinfo_proc(pid, &kp) == -1) return NULL; @@ -267,10 +267,10 @@ psutil_proc_name(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_cwd(PyObject *self, PyObject *args) { - long pid; + pid_t pid; struct proc_vnodepathinfo pathinfo; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_proc_pidinfo( @@ -288,17 +288,17 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_exe(PyObject *self, PyObject *args) { - long pid; + pid_t pid; char buf[PATH_MAX]; int ret; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; errno = 0; - ret = proc_pidpath((pid_t)pid, &buf, sizeof(buf)); + ret = proc_pidpath(pid, &buf, sizeof(buf)); if (ret == 0) { if (pid == 0) - AccessDenied(""); + AccessDenied("automatically set for PID 0"); else psutil_raise_for_pid(pid, "proc_pidpath()"); return NULL; @@ -312,10 +312,10 @@ psutil_proc_exe(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_cmdline(PyObject *self, PyObject *args) { - long pid; + pid_t pid; PyObject *py_retlist = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; // get the commandline, defined in arch/osx/process_info.c @@ -329,10 +329,10 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_environ(PyObject *self, PyObject *args) { - long pid; + pid_t pid; PyObject *py_retdict = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; // get the environment block, defined in arch/osx/process_info.c @@ -422,7 +422,7 @@ psutil_in_shared_region(mach_vm_address_t addr, cpu_type_t type) { */ static PyObject * psutil_proc_memory_uss(PyObject *self, PyObject *args) { - long pid; + pid_t pid; size_t len; cpu_type_t cpu_type; size_t private_pages = 0; @@ -435,7 +435,7 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) { vm_region_top_info_data_t info; mach_port_t object_name; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_task_for_pid(pid, &task) != 0) @@ -653,7 +653,7 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_cputime)) goto error; - Py_DECREF(py_cputime); + Py_CLEAR(py_cputime); } ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, @@ -841,9 +841,9 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_dev); - Py_DECREF(py_mountp); - Py_DECREF(py_tuple); + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); } free(fs); @@ -865,7 +865,7 @@ error: */ static PyObject * psutil_proc_threads(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int err, ret; kern_return_t kr; unsigned int info_count = TASK_BASIC_INFO_COUNT; @@ -882,7 +882,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; if (psutil_task_for_pid(pid, &task) != 0) @@ -894,7 +894,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { if (err != KERN_SUCCESS) { // errcode 4 is "invalid argument" (access denied) if (err == 4) { - AccessDenied(""); + AccessDenied("task_info"); } else { // otherwise throw a runtime error with appropriate error code @@ -911,7 +911,6 @@ psutil_proc_threads(PyObject *self, PyObject *args) { } for (j = 0; j < thread_count; j++) { - py_tuple = NULL; thread_info_count = THREAD_INFO_MAX; kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t)thinfo_basic, &thread_info_count); @@ -934,7 +933,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } ret = vm_deallocate(task, (vm_address_t)thread_list, @@ -969,7 +968,7 @@ error: */ static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int pidinfo_result; int iterations; int i; @@ -986,7 +985,7 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); @@ -1043,10 +1042,8 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); - py_tuple = NULL; - Py_DECREF(py_path); - py_path = NULL; + Py_CLEAR(py_tuple); + Py_CLEAR(py_path); // --- /construct python list } } @@ -1073,7 +1070,7 @@ error: */ static PyObject * psutil_proc_connections(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int pidinfo_result; int iterations; int i; @@ -1093,8 +1090,10 @@ psutil_proc_connections(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, &py_af_filter, + &py_type_filter)) { goto error; + } if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); @@ -1127,7 +1126,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) { if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) { errno = 0; - nb = proc_pidfdinfo((pid_t)pid, fdp_pointer->proc_fd, + nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDSOCKETINFO, &si, sizeof(si)); // --- errors checking @@ -1226,7 +1225,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } else if (family == AF_UNIX) { py_laddr = PyUnicode_DecodeFSDefault( @@ -1248,9 +1247,9 @@ psutil_proc_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); - Py_DECREF(py_laddr); - Py_DECREF(py_raddr); + Py_CLEAR(py_tuple); + Py_CLEAR(py_laddr); + Py_CLEAR(py_raddr); } } } @@ -1275,22 +1274,22 @@ error: */ static PyObject * psutil_proc_num_fds(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int pidinfo_result; int num; struct proc_fdinfo *fds_pointer; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; - pidinfo_result = proc_pidinfo((pid_t)pid, PROC_PIDLISTFDS, 0, NULL, 0); + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (pidinfo_result <= 0) return PyErr_SetFromErrno(PyExc_OSError); fds_pointer = malloc(pidinfo_result); if (fds_pointer == NULL) return PyErr_NoMemory(); - pidinfo_result = proc_pidinfo((pid_t)pid, PROC_PIDLISTFDS, 0, fds_pointer, + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result); if (pidinfo_result <= 0) { free(fds_pointer); @@ -1370,7 +1369,7 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { goto error; if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info)) goto error; - Py_DECREF(py_ifc_info); + Py_CLEAR(py_ifc_info); } else { continue; @@ -1543,7 +1542,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) { goto error; if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info)) goto error; - Py_DECREF(py_disk_info); + Py_CLEAR(py_disk_info); CFRelease(parent_dict); IOObjectRelease(parent); @@ -1605,10 +1604,10 @@ psutil_users(PyObject *self, PyObject *args) { endutxent(); goto error; } - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); } endutxent(); @@ -1745,8 +1744,7 @@ error: /* * define the psutil C module methods and initialize the module. */ -static PyMethodDef -PsutilMethods[] = { +static PyMethodDef mod_methods[] = { // --- per-process functions {"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS, @@ -1816,96 +1814,93 @@ PsutilMethods[] = { }; -struct module_state { - PyObject *error; -}; - #if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif + #define INITERR return NULL -#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_osx", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL + }; -static int -psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} + PyObject *PyInit__psutil_osx(void) +#else /* PY_MAJOR_VERSION */ + #define INITERR return -static int -psutil_osx_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_osx", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_osx_traverse, - psutil_osx_clear, - NULL -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_osx(void) - -#else -#define INITERROR return - -void -init_psutil_osx(void) -#endif + void init_psutil_osx(void) +#endif /* PY_MAJOR_VERSION */ { #if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); + PyObject *mod = PyModule_Create(&moduledef); #else - PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); + PyObject *mod = Py_InitModule("_psutil_osx", mod_methods); #endif - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; if (psutil_setup() != 0) - INITERROR; + INITERR; - PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); + if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) + INITERR; // process status constants, defined in: // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149 - PyModule_AddIntConstant(module, "SIDL", SIDL); - PyModule_AddIntConstant(module, "SRUN", SRUN); - PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); - PyModule_AddIntConstant(module, "SSTOP", SSTOP); - PyModule_AddIntConstant(module, "SZOMB", SZOMB); + if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) + INITERR; + if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) + INITERR; + if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP)) + INITERR; + if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) + INITERR; + if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) + INITERR; // connection status constants - PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED); - PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING); - PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT); - PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN); - PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED); - PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT); - PyModule_AddIntConstant(module, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED); - PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1); - PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2); - PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK); - PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT); - PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); + if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK)) + INITERR; + if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT)) + INITERR; + if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE)) + INITERR; // Exception. ZombieProcessError = PyErr_NewException( "_psutil_osx.ZombieProcessError", NULL, NULL); + if (ZombieProcessError == NULL) + INITERR; Py_INCREF(ZombieProcessError); - PyModule_AddObject(module, "ZombieProcessError", ZombieProcessError); + if (PyModule_AddObject(mod, "ZombieProcessError", ZombieProcessError)) { + Py_DECREF(ZombieProcessError); + INITERR; + } - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; #if PY_MAJOR_VERSION >= 3 - return module; + return mod; #endif } diff --git a/third_party/python/psutil/psutil/_psutil_posix.c b/third_party/python/psutil/psutil/_psutil_posix.c index d9a8f6d1d61d..38483b3b024a 100644 --- a/third_party/python/psutil/psutil/_psutil_posix.c +++ b/third_party/python/psutil/psutil/_psutil_posix.c @@ -51,7 +51,7 @@ * -1: error (Python exception is set) */ int -psutil_pid_exists(long pid) { +psutil_pid_exists(pid_t pid) { int ret; // No negative PID exists, plus -1 is an alias for sending signal @@ -71,12 +71,7 @@ psutil_pid_exists(long pid) { #endif } -#if defined(PSUTIL_OSX) - ret = kill((pid_t)pid , 0); -#else ret = kill(pid , 0); -#endif - if (ret == 0) return 1; else { @@ -111,23 +106,14 @@ psutil_pid_exists(long pid) { * If none of this is true we giveup and raise RuntimeError(msg). * This will always set a Python exception and return NULL. */ -int -psutil_raise_for_pid(long pid, char *syscall_name) { - // Set exception to AccessDenied if pid exists else NoSuchProcess. - if (errno != 0) { - // Unlikely we get here. - PyErr_SetFromErrno(PyExc_OSError); - return 0; - } - else if (psutil_pid_exists(pid) == 0) { - psutil_debug("%s syscall failed and PID %i no longer exists; " - "assume NoSuchProcess", syscall_name, pid); - NoSuchProcess(""); - } - else { - PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall_name); - } - return 0; +void +psutil_raise_for_pid(long pid, char *syscall) { + if (errno != 0) // unlikely + PyErr_SetFromOSErrnoWithSyscall(syscall); + else if (psutil_pid_exists(pid) == 0) + NoSuchProcess(syscall); + else + PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall); } @@ -136,11 +122,11 @@ psutil_raise_for_pid(long pid, char *syscall_name) { */ static PyObject * psutil_posix_getpriority(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int priority; errno = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; #ifdef PSUTIL_OSX @@ -159,11 +145,11 @@ psutil_posix_getpriority(PyObject *self, PyObject *args) { */ static PyObject * psutil_posix_setpriority(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int priority; int retval; - if (! PyArg_ParseTuple(args, "li", &pid, &priority)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &priority)) return NULL; #ifdef PSUTIL_OSX @@ -206,8 +192,8 @@ psutil_convert_ipaddr(struct sockaddr *addr, int family) { // XXX we get here on FreeBSD when processing 'lo' / AF_INET6 // broadcast. Not sure what to do other than returning None. // ifconfig does not show anything BTW. - //PyErr_Format(PyExc_RuntimeError, gai_strerror(err)); - //return NULL; + // PyErr_Format(PyExc_RuntimeError, gai_strerror(err)); + // return NULL; Py_INCREF(Py_None); return Py_None; } @@ -324,11 +310,11 @@ psutil_net_if_addrs(PyObject* self, PyObject* args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); - Py_DECREF(py_address); - Py_DECREF(py_netmask); - Py_DECREF(py_broadcast); - Py_DECREF(py_ptp); + Py_CLEAR(py_tuple); + Py_CLEAR(py_address); + Py_CLEAR(py_netmask); + Py_CLEAR(py_broadcast); + Py_CLEAR(py_ptp); } freeifaddrs(ifaddr); @@ -354,7 +340,7 @@ error: static PyObject * psutil_net_if_mtu(PyObject *self, PyObject *args) { char *nic_name; - int sock = 0; + int sock = -1; int ret; #ifdef PSUTIL_SUNOS10 struct lifreq lifr; @@ -387,7 +373,7 @@ psutil_net_if_mtu(PyObject *self, PyObject *args) { #endif error: - if (sock != 0) + if (sock != -1) close(sock); return PyErr_SetFromErrno(PyExc_OSError); } @@ -401,7 +387,7 @@ error: static PyObject * psutil_net_if_flags(PyObject *self, PyObject *args) { char *nic_name; - int sock = 0; + int sock = -1; int ret; struct ifreq ifr; @@ -424,7 +410,7 @@ psutil_net_if_flags(PyObject *self, PyObject *args) { return Py_BuildValue("O", Py_False); error: - if (sock != 0) + if (sock != -1) close(sock); return PyErr_SetFromErrno(PyExc_OSError); } @@ -579,7 +565,7 @@ int psutil_get_nic_speed(int ifm_active) { static PyObject * psutil_net_if_duplex_speed(PyObject *self, PyObject *args) { char *nic_name; - int sock = 0; + int sock = -1; int ret; int duplex; int speed; @@ -591,7 +577,7 @@ psutil_net_if_duplex_speed(PyObject *self, PyObject *args) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) - goto error; + return PyErr_SetFromErrno(PyExc_OSError); strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); // speed / duplex @@ -614,20 +600,19 @@ psutil_net_if_duplex_speed(PyObject *self, PyObject *args) { close(sock); return Py_BuildValue("[ii]", duplex, speed); - -error: - if (sock != 0) - close(sock); - return PyErr_SetFromErrno(PyExc_OSError); } #endif // net_if_stats() macOS/BSD implementation +#ifdef __cplusplus +extern "C" { +#endif + + /* * define the psutil C module methods and initialize the module. */ -static PyMethodDef -PsutilMethods[] = { +static PyMethodDef mod_methods[] = { {"getpriority", psutil_posix_getpriority, METH_VARARGS, "Return process priority"}, {"setpriority", psutil_posix_setpriority, METH_VARARGS, @@ -645,71 +630,48 @@ PsutilMethods[] = { {NULL, NULL, 0, NULL} }; -struct module_state { - PyObject *error; -}; #if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -#endif + #define INITERR return NULL -#ifdef __cplusplus -extern "C" { -#endif + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_psutil_posix", + NULL, + -1, + mod_methods, + NULL, + NULL, + NULL, + NULL + }; -#if PY_MAJOR_VERSION >= 3 + PyObject *PyInit__psutil_posix(void) +#else /* PY_MAJOR_VERSION */ + #define INITERR return -static int -psutil_posix_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} - - -static int -psutil_posix_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_posix", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_posix_traverse, - psutil_posix_clear, - NULL -}; - -#define INITERROR return NULL - -PyMODINIT_FUNC PyInit__psutil_posix(void) - -#else -#define INITERROR return - -void init_psutil_posix(void) -#endif + void init_psutil_posix(void) +#endif /* PY_MAJOR_VERSION */ { #if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); + PyObject *mod = PyModule_Create(&moduledef); #else - PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods); + PyObject *mod = Py_InitModule("_psutil_posix", mod_methods); +#endif + if (mod == NULL) + INITERR; + +#if defined(PSUTIL_BSD) || \ + defined(PSUTIL_OSX) || \ + defined(PSUTIL_SUNOS) || \ + defined(PSUTIL_AIX) + if (PyModule_AddIntConstant(mod, "AF_LINK", AF_LINK)) INITERR; #endif -#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) || defined(PSUTIL_AIX) - PyModule_AddIntConstant(module, "AF_LINK", AF_LINK); -#endif - - if (module == NULL) - INITERROR; + if (mod == NULL) + INITERR; #if PY_MAJOR_VERSION >= 3 - return module; + return mod; #endif } diff --git a/third_party/python/psutil/psutil/_psutil_posix.h b/third_party/python/psutil/psutil/_psutil_posix.h index fe25b366950b..59b9e53238af 100644 --- a/third_party/python/psutil/psutil/_psutil_posix.h +++ b/third_party/python/psutil/psutil/_psutil_posix.h @@ -4,5 +4,5 @@ * found in the LICENSE file. */ -int psutil_pid_exists(long pid); -void psutil_raise_for_pid(long pid, char *msg); +int psutil_pid_exists(pid_t pid); +void psutil_raise_for_pid(pid_t pid, char *msg); diff --git a/third_party/python/psutil/psutil/_psutil_sunos.c b/third_party/python/psutil/psutil/_psutil_sunos.c index 49100ec66773..6548640b70cf 100644 --- a/third_party/python/psutil/psutil/_psutil_sunos.c +++ b/third_party/python/psutil/psutil/_psutil_sunos.c @@ -12,12 +12,8 @@ /* fix compilation issue on SunOS 5.10, see: * https://github.com/giampaolo/psutil/issues/421 * https://github.com/giampaolo/psutil/issues/1077 - * http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt - * - * Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\ */ -#define NEW_MIB_COMPLIANT 1 #define _STRUCTURED_PROC 1 #include @@ -44,6 +40,14 @@ #include #include #include +#ifndef NEW_MIB_COMPLIANT +/* + * Solaris introduced NEW_MIB_COMPLIANT macro with Update 4. + * See https://github.com/giampaolo/psutil/issues/421 + * Prior to Update 4, one has to include mib2 by hand. + */ +#include +#endif #include #include #include // fabs() @@ -271,7 +275,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) { goto error; if (! info.pr_envp) { - AccessDenied(""); + AccessDenied("/proc/pid/psinfo struct not set"); goto error; } @@ -300,8 +304,8 @@ psutil_proc_environ(PyObject *self, PyObject *args) { if (PyDict_SetItem(py_retdict, py_envname, py_envval) < 0) goto error; - Py_DECREF(py_envname); - Py_DECREF(py_envval); + Py_CLEAR(py_envname); + Py_CLEAR(py_envval); } psutil_free_cstrings_array(env, env_count); @@ -492,7 +496,7 @@ proc_io_counters(PyObject* self, PyObject* args) { info.pr_inblk, info.pr_oublk); } - */ +*/ /* @@ -655,10 +659,10 @@ psutil_users(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_username); - Py_DECREF(py_tty); - Py_DECREF(py_hostname); - Py_DECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); } endutxent(); @@ -714,9 +718,9 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_dev); - Py_DECREF(py_mountp); - Py_DECREF(py_tuple); + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); } fclose(file); return py_retlist; @@ -767,8 +771,7 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_cputime)) goto error; - Py_DECREF(py_cputime); - py_cputime = NULL; + Py_CLEAR(py_cputime); } } @@ -824,7 +827,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) { if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_disk_info)) goto error; - Py_DECREF(py_disk_info); + Py_CLEAR(py_disk_info); } } ksp = ksp->ks_next; @@ -959,8 +962,8 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_path); - Py_DECREF(py_tuple); + Py_CLEAR(py_path); + Py_CLEAR(py_tuple); // increment pointer p += 1; @@ -1075,7 +1078,7 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { goto error; if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info)) goto error; - Py_DECREF(py_ifc_info); + Py_CLEAR(py_ifc_info); goto next; next: @@ -1120,7 +1123,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { mib2_udp6Entry_t ude6; #endif char buf[512]; - int i, flags, getcode, num_ent, state; + int i, flags, getcode, num_ent, state, ret; char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN]; int lport, rport; int processed_pid; @@ -1147,7 +1150,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; } - int ret = ioctl(sd, I_PUSH, "tcp"); + ret = ioctl(sd, I_PUSH, "tcp"); if (ret == -1) { PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -1273,7 +1276,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } } #if defined(AF_INET6) @@ -1287,7 +1290,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { #ifdef NEW_MIB_COMPLIANT processed_pid = tp6.tcp6ConnCreationProcess; #else - processed_pid = 0; + processed_pid = 0; #endif if (pid != -1 && processed_pid != pid) continue; @@ -1316,14 +1319,14 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } } #endif // UDPv4 else if (mibhdr.level == MIB2_UDP || mibhdr.level == MIB2_UDP_ENTRY) { num_ent = mibhdr.len / sizeof(mib2_udpEntry_t); - assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr.len); + assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr.len); for (i = 0; i < num_ent; i++) { memcpy(&ude, databuf.buf + i * sizeof ude, sizeof ude); #ifdef NEW_MIB_COMPLIANT @@ -1355,7 +1358,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } } #if defined(AF_INET6) @@ -1388,7 +1391,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } } #endif @@ -1561,7 +1564,7 @@ psutil_net_if_stats(PyObject* self, PyObject* args) { goto error; if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info)) goto error; - Py_DECREF(py_ifc_info); + Py_CLEAR(py_ifc_info); } } @@ -1748,7 +1751,14 @@ void init_psutil_sunos(void) PyModule_AddIntConstant(module, "SSTOP", SSTOP); PyModule_AddIntConstant(module, "SIDL", SIDL); PyModule_AddIntConstant(module, "SONPROC", SONPROC); +#ifdef SWAIT PyModule_AddIntConstant(module, "SWAIT", SWAIT); +#else + /* sys/proc.h started defining SWAIT somewhere + * after Update 3 and prior to Update 5 included. + */ + PyModule_AddIntConstant(module, "SWAIT", 0); +#endif PyModule_AddIntConstant(module, "PRNODEV", PRNODEV); // for process tty diff --git a/third_party/python/psutil/psutil/_psutil_windows.c b/third_party/python/psutil/psutil/_psutil_windows.c index f071648f1886..82fa518eeb7d 100644 --- a/third_party/python/psutil/psutil/_psutil_windows.c +++ b/third_party/python/psutil/psutil/_psutil_windows.c @@ -19,83 +19,29 @@ #include #include -#include +#include // memory_info(), memory_maps() #include -#include // disk_io_counters() -#include -#include +#include // threads(), PROCESSENTRY32 #include // users() -#include // cpu_freq() -#if (_WIN32_WINNT >= 0x0600) // Windows >= Vista -#include // net_connections() -#endif // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") -#include "arch/windows/ntextapi.h" -#include "arch/windows/global.h" +#include "_psutil_common.h" #include "arch/windows/security.h" +#include "arch/windows/process_utils.h" #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" -#include "arch/windows/inet_ntop.h" +#include "arch/windows/disk.h" +#include "arch/windows/cpu.h" +#include "arch/windows/net.h" #include "arch/windows/services.h" +#include "arch/windows/socks.h" #include "arch/windows/wmi.h" -#include "_psutil_common.h" - -/* - * ============================================================================ - * Utilities - * ============================================================================ - */ - -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -#define LO_T 1e-7 -#define HI_T 429.4967296 -#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) -#ifndef AF_INET6 -#define AF_INET6 23 -#endif - - -PIP_ADAPTER_ADDRESSES -psutil_get_nic_addresses() { - // allocate a 15 KB buffer to start with - int outBufLen = 15000; - DWORD dwRetVal = 0; - ULONG attempts = 0; - PIP_ADAPTER_ADDRESSES pAddresses = NULL; - - do { - pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); - if (pAddresses == NULL) { - PyErr_NoMemory(); - return NULL; - } - - dwRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, - &outBufLen); - if (dwRetVal == ERROR_BUFFER_OVERFLOW) { - free(pAddresses); - pAddresses = NULL; - } - else { - break; - } - - attempts++; - } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); - - if (dwRetVal != NO_ERROR) { - PyErr_SetString( - PyExc_RuntimeError, "GetAdaptersAddresses() syscall failed."); - return NULL; - } - - return pAddresses; -} +// Raised by Process.wait(). +static PyObject *TimeoutExpired; +static PyObject *TimeoutAbandoned; /* @@ -107,8 +53,8 @@ psutil_get_num_cpus(int fail_on_err) { unsigned int ncpus = 0; // Minimum requirement: Windows 7 - if (psutil_GetActiveProcessorCount != NULL) { - ncpus = psutil_GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + if (GetActiveProcessorCount != NULL) { + ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); if ((ncpus == 0) && (fail_on_err == 1)) { PyErr_SetFromWindowsErr(0); } @@ -127,56 +73,19 @@ psutil_get_num_cpus(int fail_on_err) { } -/* - * ============================================================================ - * Public Python API - * ============================================================================ - */ - -// Raised by Process.wait(). -static PyObject *TimeoutExpired; -static PyObject *TimeoutAbandoned; - /* * Return a Python float representing the system uptime expressed in seconds * since the epoch. */ static PyObject * psutil_boot_time(PyObject *self, PyObject *args) { - ULONGLONG uptime; - time_t pt; + ULONGLONG upTime; FILETIME fileTime; - ULONGLONG ll; GetSystemTimeAsFileTime(&fileTime); - /* - HUGE thanks to: - http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry - - This function converts the FILETIME structure to the 32 bit - Unix time structure. - The time_t is a 32-bit value for the number of seconds since - January 1, 1970. A FILETIME is a 64-bit for the number of - 100-nanosecond periods since January 1, 1601. Convert by - subtracting the number of 100-nanosecond period between 01-01-1970 - and 01-01-1601, from time_t the divide by 1e+7 to get to the same - base granularity. - */ - ll = (((ULONGLONG) - (fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime; - pt = (time_t)((ll - 116444736000000000ull) / 10000000ull); - - if (psutil_GetTickCount64 != NULL) { - // Windows >= Vista - uptime = psutil_GetTickCount64() / 1000ull; - } - else { - // Windows XP. - // GetTickCount() time will wrap around to zero if the - // system is run continuously for 49.7 days. - uptime = (ULONGLONG)GetTickCount() / 1000ull; - } - return Py_BuildValue("K", pt - uptime); + // Number of milliseconds that have elapsed since the system was started. + upTime = GetTickCount64() / 1000ull; + return Py_BuildValue("d", psutil_FiletimeToUnixTime(fileTime) - upTime); } @@ -185,10 +94,10 @@ psutil_boot_time(PyObject *self, PyObject *args) { */ static PyObject * psutil_pid_exists(PyObject *self, PyObject *args) { - long pid; + DWORD pid; int status; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; status = psutil_pid_is_running(pid); @@ -216,12 +125,12 @@ psutil_pids(PyObject *self, PyObject *args) { goto error; for (i = 0; i < numberOfReturnedPIDs; i++) { - py_pid = Py_BuildValue("I", proclist[i]); + py_pid = PyLong_FromPid(proclist[i]); if (!py_pid) goto error; if (PyList_Append(py_retlist, py_pid)) goto error; - Py_DECREF(py_pid); + Py_CLEAR(py_pid); } // free C array allocated for PIDs @@ -243,13 +152,12 @@ error: static PyObject * psutil_proc_kill(PyObject *self, PyObject *args) { HANDLE hProcess; - DWORD err; - long pid; + DWORD pid; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (pid == 0) - return AccessDenied(""); + return AccessDenied("automatically set for PID 0"); hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (hProcess == NULL) { @@ -257,7 +165,7 @@ psutil_proc_kill(PyObject *self, PyObject *args) { // see https://github.com/giampaolo/psutil/issues/24 psutil_debug("OpenProcess -> ERROR_INVALID_PARAMETER turned " "into NoSuchProcess"); - NoSuchProcess(""); + NoSuchProcess("OpenProcess"); } else { PyErr_SetFromWindowsErr(0); @@ -265,13 +173,11 @@ psutil_proc_kill(PyObject *self, PyObject *args) { return NULL; } - // kill the process if (! TerminateProcess(hProcess, SIGTERM)) { - err = GetLastError(); - // See: https://github.com/giampaolo/psutil/issues/1099 - if (err != ERROR_ACCESS_DENIED) { + // ERROR_ACCESS_DENIED may happen if the process already died. See: + // https://github.com/giampaolo/psutil/issues/1099 + if (GetLastError() != ERROR_ACCESS_DENIED) { PyErr_SetFromOSErrnoWithSyscall("TerminateProcess"); - CloseHandle(hProcess); return NULL; } } @@ -289,13 +195,13 @@ psutil_proc_wait(PyObject *self, PyObject *args) { HANDLE hProcess; DWORD ExitCode; DWORD retVal; - long pid; + DWORD pid; long timeout; - if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "l", &pid, &timeout)) return NULL; if (pid == 0) - return AccessDenied(""); + return AccessDenied("automatically set for PID 0"); hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); @@ -305,8 +211,10 @@ psutil_proc_wait(PyObject *self, PyObject *args) { // return None instead. Py_RETURN_NONE; } - else - return PyErr_SetFromWindowsErr(0); + else { + PyErr_SetFromWindowsErr(0); + return NULL; + } } // wait until the process has terminated @@ -357,12 +265,12 @@ psutil_proc_wait(PyObject *self, PyObject *args) { * Return a Python tuple (user_time, kernel_time) */ static PyObject * -psutil_proc_cpu_times(PyObject *self, PyObject *args) { - long pid; +psutil_proc_times(PyObject *self, PyObject *args) { + DWORD pid; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); @@ -373,7 +281,7 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) { if (GetLastError() == ERROR_ACCESS_DENIED) { // usually means the process has died so we throw a NoSuchProcess // here - NoSuchProcess(""); + NoSuchProcess("GetProcessTimes"); } else { PyErr_SetFromWindowsErr(0); @@ -394,192 +302,30 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) { * below from Python's Modules/posixmodule.c */ return Py_BuildValue( - "(dd)", - (double)(ftUser.dwHighDateTime * 429.4967296 + \ - ftUser.dwLowDateTime * 1e-7), - (double)(ftKernel.dwHighDateTime * 429.4967296 + \ - ftKernel.dwLowDateTime * 1e-7) + "(ddd)", + (double)(ftUser.dwHighDateTime * HI_T + \ + ftUser.dwLowDateTime * LO_T), + (double)(ftKernel.dwHighDateTime * HI_T + \ + ftKernel.dwLowDateTime * LO_T), + psutil_FiletimeToUnixTime(ftCreate) ); } -/* - * Return a Python float indicating the process create time expressed in - * seconds since the epoch. - */ -static PyObject * -psutil_proc_create_time(PyObject *self, PyObject *args) { - long pid; - long long unix_time; - HANDLE hProcess; - FILETIME ftCreate, ftExit, ftKernel, ftUser; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - - // special case for PIDs 0 and 4, return system boot time - if (0 == pid || 4 == pid) - return psutil_boot_time(NULL, NULL); - - hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); - if (hProcess == NULL) - return NULL; - if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { - if (GetLastError() == ERROR_ACCESS_DENIED) { - // usually means the process has died so we throw a - // NoSuchProcess here - NoSuchProcess(""); - } - else { - PyErr_SetFromWindowsErr(0); - } - CloseHandle(hProcess); - return NULL; - } - - CloseHandle(hProcess); - - /* - // Make sure the process is not gone as OpenProcess alone seems to be - // unreliable in doing so (it seems a previous call to p.wait() makes - // it unreliable). - // This check is important as creation time is used to make sure the - // process is still running. - ret = GetExitCodeProcess(hProcess, &exitCode); - CloseHandle(hProcess); - if (ret != 0) { - if (exitCode != STILL_ACTIVE) - return NoSuchProcess(""); - } - else { - // Ignore access denied as it means the process is still alive. - // For all other errors, we want an exception. - if (GetLastError() != ERROR_ACCESS_DENIED) - return PyErr_SetFromWindowsErr(0); - } - */ - - // Convert the FILETIME structure to a Unix time. - // It's the best I could find by googling and borrowing code here - // and there. The time returned has a precision of 1 second. - unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; - unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; - unix_time /= 10000000; - return Py_BuildValue("d", (double)unix_time); -} - - -/* - * Return the number of active, logical CPUs. - */ -static PyObject * -psutil_cpu_count_logical(PyObject *self, PyObject *args) { - unsigned int ncpus; - - ncpus = psutil_get_num_cpus(0); - if (ncpus != 0) - return Py_BuildValue("I", ncpus); - else - Py_RETURN_NONE; // mimick os.cpu_count() -} - - -/* - * Return the number of physical CPU cores (hyper-thread CPUs count - * is excluded). - */ -static PyObject * -psutil_cpu_count_phys(PyObject *self, PyObject *args) { - DWORD rc; - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL; - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL; - DWORD length = 0; - DWORD offset = 0; - DWORD ncpus = 0; - DWORD prev_processor_info_size = 0; - - // GetLogicalProcessorInformationEx() is available from Windows 7 - // onward. Differently from GetLogicalProcessorInformation() - // it supports process groups, meaning this is able to report more - // than 64 CPUs. See: - // https://bugs.python.org/issue33166 - if (psutil_GetLogicalProcessorInformationEx == NULL) { - psutil_debug("Win < 7; cpu_count_phys() forced to None"); - Py_RETURN_NONE; - } - - while (1) { - rc = psutil_GetLogicalProcessorInformationEx( - RelationAll, buffer, &length); - if (rc == FALSE) { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - if (buffer) { - free(buffer); - } - buffer = \ - (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length); - if (NULL == buffer) { - PyErr_NoMemory(); - return NULL; - } - } - else { - psutil_debug("GetLogicalProcessorInformationEx() returned ", - GetLastError()); - goto return_none; - } - } - else { - break; - } - } - - ptr = buffer; - while (offset < length) { - // Advance ptr by the size of the previous - // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. - ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)\ - (((char*)ptr) + prev_processor_info_size); - - if (ptr->Relationship == RelationProcessorCore) { - ncpus += 1; - } - - // When offset == length, we've reached the last processor - // info struct in the buffer. - offset += ptr->Size; - prev_processor_info_size = ptr->Size; - } - - free(buffer); - if (ncpus != 0) { - return Py_BuildValue("I", ncpus); - } - else { - psutil_debug("GetLogicalProcessorInformationEx() count was 0"); - Py_RETURN_NONE; // mimick os.cpu_count() - } - -return_none: - if (buffer != NULL) - free(buffer); - Py_RETURN_NONE; -} - - /* * Return process cmdline as a Python list of cmdline arguments. */ static PyObject * psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) { - long pid; + DWORD pid; int pid_return; int use_peb; PyObject *py_usepeb = Py_True; static char *keywords[] = {"pid", "use_peb", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "i|O", - keywords, &pid, &py_usepeb)) { + if (!PyArg_ParseTupleAndKeywords(args, kwdict, _Py_PARSE_PID "|O", + keywords, &pid, &py_usepeb)) + { return NULL; } if ((pid == 0) || (pid == 4)) @@ -587,7 +333,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) { pid_return = psutil_pid_is_running(pid); if (pid_return == 0) - return NoSuchProcess(""); + return NoSuchProcess("psutil_pid_is_running"); if (pid_return == -1) return NULL; @@ -601,17 +347,17 @@ psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) { */ static PyObject * psutil_proc_environ(PyObject *self, PyObject *args) { - long pid; + DWORD pid; int pid_return; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if ((pid == 0) || (pid == 4)) return Py_BuildValue("s", ""); pid_return = psutil_pid_is_running(pid); if (pid_return == 0) - return NoSuchProcess(""); + return NoSuchProcess("psutil_pid_is_running"); if (pid_return == -1) return NULL; @@ -620,87 +366,74 @@ psutil_proc_environ(PyObject *self, PyObject *args) { /* - * Return process executable path. + * Return process executable path. Works for all processes regardless of + * privilege. NtQuerySystemInformation has some sort of internal cache, + * since it succeeds even when a process is gone (but not if a PID never + * existed). */ static PyObject * psutil_proc_exe(PyObject *self, PyObject *args) { - long pid; - HANDLE hProcess; - wchar_t exe[MAX_PATH]; -#if (_WIN32_WINNT >= 0x0600) // >= Vista - unsigned int size = sizeof(exe); -#endif + DWORD pid; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x100; + SYSTEM_PROCESS_ID_INFORMATION processIdInfo; + PyObject *py_exe; - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); - if (NULL == hProcess) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; - // Here we differentiate between XP and Vista+ because - // QueryFullProcessImageNameW is better than GetProcessImageFileNameW - // (avoid using QueryDosDevice on the returned path), see: - // https://github.com/giampaolo/psutil/issues/1394 -#if (_WIN32_WINNT >= 0x0600) // Windows >= Vista - memset(exe, 0, MAX_PATH); - if (QueryFullProcessImageNameW(hProcess, 0, exe, &size) == 0) { - PyErr_SetFromOSErrnoWithSyscall("QueryFullProcessImageNameW"); - CloseHandle(hProcess); - return NULL; + if (pid == 0) + return AccessDenied("forced for PID 0"); + + buffer = MALLOC_ZERO(bufferSize); + if (! buffer) + return PyErr_NoMemory(); + processIdInfo.ProcessId = (HANDLE)(ULONG_PTR)pid; + processIdInfo.ImageName.Length = 0; + processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize; + processIdInfo.ImageName.Buffer = buffer; + + status = NtQuerySystemInformation( + SystemProcessIdInformation, + &processIdInfo, + sizeof(SYSTEM_PROCESS_ID_INFORMATION), + NULL); + + if (status == STATUS_INFO_LENGTH_MISMATCH) { + // Required length is stored in MaximumLength. + FREE(buffer); + buffer = MALLOC_ZERO(processIdInfo.ImageName.MaximumLength); + if (! buffer) + return PyErr_NoMemory(); + processIdInfo.ImageName.Buffer = buffer; + + status = NtQuerySystemInformation( + SystemProcessIdInformation, + &processIdInfo, + sizeof(SYSTEM_PROCESS_ID_INFORMATION), + NULL); } -#else // Windows XP - if (GetProcessImageFileNameW(hProcess, exe, MAX_PATH) == 0) { - // see: https://github.com/giampaolo/psutil/issues/1394 - if (GetLastError() == 0) - PyErr_SetFromWindowsErr(ERROR_ACCESS_DENIED); + + if (! NT_SUCCESS(status)) { + FREE(buffer); + if (psutil_pid_is_running(pid) == 0) + NoSuchProcess("NtQuerySystemInformation"); else - PyErr_SetFromOSErrnoWithSyscall("GetProcessImageFileNameW"); - CloseHandle(hProcess); + psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation"); return NULL; } -#endif - CloseHandle(hProcess); - return PyUnicode_FromWideChar(exe, wcslen(exe)); -} - -/* - * Return process base name. - * Note: psutil_proc_exe() is attempted first because it's faster - * but it raise AccessDenied for processes owned by other users - * in which case we fall back on using this. - */ -static PyObject * -psutil_proc_name(PyObject *self, PyObject *args) { - long pid; - int ok; - PROCESSENTRY32W pentry; - HANDLE hSnapShot; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); - if (hSnapShot == INVALID_HANDLE_VALUE) - return PyErr_SetFromOSErrnoWithSyscall("CreateToolhelp32Snapshot"); - pentry.dwSize = sizeof(PROCESSENTRY32W); - ok = Process32FirstW(hSnapShot, &pentry); - if (! ok) { - PyErr_SetFromOSErrnoWithSyscall("Process32FirstW"); - CloseHandle(hSnapShot); - return NULL; + if (processIdInfo.ImageName.Buffer == NULL) { + // Happens for PID 4. + py_exe = Py_BuildValue("s", ""); } - while (ok) { - if (pentry.th32ProcessID == pid) { - CloseHandle(hSnapShot); - return PyUnicode_FromWideChar( - pentry.szExeFile, wcslen(pentry.szExeFile)); - } - ok = Process32NextW(hSnapShot, &pentry); + else { + py_exe = PyUnicode_FromWideChar(processIdInfo.ImageName.Buffer, + processIdInfo.ImageName.Length / 2); } - - CloseHandle(hSnapShot); - NoSuchProcess(""); - return NULL; + FREE(buffer); + return py_exe; } @@ -711,14 +444,9 @@ static PyObject * psutil_proc_memory_info(PyObject *self, PyObject *args) { HANDLE hProcess; DWORD pid; -#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 PROCESS_MEMORY_COUNTERS_EX cnt; -#else - PROCESS_MEMORY_COUNTERS cnt; -#endif - SIZE_T private = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); @@ -731,11 +459,6 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) { CloseHandle(hProcess); return NULL; } - -#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 - private = cnt.PrivateUsage; -#endif - CloseHandle(hProcess); // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits @@ -754,7 +477,7 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) { (unsigned long long)cnt.QuotaNonPagedPoolUsage, (unsigned long long)cnt.PagefileUsage, (unsigned long long)cnt.PeakPagefileUsage, - (unsigned long long)private); + (unsigned long long)cnt.PrivateUsage); #else return Py_BuildValue( "(kIIIIIIIII)", @@ -767,7 +490,7 @@ psutil_proc_memory_info(PyObject *self, PyObject *args) { (unsigned int)cnt.QuotaNonPagedPoolUsage, (unsigned int)cnt.PagefileUsage, (unsigned int)cnt.PeakPagefileUsage, - (unsigned int)private); + (unsigned int)cnt.PrivateUsage); #endif } @@ -783,9 +506,13 @@ psutil_GetProcWsetInformation( SIZE_T bufferSize; bufferSize = 0x8000; - buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufferSize); + buffer = MALLOC_ZERO(bufferSize); + if (! buffer) { + PyErr_NoMemory(); + return 1; + } - while ((status = psutil_NtQueryVirtualMemory( + while ((status = NtQueryVirtualMemory( hProcess, NULL, MemoryWorkingSetInformation, @@ -793,24 +520,27 @@ psutil_GetProcWsetInformation( bufferSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) { - HeapFree(GetProcessHeap(), 0, buffer); + FREE(buffer); bufferSize *= 2; - psutil_debug("NtQueryVirtualMemory increase bufsize %zd", bufferSize); // Fail if we're resizing the buffer to something very large. if (bufferSize > 256 * 1024 * 1024) { PyErr_SetString(PyExc_RuntimeError, "NtQueryVirtualMemory bufsize is too large"); return 1; } - buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufferSize); + buffer = MALLOC_ZERO(bufferSize); + if (! buffer) { + PyErr_NoMemory(); + return 1; + } } if (!NT_SUCCESS(status)) { if (status == STATUS_ACCESS_DENIED) { - AccessDenied(""); + AccessDenied("NtQueryVirtualMemory"); } else if (psutil_pid_is_running(pid) == 0) { - NoSuchProcess(""); + NoSuchProcess("psutil_pid_is_running"); } else { PyErr_Clear(); @@ -840,9 +570,9 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) { PMEMORY_WORKING_SET_INFORMATION wsInfo; ULONG_PTR i; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; - hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); + hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_INFORMATION); if (hProcess == NULL) return NULL; @@ -888,8 +618,10 @@ psutil_virtual_mem(PyObject *self, PyObject *args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); - if (! GlobalMemoryStatusEx(&memInfo)) - return PyErr_SetFromWindowsErr(0); + if (! GlobalMemoryStatusEx(&memInfo)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } return Py_BuildValue("(LLLLLL)", memInfo.ullTotalPhys, // total memInfo.ullAvailPhys, // avail @@ -899,139 +631,21 @@ psutil_virtual_mem(PyObject *self, PyObject *args) { memInfo.ullAvailVirtual); // avail virtual } -/* - * Retrieves system CPU timing information as a (user, system, idle) - * tuple. On a multiprocessor system, the values returned are the - * sum of the designated times across all processors. - */ -static PyObject * -psutil_cpu_times(PyObject *self, PyObject *args) { - double idle, kernel, user, system; - FILETIME idle_time, kernel_time, user_time; - - if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) - return PyErr_SetFromWindowsErr(0); - - idle = (double)((HI_T * idle_time.dwHighDateTime) + \ - (LO_T * idle_time.dwLowDateTime)); - user = (double)((HI_T * user_time.dwHighDateTime) + \ - (LO_T * user_time.dwLowDateTime)); - kernel = (double)((HI_T * kernel_time.dwHighDateTime) + \ - (LO_T * kernel_time.dwLowDateTime)); - - // Kernel time includes idle time. - // We return only busy kernel time subtracting idle time from - // kernel time. - system = (kernel - idle); - return Py_BuildValue("(ddd)", user, system, idle); -} - - -/* - * Same as above but for all system CPUs. - */ -static PyObject * -psutil_per_cpu_times(PyObject *self, PyObject *args) { - double idle, kernel, systemt, user, interrupt, dpc; - NTSTATUS status; - _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; - UINT i; - unsigned int ncpus; - PyObject *py_tuple = NULL; - PyObject *py_retlist = PyList_New(0); - - if (py_retlist == NULL) - return NULL; - - // retrieves number of processors - ncpus = psutil_get_num_cpus(1); - if (ncpus == 0) - goto error; - - // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION - // structures, one per processor - sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ - malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); - if (sppi == NULL) { - PyErr_NoMemory(); - goto error; - } - - // gets cpu time informations - status = psutil_NtQuerySystemInformation( - SystemProcessorPerformanceInformation, - sppi, - ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, - "NtQuerySystemInformation(SystemProcessorPerformanceInformation)" - ); - goto error; - } - - // computes system global times summing each - // processor value - idle = user = kernel = interrupt = dpc = 0; - for (i = 0; i < ncpus; i++) { - py_tuple = NULL; - user = (double)((HI_T * sppi[i].UserTime.HighPart) + - (LO_T * sppi[i].UserTime.LowPart)); - idle = (double)((HI_T * sppi[i].IdleTime.HighPart) + - (LO_T * sppi[i].IdleTime.LowPart)); - kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) + - (LO_T * sppi[i].KernelTime.LowPart)); - interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) + - (LO_T * sppi[i].InterruptTime.LowPart)); - dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) + - (LO_T * sppi[i].DpcTime.LowPart)); - - // kernel time includes idle time on windows - // we return only busy kernel time subtracting - // idle time from kernel time - systemt = kernel - idle; - py_tuple = Py_BuildValue( - "(ddddd)", - user, - systemt, - idle, - interrupt, - dpc - ); - if (!py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_DECREF(py_tuple); - } - - free(sppi); - return py_retlist; - -error: - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - if (sppi) - free(sppi); - return NULL; -} - /* * Return process current working directory as a Python string. */ static PyObject * psutil_proc_cwd(PyObject *self, PyObject *args) { - long pid; + DWORD pid; int pid_return; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; pid_return = psutil_pid_is_running(pid); if (pid_return == 0) - return NoSuchProcess(""); + return NoSuchProcess("psutil_pid_is_running"); if (pid_return == -1) return NULL; @@ -1044,22 +658,22 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) { - long pid; + DWORD pid; NTSTATUS status; HANDLE hProcess; PyObject* suspend; - if (! PyArg_ParseTuple(args, "lO", &pid, &suspend)) - return NULL; + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &suspend)) + return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_SUSPEND_RESUME); if (hProcess == NULL) return NULL; if (PyObject_IsTrue(suspend)) - status = psutil_NtSuspendProcess(hProcess); + status = NtSuspendProcess(hProcess); else - status = psutil_NtResumeProcess(hProcess); + status = NtResumeProcess(hProcess); if (! NT_SUCCESS(status)) { CloseHandle(hProcess); @@ -1073,9 +687,9 @@ psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) { static PyObject * psutil_proc_threads(PyObject *self, PyObject *args) { - HANDLE hThread; + HANDLE hThread = NULL; THREADENTRY32 te32 = {0}; - long pid; + DWORD pid; int pid_return; int rc; FILETIME ftDummy, ftKernel, ftUser; @@ -1085,18 +699,18 @@ psutil_proc_threads(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; if (pid == 0) { // raise AD instead of returning 0 as procexp is able to // retrieve useful information somehow - AccessDenied(""); + AccessDenied("automatically set for PID 0"); goto error; } pid_return = psutil_pid_is_running(pid); if (pid_return == 0) { - NoSuchProcess(""); + NoSuchProcess("psutil_pid_is_running"); goto error; } if (pid_return == -1) @@ -1148,15 +762,15 @@ psutil_proc_threads(PyObject *self, PyObject *args) { py_tuple = Py_BuildValue( "kdd", te32.th32ThreadID, - (double)(ftUser.dwHighDateTime * 429.4967296 + \ - ftUser.dwLowDateTime * 1e-7), - (double)(ftKernel.dwHighDateTime * 429.4967296 + \ - ftKernel.dwLowDateTime * 1e-7)); + (double)(ftUser.dwHighDateTime * HI_T + \ + ftUser.dwLowDateTime * LO_T), + (double)(ftKernel.dwHighDateTime * HI_T + \ + ftKernel.dwLowDateTime * LO_T)); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); CloseHandle(hThread); } @@ -1178,12 +792,12 @@ error: static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) { - long pid; - HANDLE processHandle; - DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; - PyObject *py_retlist; + DWORD pid; + HANDLE processHandle; + DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; + PyObject *py_retlist; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; processHandle = psutil_handle_from_pid(pid, access); @@ -1191,52 +805,17 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { return NULL; py_retlist = psutil_get_open_files(pid, processHandle); - if (py_retlist == NULL) { - PyErr_SetFromWindowsErr(0); - CloseHandle(processHandle); - return NULL; - } - CloseHandle(processHandle); return py_retlist; } -/* - Accept a filename's drive in native format like "\Device\HarddiskVolume1\" - and return the corresponding drive letter (e.g. "C:\\"). - If no match is found return an empty string. -*/ -static PyObject * -psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) { - LPCTSTR lpDevicePath; - TCHAR d = TEXT('A'); - TCHAR szBuff[5]; - - if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) - return NULL; - - while (d <= TEXT('Z')) { - TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; - TCHAR szTarget[512] = {0}; - if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { - if (_tcscmp(lpDevicePath, szTarget) == 0) { - _stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d); - return Py_BuildValue("s", szBuff); - } - } - d++; - } - return Py_BuildValue("s", ""); -} - - /* * Return process username as a "DOMAIN//USERNAME" string. */ static PyObject * psutil_proc_username(PyObject *self, PyObject *args) { - long pid; + DWORD pid; HANDLE processHandle = NULL; HANDLE tokenHandle = NULL; PTOKEN_USER user = NULL; @@ -1250,7 +829,7 @@ psutil_proc_username(PyObject *self, PyObject *args) { PyObject *py_domain = NULL; PyObject *py_tuple = NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; processHandle = psutil_handle_from_pid( @@ -1358,474 +937,16 @@ error: } -// https://msdn.microsoft.com/library/aa365928.aspx -// TODO properly handle return code -static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call, - ULONG address_family, - PVOID * data, DWORD * size) -{ - // Due to other processes being active on the machine, it's possible - // that the size of the table increases between the moment where we - // query the size and the moment where we query the data. Therefore, it's - // important to call this in a loop to retry if that happens. - // See https://github.com/giampaolo/psutil/pull/1335 concerning 0xC0000001 error - // and https://github.com/giampaolo/psutil/issues/1294 - DWORD error = ERROR_INSUFFICIENT_BUFFER; - *size = 0; - *data = NULL; - error = call(NULL, size, FALSE, address_family, - TCP_TABLE_OWNER_PID_ALL, 0); - while (error == ERROR_INSUFFICIENT_BUFFER || error == 0xC0000001) - { - *data = malloc(*size); - if (*data == NULL) { - error = ERROR_NOT_ENOUGH_MEMORY; - continue; - } - error = call(*data, size, FALSE, address_family, - TCP_TABLE_OWNER_PID_ALL, 0); - if (error != NO_ERROR) { - free(*data); - *data = NULL; - } - } - return error; -} - - -// https://msdn.microsoft.com/library/aa365930.aspx -// TODO properly check return value -static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call, - ULONG address_family, - PVOID * data, DWORD * size) -{ - // Due to other processes being active on the machine, it's possible - // that the size of the table increases between the moment where we - // query the size and the moment where we query the data. Therefore, it's - // important to call this in a loop to retry if that happens. - // See https://github.com/giampaolo/psutil/pull/1335 concerning 0xC0000001 error - // and https://github.com/giampaolo/psutil/issues/1294 - DWORD error = ERROR_INSUFFICIENT_BUFFER; - *size = 0; - *data = NULL; - error = call(NULL, size, FALSE, address_family, - UDP_TABLE_OWNER_PID, 0); - while (error == ERROR_INSUFFICIENT_BUFFER || error == 0xC0000001) - { - *data = malloc(*size); - if (*data == NULL) { - error = ERROR_NOT_ENOUGH_MEMORY; - continue; - } - error = call(*data, size, FALSE, address_family, - UDP_TABLE_OWNER_PID, 0); - if (error != NO_ERROR) { - free(*data); - *data = NULL; - } - } - - if (error == ERROR_NOT_ENOUGH_MEMORY) { - PyErr_NoMemory(); - return 1; - } - if (error != NO_ERROR) { - PyErr_SetFromWindowsErr(error); - return 1; - } - return 0; -} - - -#define psutil_conn_decref_objs() \ - Py_DECREF(_AF_INET); \ - Py_DECREF(_AF_INET6);\ - Py_DECREF(_SOCK_STREAM);\ - Py_DECREF(_SOCK_DGRAM); - - -/* - * Return a list of network connections opened by a process - */ -static PyObject * -psutil_net_connections(PyObject *self, PyObject *args) { - static long null_address[4] = { 0, 0, 0, 0 }; - unsigned long pid; - int pid_return; - PVOID table = NULL; - DWORD tableSize; - DWORD error; - PMIB_TCPTABLE_OWNER_PID tcp4Table; - PMIB_UDPTABLE_OWNER_PID udp4Table; - PMIB_TCP6TABLE_OWNER_PID tcp6Table; - PMIB_UDP6TABLE_OWNER_PID udp6Table; - ULONG i; - CHAR addressBufferLocal[65]; - CHAR addressBufferRemote[65]; - - PyObject *py_retlist; - PyObject *py_conn_tuple = NULL; - PyObject *py_af_filter = NULL; - PyObject *py_type_filter = NULL; - PyObject *py_addr_tuple_local = NULL; - PyObject *py_addr_tuple_remote = NULL; - PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); - PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); - PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); - PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); - - // Import some functions. - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) - goto error; - - if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { - psutil_conn_decref_objs(); - PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); - return NULL; - } - - if (pid != -1) { - pid_return = psutil_pid_is_running(pid); - if (pid_return == 0) { - psutil_conn_decref_objs(); - return NoSuchProcess(""); - } - else if (pid_return == -1) { - psutil_conn_decref_objs(); - return NULL; - } - } - - py_retlist = PyList_New(0); - if (py_retlist == NULL) { - psutil_conn_decref_objs(); - return NULL; - } - - // TCP IPv4 - - if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && - (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) - { - table = NULL; - py_conn_tuple = NULL; - py_addr_tuple_local = NULL; - py_addr_tuple_remote = NULL; - tableSize = 0; - - error = __GetExtendedTcpTable(psutil_GetExtendedTcpTable, - AF_INET, &table, &tableSize); - if (error != 0) - goto error; - tcp4Table = table; - for (i = 0; i < tcp4Table->dwNumEntries; i++) { - if (pid != -1) { - if (tcp4Table->table[i].dwOwningPid != pid) { - continue; - } - } - - if (tcp4Table->table[i].dwLocalAddr != 0 || - tcp4Table->table[i].dwLocalPort != 0) - { - struct in_addr addr; - - addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; - psutil_rtlIpv4AddressToStringA(&addr, addressBufferLocal); - py_addr_tuple_local = Py_BuildValue( - "(si)", - addressBufferLocal, - BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); - } - else { - py_addr_tuple_local = PyTuple_New(0); - } - - if (py_addr_tuple_local == NULL) - goto error; - - // On Windows <= XP, remote addr is filled even if socket - // is in LISTEN mode in which case we just ignore it. - if ((tcp4Table->table[i].dwRemoteAddr != 0 || - tcp4Table->table[i].dwRemotePort != 0) && - (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) - { - struct in_addr addr; - - addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; - psutil_rtlIpv4AddressToStringA(&addr, addressBufferRemote); - py_addr_tuple_remote = Py_BuildValue( - "(si)", - addressBufferRemote, - BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); - } - else - { - py_addr_tuple_remote = PyTuple_New(0); - } - - if (py_addr_tuple_remote == NULL) - goto error; - - py_conn_tuple = Py_BuildValue( - "(iiiNNiI)", - -1, - AF_INET, - SOCK_STREAM, - py_addr_tuple_local, - py_addr_tuple_remote, - tcp4Table->table[i].dwState, - tcp4Table->table[i].dwOwningPid); - if (!py_conn_tuple) - goto error; - if (PyList_Append(py_retlist, py_conn_tuple)) - goto error; - Py_DECREF(py_conn_tuple); - } - - free(table); - table = NULL; - tableSize = 0; - } - - // TCP IPv6 - if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && - (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1) && - (psutil_rtlIpv6AddressToStringA != NULL)) - { - table = NULL; - py_conn_tuple = NULL; - py_addr_tuple_local = NULL; - py_addr_tuple_remote = NULL; - tableSize = 0; - - error = __GetExtendedTcpTable(psutil_GetExtendedTcpTable, - AF_INET6, &table, &tableSize); - if (error != 0) - goto error; - tcp6Table = table; - for (i = 0; i < tcp6Table->dwNumEntries; i++) - { - if (pid != -1) { - if (tcp6Table->table[i].dwOwningPid != pid) { - continue; - } - } - - if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) - != 0 || tcp6Table->table[i].dwLocalPort != 0) - { - struct in6_addr addr; - - memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); - psutil_rtlIpv6AddressToStringA(&addr, addressBufferLocal); - py_addr_tuple_local = Py_BuildValue( - "(si)", - addressBufferLocal, - BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); - } - else { - py_addr_tuple_local = PyTuple_New(0); - } - - if (py_addr_tuple_local == NULL) - goto error; - - // On Windows <= XP, remote addr is filled even if socket - // is in LISTEN mode in which case we just ignore it. - if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) - != 0 || - tcp6Table->table[i].dwRemotePort != 0) && - (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) - { - struct in6_addr addr; - - memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); - psutil_rtlIpv6AddressToStringA(&addr, addressBufferRemote); - py_addr_tuple_remote = Py_BuildValue( - "(si)", - addressBufferRemote, - BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); - } - else { - py_addr_tuple_remote = PyTuple_New(0); - } - - if (py_addr_tuple_remote == NULL) - goto error; - - py_conn_tuple = Py_BuildValue( - "(iiiNNiI)", - -1, - AF_INET6, - SOCK_STREAM, - py_addr_tuple_local, - py_addr_tuple_remote, - tcp6Table->table[i].dwState, - tcp6Table->table[i].dwOwningPid); - if (!py_conn_tuple) - goto error; - if (PyList_Append(py_retlist, py_conn_tuple)) - goto error; - Py_DECREF(py_conn_tuple); - } - - free(table); - table = NULL; - tableSize = 0; - } - - // UDP IPv4 - - if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && - (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) - { - table = NULL; - py_conn_tuple = NULL; - py_addr_tuple_local = NULL; - py_addr_tuple_remote = NULL; - tableSize = 0; - error = __GetExtendedUdpTable(psutil_GetExtendedUdpTable, - AF_INET, &table, &tableSize); - if (error != 0) - goto error; - udp4Table = table; - for (i = 0; i < udp4Table->dwNumEntries; i++) - { - if (pid != -1) { - if (udp4Table->table[i].dwOwningPid != pid) { - continue; - } - } - - if (udp4Table->table[i].dwLocalAddr != 0 || - udp4Table->table[i].dwLocalPort != 0) - { - struct in_addr addr; - - addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; - psutil_rtlIpv4AddressToStringA(&addr, addressBufferLocal); - py_addr_tuple_local = Py_BuildValue( - "(si)", - addressBufferLocal, - BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); - } - else { - py_addr_tuple_local = PyTuple_New(0); - } - - if (py_addr_tuple_local == NULL) - goto error; - - py_conn_tuple = Py_BuildValue( - "(iiiNNiI)", - -1, - AF_INET, - SOCK_DGRAM, - py_addr_tuple_local, - PyTuple_New(0), - PSUTIL_CONN_NONE, - udp4Table->table[i].dwOwningPid); - if (!py_conn_tuple) - goto error; - if (PyList_Append(py_retlist, py_conn_tuple)) - goto error; - Py_DECREF(py_conn_tuple); - } - - free(table); - table = NULL; - tableSize = 0; - } - - // UDP IPv6 - - if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && - (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1) && - (psutil_rtlIpv6AddressToStringA != NULL)) - { - table = NULL; - py_conn_tuple = NULL; - py_addr_tuple_local = NULL; - py_addr_tuple_remote = NULL; - tableSize = 0; - error = __GetExtendedUdpTable(psutil_GetExtendedUdpTable, - AF_INET6, &table, &tableSize); - if (error != 0) - goto error; - udp6Table = table; - for (i = 0; i < udp6Table->dwNumEntries; i++) { - if (pid != -1) { - if (udp6Table->table[i].dwOwningPid != pid) { - continue; - } - } - - if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) - != 0 || udp6Table->table[i].dwLocalPort != 0) - { - struct in6_addr addr; - - memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); - psutil_rtlIpv6AddressToStringA(&addr, addressBufferLocal); - py_addr_tuple_local = Py_BuildValue( - "(si)", - addressBufferLocal, - BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); - } - else { - py_addr_tuple_local = PyTuple_New(0); - } - - if (py_addr_tuple_local == NULL) - goto error; - - py_conn_tuple = Py_BuildValue( - "(iiiNNiI)", - -1, - AF_INET6, - SOCK_DGRAM, - py_addr_tuple_local, - PyTuple_New(0), - PSUTIL_CONN_NONE, - udp6Table->table[i].dwOwningPid); - if (!py_conn_tuple) - goto error; - if (PyList_Append(py_retlist, py_conn_tuple)) - goto error; - Py_DECREF(py_conn_tuple); - } - - free(table); - table = NULL; - tableSize = 0; - } - - psutil_conn_decref_objs(); - return py_retlist; - -error: - psutil_conn_decref_objs(); - Py_XDECREF(py_conn_tuple); - Py_XDECREF(py_addr_tuple_local); - Py_XDECREF(py_addr_tuple_remote); - Py_DECREF(py_retlist); - if (table != NULL) - free(table); - return NULL; -} - - /* * Get process priority as a Python integer. */ static PyObject * psutil_proc_priority_get(PyObject *self, PyObject *args) { - long pid; + DWORD pid; DWORD priority; HANDLE hProcess; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); @@ -1848,13 +969,13 @@ psutil_proc_priority_get(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_priority_set(PyObject *self, PyObject *args) { - long pid; + DWORD pid; int priority; int retval; HANDLE hProcess; DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; - if (! PyArg_ParseTuple(args, "li", &pid, &priority)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &priority)) return NULL; hProcess = psutil_handle_from_pid(pid, access); if (hProcess == NULL) @@ -1872,25 +993,24 @@ psutil_proc_priority_set(PyObject *self, PyObject *args) { } -#if (_WIN32_WINNT >= 0x0600) // Windows Vista /* * Get process IO priority as a Python integer. */ static PyObject * psutil_proc_io_priority_get(PyObject *self, PyObject *args) { - long pid; + DWORD pid; HANDLE hProcess; DWORD IoPriority; NTSTATUS status; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (hProcess == NULL) return NULL; - status = psutil_NtQueryInformationProcess( + status = NtQueryInformationProcess( hProcess, ProcessIoPriority, &IoPriority, @@ -1910,20 +1030,20 @@ psutil_proc_io_priority_get(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_io_priority_set(PyObject *self, PyObject *args) { - long pid; + DWORD pid; DWORD prio; HANDLE hProcess; NTSTATUS status; DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; - if (! PyArg_ParseTuple(args, "li", &pid, &prio)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &prio)) return NULL; hProcess = psutil_handle_from_pid(pid, access); if (hProcess == NULL) return NULL; - status = psutil_NtSetInformationProcess( + status = NtSetInformationProcess( hProcess, ProcessIoPriority, (PVOID)&prio, @@ -1935,7 +1055,6 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) { return psutil_SetFromNTStatusErr(status, "NtSetInformationProcess"); Py_RETURN_NONE; } -#endif /* @@ -1947,7 +1066,7 @@ psutil_proc_io_counters(PyObject *self, PyObject *args) { HANDLE hProcess; IO_COUNTERS IoCounters; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (NULL == hProcess) @@ -1980,7 +1099,7 @@ psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { DWORD_PTR proc_mask; DWORD_PTR system_mask; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (hProcess == NULL) { @@ -2012,9 +1131,9 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { DWORD_PTR mask; #ifdef _WIN64 - if (! PyArg_ParseTuple(args, "lK", &pid, &mask)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "K", &pid, &mask)) #else - if (! PyArg_ParseTuple(args, "lk", &pid, &mask)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "k", &pid, &mask)) #endif { return NULL; @@ -2044,7 +1163,7 @@ psutil_proc_is_suspended(PyObject *self, PyObject *args) { PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (! psutil_get_proc_info(pid, &process, &buffer)) return NULL; @@ -2061,448 +1180,6 @@ psutil_proc_is_suspended(PyObject *self, PyObject *args) { } -/* - * Return path's disk total and free as a Python tuple. - */ -static PyObject * -psutil_disk_usage(PyObject *self, PyObject *args) { - BOOL retval; - ULARGE_INTEGER _, total, free; - char *path; - - if (PyArg_ParseTuple(args, "u", &path)) { - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free); - Py_END_ALLOW_THREADS - goto return_; - } - - // on Python 2 we also want to accept plain strings other - // than Unicode -#if PY_MAJOR_VERSION <= 2 - PyErr_Clear(); // drop the argument parsing error - if (PyArg_ParseTuple(args, "s", &path)) { - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceEx(path, &_, &total, &free); - Py_END_ALLOW_THREADS - goto return_; - } -#endif - - return NULL; - -return_: - if (retval == 0) - return PyErr_SetFromWindowsErrWithFilename(0, path); - else - return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); -} - - -/* - * Return a Python list of named tuples with overall network I/O information - */ -static PyObject * -psutil_net_io_counters(PyObject *self, PyObject *args) { - DWORD dwRetVal = 0; - -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - MIB_IF_ROW2 *pIfRow = NULL; -#else // Windows XP - MIB_IFROW *pIfRow = NULL; -#endif - - PIP_ADAPTER_ADDRESSES pAddresses = NULL; - PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; - PyObject *py_retdict = PyDict_New(); - PyObject *py_nic_info = NULL; - PyObject *py_nic_name = NULL; - - if (py_retdict == NULL) - return NULL; - pAddresses = psutil_get_nic_addresses(); - if (pAddresses == NULL) - goto error; - pCurrAddresses = pAddresses; - - while (pCurrAddresses) { - py_nic_name = NULL; - py_nic_info = NULL; - -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); -#else // Windows XP - pIfRow = (MIB_IFROW *) malloc(sizeof(MIB_IFROW)); -#endif - - if (pIfRow == NULL) { - PyErr_NoMemory(); - goto error; - } - -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); - pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; - dwRetVal = GetIfEntry2(pIfRow); -#else // Windows XP - pIfRow->dwIndex = pCurrAddresses->IfIndex; - dwRetVal = GetIfEntry(pIfRow); -#endif - - if (dwRetVal != NO_ERROR) { - PyErr_SetString(PyExc_RuntimeError, - "GetIfEntry() or GetIfEntry2() syscalls failed."); - goto error; - } - -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - py_nic_info = Py_BuildValue("(KKKKKKKK)", - pIfRow->OutOctets, - pIfRow->InOctets, - (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts), - (pIfRow->InUcastPkts + pIfRow->InNUcastPkts), - pIfRow->InErrors, - pIfRow->OutErrors, - pIfRow->InDiscards, - pIfRow->OutDiscards); -#else // Windows XP - py_nic_info = Py_BuildValue("(kkkkkkkk)", - pIfRow->dwOutOctets, - pIfRow->dwInOctets, - (pIfRow->dwOutUcastPkts + pIfRow->dwOutNUcastPkts), - (pIfRow->dwInUcastPkts + pIfRow->dwInNUcastPkts), - pIfRow->dwInErrors, - pIfRow->dwOutErrors, - pIfRow->dwInDiscards, - pIfRow->dwOutDiscards); -#endif - - if (!py_nic_info) - goto error; - - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - - if (py_nic_name == NULL) - goto error; - if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) - goto error; - Py_XDECREF(py_nic_name); - Py_XDECREF(py_nic_info); - - free(pIfRow); - pCurrAddresses = pCurrAddresses->Next; - } - - free(pAddresses); - return py_retdict; - -error: - Py_XDECREF(py_nic_name); - Py_XDECREF(py_nic_info); - Py_DECREF(py_retdict); - if (pAddresses != NULL) - free(pAddresses); - if (pIfRow != NULL) - free(pIfRow); - return NULL; -} - - -/* - * Return a Python dict of tuples for disk I/O information. This may - * require running "diskperf -y" command first. - */ -static PyObject * -psutil_disk_io_counters(PyObject *self, PyObject *args) { - DISK_PERFORMANCE diskPerformance; - DWORD dwSize; - HANDLE hDevice = NULL; - char szDevice[MAX_PATH]; - char szDeviceDisplay[MAX_PATH]; - int devNum; - int i; - DWORD ioctrlSize; - BOOL ret; - PyObject *py_retdict = PyDict_New(); - PyObject *py_tuple = NULL; - - if (py_retdict == NULL) - return NULL; - // Apparently there's no way to figure out how many times we have - // to iterate in order to find valid drives. - // Let's assume 32, which is higher than 26, the number of letters - // in the alphabet (from A:\ to Z:\). - for (devNum = 0; devNum <= 32; ++devNum) { - py_tuple = NULL; - sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); - hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, 0, NULL); - if (hDevice == INVALID_HANDLE_VALUE) - continue; - - // DeviceIoControl() sucks! - i = 0; - ioctrlSize = sizeof(diskPerformance); - while (1) { - i += 1; - ret = DeviceIoControl( - hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance, - ioctrlSize, &dwSize, NULL); - if (ret != 0) - break; // OK! - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Retry with a bigger buffer (+ limit for retries). - if (i <= 1024) { - ioctrlSize *= 2; - continue; - } - } - else if (GetLastError() == ERROR_INVALID_FUNCTION) { - // This happens on AppVeyor: - // https://ci.appveyor.com/project/giampaolo/psutil/build/ - // 1364/job/ascpdi271b06jle3 - // Assume it means we're dealing with some exotic disk - // and go on. - psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; " - "ignore PhysicalDrive%i", devNum); - goto next; - } - else if (GetLastError() == ERROR_NOT_SUPPORTED) { - // Again, let's assume we're dealing with some exotic disk. - psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; " - "ignore PhysicalDrive%i", devNum); - goto next; - } - // XXX: it seems we should also catch ERROR_INVALID_PARAMETER: - // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ - // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/ - // openafs-1.4.14/src/usd/usd_nt.c - - // XXX: we can also bump into ERROR_MORE_DATA in which case - // (quoting doc) we're supposed to retry with a bigger buffer - // and specify a new "starting point", whatever it means. - PyErr_SetFromWindowsErr(0); - goto error; - } - - sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum); - py_tuple = Py_BuildValue( - "(IILLKK)", - diskPerformance.ReadCount, - diskPerformance.WriteCount, - diskPerformance.BytesRead, - diskPerformance.BytesWritten, - // convert to ms: - // https://github.com/giampaolo/psutil/issues/1012 - (unsigned long long) - (diskPerformance.ReadTime.QuadPart) / 10000000, - (unsigned long long) - (diskPerformance.WriteTime.QuadPart) / 10000000); - if (!py_tuple) - goto error; - if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple)) - goto error; - Py_XDECREF(py_tuple); - -next: - CloseHandle(hDevice); - } - - return py_retdict; - -error: - Py_XDECREF(py_tuple); - Py_DECREF(py_retdict); - if (hDevice != NULL) - CloseHandle(hDevice); - return NULL; -} - - -static char *psutil_get_drive_type(int type) { - switch (type) { - case DRIVE_FIXED: - return "fixed"; - case DRIVE_CDROM: - return "cdrom"; - case DRIVE_REMOVABLE: - return "removable"; - case DRIVE_UNKNOWN: - return "unknown"; - case DRIVE_NO_ROOT_DIR: - return "unmounted"; - case DRIVE_REMOTE: - return "remote"; - case DRIVE_RAMDISK: - return "ramdisk"; - default: - return "?"; - } -} - - -#ifndef _ARRAYSIZE -#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) -#endif - - -/* - * Return disk partitions as a list of tuples such as - * (drive_letter, drive_letter, type, "") - */ -static PyObject * -psutil_disk_partitions(PyObject *self, PyObject *args) { - DWORD num_bytes; - char drive_strings[255]; - char *drive_letter = drive_strings; - char mp_buf[MAX_PATH]; - char mp_path[MAX_PATH]; - int all; - int type; - int ret; - unsigned int old_mode = 0; - char opts[20]; - HANDLE mp_h; - BOOL mp_flag= TRUE; - LPTSTR fs_type[MAX_PATH + 1] = { 0 }; - DWORD pflags = 0; - PyObject *py_all; - PyObject *py_retlist = PyList_New(0); - PyObject *py_tuple = NULL; - - if (py_retlist == NULL) { - return NULL; - } - - // avoid to visualize a message box in case something goes wrong - // see https://github.com/giampaolo/psutil/issues/264 - old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); - - if (! PyArg_ParseTuple(args, "O", &py_all)) - goto error; - all = PyObject_IsTrue(py_all); - - Py_BEGIN_ALLOW_THREADS - num_bytes = GetLogicalDriveStrings(254, drive_letter); - Py_END_ALLOW_THREADS - - if (num_bytes == 0) { - PyErr_SetFromWindowsErr(0); - goto error; - } - - while (*drive_letter != 0) { - py_tuple = NULL; - opts[0] = 0; - fs_type[0] = 0; - - Py_BEGIN_ALLOW_THREADS - type = GetDriveType(drive_letter); - Py_END_ALLOW_THREADS - - // by default we only show hard drives and cd-roms - if (all == 0) { - if ((type == DRIVE_UNKNOWN) || - (type == DRIVE_NO_ROOT_DIR) || - (type == DRIVE_REMOTE) || - (type == DRIVE_RAMDISK)) { - goto next; - } - // floppy disk: skip it by default as it introduces a - // considerable slowdown. - if ((type == DRIVE_REMOVABLE) && - (strcmp(drive_letter, "A:\\") == 0)) { - goto next; - } - } - - ret = GetVolumeInformation( - (LPCTSTR)drive_letter, NULL, _ARRAYSIZE(drive_letter), - NULL, NULL, &pflags, (LPTSTR)fs_type, _ARRAYSIZE(fs_type)); - if (ret == 0) { - // We might get here in case of a floppy hard drive, in - // which case the error is (21, "device not ready"). - // Let's pretend it didn't happen as we already have - // the drive name and type ('removable'). - strcat_s(opts, _countof(opts), ""); - SetLastError(0); - } - else { - if (pflags & FILE_READ_ONLY_VOLUME) - strcat_s(opts, _countof(opts), "ro"); - else - strcat_s(opts, _countof(opts), "rw"); - if (pflags & FILE_VOLUME_IS_COMPRESSED) - strcat_s(opts, _countof(opts), ",compressed"); - - // Check for mount points on this volume and add/get info - // (checks first to know if we can even have mount points) - if (pflags & FILE_SUPPORTS_REPARSE_POINTS) { - - mp_h = FindFirstVolumeMountPoint(drive_letter, mp_buf, MAX_PATH); - if (mp_h != INVALID_HANDLE_VALUE) { - while (mp_flag) { - - // Append full mount path with drive letter - strcpy_s(mp_path, _countof(mp_path), drive_letter); - strcat_s(mp_path, _countof(mp_path), mp_buf); - - py_tuple = Py_BuildValue( - "(ssss)", - drive_letter, - mp_path, - fs_type, // Typically NTFS - opts); - - if (!py_tuple || PyList_Append(py_retlist, py_tuple) == -1) { - FindVolumeMountPointClose(mp_h); - goto error; - } - - Py_DECREF(py_tuple); - - // Continue looking for more mount points - mp_flag = FindNextVolumeMountPoint(mp_h, mp_buf, MAX_PATH); - } - FindVolumeMountPointClose(mp_h); - } - - } - } - - if (strlen(opts) > 0) - strcat_s(opts, _countof(opts), ","); - strcat_s(opts, _countof(opts), psutil_get_drive_type(type)); - - py_tuple = Py_BuildValue( - "(ssss)", - drive_letter, - drive_letter, - fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS - opts); - if (!py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_DECREF(py_tuple); - goto next; - -next: - drive_letter = strchr(drive_letter, 0) + 1; - } - - SetErrorMode(old_mode); - return py_retlist; - -error: - SetErrorMode(old_mode); - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - return NULL; -} - /* * Return a Python dict of tuples for disk I/O information */ @@ -2518,7 +1195,6 @@ psutil_users(PyObject *self, PyObject *args) { DWORD bytes; PWTS_CLIENT_ADDRESS address; char address_str[50]; - long long unix_time; WINSTATION_INFO station_info; ULONG returnLen; PyObject *py_tuple = NULL; @@ -2582,7 +1258,7 @@ psutil_users(PyObject *self, PyObject *args) { } // login time - if (! psutil_WinStationQueryInformationW( + if (! WinStationQueryInformationW( hServer, sessionId, WinStationInformation, @@ -2594,25 +1270,22 @@ psutil_users(PyObject *self, PyObject *args) { goto error; } - unix_time = ((LONGLONG)station_info.ConnectTime.dwHighDateTime) << 32; - unix_time += \ - station_info.ConnectTime.dwLowDateTime - 116444736000000000LL; - unix_time /= 10000000; - py_username = PyUnicode_FromWideChar(buffer_user, wcslen(buffer_user)); if (py_username == NULL) goto error; - py_tuple = Py_BuildValue("OOd", - py_username, - py_address, - (double)unix_time); + py_tuple = Py_BuildValue( + "OOd", + py_username, + py_address, + psutil_FiletimeToUnixTime(station_info.ConnectTime) + ); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_XDECREF(py_username); - Py_XDECREF(py_address); - Py_XDECREF(py_tuple); + Py_CLEAR(py_username); + Py_CLEAR(py_address); + Py_CLEAR(py_tuple); } WTSFreeMemory(sessions); @@ -2645,7 +1318,7 @@ psutil_proc_num_handles(PyObject *self, PyObject *args) { HANDLE hProcess; DWORD handleCount; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); if (NULL == hProcess) @@ -2660,101 +1333,6 @@ psutil_proc_num_handles(PyObject *self, PyObject *args) { } -/* - * Get various process information by using NtQuerySystemInformation. - * We use this as a fallback when faster functions fail with access - * denied. This is slower because it iterates over all processes. - * Returned tuple includes the following process info: - * - * - num_threads() - * - ctx_switches() - * - num_handles() (fallback) - * - cpu_times() (fallback) - * - create_time() (fallback) - * - io_counters() (fallback) - * - memory_info() (fallback) - */ -static PyObject * -psutil_proc_info(PyObject *self, PyObject *args) { - DWORD pid; - PSYSTEM_PROCESS_INFORMATION process; - PVOID buffer; - ULONG i; - ULONG ctx_switches = 0; - double user_time; - double kernel_time; - long long create_time; - SIZE_T mem_private; - PyObject *py_retlist; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - if (! psutil_get_proc_info(pid, &process, &buffer)) - return NULL; - - for (i = 0; i < process->NumberOfThreads; i++) - ctx_switches += process->Threads[i].ContextSwitches; - user_time = (double)process->UserTime.HighPart * HI_T + \ - (double)process->UserTime.LowPart * LO_T; - kernel_time = (double)process->KernelTime.HighPart * HI_T + \ - (double)process->KernelTime.LowPart * LO_T; - - // Convert the LARGE_INTEGER union to a Unix time. - // It's the best I could find by googling and borrowing code here - // and there. The time returned has a precision of 1 second. - if (0 == pid || 4 == pid) { - // the python module will translate this into BOOT_TIME later - create_time = 0; - } - else { - create_time = ((LONGLONG)process->CreateTime.HighPart) << 32; - create_time += process->CreateTime.LowPart - 116444736000000000LL; - create_time /= 10000000; - } - -#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 - mem_private = process->PrivatePageCount; -#else - mem_private = 0; -#endif - - py_retlist = Py_BuildValue( -#if defined(_WIN64) - "kkdddiKKKKKK" "kKKKKKKKKK", -#else - "kkdddiKKKKKK" "kIIIIIIIII", -#endif - process->HandleCount, // num handles - ctx_switches, // num ctx switches - user_time, // cpu user time - kernel_time, // cpu kernel time - (double)create_time, // create time - (int)process->NumberOfThreads, // num threads - // IO counters - process->ReadOperationCount.QuadPart, // io rcount - process->WriteOperationCount.QuadPart, // io wcount - process->ReadTransferCount.QuadPart, // io rbytes - process->WriteTransferCount.QuadPart, // io wbytes - process->OtherOperationCount.QuadPart, // io others count - process->OtherTransferCount.QuadPart, // io others bytes - // memory - process->PageFaultCount, // num page faults - process->PeakWorkingSetSize, // peak wset - process->WorkingSetSize, // wset - process->QuotaPeakPagedPoolUsage, // peak paged pool - process->QuotaPagedPoolUsage, // paged pool - process->QuotaPeakNonPagedPoolUsage, // peak non paged pool - process->QuotaNonPagedPoolUsage, // non paged pool - process->PagefileUsage, // pagefile - process->PeakPagefileUsage, // peak pagefile - mem_private // private - ); - - free(buffer); - return py_retlist; -} - - static char *get_region_protection_string(ULONG protection) { switch (protection & 0xff) { case PAGE_NOACCESS: @@ -2788,7 +1366,6 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess = NULL; PVOID baseAddress; - ULONGLONG previousAllocationBase; WCHAR mappedFileName[MAX_PATH]; LPVOID maxAddr; // required by GetMappedFileNameW @@ -2799,7 +1376,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; hProcess = psutil_handle_from_pid(pid, access); if (NULL == hProcess) @@ -2838,10 +1415,9 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); - Py_DECREF(py_str); + Py_CLEAR(py_tuple); + Py_CLEAR(py_str); } - previousAllocationBase = (ULONGLONG)basicInfo.AllocationBase; baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize; } @@ -2881,16 +1457,16 @@ psutil_ppid_map(PyObject *self, PyObject *args) { if (Process32First(handle, &pe)) { do { - py_pid = Py_BuildValue("I", pe.th32ProcessID); + py_pid = PyLong_FromPid(pe.th32ProcessID); if (py_pid == NULL) goto error; - py_ppid = Py_BuildValue("I", pe.th32ParentProcessID); + py_ppid = PyLong_FromPid(pe.th32ParentProcessID); if (py_ppid == NULL) goto error; if (PyDict_SetItem(py_retdict, py_pid, py_ppid)) goto error; - Py_DECREF(py_pid); - Py_DECREF(py_ppid); + Py_CLEAR(py_pid); + Py_CLEAR(py_ppid); } while (Process32Next(handle, &pe)); } @@ -2906,472 +1482,6 @@ error: } -/* - * Return NICs addresses. - */ - -static PyObject * -psutil_net_if_addrs(PyObject *self, PyObject *args) { - unsigned int i = 0; - ULONG family; - PCTSTR intRet; - PCTSTR netmaskIntRet; - char *ptr; - char buff_addr[1024]; - char buff_macaddr[1024]; - char buff_netmask[1024]; - DWORD dwRetVal = 0; -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - ULONG converted_netmask; - UINT netmask_bits; - struct in_addr in_netmask; -#endif - PIP_ADAPTER_ADDRESSES pAddresses = NULL; - PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; - - PyObject *py_retlist = PyList_New(0); - PyObject *py_tuple = NULL; - PyObject *py_address = NULL; - PyObject *py_mac_address = NULL; - PyObject *py_nic_name = NULL; - PyObject *py_netmask = NULL; - - if (py_retlist == NULL) - return NULL; - - pAddresses = psutil_get_nic_addresses(); - if (pAddresses == NULL) - goto error; - pCurrAddresses = pAddresses; - - while (pCurrAddresses) { - pUnicast = pCurrAddresses->FirstUnicastAddress; - - netmaskIntRet = NULL; - py_nic_name = NULL; - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - if (py_nic_name == NULL) - goto error; - - // MAC address - if (pCurrAddresses->PhysicalAddressLength != 0) { - ptr = buff_macaddr; - *ptr = '\0'; - for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { - if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { - sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n", - (int)pCurrAddresses->PhysicalAddress[i]); - } - else { - sprintf_s(ptr, _countof(buff_macaddr), "%.2X-", - (int)pCurrAddresses->PhysicalAddress[i]); - } - ptr += 3; - } - *--ptr = '\0'; - - py_mac_address = Py_BuildValue("s", buff_macaddr); - if (py_mac_address == NULL) - goto error; - - Py_INCREF(Py_None); - Py_INCREF(Py_None); - Py_INCREF(Py_None); - py_tuple = Py_BuildValue( - "(OiOOOO)", - py_nic_name, - -1, // this will be converted later to AF_LINK - py_mac_address, - Py_None, // netmask (not supported) - Py_None, // broadcast (not supported) - Py_None // ptp (not supported on Windows) - ); - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_DECREF(py_tuple); - Py_DECREF(py_mac_address); - } - - // find out the IP address associated with the NIC - if (pUnicast != NULL) { - for (i = 0; pUnicast != NULL; i++) { - family = pUnicast->Address.lpSockaddr->sa_family; - if (family == AF_INET) { - struct sockaddr_in *sa_in = (struct sockaddr_in *) - pUnicast->Address.lpSockaddr; - intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff_addr, - sizeof(buff_addr)); - if (!intRet) - goto error; -#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above - netmask_bits = pUnicast->OnLinkPrefixLength; - dwRetVal = ConvertLengthToIpv4Mask(netmask_bits, &converted_netmask); - if (dwRetVal == NO_ERROR) { - in_netmask.s_addr = converted_netmask; - netmaskIntRet = inet_ntop( - AF_INET, &in_netmask, buff_netmask, - sizeof(buff_netmask)); - if (!netmaskIntRet) - goto error; - } -#endif - } - else if (family == AF_INET6) { - struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) - pUnicast->Address.lpSockaddr; - intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr), - buff_addr, sizeof(buff_addr)); - if (!intRet) - goto error; - } - else { - // we should never get here - pUnicast = pUnicast->Next; - continue; - } - -#if PY_MAJOR_VERSION >= 3 - py_address = PyUnicode_FromString(buff_addr); -#else - py_address = PyString_FromString(buff_addr); -#endif - if (py_address == NULL) - goto error; - - if (netmaskIntRet != NULL) { -#if PY_MAJOR_VERSION >= 3 - py_netmask = PyUnicode_FromString(buff_netmask); -#else - py_netmask = PyString_FromString(buff_netmask); -#endif - } else { - Py_INCREF(Py_None); - py_netmask = Py_None; - } - - Py_INCREF(Py_None); - Py_INCREF(Py_None); - py_tuple = Py_BuildValue( - "(OiOOOO)", - py_nic_name, - family, - py_address, - py_netmask, - Py_None, // broadcast (not supported) - Py_None // ptp (not supported on Windows) - ); - - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_DECREF(py_tuple); - Py_DECREF(py_address); - Py_DECREF(py_netmask); - - pUnicast = pUnicast->Next; - } - } - Py_DECREF(py_nic_name); - pCurrAddresses = pCurrAddresses->Next; - } - - free(pAddresses); - return py_retlist; - -error: - if (pAddresses) - free(pAddresses); - Py_DECREF(py_retlist); - Py_XDECREF(py_tuple); - Py_XDECREF(py_address); - Py_XDECREF(py_nic_name); - Py_XDECREF(py_netmask); - return NULL; -} - - -/* - * Provides stats about NIC interfaces installed on the system. - * TODO: get 'duplex' (currently it's hard coded to '2', aka - 'full duplex') - */ -static PyObject * -psutil_net_if_stats(PyObject *self, PyObject *args) { - int i; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - MIB_IFTABLE *pIfTable; - MIB_IFROW *pIfRow; - PIP_ADAPTER_ADDRESSES pAddresses = NULL; - PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; - char descr[MAX_PATH]; - int ifname_found; - - PyObject *py_nic_name = NULL; - PyObject *py_retdict = PyDict_New(); - PyObject *py_ifc_info = NULL; - PyObject *py_is_up = NULL; - - if (py_retdict == NULL) - return NULL; - - pAddresses = psutil_get_nic_addresses(); - if (pAddresses == NULL) - goto error; - - pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); - if (pIfTable == NULL) { - PyErr_NoMemory(); - goto error; - } - dwSize = sizeof(MIB_IFTABLE); - if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { - free(pIfTable); - pIfTable = (MIB_IFTABLE *) malloc(dwSize); - if (pIfTable == NULL) { - PyErr_NoMemory(); - goto error; - } - } - // Make a second call to GetIfTable to get the actual - // data we want. - if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { - PyErr_SetString(PyExc_RuntimeError, "GetIfTable() syscall failed"); - goto error; - } - - for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { - pIfRow = (MIB_IFROW *) & pIfTable->table[i]; - - // GetIfTable is not able to give us NIC with "friendly names" - // so we determine them via GetAdapterAddresses() which - // provides friendly names *and* descriptions and find the - // ones that match. - ifname_found = 0; - pCurrAddresses = pAddresses; - while (pCurrAddresses) { - sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description); - if (lstrcmp(descr, pIfRow->bDescr) == 0) { - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - if (py_nic_name == NULL) - goto error; - ifname_found = 1; - break; - } - pCurrAddresses = pCurrAddresses->Next; - } - if (ifname_found == 0) { - // Name not found means GetAdapterAddresses() doesn't list - // this NIC, only GetIfTable, meaning it's not really a NIC - // interface so we skip it. - continue; - } - - // is up? - if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || - pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && - pIfRow->dwAdminStatus == 1 ) { - py_is_up = Py_True; - } - else { - py_is_up = Py_False; - } - Py_INCREF(py_is_up); - - py_ifc_info = Py_BuildValue( - "(Oikk)", - py_is_up, - 2, // there's no way to know duplex so let's assume 'full' - pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb - pIfRow->dwMtu - ); - if (!py_ifc_info) - goto error; - if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info)) - goto error; - Py_DECREF(py_nic_name); - Py_DECREF(py_ifc_info); - } - - free(pIfTable); - free(pAddresses); - return py_retdict; - -error: - Py_XDECREF(py_is_up); - Py_XDECREF(py_ifc_info); - Py_XDECREF(py_nic_name); - Py_DECREF(py_retdict); - if (pIfTable != NULL) - free(pIfTable); - if (pAddresses != NULL) - free(pAddresses); - return NULL; -} - - -/* - * Return CPU statistics. - */ -static PyObject * -psutil_cpu_stats(PyObject *self, PyObject *args) { - NTSTATUS status; - _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; - _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; - _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL; - unsigned int ncpus; - UINT i; - ULONG64 dpcs = 0; - ULONG interrupts = 0; - - // retrieves number of processors - ncpus = psutil_get_num_cpus(1); - if (ncpus == 0) - goto error; - - // get syscalls / ctx switches - spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \ - malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)); - if (spi == NULL) { - PyErr_NoMemory(); - goto error; - } - status = psutil_NtQuerySystemInformation( - SystemPerformanceInformation, - spi, - ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemPerformanceInformation)"); - goto error; - } - - // get DPCs - InterruptInformation = \ - malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus); - if (InterruptInformation == NULL) { - PyErr_NoMemory(); - goto error; - } - - status = psutil_NtQuerySystemInformation( - SystemInterruptInformation, - InterruptInformation, - ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemInterruptInformation)"); - goto error; - } - for (i = 0; i < ncpus; i++) { - dpcs += InterruptInformation[i].DpcCount; - } - - // get interrupts - sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ - malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); - if (sppi == NULL) { - PyErr_NoMemory(); - goto error; - } - - status = psutil_NtQuerySystemInformation( - SystemProcessorPerformanceInformation, - sppi, - ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, - "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"); - goto error; - } - - for (i = 0; i < ncpus; i++) { - interrupts += sppi[i].InterruptCount; - } - - // done - free(spi); - free(InterruptInformation); - free(sppi); - return Py_BuildValue( - "kkkk", - spi->ContextSwitches, - interrupts, - (unsigned long)dpcs, - spi->SystemCalls - ); - -error: - if (spi) - free(spi); - if (InterruptInformation) - free(InterruptInformation); - if (sppi) - free(sppi); - return NULL; -} - - -/* - * Return CPU frequency. - */ -static PyObject * -psutil_cpu_freq(PyObject *self, PyObject *args) { - PROCESSOR_POWER_INFORMATION *ppi; - NTSTATUS ret; - ULONG size; - LPBYTE pBuffer = NULL; - ULONG current; - ULONG max; - unsigned int ncpus; - - // Get the number of CPUs. - ncpus = psutil_get_num_cpus(1); - if (ncpus == 0) - return NULL; - - // Allocate size. - size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION); - pBuffer = (BYTE*)LocalAlloc(LPTR, size); - if (! pBuffer) - return PyErr_SetFromWindowsErr(0); - - // 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; -} - - /* * Return battery usage stats. */ @@ -3379,8 +1489,10 @@ static PyObject * psutil_sensors_battery(PyObject *self, PyObject *args) { SYSTEM_POWER_STATUS sps; - if (GetSystemPowerStatus(&sps) == 0) - return PyErr_SetFromWindowsErr(0); + if (GetSystemPowerStatus(&sps) == 0) { + PyErr_SetFromWindowsErr(0); + return NULL; + } return Py_BuildValue( "iiiI", sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown @@ -3418,15 +1530,10 @@ PsutilMethods[] = { "Return process environment data"}, {"proc_exe", psutil_proc_exe, METH_VARARGS, "Return path of the process executable"}, - {"proc_name", psutil_proc_name, METH_VARARGS, - "Return process name"}, {"proc_kill", psutil_proc_kill, METH_VARARGS, "Kill the process identified by the given PID"}, - {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS, + {"proc_times", psutil_proc_times, METH_VARARGS, "Return tuple of user/kern time for the given PID"}, - {"proc_create_time", psutil_proc_create_time, METH_VARARGS, - "Return a float indicating the process create time expressed in " - "seconds since the epoch"}, {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, "Return a tuple of process memory information"}, {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS, @@ -3447,12 +1554,10 @@ PsutilMethods[] = { "Return process priority."}, {"proc_priority_set", psutil_proc_priority_set, METH_VARARGS, "Set process priority."}, -#if (_WIN32_WINNT >= 0x0600) // Windows Vista {"proc_io_priority_get", psutil_proc_io_priority_get, METH_VARARGS, "Return process IO priority."}, {"proc_io_priority_set", psutil_proc_io_priority_set, METH_VARARGS, "Set process IO priority."}, -#endif {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, "Return process CPU affinity as a bitmask."}, {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, @@ -3509,12 +1614,11 @@ PsutilMethods[] = { "Return NICs stats."}, {"cpu_freq", psutil_cpu_freq, METH_VARARGS, "Return CPU frequency."}, -#if (_WIN32_WINNT >= 0x0600) // Windows Vista - {"init_loadavg_counter", psutil_init_loadavg_counter, METH_VARARGS, + {"init_loadavg_counter", (PyCFunction)psutil_init_loadavg_counter, + METH_VARARGS, "Initializes the emulated load average calculator."}, - {"getloadavg", psutil_get_loadavg, METH_VARARGS, + {"getloadavg", (PyCFunction)psutil_get_loadavg, METH_VARARGS, "Returns the emulated POSIX-like load average."}, -#endif {"sensors_battery", psutil_sensors_battery, METH_VARARGS, "Return battery metrics usage."}, {"getpagesize", psutil_getpagesize, METH_VARARGS, @@ -3704,10 +1808,6 @@ void init_psutil_windows(void) module, "ERROR_PRIVILEGE_NOT_HELD", ERROR_PRIVILEGE_NOT_HELD); PyModule_AddIntConstant( module, "WINVER", PSUTIL_WINVER); - PyModule_AddIntConstant( - module, "WINDOWS_XP", PSUTIL_WINDOWS_XP); - PyModule_AddIntConstant( - module, "WINDOWS_SERVER_2003", PSUTIL_WINDOWS_SERVER_2003); PyModule_AddIntConstant( module, "WINDOWS_VISTA", PSUTIL_WINDOWS_VISTA); PyModule_AddIntConstant( diff --git a/third_party/python/psutil/psutil/_pswindows.py b/third_party/python/psutil/psutil/_pswindows.py index 3f1319806fa7..99d5d71499ba 100644 --- a/third_party/python/psutil/psutil/_pswindows.py +++ b/third_party/python/psutil/psutil/_pswindows.py @@ -8,39 +8,24 @@ import contextlib import errno import functools import os +import signal import sys import time from collections import namedtuple from . import _common -try: - from . import _psutil_windows as cext -except ImportError as err: - if str(err).lower().startswith("dll load failed") and \ - sys.getwindowsversion()[0] < 6: - # We may get here if: - # 1) we are on an old Windows version - # 2) psutil was installed via pip + wheel - # See: https://github.com/giampaolo/psutil/issues/811 - # It must be noted that psutil can still (kind of) work - # on outdated systems if compiled / installed from sources, - # but if we get here it means this this was a wheel (or exe). - msg = "this Windows version is too old (< Windows Vista); " - msg += "psutil 3.4.2 is the latest version which supports Windows " - msg += "2000, XP and 2003 server" - raise RuntimeError(msg) - else: - raise - +from ._common import AccessDenied from ._common import conn_tmap +from ._common import conn_to_ntuple +from ._common import debug from ._common import ENCODING from ._common import ENCODING_ERRS from ._common import isfile_strict from ._common import memoize from ._common import memoize_when_activated +from ._common import NoSuchProcess from ._common import parse_environ_block -from ._common import sockfam_to_enum -from ._common import socktype_to_enum +from ._common import TimeoutExpired from ._common import usage_percent from ._compat import long from ._compat import lru_cache @@ -54,6 +39,22 @@ from ._psutil_windows import IDLE_PRIORITY_CLASS from ._psutil_windows import NORMAL_PRIORITY_CLASS from ._psutil_windows import REALTIME_PRIORITY_CLASS +try: + from . import _psutil_windows as cext +except ImportError as err: + if str(err).lower().startswith("dll load failed") and \ + sys.getwindowsversion()[0] < 6: + # We may get here if: + # 1) we are on an old Windows version + # 2) psutil was installed via pip + wheel + # See: https://github.com/giampaolo/psutil/issues/811 + msg = "this Windows version is too old (< Windows Vista); " + msg += "psutil 3.4.2 is the latest version which supports Windows " + msg += "2000, XP and 2003 server" + raise RuntimeError(msg) + else: + raise + if sys.version_info >= (3, 4): import enum else: @@ -79,9 +80,8 @@ __extra__all__ = [ # ===================================================================== CONN_DELETE_TCB = "DELETE_TCB" -HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_io_priority_get") -HAS_GETLOADAVG = hasattr(cext, "getloadavg") - +ERROR_PARTIAL_COPY = 299 +PYPY = '__pypy__' in sys.builtin_module_names if enum is None: AF_LINK = -1 @@ -154,35 +154,6 @@ pinfo_map = dict( mem_private=21, ) -# These objects get set on "import psutil" from the __init__.py -# file, see: https://github.com/giampaolo/psutil/issues/1402 -NoSuchProcess = None -ZombieProcess = None -AccessDenied = None -TimeoutExpired = None - -# More values at: https://stackoverflow.com/a/20804735/376587 -WIN_10 = (10, 0) -WIN_8 = (6, 2) -WIN_7 = (6, 1) -WIN_SERVER_2008 = (6, 0) -WIN_VISTA = (6, 0) -WIN_SERVER_2003 = (5, 2) -WIN_XP = (5, 1) - - -@lru_cache() -def get_winver(): - """Usage: - >>> if get_winver() <= WIN_VISTA: - ... ... - """ - wv = sys.getwindowsversion() - return (wv.major, wv.minor) - - -IS_WIN_XP = get_winver() < WIN_VISTA - # ===================================================================== # --- named tuples @@ -227,7 +198,8 @@ def convert_dos_path(s): """ rawdrive = '\\'.join(s.split('\\')[:3]) driveletter = cext.win32_QueryDosDevice(rawdrive) - return os.path.join(driveletter, s[len(rawdrive):]) + remainder = s[len(rawdrive):] + return os.path.join(driveletter, remainder) def py2_strencode(s): @@ -354,21 +326,21 @@ def cpu_freq(): return [_common.scpufreq(float(curr), min_, float(max_))] -if HAS_GETLOADAVG: - _loadavg_inititialized = False +_loadavg_inititialized = False - def getloadavg(): - """Return the number of processes in the system run queue averaged - over the last 1, 5, and 15 minutes respectively as a tuple""" - global _loadavg_inititialized - if not _loadavg_inititialized: - cext.init_loadavg_counter() - _loadavg_inititialized = True +def getloadavg(): + """Return the number of processes in the system run queue averaged + over the last 1, 5, and 15 minutes respectively as a tuple""" + global _loadavg_inititialized - # Drop to 2 decimal points which is what Linux does - raw_loads = cext.getloadavg() - return tuple([round(load, 2) for load in raw_loads]) + if not _loadavg_inititialized: + cext.init_loadavg_counter() + _loadavg_inititialized = True + + # Drop to 2 decimal points which is what Linux does + raw_loads = cext.getloadavg() + return tuple([round(load, 2) for load in raw_loads]) # ===================================================================== @@ -388,17 +360,8 @@ def net_connections(kind, _pid=-1): ret = set() for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - if laddr: - laddr = _common.addr(*laddr) - if raddr: - raddr = _common.addr(*raddr) - status = TCP_STATUSES[status] - fam = sockfam_to_enum(fam) - type = socktype_to_enum(type) - if _pid == -1: - nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid) - else: - nt = _common.pconn(fd, fam, type, laddr, raddr, status) + nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES, + pid=pid if _pid == -1 else None) ret.add(nt) return list(ret) @@ -719,6 +682,32 @@ def wrap_exceptions(fun): return wrapper +def retry_error_partial_copy(fun): + """Workaround for https://github.com/giampaolo/psutil/issues/875. + See: https://stackoverflow.com/questions/4457745#4457745 + """ + @functools.wraps(fun) + def wrapper(self, *args, **kwargs): + delay = 0.0001 + times = 33 + for x in range(times): # retries for roughly 1 second + try: + return fun(self, *args, **kwargs) + except WindowsError as _: + err = _ + if err.winerror == ERROR_PARTIAL_COPY: + time.sleep(delay) + delay = min(delay * 2, 0.04) + continue + else: + raise + else: + msg = "%s retried %s times, converted to AccessDenied as it's " \ + "still returning %r" % (fun, times, err) + raise AccessDenied(pid=self.pid, name=self._name, msg=msg) + return wrapper + + class Process(object): """Wrapper class around underlying C implementation.""" @@ -732,14 +721,15 @@ class Process(object): # --- oneshot() stuff def oneshot_enter(self): - self.oneshot_info.cache_activate(self) + self._proc_info.cache_activate(self) + self.exe.cache_activate(self) def oneshot_exit(self): - self.oneshot_info.cache_deactivate(self) + self._proc_info.cache_deactivate(self) + self.exe.cache_deactivate(self) - @wrap_exceptions @memoize_when_activated - def oneshot_info(self): + def _proc_info(self): """Return multiple information about this process as a raw tuple. """ @@ -747,7 +737,6 @@ class Process(object): assert len(ret) == len(pinfo_map) return ret - @wrap_exceptions def name(self): """Return process name, which on Windows is always the final part of the executable. @@ -756,32 +745,33 @@ class Process(object): # and process-hacker. if self.pid == 0: return "System Idle Process" - elif self.pid == 4: + if self.pid == 4: return "System" - else: - try: - # Note: this will fail with AD for most PIDs owned - # by another user but it's faster. - return py2_strencode(os.path.basename(self.exe())) - except AccessDenied: - return py2_strencode(cext.proc_name(self.pid)) + return os.path.basename(self.exe()) @wrap_exceptions + @memoize_when_activated def exe(self): - # Dual implementation, see: - # https://github.com/giampaolo/psutil/pull/1413 - if not IS_WIN_XP: - exe = cext.proc_exe(self.pid) + if PYPY: + try: + exe = cext.proc_exe(self.pid) + except WindowsError as err: + # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens + # (perhaps PyPy's JIT delaying garbage collection of files?). + if err.errno == 24: + debug("%r forced into AccessDenied" % err) + raise AccessDenied(self.pid, self._name) + raise else: - if self.pid in (0, 4): - # https://github.com/giampaolo/psutil/issues/414 - # https://github.com/giampaolo/psutil/issues/528 - raise AccessDenied(self.pid, self._name) exe = cext.proc_exe(self.pid) - exe = convert_dos_path(exe) - return py2_strencode(exe) + if not PY3: + exe = py2_strencode(exe) + if exe.startswith('\\'): + return convert_dos_path(exe) + return exe # May be "Registry", "MemCompression", ... @wrap_exceptions + @retry_error_partial_copy def cmdline(self): if cext.WINVER >= cext.WINDOWS_8_1: # PEB method detects cmdline changes but requires more @@ -801,6 +791,7 @@ class Process(object): return [py2_strencode(s) for s in ret] @wrap_exceptions + @retry_error_partial_copy def environ(self): ustr = cext.proc_environ(self.pid) if ustr and not PY3: @@ -820,7 +811,7 @@ class Process(object): if is_permission_err(err): # TODO: the C ext can probably be refactored in order # to get this from cext.proc_info() - info = self.oneshot_info() + info = self._proc_info() return ( info[pinfo_map['num_page_faults']], info[pinfo_map['peak_wset']], @@ -863,7 +854,6 @@ class Process(object): for addr, perm, path, rss in raw: path = convert_dos_path(path) if not PY3: - assert isinstance(path, unicode), type(path) path = py2_strencode(path) addr = hex(addr) yield (addr, perm, path, rss) @@ -874,7 +864,16 @@ class Process(object): @wrap_exceptions def send_signal(self, sig): - os.kill(self.pid, sig) + if sig == signal.SIGTERM: + cext.proc_kill(self.pid) + # py >= 2.7 + elif sig in (getattr(signal, "CTRL_C_EVENT", object()), + getattr(signal, "CTRL_BREAK_EVENT", object())): + os.kill(self.pid, sig) + else: + raise ValueError( + "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " + "are supported on Windows") @wrap_exceptions def wait(self, timeout=None): @@ -928,19 +927,19 @@ class Process(object): @wrap_exceptions def create_time(self): - # special case for kernel process PIDs; return system boot time - if self.pid in (0, 4): - return boot_time() + # Note: proc_times() not put under oneshot() 'cause create_time() + # is already cached by the main Process class. try: - return cext.proc_create_time(self.pid) + user, system, created = cext.proc_times(self.pid) + return created except OSError as err: if is_permission_err(err): - return self.oneshot_info()[pinfo_map['create_time']] + return self._proc_info()[pinfo_map['create_time']] raise @wrap_exceptions def num_threads(self): - return self.oneshot_info()[pinfo_map['num_threads']] + return self._proc_info()[pinfo_map['num_threads']] @wrap_exceptions def threads(self): @@ -954,11 +953,11 @@ class Process(object): @wrap_exceptions def cpu_times(self): try: - user, system = cext.proc_cpu_times(self.pid) + user, system, created = cext.proc_times(self.pid) except OSError as err: if not is_permission_err(err): raise - info = self.oneshot_info() + info = self._proc_info() user = info[pinfo_map['user_time']] system = info[pinfo_map['kernel_time']] # Children user/system times are not retrievable (set to 0). @@ -973,6 +972,7 @@ class Process(object): cext.proc_suspend_or_resume(self.pid, False) @wrap_exceptions + @retry_error_partial_copy def cwd(self): if self.pid in (0, 4): raise AccessDenied(self.pid, self._name) @@ -1015,23 +1015,21 @@ class Process(object): def nice_set(self, value): return cext.proc_priority_set(self.pid, value) - # available on Windows >= Vista - if HAS_PROC_IO_PRIORITY: - @wrap_exceptions - def ionice_get(self): - ret = cext.proc_io_priority_get(self.pid) - if enum is not None: - ret = IOPriority(ret) - return ret + @wrap_exceptions + def ionice_get(self): + ret = cext.proc_io_priority_get(self.pid) + if enum is not None: + ret = IOPriority(ret) + return ret - @wrap_exceptions - def ionice_set(self, ioclass, value): - if value: - raise TypeError("value argument not accepted on Windows") - if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, - IOPRIO_HIGH): - raise ValueError("%s is not a valid priority" % ioclass) - cext.proc_io_priority_set(self.pid, ioclass) + @wrap_exceptions + def ionice_set(self, ioclass, value): + if value: + raise TypeError("value argument not accepted on Windows") + if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, + IOPRIO_HIGH): + raise ValueError("%s is not a valid priority" % ioclass) + cext.proc_io_priority_set(self.pid, ioclass) @wrap_exceptions def io_counters(self): @@ -1040,7 +1038,7 @@ class Process(object): except OSError as err: if not is_permission_err(err): raise - info = self.oneshot_info() + info = self._proc_info() ret = ( info[pinfo_map['io_rcount']], info[pinfo_map['io_wcount']], @@ -1097,11 +1095,11 @@ class Process(object): return cext.proc_num_handles(self.pid) except OSError as err: if is_permission_err(err): - return self.oneshot_info()[pinfo_map['num_handles']] + return self._proc_info()[pinfo_map['num_handles']] raise @wrap_exceptions def num_ctx_switches(self): - ctx_switches = self.oneshot_info()[pinfo_map['ctx_switches']] + ctx_switches = self._proc_info()[pinfo_map['ctx_switches']] # only voluntary ctx switches are supported return _common.pctxsw(ctx_switches, 0) diff --git a/third_party/python/psutil/psutil/arch/aix/common.c b/third_party/python/psutil/psutil/arch/aix/common.c index 6115a15db51b..945cbd978b30 100644 --- a/third_party/python/psutil/psutil/arch/aix/common.c +++ b/third_party/python/psutil/psutil/arch/aix/common.c @@ -76,4 +76,4 @@ psutil_read_process_table(int * num) { *num = np; return processes; -} \ No newline at end of file +} diff --git a/third_party/python/psutil/psutil/arch/aix/ifaddrs.c b/third_party/python/psutil/psutil/arch/aix/ifaddrs.c index 1a819365ab8d..1480b60fab2a 100644 --- a/third_party/python/psutil/psutil/arch/aix/ifaddrs.c +++ b/third_party/python/psutil/psutil/arch/aix/ifaddrs.c @@ -146,4 +146,4 @@ error: close(sd); freeifaddrs(*ifap); return (-1); -} \ No newline at end of file +} diff --git a/third_party/python/psutil/psutil/arch/aix/ifaddrs.h b/third_party/python/psutil/psutil/arch/aix/ifaddrs.h index 3920c1ccca72..e15802bf7b21 100644 --- a/third_party/python/psutil/psutil/arch/aix/ifaddrs.h +++ b/third_party/python/psutil/psutil/arch/aix/ifaddrs.h @@ -31,5 +31,4 @@ struct ifaddrs { extern int getifaddrs(struct ifaddrs **); extern void freeifaddrs(struct ifaddrs *); - -#endif \ No newline at end of file +#endif diff --git a/third_party/python/psutil/psutil/arch/aix/net_connections.h b/third_party/python/psutil/psutil/arch/aix/net_connections.h index 222bcaf354e8..d57ee4284711 100644 --- a/third_party/python/psutil/psutil/arch/aix/net_connections.h +++ b/third_party/python/psutil/psutil/arch/aix/net_connections.h @@ -12,4 +12,4 @@ PyObject* psutil_net_connections(PyObject *self, PyObject *args); -#endif /* __NET_CONNECTIONS_H__ */ \ No newline at end of file +#endif /* __NET_CONNECTIONS_H__ */ diff --git a/third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h b/third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h index 4e7a088c1432..7e22a1639a02 100644 --- a/third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h +++ b/third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h @@ -108,4 +108,4 @@ struct mbuf64 #define m_len m_hdr.mh_len -#endif /* __64BIT__ */ \ No newline at end of file +#endif /* __64BIT__ */ diff --git a/third_party/python/psutil/psutil/arch/freebsd/proc_socks.c b/third_party/python/psutil/psutil/arch/freebsd/proc_socks.c index a458a01e5316..cdf5770b6f8f 100644 --- a/third_party/python/psutil/psutil/arch/freebsd/proc_socks.c +++ b/third_party/python/psutil/psutil/arch/freebsd/proc_socks.c @@ -179,7 +179,7 @@ psutil_search_tcplist(char *buf, struct kinfo_file *kif) { PyObject * psutil_proc_connections(PyObject *self, PyObject *args) { // Return connections opened by process. - long pid; + pid_t pid; int i; int cnt; struct kinfo_file *freep = NULL; @@ -202,8 +202,11 @@ psutil_proc_connections(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, + &py_af_filter, &py_type_filter)) + { goto error; + } if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); goto error; diff --git a/third_party/python/psutil/psutil/arch/freebsd/specific.c b/third_party/python/psutil/psutil/arch/freebsd/specific.c index 26b802422dbc..3f37a08e2d2a 100644 --- a/third_party/python/psutil/psutil/arch/freebsd/specific.c +++ b/third_party/python/psutil/psutil/arch/freebsd/specific.c @@ -28,6 +28,7 @@ #include "../../_psutil_common.h" #include "../../_psutil_posix.h" + #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) #define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * (uint32_t) \ (bt.frac >> 32) ) >> 32 ) / 1000000) @@ -43,7 +44,7 @@ int -psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) { +psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { // Fills a kinfo_proc struct based on process pid. int mib[4]; size_t size; @@ -60,7 +61,7 @@ psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) { // sysctl stores 0 in the size if we can't find the process information. if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); return -1; } return 0; @@ -87,81 +88,42 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { // Returns a list of all BSD processes on the system. This routine // allocates the list and puts it in *procList and a count of the // number of entries in *procCount. You are responsible for freeing - // this list (use "free" from System framework). - // On success, the function returns 0. - // On error, the function returns a BSD errno value. + // this list. On success returns 0, else 1 with exception set. int err; - struct kinfo_proc *result; - int done; + struct kinfo_proc *buf = NULL; int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; - size_t length; + size_t length = 0; - assert( procList != NULL); + assert(procList != NULL); assert(*procList == NULL); assert(procCount != NULL); - *procCount = 0; - - /* - * We start by calling sysctl with result == NULL and length == 0. - * That will succeed, and set length to the appropriate length. - * We then allocate a buffer of that size and call sysctl again - * with that buffer. If that succeeds, we're done. If that fails - * with ENOMEM, we have to throw away our buffer and loop. Note - * that the loop causes use to call sysctl with NULL again; this - * is necessary because the ENOMEM failure case sets length to - * the amount of data returned, not the amount of data that - * could have been returned. - */ - result = NULL; - done = 0; - do { - assert(result == NULL); - // Call sysctl with a NULL buffer. - length = 0; - err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, - NULL, &length, NULL, 0); - if (err == -1) - err = errno; - - // Allocate an appropriately sized buffer based on the results - // from the previous call. - if (err == 0) { - result = malloc(length); - if (result == NULL) - err = ENOMEM; - } - - // Call sysctl again with the new buffer. If we get an ENOMEM - // error, toss away our buffer and start again. - if (err == 0) { - err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, - result, &length, NULL, 0); - if (err == -1) - err = errno; - if (err == 0) { - done = 1; - } - else if (err == ENOMEM) { - assert(result != NULL); - free(result); - result = NULL; - err = 0; - } - } - } while (err == 0 && ! done); - - // Clean up and establish post conditions. - if (err != 0 && result != NULL) { - free(result); - result = NULL; + // Call sysctl with a NULL buffer in order to get buffer length. + err = sysctl(name, 3, NULL, &length, NULL, 0); + if (err == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl (null buffer)"); + return 1; } - *procList = result; - *procCount = length / sizeof(struct kinfo_proc); + // Allocate an appropriately sized buffer based on the results + // from the previous call. + buf = malloc(length); + if (buf == NULL) { + PyErr_NoMemory(); + return 1; + } - assert((err == 0) == (*procList != NULL)); - return err; + // Call sysctl again with the new buffer. + err = sysctl(name, 3, buf, &length, NULL, 0); + if (err == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl"); + free(buf); + return 1; + } + + *procList = buf; + *procCount = length / sizeof(struct kinfo_proc); + return 0; } @@ -179,7 +141,7 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { * 1 for insufficient privileges. */ static char -*psutil_get_cmd_args(long pid, size_t *argsize) { +*psutil_get_cmd_args(pid_t pid, size_t *argsize) { int mib[4]; int argmax; size_t size = sizeof(argmax); @@ -221,7 +183,7 @@ static char // returns the command line as a python list object PyObject * -psutil_get_cmdline(long pid) { +psutil_get_cmdline(pid_t pid) { char *argstr = NULL; size_t pos = 0; size_t argsize = 0; @@ -268,14 +230,14 @@ error: */ PyObject * psutil_proc_exe(PyObject *self, PyObject *args) { - long pid; + pid_t pid; char pathname[PATH_MAX]; int error; int mib[4]; int ret; size_t size; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; mib[0] = CTL_KERN; @@ -300,7 +262,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) { if (ret == -1) return NULL; else if (ret == 0) - return NoSuchProcess(""); + return NoSuchProcess("psutil_pid_exists"); else strcpy(pathname, ""); } @@ -312,9 +274,9 @@ psutil_proc_exe(PyObject *self, PyObject *args) { PyObject * psutil_proc_num_threads(PyObject *self, PyObject *args) { // Return number of threads used by process as a Python integer. - long pid; + pid_t pid; struct kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; @@ -329,7 +291,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { // Thanks to Robert N. M. Watson: // http://code.metager.de/source/xref/freebsd/usr.bin/procstat/ // procstat_threads.c - long pid; + pid_t pid; int mib[4]; struct kinfo_proc *kip = NULL; struct kinfo_proc *kipp = NULL; @@ -341,7 +303,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; // we need to re-query for thread information, so don't use *kipp @@ -357,7 +319,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { goto error; } if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); goto error; } @@ -373,7 +335,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { goto error; } if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); goto error; } @@ -558,7 +520,7 @@ psutil_swap_mem(PyObject *self, PyObject *args) { #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 PyObject * psutil_proc_cwd(PyObject *self, PyObject *args) { - long pid; + pid_t pid; struct kinfo_file *freep = NULL; struct kinfo_file *kif; struct kinfo_proc kipp; @@ -566,7 +528,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { int i, cnt; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; if (psutil_kinfo_proc(pid, &kipp) == -1) goto error; @@ -609,13 +571,13 @@ error: #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 PyObject * psutil_proc_num_fds(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int cnt; struct kinfo_file *freep; struct kinfo_proc kipp; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_kinfo_proc(pid, &kipp) == -1) return NULL; @@ -767,8 +729,8 @@ error: PyObject * psutil_proc_memory_maps(PyObject *self, PyObject *args) { // Return a list of tuples for every process memory maps. - //'procstat' cmdline utility has been used as an example. - long pid; + // 'procstat' cmdline utility has been used as an example. + pid_t pid; int ptrwidth; int i, cnt; char addr[1000]; @@ -784,7 +746,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; if (psutil_kinfo_proc(pid, &kp) == -1) goto error; @@ -884,14 +846,14 @@ psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) { // Get process CPU affinity. // Reference: // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c - long pid; + pid_t pid; int ret; int i; cpuset_t mask; PyObject* py_retlist; PyObject* py_cpu_num; - if (!PyArg_ParseTuple(args, "i", &pid)) + if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, sizeof(mask), &mask); @@ -926,7 +888,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { // Set process CPU affinity. // Reference: // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c - long pid; + pid_t pid; int i; int seq_len; int ret; @@ -934,7 +896,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { PyObject *py_cpu_set; PyObject *py_cpu_seq = NULL; - if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set)) return NULL; py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer"); diff --git a/third_party/python/psutil/psutil/arch/freebsd/sys_socks.c b/third_party/python/psutil/psutil/arch/freebsd/sys_socks.c index e0e2046be6d5..ab61f393b99c 100644 --- a/third_party/python/psutil/psutil/arch/freebsd/sys_socks.c +++ b/third_party/python/psutil/psutil/arch/freebsd/sys_socks.c @@ -129,7 +129,7 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) { } while (xig->xig_gen != exig->xig_gen && retry--); for (;;) { - struct xfile *xf; + struct xfile *xf; int lport, rport, status, family; xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); @@ -203,19 +203,20 @@ int psutil_gather_inet(int proto, PyObject *py_retlist) { if (!py_raddr) goto error; py_tuple = Py_BuildValue( - "(iiiNNii)", + "iiiNNi" _Py_PARSE_PID, xf->xf_fd, // fd family, // family type, // type py_laddr, // laddr py_raddr, // raddr status, // status - xf->xf_pid); // pid + xf->xf_pid // pid + ); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; - Py_DECREF(py_tuple); + Py_CLEAR(py_tuple); } free(buf); @@ -286,7 +287,7 @@ int psutil_gather_unix(int proto, PyObject *py_retlist) { } while (xug->xug_gen != exug->xug_gen && retry--); for (;;) { - struct xfile *xf; + struct xfile *xf; xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); if (xug >= exug) diff --git a/third_party/python/psutil/psutil/arch/netbsd/specific.c b/third_party/python/psutil/psutil/arch/netbsd/specific.c index 195896f2fb09..9dab36183994 100644 --- a/third_party/python/psutil/psutil/arch/netbsd/specific.c +++ b/third_party/python/psutil/psutil/arch/netbsd/specific.c @@ -31,15 +31,16 @@ #include #include // for CPUSTATES & CP_* #define _KERNEL // for DTYPE_* -#include + #include #undef _KERNEL #include // struct diskstats #include #include -#include "specific.h" #include "../../_psutil_common.h" #include "../../_psutil_posix.h" +#include "specific.h" + #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0) #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) @@ -71,7 +72,7 @@ psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) { } // sysctl stores 0 in the size if we can't find the process information. if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); return -1; } return 0; @@ -111,6 +112,47 @@ kinfo_getfile(pid_t pid, int* cnt) { return kf; } +PyObject * +psutil_proc_cwd(PyObject *self, PyObject *args) { + long pid; + + char path[MAXPATHLEN]; + size_t pathlen = sizeof path; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + +#ifdef KERN_PROC_CWD + int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD}; + if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) { + if (errno == ENOENT) + NoSuchProcess(""); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } +#else + char *buf; + if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) { + PyErr_NoMemory(); + return NULL; + } + + ssize_t len = readlink(buf, path, sizeof(path) - 1); + free(buf); + if (len == -1) { + if (errno == ENOENT) + NoSuchProcess("readlink (ENOENT)"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + path[len] = '\0'; +#endif + + return PyUnicode_DecodeFSDefault(path); +} + // XXX: This is no longer used as per // https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820 @@ -156,7 +198,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) { if (ret == -1) return NULL; else if (ret == 0) - return NoSuchProcess(""); + return NoSuchProcess("psutil_pid_exists"); else strcpy(pathname, ""); } @@ -208,7 +250,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { goto error; } if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); goto error; } @@ -225,7 +267,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { goto error; } if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); goto error; } @@ -281,14 +323,14 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { if (kd == NULL) { PyErr_Format( PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf); - return errno; + return 1; } result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt); if (result == NULL) { PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed"); kvm_close(kd); - return errno; + return 1; } *procCount = (size_t)cnt; @@ -298,7 +340,7 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { if ((*procList = malloc(mlen)) == NULL) { PyErr_NoMemory(); kvm_close(kd); - return errno; + return 1; } memcpy(*procList, result, mlen); diff --git a/third_party/python/psutil/psutil/arch/netbsd/specific.h b/third_party/python/psutil/psutil/arch/netbsd/specific.h index 96ad9f7d269d..391ed164a4c7 100644 --- a/third_party/python/psutil/psutil/arch/netbsd/specific.h +++ b/third_party/python/psutil/psutil/arch/netbsd/specific.h @@ -26,3 +26,4 @@ PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args); PyObject* psutil_proc_exe(PyObject* self, PyObject* args); PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args); PyObject* psutil_cpu_stats(PyObject* self, PyObject* args); +PyObject *psutil_proc_cwd(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/openbsd/specific.c b/third_party/python/psutil/psutil/arch/openbsd/specific.c index 33ebdeecbaf6..d97a8f9b93fa 100644 --- a/third_party/python/psutil/psutil/arch/openbsd/specific.c +++ b/third_party/python/psutil/psutil/arch/openbsd/specific.c @@ -46,6 +46,21 @@ // Utility functions // ============================================================================ + +static void +convert_kvm_err(const char *syscall, char *errbuf) { + char fullmsg[8192]; + + sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf); + if (strstr(errbuf, "Permission denied") != NULL) + AccessDenied(fullmsg); + else if (strstr(errbuf, "Operation not permitted") != NULL) + AccessDenied(fullmsg); + else + PyErr_Format(PyExc_RuntimeError, fullmsg); +} + + int psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { // Fills a kinfo_proc struct based on process pid. @@ -67,7 +82,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { } // sysctl stores 0 in the size if we can't find the process information. if (size == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (size = 0)"); return -1; } return 0; @@ -75,7 +90,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { struct kinfo_file * -kinfo_getfile(long pid, int* cnt) { +kinfo_getfile(pid_t pid, int* cnt) { // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an // int as arg and returns an array with cnt struct kinfo_file. int mib[6]; @@ -84,7 +99,7 @@ kinfo_getfile(long pid, int* cnt) { mib[0] = CTL_KERN; mib[1] = KERN_FILE; mib[2] = KERN_FILE_BYPID; - mib[3] = (int) pid; + mib[3] = pid; mib[4] = sizeof(struct kinfo_file); mib[5] = 0; @@ -133,16 +148,16 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { assert(procCount != NULL); kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - - if (kd == NULL) { - return errno; + if (! kd) { + convert_kvm_err("kvm_openfiles", errbuf); + return 1; } result = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt); if (result == NULL) { + PyErr_Format(PyExc_RuntimeError, "kvm_getprocs syscall failed"); kvm_close(kd); - err(1, NULL); - return errno; + return 1; } *procCount = (size_t)cnt; @@ -150,9 +165,9 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { size_t mlen = cnt * sizeof(struct kinfo_proc); if ((*procList = malloc(mlen)) == NULL) { + PyErr_NoMemory(); kvm_close(kd); - err(1, NULL); - return errno; + return 1; } memcpy(*procList, result, mlen); @@ -163,8 +178,8 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) { } -char ** -_psutil_get_argv(long pid) { +static char ** +_psutil_get_argv(pid_t pid) { static char **argv; int argv_mib[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV}; size_t argv_size = 128; @@ -189,7 +204,7 @@ _psutil_get_argv(long pid) { // returns the command line as a python list object PyObject * -psutil_get_cmdline(long pid) { +psutil_get_cmdline(pid_t pid) { static char **argv; char **p; PyObject *py_arg = NULL; @@ -226,7 +241,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { // https://github.com/janmojzis/pstree/blob/master/proc_kvm.c // Note: this requires root access, else it will fail trying // to access /dev/kmem. - long pid; + pid_t pid; kvm_t *kd = NULL; int nentries, i; char errbuf[4096]; @@ -236,15 +251,12 @@ psutil_proc_threads(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) goto error; kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf); if (! kd) { - if (strstr(errbuf, "Permission denied") != NULL) - AccessDenied(""); - else - PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() syscall failed"); + convert_kvm_err("kvm_openfiles()", errbuf); goto error; } @@ -253,7 +265,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { sizeof(*kp), &nentries); if (! kp) { if (strstr(errbuf, "Permission denied") != NULL) - AccessDenied(""); + AccessDenied("kvm_getprocs"); else PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed"); goto error; @@ -264,7 +276,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) { continue; if (kp[i].p_pid == pid) { py_tuple = Py_BuildValue( - "Idd", + _Py_PARSE_PID "dd", kp[i].p_tid, PSUTIL_KPT2DOUBLE(kp[i].p_uutime), PSUTIL_KPT2DOUBLE(kp[i].p_ustime)); @@ -390,25 +402,23 @@ error: PyObject * psutil_proc_num_fds(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int cnt; struct kinfo_file *freep; struct kinfo_proc kipp; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; + if (psutil_kinfo_proc(pid, &kipp) == -1) return NULL; - errno = 0; freep = kinfo_getfile(pid, &cnt); - if (freep == NULL) { - psutil_raise_for_pid(pid, "kinfo_getfile()"); + if (freep == NULL) return NULL; - } - free(freep); + free(freep); return Py_BuildValue("i", cnt); } @@ -418,12 +428,12 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { // Reference: // https://github.com/openbsd/src/blob/ // 588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L191 - long pid; + pid_t pid; struct kinfo_proc kp; char path[MAXPATHLEN]; size_t pathlen = sizeof path; - if (! PyArg_ParseTuple(args, "l", &pid)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) return NULL; if (psutil_kinfo_proc(pid, &kp) == -1) return NULL; @@ -482,7 +492,7 @@ psutil_inet6_addrstr(struct in6_addr *p) */ PyObject * psutil_proc_connections(PyObject *self, PyObject *args) { - long pid; + pid_t pid; int i; int cnt; struct kinfo_file *freep = NULL; @@ -499,17 +509,16 @@ psutil_proc_connections(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, &py_af_filter, + &py_type_filter)) goto error; if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); goto error; } - errno = 0; freep = kinfo_getfile(pid, &cnt); if (freep == NULL) { - psutil_raise_for_pid(pid, "kinfo_getfile()"); goto error; } diff --git a/third_party/python/psutil/psutil/arch/openbsd/specific.h b/third_party/python/psutil/psutil/arch/openbsd/specific.h index 4f870268d61d..b8170a02239b 100644 --- a/third_party/python/psutil/psutil/arch/openbsd/specific.h +++ b/third_party/python/psutil/psutil/arch/openbsd/specific.h @@ -10,10 +10,10 @@ typedef struct kinfo_proc kinfo_proc; int psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc); -struct kinfo_file * kinfo_getfile(long pid, int* cnt); +struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt); int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount); -char **_psutil_get_argv(long pid); -PyObject * psutil_get_cmdline(long pid); +char **_psutil_get_argv(pid_t pid); +PyObject * psutil_get_cmdline(pid_t pid); // PyObject *psutil_proc_threads(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/osx/process_info.c b/third_party/python/psutil/psutil/arch/osx/process_info.c index d21c048edb32..4b84a723a0f9 100644 --- a/third_party/python/psutil/psutil/arch/osx/process_info.c +++ b/third_party/python/psutil/psutil/arch/osx/process_info.c @@ -19,9 +19,9 @@ #include #include -#include "process_info.h" #include "../../_psutil_common.h" #include "../../_psutil_posix.h" +#include "process_info.h" /* * Returns a list of all BSD processes on the system. This routine @@ -119,7 +119,7 @@ psutil_get_argmax() { // Return 1 if pid refers to a zombie process else 0. int -psutil_is_zombie(long pid) { +psutil_is_zombie(pid_t pid) { struct kinfo_proc kp; if (psutil_get_kinfo_proc(pid, &kp) == -1) @@ -131,7 +131,7 @@ psutil_is_zombie(long pid) { // return process args as a python list PyObject * -psutil_get_cmdline(long pid) { +psutil_get_cmdline(pid_t pid) { int mib[3]; int nargs; size_t len; @@ -162,12 +162,12 @@ psutil_get_cmdline(long pid) { // read argument space mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; - mib[2] = (pid_t)pid; + mib[2] = pid; if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) { // In case of zombie process we'll get EINVAL. We translate it // to NSP and _psosx.py will translate it to ZP. if ((errno == EINVAL) && (psutil_pid_exists(pid))) - NoSuchProcess(""); + NoSuchProcess("sysctl"); else PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -225,7 +225,7 @@ error: // return process environment as a python string PyObject * -psutil_get_environ(long pid) { +psutil_get_environ(pid_t pid) { int mib[3]; int nargs; char *procargs = NULL; @@ -254,12 +254,12 @@ psutil_get_environ(long pid) { // read argument space mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; - mib[2] = (pid_t)pid; + mib[2] = pid; if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) { // In case of zombie process we'll get EINVAL. We translate it // to NSP and _psosx.py will translate it to ZP. if ((errno == EINVAL) && (psutil_pid_exists(pid))) - NoSuchProcess(""); + NoSuchProcess("sysctl"); else PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -339,13 +339,13 @@ error: int -psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) { +psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) { int mib[4]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; - mib[3] = (pid_t)pid; + mib[3] = pid; // fetch the info with sysctl() len = sizeof(struct kinfo_proc); @@ -359,7 +359,7 @@ psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) { // sysctl succeeds but len is zero, happens when process has gone away if (len == 0) { - NoSuchProcess(""); + NoSuchProcess("sysctl (len == 0)"); return -1; } return 0; @@ -371,9 +371,9 @@ psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) { * Returns 0 on failure (and Python exception gets already set). */ int -psutil_proc_pidinfo(long pid, int flavor, uint64_t arg, void *pti, int size) { +psutil_proc_pidinfo(pid_t pid, int flavor, uint64_t arg, void *pti, int size) { errno = 0; - int ret = proc_pidinfo((int)pid, flavor, arg, pti, size); + int ret = proc_pidinfo(pid, flavor, arg, pti, size); if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) { psutil_raise_for_pid(pid, "proc_pidinfo()"); return 0; diff --git a/third_party/python/psutil/psutil/arch/osx/process_info.h b/third_party/python/psutil/psutil/arch/osx/process_info.h index bd7ffa89caf8..35755247aa41 100644 --- a/third_party/python/psutil/psutil/arch/osx/process_info.h +++ b/third_party/python/psutil/psutil/arch/osx/process_info.h @@ -9,10 +9,10 @@ typedef struct kinfo_proc kinfo_proc; int psutil_get_argmax(void); -int psutil_is_zombie(long pid); -int psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp); +int psutil_is_zombie(pid_t pid); +int psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp); int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount); int psutil_proc_pidinfo( - long pid, int flavor, uint64_t arg, void *pti, int size); -PyObject* psutil_get_cmdline(long pid); -PyObject* psutil_get_environ(long pid); + pid_t pid, int flavor, uint64_t arg, void *pti, int size); +PyObject* psutil_get_cmdline(pid_t pid); +PyObject* psutil_get_environ(pid_t pid); diff --git a/third_party/python/psutil/psutil/arch/solaris/environ.c b/third_party/python/psutil/psutil/arch/solaris/environ.c index 1af4c129344a..482fe1fc1658 100644 --- a/third_party/python/psutil/psutil/arch/solaris/environ.c +++ b/third_party/python/psutil/psutil/arch/solaris/environ.c @@ -6,14 +6,13 @@ * Functions specific for Process.environ(). */ -#define NEW_MIB_COMPLIANT 1 #define _STRUCTURED_PROC 1 #include #if !defined(_LP64) && _FILE_OFFSET_BITS == 64 -# undef _FILE_OFFSET_BITS -# undef _LARGEFILE64_SOURCE + #undef _FILE_OFFSET_BITS + #undef _LARGEFILE64_SOURCE #endif #include diff --git a/third_party/python/psutil/psutil/arch/windows/cpu.c b/third_party/python/psutil/psutil/arch/windows/cpu.c new file mode 100644 index 000000000000..18f32e598326 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/cpu.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include + +#include "../../_psutil_common.h" + + +/* + * Return the number of logical, active CPUs. Return 0 if undetermined. + * See discussion at: https://bugs.python.org/issue33166#msg314631 + */ +static unsigned int +psutil_get_num_cpus(int fail_on_err) { + unsigned int ncpus = 0; + + // Minimum requirement: Windows 7 + if (GetActiveProcessorCount != NULL) { + ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + if ((ncpus == 0) && (fail_on_err == 1)) { + PyErr_SetFromWindowsErr(0); + } + } + else { + psutil_debug("GetActiveProcessorCount() not available; " + "using GetSystemInfo()"); + ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors; + if ((ncpus <= 0) && (fail_on_err == 1)) { + PyErr_SetString( + PyExc_RuntimeError, + "GetSystemInfo() failed to retrieve CPU count"); + } + } + return ncpus; +} + + +/* + * Retrieves system CPU timing information as a (user, system, idle) + * tuple. On a multiprocessor system, the values returned are the + * sum of the designated times across all processors. + */ +PyObject * +psutil_cpu_times(PyObject *self, PyObject *args) { + double idle, kernel, user, system; + FILETIME idle_time, kernel_time, user_time; + + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + + idle = (double)((HI_T * idle_time.dwHighDateTime) + \ + (LO_T * idle_time.dwLowDateTime)); + user = (double)((HI_T * user_time.dwHighDateTime) + \ + (LO_T * user_time.dwLowDateTime)); + kernel = (double)((HI_T * kernel_time.dwHighDateTime) + \ + (LO_T * kernel_time.dwLowDateTime)); + + // Kernel time includes idle time. + // We return only busy kernel time subtracting idle time from + // kernel time. + system = (kernel - idle); + return Py_BuildValue("(ddd)", user, system, idle); +} + + +/* + * Same as above but for all system CPUs. + */ +PyObject * +psutil_per_cpu_times(PyObject *self, PyObject *args) { + double idle, kernel, systemt, user, interrupt, dpc; + NTSTATUS status; + _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + UINT i; + unsigned int ncpus; + PyObject *py_tuple = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return NULL; + + // retrieves number of processors + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + goto error; + + // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + // structures, one per processor + sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi == NULL) { + PyErr_NoMemory(); + goto error; + } + + // gets cpu time informations + status = NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtQuerySystemInformation(SystemProcessorPerformanceInformation)" + ); + goto error; + } + + // computes system global times summing each + // processor value + idle = user = kernel = interrupt = dpc = 0; + for (i = 0; i < ncpus; i++) { + py_tuple = NULL; + user = (double)((HI_T * sppi[i].UserTime.HighPart) + + (LO_T * sppi[i].UserTime.LowPart)); + idle = (double)((HI_T * sppi[i].IdleTime.HighPart) + + (LO_T * sppi[i].IdleTime.LowPart)); + kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) + + (LO_T * sppi[i].KernelTime.LowPart)); + interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) + + (LO_T * sppi[i].InterruptTime.LowPart)); + dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) + + (LO_T * sppi[i].DpcTime.LowPart)); + + // kernel time includes idle time on windows + // we return only busy kernel time subtracting + // idle time from kernel time + systemt = kernel - idle; + py_tuple = Py_BuildValue( + "(ddddd)", + user, + systemt, + idle, + interrupt, + dpc + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + } + + free(sppi); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (sppi) + free(sppi); + return NULL; +} + + +/* + * Return the number of active, logical CPUs. + */ +PyObject * +psutil_cpu_count_logical(PyObject *self, PyObject *args) { + unsigned int ncpus; + + ncpus = psutil_get_num_cpus(0); + if (ncpus != 0) + return Py_BuildValue("I", ncpus); + else + Py_RETURN_NONE; // mimick os.cpu_count() +} + + +/* + * Return the number of physical CPU cores (hyper-thread CPUs count + * is excluded). + */ +PyObject * +psutil_cpu_count_phys(PyObject *self, PyObject *args) { + DWORD rc; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL; + DWORD length = 0; + DWORD offset = 0; + DWORD ncpus = 0; + DWORD prev_processor_info_size = 0; + + // GetLogicalProcessorInformationEx() is available from Windows 7 + // onward. Differently from GetLogicalProcessorInformation() + // it supports process groups, meaning this is able to report more + // than 64 CPUs. See: + // https://bugs.python.org/issue33166 + if (GetLogicalProcessorInformationEx == NULL) { + psutil_debug("Win < 7; cpu_count_phys() forced to None"); + Py_RETURN_NONE; + } + + while (1) { + rc = GetLogicalProcessorInformationEx( + RelationAll, buffer, &length); + if (rc == FALSE) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + if (buffer) { + free(buffer); + } + buffer = \ + (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length); + if (NULL == buffer) { + PyErr_NoMemory(); + return NULL; + } + } + else { + psutil_debug("GetLogicalProcessorInformationEx() returned ", + GetLastError()); + goto return_none; + } + } + else { + break; + } + } + + ptr = buffer; + while (offset < length) { + // Advance ptr by the size of the previous + // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) \ + (((char*)ptr) + prev_processor_info_size); + + if (ptr->Relationship == RelationProcessorCore) { + ncpus += 1; + } + + // When offset == length, we've reached the last processor + // info struct in the buffer. + offset += ptr->Size; + prev_processor_info_size = ptr->Size; + } + + free(buffer); + if (ncpus != 0) { + return Py_BuildValue("I", ncpus); + } + else { + psutil_debug("GetLogicalProcessorInformationEx() count was 0"); + Py_RETURN_NONE; // mimick os.cpu_count() + } + +return_none: + if (buffer != NULL) + free(buffer); + Py_RETURN_NONE; +} + + +/* + * Return CPU statistics. + */ +PyObject * +psutil_cpu_stats(PyObject *self, PyObject *args) { + NTSTATUS status; + _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; + _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL; + unsigned int ncpus; + UINT i; + ULONG64 dpcs = 0; + ULONG interrupts = 0; + + // retrieves number of processors + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + goto error; + + // get syscalls / ctx switches + spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)); + if (spi == NULL) { + PyErr_NoMemory(); + goto error; + } + status = NtQuerySystemInformation( + SystemPerformanceInformation, + spi, + ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, "NtQuerySystemInformation(SystemPerformanceInformation)"); + goto error; + } + + // get DPCs + InterruptInformation = \ + malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus); + if (InterruptInformation == NULL) { + PyErr_NoMemory(); + goto error; + } + + status = NtQuerySystemInformation( + SystemInterruptInformation, + InterruptInformation, + ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, "NtQuerySystemInformation(SystemInterruptInformation)"); + goto error; + } + for (i = 0; i < ncpus; i++) { + dpcs += InterruptInformation[i].DpcCount; + } + + // get interrupts + sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi == NULL) { + PyErr_NoMemory(); + goto error; + } + + status = NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"); + goto error; + } + + for (i = 0; i < ncpus; i++) { + interrupts += sppi[i].InterruptCount; + } + + // done + free(spi); + free(InterruptInformation); + free(sppi); + return Py_BuildValue( + "kkkk", + spi->ContextSwitches, + interrupts, + (unsigned long)dpcs, + spi->SystemCalls + ); + +error: + if (spi) + free(spi); + if (InterruptInformation) + free(InterruptInformation); + if (sppi) + free(sppi); + return NULL; +} + + +/* + * Return CPU frequency. + */ +PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + PROCESSOR_POWER_INFORMATION *ppi; + NTSTATUS ret; + ULONG size; + LPBYTE pBuffer = NULL; + ULONG current; + ULONG max; + unsigned int ncpus; + + // Get the number of CPUs. + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + return NULL; + + // Allocate size. + size = ncpus * 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; +} diff --git a/third_party/python/psutil/psutil/arch/windows/cpu.h b/third_party/python/psutil/psutil/arch/windows/cpu.h new file mode 100644 index 000000000000..d88c22121082 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/cpu.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args); +PyObject *psutil_cpu_count_phys(PyObject *self, PyObject *args); +PyObject *psutil_cpu_freq(PyObject *self, PyObject *args); +PyObject *psutil_cpu_stats(PyObject *self, PyObject *args); +PyObject *psutil_cpu_times(PyObject *self, PyObject *args); +PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/disk.c b/third_party/python/psutil/psutil/arch/windows/disk.c new file mode 100644 index 000000000000..45e0ee1e6290 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/disk.c @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include + +#include "../../_psutil_common.h" + + +#ifndef _ARRAYSIZE +#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +static char *psutil_get_drive_type(int type) { + switch (type) { + case DRIVE_FIXED: + return "fixed"; + case DRIVE_CDROM: + return "cdrom"; + case DRIVE_REMOVABLE: + return "removable"; + case DRIVE_UNKNOWN: + return "unknown"; + case DRIVE_NO_ROOT_DIR: + return "unmounted"; + case DRIVE_REMOTE: + return "remote"; + case DRIVE_RAMDISK: + return "ramdisk"; + default: + return "?"; + } +} + + +/* + * Return path's disk total and free as a Python tuple. + */ +PyObject * +psutil_disk_usage(PyObject *self, PyObject *args) { + BOOL retval; + ULARGE_INTEGER _, total, free; + char *path; + + if (PyArg_ParseTuple(args, "u", &path)) { + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free); + Py_END_ALLOW_THREADS + goto return_; + } + + // on Python 2 we also want to accept plain strings other + // than Unicode +#if PY_MAJOR_VERSION <= 2 + PyErr_Clear(); // drop the argument parsing error + if (PyArg_ParseTuple(args, "s", &path)) { + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceEx(path, &_, &total, &free); + Py_END_ALLOW_THREADS + goto return_; + } +#endif + + return NULL; + +return_: + if (retval == 0) + return PyErr_SetFromWindowsErrWithFilename(0, path); + else + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); +} + + +/* + * Return a Python dict of tuples for disk I/O information. This may + * require running "diskperf -y" command first. + */ +PyObject * +psutil_disk_io_counters(PyObject *self, PyObject *args) { + DISK_PERFORMANCE diskPerformance; + DWORD dwSize; + HANDLE hDevice = NULL; + char szDevice[MAX_PATH]; + char szDeviceDisplay[MAX_PATH]; + int devNum; + int i; + DWORD ioctrlSize; + BOOL ret; + PyObject *py_retdict = PyDict_New(); + PyObject *py_tuple = NULL; + + if (py_retdict == NULL) + return NULL; + // Apparently there's no way to figure out how many times we have + // to iterate in order to find valid drives. + // Let's assume 32, which is higher than 26, the number of letters + // in the alphabet (from A:\ to Z:\). + for (devNum = 0; devNum <= 32; ++devNum) { + py_tuple = NULL; + sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); + hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hDevice == INVALID_HANDLE_VALUE) + continue; + + // DeviceIoControl() sucks! + i = 0; + ioctrlSize = sizeof(diskPerformance); + while (1) { + i += 1; + ret = DeviceIoControl( + hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance, + ioctrlSize, &dwSize, NULL); + if (ret != 0) + break; // OK! + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Retry with a bigger buffer (+ limit for retries). + if (i <= 1024) { + ioctrlSize *= 2; + continue; + } + } + else if (GetLastError() == ERROR_INVALID_FUNCTION) { + // This happens on AppVeyor: + // https://ci.appveyor.com/project/giampaolo/psutil/build/ + // 1364/job/ascpdi271b06jle3 + // Assume it means we're dealing with some exotic disk + // and go on. + psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; " + "ignore PhysicalDrive%i", devNum); + goto next; + } + else if (GetLastError() == ERROR_NOT_SUPPORTED) { + // Again, let's assume we're dealing with some exotic disk. + psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; " + "ignore PhysicalDrive%i", devNum); + goto next; + } + // XXX: it seems we should also catch ERROR_INVALID_PARAMETER: + // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ + // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/ + // openafs-1.4.14/src/usd/usd_nt.c + + // XXX: we can also bump into ERROR_MORE_DATA in which case + // (quoting doc) we're supposed to retry with a bigger buffer + // and specify a new "starting point", whatever it means. + PyErr_SetFromWindowsErr(0); + goto error; + } + + sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum); + py_tuple = Py_BuildValue( + "(IILLKK)", + diskPerformance.ReadCount, + diskPerformance.WriteCount, + diskPerformance.BytesRead, + diskPerformance.BytesWritten, + // convert to ms: + // https://github.com/giampaolo/psutil/issues/1012 + (unsigned long long) + (diskPerformance.ReadTime.QuadPart) / 10000000, + (unsigned long long) + (diskPerformance.WriteTime.QuadPart) / 10000000); + if (!py_tuple) + goto error; + if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + +next: + CloseHandle(hDevice); + } + + return py_retdict; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retdict); + if (hDevice != NULL) + CloseHandle(hDevice); + return NULL; +} + + +/* + * Return disk partitions as a list of tuples such as + * (drive_letter, drive_letter, type, "") + */ +PyObject * +psutil_disk_partitions(PyObject *self, PyObject *args) { + DWORD num_bytes; + char drive_strings[255]; + char *drive_letter = drive_strings; + char mp_buf[MAX_PATH]; + char mp_path[MAX_PATH]; + int all; + int type; + int ret; + unsigned int old_mode = 0; + char opts[20]; + HANDLE mp_h; + BOOL mp_flag= TRUE; + LPTSTR fs_type[MAX_PATH + 1] = { 0 }; + DWORD pflags = 0; + PyObject *py_all; + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + + if (py_retlist == NULL) { + return NULL; + } + + // avoid to visualize a message box in case something goes wrong + // see https://github.com/giampaolo/psutil/issues/264 + old_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + + if (! PyArg_ParseTuple(args, "O", &py_all)) + goto error; + all = PyObject_IsTrue(py_all); + + Py_BEGIN_ALLOW_THREADS + num_bytes = GetLogicalDriveStrings(254, drive_letter); + Py_END_ALLOW_THREADS + + if (num_bytes == 0) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + while (*drive_letter != 0) { + py_tuple = NULL; + opts[0] = 0; + fs_type[0] = 0; + + Py_BEGIN_ALLOW_THREADS + type = GetDriveType(drive_letter); + Py_END_ALLOW_THREADS + + // by default we only show hard drives and cd-roms + if (all == 0) { + if ((type == DRIVE_UNKNOWN) || + (type == DRIVE_NO_ROOT_DIR) || + (type == DRIVE_REMOTE) || + (type == DRIVE_RAMDISK)) { + goto next; + } + // floppy disk: skip it by default as it introduces a + // considerable slowdown. + if ((type == DRIVE_REMOVABLE) && + (strcmp(drive_letter, "A:\\") == 0)) { + goto next; + } + } + + ret = GetVolumeInformation( + (LPCTSTR)drive_letter, NULL, _ARRAYSIZE(drive_letter), + NULL, NULL, &pflags, (LPTSTR)fs_type, _ARRAYSIZE(fs_type)); + if (ret == 0) { + // We might get here in case of a floppy hard drive, in + // which case the error is (21, "device not ready"). + // Let's pretend it didn't happen as we already have + // the drive name and type ('removable'). + strcat_s(opts, _countof(opts), ""); + SetLastError(0); + } + else { + if (pflags & FILE_READ_ONLY_VOLUME) + strcat_s(opts, _countof(opts), "ro"); + else + strcat_s(opts, _countof(opts), "rw"); + if (pflags & FILE_VOLUME_IS_COMPRESSED) + strcat_s(opts, _countof(opts), ",compressed"); + + // Check for mount points on this volume and add/get info + // (checks first to know if we can even have mount points) + if (pflags & FILE_SUPPORTS_REPARSE_POINTS) { + mp_h = FindFirstVolumeMountPoint( + drive_letter, mp_buf, MAX_PATH); + if (mp_h != INVALID_HANDLE_VALUE) { + while (mp_flag) { + + // Append full mount path with drive letter + strcpy_s(mp_path, _countof(mp_path), drive_letter); + strcat_s(mp_path, _countof(mp_path), mp_buf); + + py_tuple = Py_BuildValue( + "(ssss)", + drive_letter, + mp_path, + fs_type, // Typically NTFS + opts); + + if (!py_tuple || + PyList_Append(py_retlist, py_tuple) == -1) { + FindVolumeMountPointClose(mp_h); + goto error; + } + + Py_CLEAR(py_tuple); + + // Continue looking for more mount points + mp_flag = FindNextVolumeMountPoint( + mp_h, mp_buf, MAX_PATH); + } + FindVolumeMountPointClose(mp_h); + } + + } + } + + if (strlen(opts) > 0) + strcat_s(opts, _countof(opts), ","); + strcat_s(opts, _countof(opts), psutil_get_drive_type(type)); + + py_tuple = Py_BuildValue( + "(ssss)", + drive_letter, + drive_letter, + fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS + opts); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + goto next; + +next: + drive_letter = strchr(drive_letter, 0) + 1; + } + + SetErrorMode(old_mode); + return py_retlist; + +error: + SetErrorMode(old_mode); + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; +} + + +/* + Accept a filename's drive in native format like "\Device\HarddiskVolume1\" + and return the corresponding drive letter (e.g. "C:\\"). + If no match is found return an empty string. +*/ +PyObject * +psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) { + LPCTSTR lpDevicePath; + TCHAR d = TEXT('A'); + TCHAR szBuff[5]; + + if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) + return NULL; + + while (d <= TEXT('Z')) { + TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; + TCHAR szTarget[512] = {0}; + if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { + if (_tcscmp(lpDevicePath, szTarget) == 0) { + _stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d); + return Py_BuildValue("s", szBuff); + } + } + d++; + } + return Py_BuildValue("s", ""); +} diff --git a/third_party/python/psutil/psutil/arch/windows/disk.h b/third_party/python/psutil/psutil/arch/windows/disk.h new file mode 100644 index 000000000000..298fb6ba0e07 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/disk.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args); +PyObject *psutil_disk_partitions(PyObject *self, PyObject *args); +PyObject *psutil_disk_usage(PyObject *self, PyObject *args); +PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/global.c b/third_party/python/psutil/psutil/arch/windows/global.c deleted file mode 100644 index 4d8526e31d6b..000000000000 --- a/third_party/python/psutil/psutil/arch/windows/global.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * This code is executed on import. It loads private/undocumented - * Windows APIs and sets Windows version constants so that they are - * available globally. - */ - -#include -#include -#include "ntextapi.h" -#include "global.h" - - -// Needed to make these globally visible. -int PSUTIL_WINVER; -SYSTEM_INFO PSUTIL_SYSTEM_INFO; - -#define NT_FACILITY_MASK 0xfff -#define NT_FACILITY_SHIFT 16 -#define NT_FACILITY(Status) \ - ((((ULONG)(Status)) >> NT_FACILITY_SHIFT) & NT_FACILITY_MASK) -#define NT_NTWIN32(status) (NT_FACILITY(Status) == FACILITY_WIN32) -#define WIN32_FROM_NTSTATUS(Status) (((ULONG)(Status)) & 0xffff) - - -// A wrapper around GetModuleHandle and GetProcAddress. -PVOID -psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) { - HMODULE mod; - FARPROC addr; - - if ((mod = GetModuleHandleA(libname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, libname); - return NULL; - } - if ((addr = GetProcAddress(mod, procname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, procname); - return NULL; - } - return addr; -} - - -// A wrapper around LoadLibrary and GetProcAddress. -PVOID -psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) { - HMODULE mod; - FARPROC addr; - - Py_BEGIN_ALLOW_THREADS - mod = LoadLibraryA(libname); - Py_END_ALLOW_THREADS - if (mod == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, libname); - return NULL; - } - if ((addr = GetProcAddress(mod, procname)) == NULL) { - PyErr_SetFromWindowsErrWithFilename(0, procname); - FreeLibrary(mod); - return NULL; - } - // Causes crash. - // FreeLibrary(mod); - return addr; -} - - -/* - * Convert a NTSTATUS value to a Win32 error code and set the proper - * Python exception. - */ -PVOID -psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) { - ULONG err; - char fullmsg[1024]; - - if (NT_NTWIN32(Status)) - err = WIN32_FROM_NTSTATUS(Status); - else - err = psutil_RtlNtStatusToDosErrorNoTeb(Status); - // if (GetLastError() != 0) - // err = GetLastError(); - sprintf(fullmsg, "(originated from %s)", syscall); - return PyErr_SetFromWindowsErrWithFilename(err, fullmsg); -} - - -static int -psutil_loadlibs() { - /* - * Mandatory. - */ - psutil_NtQuerySystemInformation = psutil_GetProcAddressFromLib( - "ntdll.dll", "NtQuerySystemInformation"); - if (psutil_NtQuerySystemInformation == NULL) - return 1; - - psutil_NtQueryInformationProcess = psutil_GetProcAddress( - "ntdll.dll", "NtQueryInformationProcess"); - if (! psutil_NtQueryInformationProcess) - return 1; - - psutil_NtSetInformationProcess = psutil_GetProcAddress( - "ntdll.dll", "NtSetInformationProcess"); - if (! psutil_NtSetInformationProcess) - return 1; - - psutil_WinStationQueryInformationW = psutil_GetProcAddressFromLib( - "winsta.dll", "WinStationQueryInformationW"); - if (! psutil_WinStationQueryInformationW) - return 1; - - psutil_NtQueryObject = psutil_GetProcAddressFromLib( - "ntdll.dll", "NtQueryObject"); - if (! psutil_NtQueryObject) - return 1; - - psutil_rtlIpv4AddressToStringA = psutil_GetProcAddressFromLib( - "ntdll.dll", "RtlIpv4AddressToStringA"); - if (! psutil_rtlIpv4AddressToStringA) - return 1; - - // minimum requirement: Win XP SP3 - psutil_GetExtendedTcpTable = psutil_GetProcAddressFromLib( - "iphlpapi.dll", "GetExtendedTcpTable"); - if (! psutil_GetExtendedTcpTable) - return 1; - - // minimum requirement: Win XP SP3 - psutil_GetExtendedUdpTable = psutil_GetProcAddressFromLib( - "iphlpapi.dll", "GetExtendedUdpTable"); - if (! psutil_GetExtendedUdpTable) - return 1; - - psutil_RtlGetVersion = psutil_GetProcAddressFromLib( - "ntdll.dll", "RtlGetVersion"); - if (! psutil_RtlGetVersion) - return 1; - - psutil_NtSuspendProcess = psutil_GetProcAddressFromLib( - "ntdll", "NtSuspendProcess"); - if (! psutil_NtSuspendProcess) - return 1; - - psutil_NtResumeProcess = psutil_GetProcAddressFromLib( - "ntdll", "NtResumeProcess"); - if (! psutil_NtResumeProcess) - return 1; - - psutil_NtQueryVirtualMemory = psutil_GetProcAddressFromLib( - "ntdll", "NtQueryVirtualMemory"); - if (! psutil_NtQueryVirtualMemory) - return 1; - - psutil_RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib( - "ntdll", "RtlNtStatusToDosErrorNoTeb"); - if (! psutil_RtlNtStatusToDosErrorNoTeb) - return 1; - - /* - * Optional. - */ - // not available on Wine - psutil_rtlIpv6AddressToStringA = psutil_GetProcAddressFromLib( - "ntdll.dll", "RtlIpv6AddressToStringA"); - - // minimum requirement: Win Vista - psutil_GetTickCount64 = psutil_GetProcAddress( - "kernel32", "GetTickCount64"); - - // minimum requirement: Win 7 - psutil_GetActiveProcessorCount = psutil_GetProcAddress( - "kernel32", "GetActiveProcessorCount"); - - // minumum requirement: Win 7 - psutil_GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib( - "kernel32", "GetLogicalProcessorInformationEx"); - - PyErr_Clear(); - return 0; -} - - -static int -psutil_set_winver() { - RTL_OSVERSIONINFOEXW versionInfo; - ULONG maj; - ULONG min; - - versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); - memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW)); - psutil_RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo); - maj = versionInfo.dwMajorVersion; - min = versionInfo.dwMinorVersion; - if (maj == 5 && min == 1) - PSUTIL_WINVER = PSUTIL_WINDOWS_XP; - else if (maj == 5 && min == 2) - PSUTIL_WINVER = PSUTIL_WINDOWS_SERVER_2003; - else if (maj == 6 && min == 0) - PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA; // or Server 2008 - else if (maj == 6 && min == 1) - PSUTIL_WINVER = PSUTIL_WINDOWS_7; - else if (maj == 6 && min == 2) - PSUTIL_WINVER = PSUTIL_WINDOWS_8; - else if (maj == 6 && min == 3) - PSUTIL_WINVER = PSUTIL_WINDOWS_8_1; - else if (maj == 10 && min == 0) - PSUTIL_WINVER = PSUTIL_WINDOWS_10; - else - PSUTIL_WINVER = PSUTIL_WINDOWS_NEW; - return 0; -} - - -static int -psutil_load_sysinfo() { - GetSystemInfo(&PSUTIL_SYSTEM_INFO); - return 0; -} - - -int -psutil_load_globals() { - if (psutil_loadlibs() != 0) - return 1; - if (psutil_set_winver() != 0) - return 1; - if (psutil_load_sysinfo() != 0) - return 1; - return 0; -} diff --git a/third_party/python/psutil/psutil/arch/windows/global.h b/third_party/python/psutil/psutil/arch/windows/global.h deleted file mode 100644 index 10ae640595d9..000000000000 --- a/third_party/python/psutil/psutil/arch/windows/global.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - - * List of constants and objects that are globally available. - */ - -#include -#include "ntextapi.h" - -extern int PSUTIL_WINVER; -extern SYSTEM_INFO PSUTIL_SYSTEM_INFO; -#define PSUTIL_WINDOWS_XP 51 -#define PSUTIL_WINDOWS_SERVER_2003 52 -#define PSUTIL_WINDOWS_VISTA 60 -#define PSUTIL_WINDOWS_7 61 -#define PSUTIL_WINDOWS_8 62 -#define PSUTIL_WINDOWS_8_1 63 -#define PSUTIL_WINDOWS_10 100 -#define PSUTIL_WINDOWS_NEW MAXLONG - -int psutil_load_globals(); -PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); -PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); -PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall); - -_NtQuerySystemInformation \ - psutil_NtQuerySystemInformation; - -_NtQueryInformationProcess \ - psutil_NtQueryInformationProcess; - -_NtSetInformationProcess - psutil_NtSetInformationProcess; - -_WinStationQueryInformationW \ - psutil_WinStationQueryInformationW; - -_RtlIpv4AddressToStringA \ - psutil_rtlIpv4AddressToStringA; - -_RtlIpv6AddressToStringA \ - psutil_rtlIpv6AddressToStringA; - -_GetExtendedTcpTable \ - psutil_GetExtendedTcpTable; - -_GetExtendedUdpTable \ - psutil_GetExtendedUdpTable; - -_GetActiveProcessorCount \ - psutil_GetActiveProcessorCount; - -_GetTickCount64 \ - psutil_GetTickCount64; - -_NtQueryObject \ - psutil_NtQueryObject; - -_GetLogicalProcessorInformationEx \ - psutil_GetLogicalProcessorInformationEx; - -_RtlGetVersion \ - psutil_RtlGetVersion; - -_NtSuspendProcess \ - psutil_NtSuspendProcess; - -_NtResumeProcess \ - psutil_NtResumeProcess; - -_NtQueryVirtualMemory \ - psutil_NtQueryVirtualMemory; - -_RtlNtStatusToDosErrorNoTeb \ - psutil_RtlNtStatusToDosErrorNoTeb; diff --git a/third_party/python/psutil/psutil/arch/windows/inet_ntop.c b/third_party/python/psutil/psutil/arch/windows/inet_ntop.c deleted file mode 100644 index 3db507b9ba0e..000000000000 --- a/third_party/python/psutil/psutil/arch/windows/inet_ntop.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola', Jeff Tang. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include -#include "inet_ntop.h" - -// From: https://memset.wordpress.com/2010/10/09/inet_ntop-for-win32/ -PCSTR WSAAPI -inet_ntop(INT family, PVOID pAddr, PSTR stringBuf, size_t strBufSize) { - DWORD dwAddressLength = 0; - struct sockaddr_storage srcaddr; - struct sockaddr_in *srcaddr4 = (struct sockaddr_in*) &srcaddr; - struct sockaddr_in6 *srcaddr6 = (struct sockaddr_in6*) &srcaddr; - - memset(&srcaddr, 0, sizeof(struct sockaddr_storage)); - srcaddr.ss_family = family; - - if (family == AF_INET) { - dwAddressLength = sizeof(struct sockaddr_in); - memcpy(&(srcaddr4->sin_addr), pAddr, sizeof(struct in_addr)); - } - else if (family == AF_INET6) { - dwAddressLength = sizeof(struct sockaddr_in6); - memcpy(&(srcaddr6->sin6_addr), pAddr, sizeof(struct in6_addr)); - } - else { - PyErr_SetString(PyExc_ValueError, "invalid family"); - return NULL; - } - - if (WSAAddressToStringA( - (LPSOCKADDR) &srcaddr, - dwAddressLength, - 0, - stringBuf, - (LPDWORD) &strBufSize) != 0) - { - PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); - return NULL; - } - return stringBuf; -} diff --git a/third_party/python/psutil/psutil/arch/windows/inet_ntop.h b/third_party/python/psutil/psutil/arch/windows/inet_ntop.h deleted file mode 100644 index 2d86c26cf186..000000000000 --- a/third_party/python/psutil/psutil/arch/windows/inet_ntop.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola', Jeff Tang. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// because of WSAAddressToStringA -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#include - -PCSTR WSAAPI -inet_ntop( - __in INT Family, - __in const VOID * pAddr, - __out_ecount(StringBufSize) PSTR pStringBuf, - __in size_t StringBufSize -); diff --git a/third_party/python/psutil/psutil/arch/windows/net.c b/third_party/python/psutil/psutil/arch/windows/net.c new file mode 100644 index 000000000000..56c6b6f1f7e3 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/net.c @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Fixes clash between winsock2.h and windows.h +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +#include "../../_psutil_common.h" + + +static PIP_ADAPTER_ADDRESSES +psutil_get_nic_addresses() { + // allocate a 15 KB buffer to start with + int outBufLen = 15000; + DWORD dwRetVal = 0; + ULONG attempts = 0; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); + if (pAddresses == NULL) { + PyErr_NoMemory(); + return NULL; + } + + dwRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, + &outBufLen); + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + free(pAddresses); + pAddresses = NULL; + } + else { + break; + } + + attempts++; + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); + + if (dwRetVal != NO_ERROR) { + PyErr_SetString( + PyExc_RuntimeError, "GetAdaptersAddresses() syscall failed."); + return NULL; + } + + return pAddresses; +} + + +/* + * Return a Python list of named tuples with overall network I/O information + */ +PyObject * +psutil_net_io_counters(PyObject *self, PyObject *args) { + DWORD dwRetVal = 0; + MIB_IF_ROW2 *pIfRow = NULL; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PyObject *py_retdict = PyDict_New(); + PyObject *py_nic_info = NULL; + PyObject *py_nic_name = NULL; + + if (py_retdict == NULL) + return NULL; + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + pCurrAddresses = pAddresses; + + while (pCurrAddresses) { + py_nic_name = NULL; + py_nic_info = NULL; + + pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); + if (pIfRow == NULL) { + PyErr_NoMemory(); + goto error; + } + + SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); + pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; + dwRetVal = GetIfEntry2(pIfRow); + if (dwRetVal != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, + "GetIfEntry() or GetIfEntry2() syscalls failed."); + goto error; + } + + py_nic_info = Py_BuildValue( + "(KKKKKKKK)", + pIfRow->OutOctets, + pIfRow->InOctets, + (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts), + (pIfRow->InUcastPkts + pIfRow->InNUcastPkts), + pIfRow->InErrors, + pIfRow->OutErrors, + pIfRow->InDiscards, + pIfRow->OutDiscards); + if (!py_nic_info) + goto error; + + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + + if (py_nic_name == NULL) + goto error; + if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) + goto error; + Py_CLEAR(py_nic_name); + Py_CLEAR(py_nic_info); + + free(pIfRow); + pCurrAddresses = pCurrAddresses->Next; + } + + free(pAddresses); + return py_retdict; + +error: + Py_XDECREF(py_nic_name); + Py_XDECREF(py_nic_info); + Py_DECREF(py_retdict); + if (pAddresses != NULL) + free(pAddresses); + if (pIfRow != NULL) + free(pIfRow); + return NULL; +} + + +/* + * Return NICs addresses. + */ +PyObject * +psutil_net_if_addrs(PyObject *self, PyObject *args) { + unsigned int i = 0; + ULONG family; + PCTSTR intRet; + PCTSTR netmaskIntRet; + char *ptr; + char buff_addr[1024]; + char buff_macaddr[1024]; + char buff_netmask[1024]; + DWORD dwRetVal = 0; + ULONG converted_netmask; + UINT netmask_bits; + struct in_addr in_netmask; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + PyObject *py_address = NULL; + PyObject *py_mac_address = NULL; + PyObject *py_nic_name = NULL; + PyObject *py_netmask = NULL; + + if (py_retlist == NULL) + return NULL; + + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + pCurrAddresses = pAddresses; + + while (pCurrAddresses) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + + netmaskIntRet = NULL; + py_nic_name = NULL; + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; + + // MAC address + if (pCurrAddresses->PhysicalAddressLength != 0) { + ptr = buff_macaddr; + *ptr = '\0'; + for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { + if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { + sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n", + (int)pCurrAddresses->PhysicalAddress[i]); + } + else { + sprintf_s(ptr, _countof(buff_macaddr), "%.2X-", + (int)pCurrAddresses->PhysicalAddress[i]); + } + ptr += 3; + } + *--ptr = '\0'; + + py_mac_address = Py_BuildValue("s", buff_macaddr); + if (py_mac_address == NULL) + goto error; + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(OiOOOO)", + py_nic_name, + -1, // this will be converted later to AF_LINK + py_mac_address, + Py_None, // netmask (not supported) + Py_None, // broadcast (not supported) + Py_None // ptp (not supported on Windows) + ); + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + Py_CLEAR(py_mac_address); + } + + // find out the IP address associated with the NIC + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + family = pUnicast->Address.lpSockaddr->sa_family; + if (family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in *) + pUnicast->Address.lpSockaddr; + intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff_addr, + sizeof(buff_addr)); + if (!intRet) + goto error; + netmask_bits = pUnicast->OnLinkPrefixLength; + dwRetVal = ConvertLengthToIpv4Mask( + netmask_bits, &converted_netmask); + if (dwRetVal == NO_ERROR) { + in_netmask.s_addr = converted_netmask; + netmaskIntRet = inet_ntop( + AF_INET, &in_netmask, buff_netmask, + sizeof(buff_netmask)); + if (!netmaskIntRet) + goto error; + } + } + else if (family == AF_INET6) { + struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) + pUnicast->Address.lpSockaddr; + intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr), + buff_addr, sizeof(buff_addr)); + if (!intRet) + goto error; + } + else { + // we should never get here + pUnicast = pUnicast->Next; + continue; + } + +#if PY_MAJOR_VERSION >= 3 + py_address = PyUnicode_FromString(buff_addr); +#else + py_address = PyString_FromString(buff_addr); +#endif + if (py_address == NULL) + goto error; + + if (netmaskIntRet != NULL) { +#if PY_MAJOR_VERSION >= 3 + py_netmask = PyUnicode_FromString(buff_netmask); +#else + py_netmask = PyString_FromString(buff_netmask); +#endif + } else { + Py_INCREF(Py_None); + py_netmask = Py_None; + } + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(OiOOOO)", + py_nic_name, + family, + py_address, + py_netmask, + Py_None, // broadcast (not supported) + Py_None // ptp (not supported on Windows) + ); + + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + Py_CLEAR(py_address); + Py_CLEAR(py_netmask); + + pUnicast = pUnicast->Next; + } + } + Py_CLEAR(py_nic_name); + pCurrAddresses = pCurrAddresses->Next; + } + + free(pAddresses); + return py_retlist; + +error: + if (pAddresses) + free(pAddresses); + Py_DECREF(py_retlist); + Py_XDECREF(py_tuple); + Py_XDECREF(py_address); + Py_XDECREF(py_nic_name); + Py_XDECREF(py_netmask); + return NULL; +} + + +/* + * Provides stats about NIC interfaces installed on the system. + * TODO: get 'duplex' (currently it's hard coded to '2', aka + 'full duplex') + */ +PyObject * +psutil_net_if_stats(PyObject *self, PyObject *args) { + int i; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + MIB_IFTABLE *pIfTable; + MIB_IFROW *pIfRow; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + char descr[MAX_PATH]; + int ifname_found; + + PyObject *py_nic_name = NULL; + PyObject *py_retdict = PyDict_New(); + PyObject *py_ifc_info = NULL; + PyObject *py_is_up = NULL; + + if (py_retdict == NULL) + return NULL; + + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + + pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); + if (pIfTable == NULL) { + PyErr_NoMemory(); + goto error; + } + dwSize = sizeof(MIB_IFTABLE); + if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { + free(pIfTable); + pIfTable = (MIB_IFTABLE *) malloc(dwSize); + if (pIfTable == NULL) { + PyErr_NoMemory(); + goto error; + } + } + // Make a second call to GetIfTable to get the actual + // data we want. + if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, "GetIfTable() syscall failed"); + goto error; + } + + for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { + pIfRow = (MIB_IFROW *) & pIfTable->table[i]; + + // GetIfTable is not able to give us NIC with "friendly names" + // so we determine them via GetAdapterAddresses() which + // provides friendly names *and* descriptions and find the + // ones that match. + ifname_found = 0; + pCurrAddresses = pAddresses; + while (pCurrAddresses) { + sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description); + if (lstrcmp(descr, pIfRow->bDescr) == 0) { + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; + ifname_found = 1; + break; + } + pCurrAddresses = pCurrAddresses->Next; + } + if (ifname_found == 0) { + // Name not found means GetAdapterAddresses() doesn't list + // this NIC, only GetIfTable, meaning it's not really a NIC + // interface so we skip it. + continue; + } + + // is up? + if ((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || + pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && + pIfRow->dwAdminStatus == 1 ) { + py_is_up = Py_True; + } + else { + py_is_up = Py_False; + } + Py_INCREF(py_is_up); + + py_ifc_info = Py_BuildValue( + "(Oikk)", + py_is_up, + 2, // there's no way to know duplex so let's assume 'full' + pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb + pIfRow->dwMtu + ); + if (!py_ifc_info) + goto error; + if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info)) + goto error; + Py_CLEAR(py_nic_name); + Py_CLEAR(py_ifc_info); + } + + free(pIfTable); + free(pAddresses); + return py_retdict; + +error: + Py_XDECREF(py_is_up); + Py_XDECREF(py_ifc_info); + Py_XDECREF(py_nic_name); + Py_DECREF(py_retdict); + if (pIfTable != NULL) + free(pIfTable); + if (pAddresses != NULL) + free(pAddresses); + return NULL; +} diff --git a/third_party/python/psutil/psutil/arch/windows/net.h b/third_party/python/psutil/psutil/arch/windows/net.h new file mode 100644 index 000000000000..7a6158d13b16 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/net.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_net_if_addrs(PyObject *self, PyObject *args); +PyObject *psutil_net_if_stats(PyObject *self, PyObject *args); +PyObject *psutil_net_io_counters(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/ntextapi.h b/third_party/python/psutil/psutil/arch/windows/ntextapi.h index b6f23d996762..8cb00430e29c 100644 --- a/third_party/python/psutil/psutil/arch/windows/ntextapi.h +++ b/third_party/python/psutil/psutil/arch/windows/ntextapi.h @@ -2,7 +2,9 @@ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. + * Define Windows structs and constants which are considered private. */ + #if !defined(__NTEXTAPI_H__) #define __NTEXTAPI_H__ #include @@ -10,74 +12,92 @@ typedef LONG NTSTATUS; -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 -#define STATUS_BUFFER_TOO_SMALL 0xC0000023L -#define SystemExtendedHandleInformation 64 -#define MemoryWorkingSetInformation 0x1 +// https://github.com/ajkhoury/TestDll/blob/master/nt_ddk.h +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) -/* - * ================================================================ - * Enums. - * ================================================================ - */ +// ================================================================ +// Enums +// ================================================================ -typedef enum _PROCESSINFOCLASS2 { - _ProcessBasicInformation, - ProcessQuotaLimits, - ProcessIoCounters, - ProcessVmCounters, - ProcessTimes, - ProcessBasePriority, - ProcessRaisePriority, - _ProcessDebugPort, - ProcessExceptionPort, - ProcessAccessToken, - ProcessLdtInformation, - ProcessLdtSize, - ProcessDefaultHardErrorMode, - ProcessIoPortHandlers, - ProcessPooledUsageAndLimits, - ProcessWorkingSetWatch, - ProcessUserModeIOPL, - ProcessEnableAlignmentFaultFixup, - ProcessPriorityClass, - ProcessWx86Information, - ProcessHandleCount, - ProcessAffinityMask, - ProcessPriorityBoost, - ProcessDeviceMap, - ProcessSessionInformation, - ProcessForegroundInformation, - _ProcessWow64Information, - /* added after XP+ */ - _ProcessImageFileName, - ProcessLUIDDeviceMapsEnabled, - _ProcessBreakOnTermination, - ProcessDebugObjectHandle, - ProcessDebugFlags, - ProcessHandleTracing, - ProcessIoPriority, - ProcessExecuteFlags, - ProcessResourceManagement, - ProcessCookie, - ProcessImageInformation, - MaxProcessInfoClass -} PROCESSINFOCLASS2; +#undef SystemExtendedHandleInformation +#define SystemExtendedHandleInformation 64 +#undef MemoryWorkingSetInformation +#define MemoryWorkingSetInformation 0x1 +#undef ObjectNameInformation +#define ObjectNameInformation 1 +#undef ProcessIoPriority +#define ProcessIoPriority 33 +#undef ProcessWow64Information +#define ProcessWow64Information 26 +#undef SystemProcessIdInformation +#define SystemProcessIdInformation 88 -#define PROCESSINFOCLASS PROCESSINFOCLASS2 -#define ProcessBasicInformation _ProcessBasicInformation -#define ProcessWow64Information _ProcessWow64Information -#define ProcessDebugPort _ProcessDebugPort -#define ProcessImageFileName _ProcessImageFileName -#define ProcessBreakOnTermination _ProcessBreakOnTermination +// process suspend() / resume() +typedef enum _KTHREAD_STATE { + Initialized, + Ready, + Running, + Standby, + Terminated, + Waiting, + Transition, + DeferredReady, + GateWait, + MaximumThreadState +} KTHREAD_STATE, *PKTHREAD_STATE; -/* - * ================================================================ - * Structs. - * ================================================================ - */ +typedef enum _KWAIT_REASON { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + WrKeyedEvent, + WrTerminated, + WrProcessInSwap, + WrCpuRateControl, + WrCalloutStack, + WrKernel, + WrResource, + WrPushLock, + WrMutex, + WrQuantumEnd, + WrDispatchInt, + WrPreempted, + WrYieldExecution, + WrFastMutex, + WrGuardedMutex, + WrRundown, + WrAlertByThreadId, + WrDeferredPreempt, + MaximumWaitReason +} KWAIT_REASON, *PKWAIT_REASON; +// ================================================================ +// Structs. +// ================================================================ + +// cpu_stats(), per_cpu_times() typedef struct { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; @@ -87,6 +107,7 @@ typedef struct { ULONG InterruptCount; } _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; +// cpu_stats() typedef struct { LARGE_INTEGER IdleProcessTime; LARGE_INTEGER IoReadTransferCount; @@ -164,6 +185,7 @@ typedef struct { ULONG SystemCalls; } _SYSTEM_PERFORMANCE_INFORMATION; +// cpu_stats() typedef struct { ULONG ContextSwitches; ULONG DpcCount; @@ -173,62 +195,6 @@ typedef struct { ULONG ApcBypassCount; } _SYSTEM_INTERRUPT_INFORMATION; -typedef enum _KTHREAD_STATE { - Initialized, - Ready, - Running, - Standby, - Terminated, - Waiting, - Transition, - DeferredReady, - GateWait, - MaximumThreadState -} KTHREAD_STATE, *PKTHREAD_STATE; - -typedef enum _KWAIT_REASON { - Executive, - FreePage, - PageIn, - PoolAllocation, - DelayExecution, - Suspended, - UserRequest, - WrExecutive, - WrFreePage, - WrPageIn, - WrPoolAllocation, - WrDelayExecution, - WrSuspended, - WrUserRequest, - WrEventPair, - WrQueue, - WrLpcReceive, - WrLpcReply, - WrVirtualMemory, - WrPageOut, - WrRendezvous, - WrKeyedEvent, - WrTerminated, - WrProcessInSwap, - WrCpuRateControl, - WrCalloutStack, - WrKernel, - WrResource, - WrPushLock, - WrMutex, - WrQuantumEnd, - WrDispatchInt, - WrPreempted, - WrYieldExecution, - WrFastMutex, - WrGuardedMutex, - WrRundown, - WrAlertByThreadId, - WrDeferredPreempt, - MaximumWaitReason -} KWAIT_REASON, *PKWAIT_REASON; - typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { PVOID Object; HANDLE UniqueProcessId; @@ -271,19 +237,6 @@ typedef struct _SYSTEM_THREAD_INFORMATION2 { #define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2 #define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2 -typedef struct _TEB *PTEB; - -typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { - SYSTEM_THREAD_INFORMATION ThreadInfo; - PVOID StackBase; - PVOID StackLimit; - PVOID Win32StartAddress; - PTEB TebBase; - ULONG_PTR Reserved2; - ULONG_PTR Reserved3; - ULONG_PTR Reserved4; -} SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; - typedef struct _SYSTEM_PROCESS_INFORMATION2 { ULONG NextEntryOffset; ULONG NumberOfThreads; @@ -324,6 +277,7 @@ typedef struct _SYSTEM_PROCESS_INFORMATION2 { #define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2 #define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2 +// cpu_freq() typedef struct _PROCESSOR_POWER_INFORMATION { ULONG Number; ULONG MaxMhz; @@ -342,7 +296,7 @@ typedef struct in6_addr { } IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; #endif -// http://msdn.microsoft.com/en-us/library/aa813741(VS.85).aspx +// PEB / cmdline(), cwd(), environ() typedef struct { BYTE Reserved1[16]; PVOID Reserved2[5]; @@ -354,6 +308,7 @@ typedef struct { LPCWSTR env; } RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_; +// users() typedef struct _WINSTATION_INFO { BYTE Reserved1[72]; ULONG SessionId; @@ -366,6 +321,7 @@ typedef struct _WINSTATION_INFO { FILETIME CurrentTime; } WINSTATION_INFO, *PWINSTATION_INFO; +// cpu_count_phys() #if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP) typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { LOGICAL_PROCESSOR_RELATIONSHIP Relationship; @@ -377,9 +333,11 @@ typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { CACHE_RELATIONSHIP Cache; GROUP_RELATIONSHIP Group; } DUMMYUNIONNAME; -} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; +} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, \ + *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; #endif +// memory_uss() typedef struct _MEMORY_WORKING_SET_BLOCK { ULONG_PTR Protection : 5; ULONG_PTR ShareCount : 3; @@ -392,11 +350,13 @@ typedef struct _MEMORY_WORKING_SET_BLOCK { #endif } MEMORY_WORKING_SET_BLOCK, *PMEMORY_WORKING_SET_BLOCK; +// memory_uss() typedef struct _MEMORY_WORKING_SET_INFORMATION { ULONG_PTR NumberOfEntries; MEMORY_WORKING_SET_BLOCK WorkingSetInfo[1]; } MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION; +// memory_uss() typedef struct _PSUTIL_PROCESS_WS_COUNTERS { SIZE_T NumberOfPages; SIZE_T NumberOfPrivatePages; @@ -404,18 +364,122 @@ typedef struct _PSUTIL_PROCESS_WS_COUNTERS { SIZE_T NumberOfShareablePages; } PSUTIL_PROCESS_WS_COUNTERS, *PPSUTIL_PROCESS_WS_COUNTERS; -/* - * ================================================================ - * Type defs for modules loaded at runtime. - * ================================================================ - */ +// exe() +typedef struct _SYSTEM_PROCESS_ID_INFORMATION { + HANDLE ProcessId; + UNICODE_STRING ImageName; +} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION; -typedef BOOL (WINAPI *_GetLogicalProcessorInformationEx)( +// ==================================================================== +// PEB structs for cmdline(), cwd(), environ() +// ==================================================================== + +#ifdef _WIN64 +typedef struct { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[21]; + PVOID LoaderData; + PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters; + // more fields... +} PEB_; + +// When we are a 64 bit process accessing a 32 bit (WoW64) +// process we need to use the 32 bit structure layout. +typedef struct { + USHORT Length; + USHORT MaxLength; + DWORD Buffer; +} UNICODE_STRING32; + +typedef struct { + BYTE Reserved1[16]; + DWORD Reserved2[5]; + UNICODE_STRING32 CurrentDirectoryPath; + DWORD CurrentDirectoryHandle; + UNICODE_STRING32 DllPath; + UNICODE_STRING32 ImagePathName; + UNICODE_STRING32 CommandLine; + DWORD env; +} RTL_USER_PROCESS_PARAMETERS32; + +typedef struct { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + DWORD Reserved3[2]; + DWORD Ldr; + DWORD ProcessParameters; + // more fields... +} PEB32; +#else // ! _WIN64 +typedef struct { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + PVOID Reserved3[2]; + PVOID Ldr; + PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters; + // more fields... +} PEB_; + +// When we are a 32 bit (WoW64) process accessing a 64 bit process +// we need to use the 64 bit structure layout and a special function +// to read its memory. +typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)( + HANDLE ProcessHandle, + PVOID64 BaseAddress, + PVOID Buffer, + ULONG64 Size, + PULONG64 NumberOfBytesRead); + +typedef struct { + PVOID Reserved1[2]; + PVOID64 PebBaseAddress; + PVOID Reserved2[4]; + PVOID UniqueProcessId[2]; + PVOID Reserved3[2]; +} PROCESS_BASIC_INFORMATION64; + +typedef struct { + USHORT Length; + USHORT MaxLength; + PVOID64 Buffer; +} UNICODE_STRING64; + +typedef struct { + BYTE Reserved1[16]; + PVOID64 Reserved2[5]; + UNICODE_STRING64 CurrentDirectoryPath; + PVOID64 CurrentDirectoryHandle; + UNICODE_STRING64 DllPath; + UNICODE_STRING64 ImagePathName; + UNICODE_STRING64 CommandLine; + PVOID64 env; +} RTL_USER_PROCESS_PARAMETERS64; + +typedef struct { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[21]; + PVOID64 LoaderData; + PVOID64 ProcessParameters; + // more fields... +} PEB64; +#endif // _WIN64 + +// ================================================================ +// Type defs for modules loaded at runtime. +// ================================================================ + +BOOL (WINAPI *_GetLogicalProcessorInformationEx) ( LOGICAL_PROCESSOR_RELATIONSHIP relationship, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer, PDWORD ReturnLength); -typedef BOOLEAN (WINAPI * _WinStationQueryInformationW)( +#define GetLogicalProcessorInformationEx _GetLogicalProcessorInformationEx + +BOOLEAN (WINAPI * _WinStationQueryInformationW) ( HANDLE ServerHandle, ULONG SessionId, WINSTATIONINFOCLASS WinStationInformationClass, @@ -423,34 +487,46 @@ typedef BOOLEAN (WINAPI * _WinStationQueryInformationW)( ULONG WinStationInformationLength, PULONG pReturnLength); -typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( +#define WinStationQueryInformationW _WinStationQueryInformationW + +NTSTATUS (NTAPI *_NtQueryInformationProcess) ( HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, DWORD ProcessInformationLength, PDWORD ReturnLength); -typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( +#define NtQueryInformationProcess _NtQueryInformationProcess + +NTSTATUS (NTAPI *_NtQuerySystemInformation) ( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); -typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( +#define NtQuerySystemInformation _NtQuerySystemInformation + +NTSTATUS (NTAPI *_NtSetInformationProcess) ( HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, DWORD ProcessInformationLength); -typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)( +#define NtSetInformationProcess _NtSetInformationProcess + +PSTR (NTAPI * _RtlIpv4AddressToStringA) ( struct in_addr *Addr, PSTR S); -typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)( +#define RtlIpv4AddressToStringA _RtlIpv4AddressToStringA + +PSTR (NTAPI * _RtlIpv6AddressToStringA) ( struct in6_addr *Addr, PSTR P); -typedef DWORD (WINAPI * _GetExtendedTcpTable)( +#define RtlIpv6AddressToStringA _RtlIpv6AddressToStringA + +DWORD (WINAPI * _GetExtendedTcpTable) ( PVOID pTcpTable, PDWORD pdwSize, BOOL bOrder, @@ -458,7 +534,9 @@ typedef DWORD (WINAPI * _GetExtendedTcpTable)( TCP_TABLE_CLASS TableClass, ULONG Reserved); -typedef DWORD (WINAPI * _GetExtendedUdpTable)( +#define GetExtendedTcpTable _GetExtendedTcpTable + +DWORD (WINAPI * _GetExtendedUdpTable) ( PVOID pUdpTable, PDWORD pdwSize, BOOL bOrder, @@ -466,32 +544,46 @@ typedef DWORD (WINAPI * _GetExtendedUdpTable)( UDP_TABLE_CLASS TableClass, ULONG Reserved); -typedef DWORD (CALLBACK *_GetActiveProcessorCount)( +#define GetExtendedUdpTable _GetExtendedUdpTable + +DWORD (CALLBACK *_GetActiveProcessorCount) ( WORD GroupNumber); -typedef ULONGLONG (CALLBACK *_GetTickCount64)( +#define GetActiveProcessorCount _GetActiveProcessorCount + +ULONGLONG (CALLBACK *_GetTickCount64) ( void); -typedef NTSTATUS (NTAPI *_NtQueryObject)( +#define GetTickCount64 _GetTickCount64 + +NTSTATUS (NTAPI *_NtQueryObject) ( HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); -typedef NTSTATUS (WINAPI *_RtlGetVersion) ( +#define NtQueryObject _NtQueryObject + +NTSTATUS (WINAPI *_RtlGetVersion) ( PRTL_OSVERSIONINFOW lpVersionInformation ); -typedef NTSTATUS (WINAPI *_NtResumeProcess) ( +#define RtlGetVersion _RtlGetVersion + +NTSTATUS (WINAPI *_NtResumeProcess) ( HANDLE hProcess ); -typedef NTSTATUS (WINAPI *_NtSuspendProcess) ( +#define NtResumeProcess _NtResumeProcess + +NTSTATUS (WINAPI *_NtSuspendProcess) ( HANDLE hProcess ); -typedef NTSTATUS (NTAPI *_NtQueryVirtualMemory) ( +#define NtSuspendProcess _NtSuspendProcess + +NTSTATUS (NTAPI *_NtQueryVirtualMemory) ( HANDLE ProcessHandle, PVOID BaseAddress, int MemoryInformationClass, @@ -500,8 +592,12 @@ typedef NTSTATUS (NTAPI *_NtQueryVirtualMemory) ( PSIZE_T ReturnLength ); -typedef ULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) ( +#define NtQueryVirtualMemory _NtQueryVirtualMemory + +ULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) ( NTSTATUS status ); +#define RtlNtStatusToDosErrorNoTeb _RtlNtStatusToDosErrorNoTeb + #endif // __NTEXTAPI_H__ diff --git a/third_party/python/psutil/psutil/arch/windows/process_handles.c b/third_party/python/psutil/psutil/arch/windows/process_handles.c index 5966669e2803..f63d4af36cef 100644 --- a/third_party/python/psutil/psutil/arch/windows/process_handles.c +++ b/third_party/python/psutil/psutil/arch/windows/process_handles.c @@ -2,512 +2,291 @@ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. + */ + +/* + * This module retrieves handles opened by a process. + * We use NtQuerySystemInformation to enumerate them and NtQueryObject + * to obtain the corresponding file name. + * Since NtQueryObject hangs for certain handle types we call it in a + * separate thread which gets killed if it doesn't complete within 100ms. + * This is a limitation of the Windows API and ProcessHacker uses the + * same trick: https://github.com/giampaolo/psutil/pull/597 * + * CREDITS: original implementation was written by Jeff Tang. + * It was then rewritten by Giampaolo Rodola many years later. + * Utility functions for getting the file handles and names were re-adapted + * from the excellent ProcessHacker. */ #include -#include #include -#include "ntextapi.h" -#include "global.h" -#include "process_handles.h" -#include "process_info.h" + #include "../../_psutil_common.h" - -CRITICAL_SECTION g_cs; -BOOL g_initialized = FALSE; -NTSTATUS g_status; -HANDLE g_hFile = NULL; -HANDLE g_hEvtStart = NULL; -HANDLE g_hEvtFinish = NULL; -HANDLE g_hThread = NULL; -PUNICODE_STRING g_pNameBuffer = NULL; -ULONG g_dwSize = 0; -ULONG g_dwLength = 0; +#include "process_utils.h" -#define ObjectNameInformation 1 -#define NTQO_TIMEOUT 100 +#define THREAD_TIMEOUT 100 // ms +// Global object shared between the 2 threads. +PUNICODE_STRING globalFileName = NULL; -static VOID -psutil_get_open_files_init(BOOL threaded) { - if (g_initialized == TRUE) - return; +static int +psutil_enum_handles(PSYSTEM_HANDLE_INFORMATION_EX *handles) { + static ULONG initialBufferSize = 0x10000; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; - // Create events for signalling work between threads - if (threaded == TRUE) { - g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL); - g_hEvtFinish = CreateEvent(NULL, FALSE, FALSE, NULL); - InitializeCriticalSection(&g_cs); + bufferSize = initialBufferSize; + buffer = MALLOC_ZERO(bufferSize); + if (buffer == NULL) { + PyErr_NoMemory(); + return 1; } - g_initialized = TRUE; + while ((status = NtQuerySystemInformation( + SystemExtendedHandleInformation, + buffer, + bufferSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + { + FREE(buffer); + bufferSize *= 2; + + // Fail if we're resizing the buffer to something very large. + if (bufferSize > 256 * 1024 * 1024) { + PyErr_SetString( + PyExc_RuntimeError, + "SystemExtendedHandleInformation buffer too big"); + return 1; + } + + buffer = MALLOC_ZERO(bufferSize); + if (buffer == NULL) { + PyErr_NoMemory(); + return 1; + } + } + + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation"); + FREE(buffer); + return 1; + } + + *handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer; + return 0; } -static DWORD WINAPI -psutil_wait_thread(LPVOID lpvParam) { - // Loop infinitely waiting for work - while (TRUE) { - WaitForSingleObject(g_hEvtStart, INFINITE); +static int +psutil_get_filename(LPVOID lpvParam) { + HANDLE hFile = *((HANDLE*)lpvParam); + NTSTATUS status; + ULONG bufferSize; + ULONG attempts = 8; - // TODO: return code not checked - g_status = psutil_NtQueryObject( - g_hFile, - ObjectNameInformation, - g_pNameBuffer, - g_dwSize, - &g_dwLength); - SetEvent(g_hEvtFinish); + bufferSize = 0x200; + globalFileName = MALLOC_ZERO(bufferSize); + if (globalFileName == NULL) { + PyErr_NoMemory(); + goto error; } + + + // Note: also this is supposed to hang, hence why we do it in here. + if (GetFileType(hFile) != FILE_TYPE_DISK) { + SetLastError(0); + globalFileName->Length = 0; + return 0; + } + + // A loop is needed because the I/O subsystem likes to give us the + // wrong return lengths... + do { + status = NtQueryObject( + hFile, + ObjectNameInformation, + globalFileName, + bufferSize, + &bufferSize + ); + if (status == STATUS_BUFFER_OVERFLOW || + status == STATUS_INFO_LENGTH_MISMATCH || + status == STATUS_BUFFER_TOO_SMALL) + { + FREE(globalFileName); + globalFileName = MALLOC_ZERO(bufferSize); + if (globalFileName == NULL) { + PyErr_NoMemory(); + goto error; + } + } + else { + break; + } + } while (--attempts); + + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation"); + FREE(globalFileName); + globalFileName = NULL; + return 1; + } + + return 0; + +error: + if (globalFileName != NULL) { + FREE(globalFileName); + globalFileName = NULL; + } + return 1; } static DWORD -psutil_create_thread() { - DWORD dwWait = 0; +psutil_threaded_get_filename(HANDLE hFile) { + DWORD dwWait; + HANDLE hThread; + DWORD threadRetValue; - if (g_hThread == NULL) - g_hThread = CreateThread( - NULL, - 0, - psutil_wait_thread, - NULL, - 0, - NULL); - if (g_hThread == NULL) - return GetLastError(); + hThread = CreateThread( + NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL); + if (hThread == NULL) { + PyErr_SetFromOSErrnoWithSyscall("CreateThread"); + return 1; + } - // Signal the worker thread to start - SetEvent(g_hEvtStart); + // Wait for the worker thread to finish. + dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT); - // Wait for the worker thread to finish - dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); - - // If the thread hangs, kill it and cleanup + // If the thread hangs, kill it and cleanup. if (dwWait == WAIT_TIMEOUT) { - SuspendThread(g_hThread); - TerminateThread(g_hThread, 1); - WaitForSingleObject(g_hThread, INFINITE); - CloseHandle(g_hThread); - - g_hThread = NULL; + psutil_debug( + "get handle name thread timed out after %i ms", THREAD_TIMEOUT); + if (TerminateThread(hThread, 0) == 0) { + PyErr_SetFromOSErrnoWithSyscall("TerminateThread"); + CloseHandle(hThread); + return 1; + } + CloseHandle(hThread); + return 0; } - return dwWait; + if (dwWait == WAIT_FAILED) { + psutil_debug("WaitForSingleObject -> WAIT_FAILED"); + if (TerminateThread(hThread, 0) == 0) { + PyErr_SetFromOSErrnoWithSyscall( + "WaitForSingleObject -> WAIT_FAILED -> TerminateThread"); + CloseHandle(hThread); + return 1; + } + PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject"); + CloseHandle(hThread); + return 1; + } + + if (GetExitCodeThread(hThread, &threadRetValue) == 0) { + if (TerminateThread(hThread, 0) == 0) { + PyErr_SetFromOSErrnoWithSyscall( + "GetExitCodeThread (failed) -> TerminateThread"); + CloseHandle(hThread); + return 1; + } + + CloseHandle(hThread); + PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread"); + return 1; + } + CloseHandle(hThread); + return threadRetValue; } -static PyObject * -psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) { - NTSTATUS status; - PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; - DWORD dwInfoSize = 0x10000; - DWORD dwRet = 0; - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; - DWORD i = 0; - BOOLEAN error = FALSE; - DWORD dwWait = 0; - PyObject* py_retlist = NULL; - PyObject* py_path = NULL; - - if (g_initialized == FALSE) - psutil_get_open_files_init(TRUE); - - // Due to the use of global variables, ensure only 1 call - // to psutil_get_open_files() is running - EnterCriticalSection(&g_cs); - - if (g_hEvtStart == NULL || - g_hEvtFinish == NULL) - - { - PyErr_SetFromWindowsErr(0); - error = TRUE; - goto cleanup; - } - - // Py_BuildValue raises an exception if NULL is returned - py_retlist = PyList_New(0); - if (py_retlist == NULL) { - error = TRUE; - goto cleanup; - } - - do { - if (pHandleInfo != NULL) { - HeapFree(GetProcessHeap(), 0, pHandleInfo); - pHandleInfo = NULL; - } - - // NtQuerySystemInformation won't give us the correct buffer size, - // so we guess by doubling the buffer size. - dwInfoSize *= 2; - pHandleInfo = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - dwInfoSize); - - if (pHandleInfo == NULL) { - PyErr_NoMemory(); - error = TRUE; - goto cleanup; - } - } while ((status = psutil_NtQuerySystemInformation( - SystemExtendedHandleInformation, - pHandleInfo, - dwInfoSize, - &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); - - // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemExtendedHandleInformation)"); - error = TRUE; - goto cleanup; - } - - for (i = 0; i < pHandleInfo->NumberOfHandles; i++) { - hHandle = &pHandleInfo->Handles[i]; - - // Check if this hHandle belongs to the PID the user specified. - if (hHandle->UniqueProcessId != (ULONG_PTR)dwPid) - goto loop_cleanup; - - if (!DuplicateHandle(hProcess, - (HANDLE)hHandle->HandleValue, - GetCurrentProcess(), - &g_hFile, - 0, - TRUE, - DUPLICATE_SAME_ACCESS)) - { - /* - printf("[%d] DuplicateHandle (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - goto loop_cleanup; - } - - // Guess buffer size is MAX_PATH + 1 - g_dwLength = (MAX_PATH+1) * sizeof(WCHAR); - - do { - // Release any previously allocated buffer - if (g_pNameBuffer != NULL) { - HeapFree(GetProcessHeap(), 0, g_pNameBuffer); - g_pNameBuffer = NULL; - g_dwSize = 0; - } - - // NtQueryObject puts the required buffer size in g_dwLength - // WinXP edge case puts g_dwLength == 0, just skip this handle - if (g_dwLength == 0) - goto loop_cleanup; - - g_dwSize = g_dwLength; - if (g_dwSize > 0) { - g_pNameBuffer = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - g_dwSize); - - if (g_pNameBuffer == NULL) - goto loop_cleanup; - } - - dwWait = psutil_create_thread(); - - // If the call does not return, skip this handle - if (dwWait != WAIT_OBJECT_0) - goto loop_cleanup; - - } while (g_status == STATUS_INFO_LENGTH_MISMATCH); - - // NtQueryObject stopped returning STATUS_INFO_LENGTH_MISMATCH - if (!NT_SUCCESS(g_status)) - goto loop_cleanup; - - // Convert to PyUnicode and append it to the return list - if (g_pNameBuffer->Length > 0) { - /* - printf("[%d] Filename (%#x) %#d bytes: %S\n", - dwPid, - hHandle->HandleValue, - g_pNameBuffer->Length, - g_pNameBuffer->Buffer); - */ - - py_path = PyUnicode_FromWideChar(g_pNameBuffer->Buffer, - g_pNameBuffer->Length/2); - if (py_path == NULL) { - /* - printf("[%d] PyUnicode_FromWideChar (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - error = TRUE; - goto loop_cleanup; - } - - if (PyList_Append(py_retlist, py_path)) { - /* - printf("[%d] PyList_Append (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - error = TRUE; - goto loop_cleanup; - } - } - -loop_cleanup: - Py_XDECREF(py_path); - py_path = NULL; - - if (g_pNameBuffer != NULL) - HeapFree(GetProcessHeap(), 0, g_pNameBuffer); - g_pNameBuffer = NULL; - g_dwSize = 0; - g_dwLength = 0; - - if (g_hFile != NULL) - CloseHandle(g_hFile); - g_hFile = NULL; -} - -cleanup: - if (g_pNameBuffer != NULL) - HeapFree(GetProcessHeap(), 0, g_pNameBuffer); - g_pNameBuffer = NULL; - g_dwSize = 0; - g_dwLength = 0; - - if (g_hFile != NULL) - CloseHandle(g_hFile); - g_hFile = NULL; - - if (pHandleInfo != NULL) - HeapFree(GetProcessHeap(), 0, pHandleInfo); - pHandleInfo = NULL; - - if (error) { - Py_XDECREF(py_retlist); - py_retlist = NULL; - } - - LeaveCriticalSection(&g_cs); - - return py_retlist; -} - - -static PyObject * -psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) { - NTSTATUS status; - PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; - DWORD dwInfoSize = 0x10000; - DWORD dwRet = 0; +PyObject * +psutil_get_open_files(DWORD dwPid, HANDLE hProcess) { + PSYSTEM_HANDLE_INFORMATION_EX handlesList = NULL; PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; HANDLE hFile = NULL; - HANDLE hMap = NULL; - DWORD i = 0; - BOOLEAN error = FALSE; - PyObject* py_retlist = NULL; + ULONG i = 0; + BOOLEAN errorOccurred = FALSE; PyObject* py_path = NULL; - ULONG dwSize = 0; - LPVOID pMem = NULL; - wchar_t pszFilename[MAX_PATH+1]; + PyObject* py_retlist = PyList_New(0);; - if (g_initialized == FALSE) - psutil_get_open_files_init(FALSE); + if (!py_retlist) + return NULL; - // Py_BuildValue raises an exception if NULL is returned - py_retlist = PyList_New(0); - if (py_retlist == NULL) { - error = TRUE; - goto cleanup; - } + // Due to the use of global variables, ensure only 1 call + // to psutil_get_open_files() is running. + EnterCriticalSection(&PSUTIL_CRITICAL_SECTION); - do { - if (pHandleInfo != NULL) { - HeapFree(GetProcessHeap(), 0, pHandleInfo); - pHandleInfo = NULL; - } + if (psutil_enum_handles(&handlesList) != 0) + goto error; - // NtQuerySystemInformation won't give us the correct buffer size, - // so we guess by doubling the buffer size. - dwInfoSize *= 2; - pHandleInfo = HeapAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - dwInfoSize); - - if (pHandleInfo == NULL) { - PyErr_NoMemory(); - error = TRUE; - goto cleanup; - } - } while ((status = psutil_NtQuerySystemInformation( - SystemExtendedHandleInformation, - pHandleInfo, - dwInfoSize, - &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); - - // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemExtendedHandleInformation)"); - error = TRUE; - goto cleanup; - } - - for (i = 0; i < pHandleInfo->NumberOfHandles; i++) { - hHandle = &pHandleInfo->Handles[i]; - - // Check if this hHandle belongs to the PID the user specified. - if (hHandle->UniqueProcessId != (ULONG_PTR)dwPid) - goto loop_cleanup; - - if (!DuplicateHandle(hProcess, - (HANDLE)hHandle->HandleValue, - GetCurrentProcess(), - &hFile, - 0, - TRUE, - DUPLICATE_SAME_ACCESS)) + for (i = 0; i < handlesList->NumberOfHandles; i++) { + hHandle = &handlesList->Handles[i]; + if ((ULONG_PTR)hHandle->UniqueProcessId != dwPid) + continue; + if (! DuplicateHandle( + hProcess, + hHandle->HandleValue, + GetCurrentProcess(), + &hFile, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) { - /* - printf("[%d] DuplicateHandle (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - goto loop_cleanup; + // Will fail if not a regular file; just skip it. + continue; } - hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (hMap == NULL) { - /* - printf("[%d] CreateFileMapping (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - goto loop_cleanup; + // This will set *globalFileName* global variable. + if (psutil_threaded_get_filename(hFile) != 0) + goto error; + + if (globalFileName->Length > 0) { + py_path = PyUnicode_FromWideChar(globalFileName->Buffer, + wcslen(globalFileName->Buffer)); + if (! py_path) + goto error; + if (PyList_Append(py_retlist, py_path)) + goto error; + Py_CLEAR(py_path); // also sets to NULL } - pMem = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 1); - - if (pMem == NULL) { - /* - printf("[%d] MapViewOfFile (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - goto loop_cleanup; + // Loop cleanup section. + if (globalFileName != NULL) { + FREE(globalFileName); + globalFileName = NULL; } - - dwSize = GetMappedFileName( - GetCurrentProcess(), pMem, (LPSTR)pszFilename, MAX_PATH); - if (dwSize == 0) { - /* - printf("[%d] GetMappedFileName (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - goto loop_cleanup; - } - - pszFilename[dwSize] = '\0'; - /* - printf("[%d] Filename (%#x) %#d bytes: %S\n", - dwPid, - hHandle->HandleValue, - dwSize, - pszFilename); - */ - - py_path = PyUnicode_FromWideChar(pszFilename, dwSize); - if (py_path == NULL) { - /* - printf("[%d] PyUnicode_FromStringAndSize (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - error = TRUE; - goto loop_cleanup; - } - - if (PyList_Append(py_retlist, py_path)) { - /* - printf("[%d] PyList_Append (%#x): %#x \n", - dwPid, - hHandle->HandleValue, - GetLastError()); - */ - error = TRUE; - goto loop_cleanup; - } - -loop_cleanup: - Py_XDECREF(py_path); - py_path = NULL; - - if (pMem != NULL) - UnmapViewOfFile(pMem); - pMem = NULL; - - if (hMap != NULL) - CloseHandle(hMap); - hMap = NULL; - - if (hFile != NULL) - CloseHandle(hFile); + CloseHandle(hFile); hFile = NULL; - - dwSize = 0; } -cleanup: - if (pMem != NULL) - UnmapViewOfFile(pMem); - pMem = NULL; + goto exit; - if (hMap != NULL) - CloseHandle(hMap); - hMap = NULL; +error: + Py_XDECREF(py_retlist); + errorOccurred = TRUE; + goto exit; +exit: if (hFile != NULL) CloseHandle(hFile); - hFile = NULL; - - if (pHandleInfo != NULL) - HeapFree(GetProcessHeap(), 0, pHandleInfo); - pHandleInfo = NULL; - - if (error) { - Py_XDECREF(py_retlist); - py_retlist = NULL; + if (globalFileName != NULL) { + FREE(globalFileName); + globalFileName = NULL; } + if (py_path != NULL) + Py_DECREF(py_path); + if (handlesList != NULL) + FREE(handlesList); + LeaveCriticalSection(&PSUTIL_CRITICAL_SECTION); + if (errorOccurred == TRUE) + return NULL; return py_retlist; } - - -/* - * The public function. - */ -PyObject * -psutil_get_open_files(long dwPid, HANDLE hProcess) { - // Threaded version only works for Vista+ - if (PSUTIL_WINVER >= PSUTIL_WINDOWS_VISTA) - return psutil_get_open_files_ntqueryobject(dwPid, hProcess); - else - return psutil_get_open_files_getmappedfilename(dwPid, hProcess); -} diff --git a/third_party/python/psutil/psutil/arch/windows/process_handles.h b/third_party/python/psutil/psutil/arch/windows/process_handles.h index 342ce8fd26ad..d1be3152d5f9 100644 --- a/third_party/python/psutil/psutil/arch/windows/process_handles.h +++ b/third_party/python/psutil/psutil/arch/windows/process_handles.h @@ -7,4 +7,4 @@ #include #include -PyObject* psutil_get_open_files(long pid, HANDLE processHandle); +PyObject* psutil_get_open_files(DWORD pid, HANDLE hProcess); diff --git a/third_party/python/psutil/psutil/arch/windows/process_info.c b/third_party/python/psutil/psutil/arch/windows/process_info.c index ba9966bb390b..73a6991279fa 100644 --- a/third_party/python/psutil/psutil/arch/windows/process_info.c +++ b/third_party/python/psutil/psutil/arch/windows/process_info.c @@ -9,408 +9,26 @@ #include #include -#include -#include -#include "ntextapi.h" -#include "global.h" -#include "security.h" -#include "process_info.h" #include "../../_psutil_common.h" +#include "process_info.h" +#include "process_utils.h" -// ==================================================================== -// Helper structures to access the memory correctly. -// Some of these might also be defined in the winternl.h header file -// but unfortunately not in a usable way. -// ==================================================================== - -// https://msdn.microsoft.com/en-us/library/aa813706(v=vs.85).aspx -#ifdef _WIN64 -typedef struct { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[21]; - PVOID LoaderData; - PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters; - /* More fields ... */ -} PEB_; -#else -typedef struct { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - PVOID Reserved3[2]; - PVOID Ldr; - PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters; - /* More fields ... */ -} PEB_; -#endif - -#ifdef _WIN64 -/* When we are a 64 bit process accessing a 32 bit (WoW64) process we need to - use the 32 bit structure layout. */ -typedef struct { - USHORT Length; - USHORT MaxLength; - DWORD Buffer; -} UNICODE_STRING32; - -typedef struct { - BYTE Reserved1[16]; - DWORD Reserved2[5]; - UNICODE_STRING32 CurrentDirectoryPath; - DWORD CurrentDirectoryHandle; - UNICODE_STRING32 DllPath; - UNICODE_STRING32 ImagePathName; - UNICODE_STRING32 CommandLine; - DWORD env; -} RTL_USER_PROCESS_PARAMETERS32; - -typedef struct { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[1]; - DWORD Reserved3[2]; - DWORD Ldr; - DWORD ProcessParameters; - /* More fields ... */ -} PEB32; -#else -/* When we are a 32 bit (WoW64) process accessing a 64 bit process we need to - use the 64 bit structure layout and a special function to read its memory. - */ -typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)( +#ifndef _WIN64 +typedef NTSTATUS (NTAPI *__NtQueryInformationProcess)( HANDLE ProcessHandle, - PVOID64 BaseAddress, - PVOID Buffer, - ULONG64 Size, - PULONG64 NumberOfBytesRead); - -typedef struct { - PVOID Reserved1[2]; - PVOID64 PebBaseAddress; - PVOID Reserved2[4]; - PVOID UniqueProcessId[2]; - PVOID Reserved3[2]; -} PROCESS_BASIC_INFORMATION64; - -typedef struct { - USHORT Length; - USHORT MaxLength; - PVOID64 Buffer; -} UNICODE_STRING64; - -typedef struct { - BYTE Reserved1[16]; - PVOID64 Reserved2[5]; - UNICODE_STRING64 CurrentDirectoryPath; - PVOID64 CurrentDirectoryHandle; - UNICODE_STRING64 DllPath; - UNICODE_STRING64 ImagePathName; - UNICODE_STRING64 CommandLine; - PVOID64 env; -} RTL_USER_PROCESS_PARAMETERS64; - -typedef struct { - BYTE Reserved1[2]; - BYTE BeingDebugged; - BYTE Reserved2[21]; - PVOID64 LoaderData; - PVOID64 ProcessParameters; - /* More fields ... */ -} PEB64; + DWORD ProcessInformationClass, + PVOID ProcessInformation, + DWORD ProcessInformationLength, + PDWORD ReturnLength); #endif -#define PSUTIL_FIRST_PROCESS(Processes) ( \ - (PSYSTEM_PROCESS_INFORMATION)(Processes)) -#define PSUTIL_NEXT_PROCESS(Process) ( \ - ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ - (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ - ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL) - - -// ==================================================================== -// Process and PIDs utiilties. -// ==================================================================== - - -#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) - /* - * Return 1 if PID exists, 0 if not, -1 on error. + * Given a pointer into a process's memory, figure out how much + * data can be read from it. */ -int -psutil_pid_in_pids(DWORD pid) { - DWORD *proclist = NULL; - DWORD numberOfReturnedPIDs; - DWORD i; - - proclist = psutil_get_pids(&numberOfReturnedPIDs); - if (proclist == NULL) - return -1; - for (i = 0; i < numberOfReturnedPIDs; i++) { - if (proclist[i] == pid) { - free(proclist); - return 1; - } - } - free(proclist); - return 0; -} - - -/* - * Given a process HANDLE checks whether it's actually running. - * Returns: - * - 1: running - * - 0: not running - * - -1: WindowsError - * - -2: AssertionError - */ -int -psutil_is_phandle_running(HANDLE hProcess, DWORD pid) { - DWORD processExitCode = 0; - - if (hProcess == NULL) { - if (GetLastError() == ERROR_INVALID_PARAMETER) { - // Yeah, this is the actual error code in case of - // "no such process". - if (! psutil_assert_pid_not_exists( - pid, "iphr: OpenProcess() -> ERROR_INVALID_PARAMETER")) { - return -2; - } - return 0; - } - return -1; - } - - if (GetExitCodeProcess(hProcess, &processExitCode)) { - // XXX - maybe STILL_ACTIVE is not fully reliable as per: - // http://stackoverflow.com/questions/1591342/#comment47830782_1591379 - if (processExitCode == STILL_ACTIVE) { - if (! psutil_assert_pid_exists( - pid, "iphr: GetExitCodeProcess() -> STILL_ACTIVE")) { - return -2; - } - return 1; - } - else { - // We can't be sure so we look into pids. - if (psutil_pid_in_pids(pid) == 1) { - return 1; - } - else { - CloseHandle(hProcess); - return 0; - } - } - } - - CloseHandle(hProcess); - if (! psutil_assert_pid_not_exists( pid, "iphr: exit fun")) { - return -2; - } - return -1; -} - - -/* - * Given a process HANDLE checks whether it's actually running and if - * it does return it, else return NULL with the proper Python exception - * set. - */ -HANDLE -psutil_check_phandle(HANDLE hProcess, DWORD pid) { - int ret = psutil_is_phandle_running(hProcess, pid); - if (ret == 1) { - return hProcess; - } - else if (ret == 0) { - return NoSuchProcess(""); - } - else if (ret == -1) { - if (GetLastError() == ERROR_ACCESS_DENIED) - return PyErr_SetFromWindowsErr(0); - else - return PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); - } - else { - return NULL; - } -} - - -/* - * A wrapper around OpenProcess setting NSP exception if process - * no longer exists. - * "pid" is the process pid, "dwDesiredAccess" is the first argument - * exptected by OpenProcess. - * Return a process handle or NULL. - */ -HANDLE -psutil_handle_from_pid(DWORD pid, DWORD access) { - HANDLE hProcess; - - if (pid == 0) { - // otherwise we'd get NoSuchProcess - return AccessDenied(""); - } - // needed for GetExitCodeProcess - access |= PROCESS_QUERY_LIMITED_INFORMATION; - hProcess = OpenProcess(access, FALSE, pid); - return psutil_check_phandle(hProcess, pid); -} - - -DWORD * -psutil_get_pids(DWORD *numberOfReturnedPIDs) { - // Win32 SDK says the only way to know if our process array - // wasn't large enough is to check the returned size and make - // sure that it doesn't match the size of the array. - // If it does we allocate a larger array and try again - - // Stores the actual array - DWORD *procArray = NULL; - DWORD procArrayByteSz; - int procArraySz = 0; - - // Stores the byte size of the returned array from enumprocesses - DWORD enumReturnSz = 0; - - do { - procArraySz += 1024; - if (procArray != NULL) - free(procArray); - procArrayByteSz = procArraySz * sizeof(DWORD); - procArray = malloc(procArrayByteSz); - if (procArray == NULL) { - PyErr_NoMemory(); - return NULL; - } - if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) { - free(procArray); - PyErr_SetFromWindowsErr(0); - return NULL; - } - } while (enumReturnSz == procArraySz * sizeof(DWORD)); - - // The number of elements is the returned size / size of each element - *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); - - return procArray; -} - - -int -psutil_assert_pid_exists(DWORD pid, char *err) { - if (PSUTIL_TESTING) { - if (psutil_pid_in_pids(pid) == 0) { - PyErr_SetString(PyExc_AssertionError, err); - return 0; - } - } - return 1; -} - - -int -psutil_assert_pid_not_exists(DWORD pid, char *err) { - if (PSUTIL_TESTING) { - if (psutil_pid_in_pids(pid) == 1) { - PyErr_SetString(PyExc_AssertionError, err); - return 0; - } - } - return 1; -} - - -/* -/* Check for PID existance by using OpenProcess() + GetExitCodeProcess. -/* Returns: - * 1: pid exists - * 0: it doesn't - * -1: error - */ -int -psutil_pid_is_running(DWORD pid) { - HANDLE hProcess; - DWORD exitCode; - DWORD err; - - // Special case for PID 0 System Idle Process - if (pid == 0) - return 1; - if (pid < 0) - return 0; - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, pid); - if (NULL == hProcess) { - err = GetLastError(); - // Yeah, this is the actual error code in case of "no such process". - if (err == ERROR_INVALID_PARAMETER) { - if (! psutil_assert_pid_not_exists( - pid, "pir: OpenProcess() -> INVALID_PARAMETER")) { - return -1; - } - return 0; - } - // Access denied obviously means there's a process to deny access to. - else if (err == ERROR_ACCESS_DENIED) { - if (! psutil_assert_pid_exists( - pid, "pir: OpenProcess() ACCESS_DENIED")) { - return -1; - } - return 1; - } - // Be strict and raise an exception; the caller is supposed - // to take -1 into account. - else { - PyErr_SetFromOSErrnoWithSyscall("OpenProcess(PROCESS_VM_READ)"); - return -1; - } - } - - if (GetExitCodeProcess(hProcess, &exitCode)) { - CloseHandle(hProcess); - // XXX - maybe STILL_ACTIVE is not fully reliable as per: - // http://stackoverflow.com/questions/1591342/#comment47830782_1591379 - if (exitCode == STILL_ACTIVE) { - if (! psutil_assert_pid_exists( - pid, "pir: GetExitCodeProcess() -> STILL_ACTIVE")) { - return -1; - } - return 1; - } - // We can't be sure so we look into pids. - else { - return psutil_pid_in_pids(pid); - } - } - else { - err = GetLastError(); - CloseHandle(hProcess); - // Same as for OpenProcess, assume access denied means there's - // a process to deny access to. - if (err == ERROR_ACCESS_DENIED) { - if (! psutil_assert_pid_exists( - pid, "pir: GetExitCodeProcess() -> ERROR_ACCESS_DENIED")) { - return -1; - } - return 1; - } - else { - PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); - return -1; - } - } -} - - -/* Given a pointer into a process's memory, figure out how much data can be - * read from it. */ static int psutil_get_process_region_size(HANDLE hProcess, LPCVOID src, SIZE_T *psize) { MEMORY_BASIC_INFORMATION info; @@ -431,14 +49,16 @@ enum psutil_process_data_kind { KIND_ENVIRON, }; -/* Get data from the process with the given pid. The data is returned in the - pdata output member as a nul terminated string which must be freed on - success. - On success 0 is returned. On error the output parameter is not touched, -1 - is returned, and an appropriate Python exception is set. */ +/* + * Get data from the process with the given pid. The data is returned + * in the pdata output member as a nul terminated string which must be + * freed on success. + * On success 0 is returned. On error the output parameter is not touched, + * -1 is returned, and an appropriate Python exception is set. + */ static int -psutil_get_process_data(long pid, +psutil_get_process_data(DWORD pid, enum psutil_process_data_kind kind, WCHAR **pdata, SIZE_T *psize) { @@ -466,14 +86,13 @@ psutil_get_process_data(long pid, http://stackoverflow.com/a/14012919 http://www.drdobbs.com/embracing-64-bit-windows/184401966 */ - _NtQueryInformationProcess NtQueryInformationProcess = NULL; + SIZE_T size = 0; #ifndef _WIN64 - static _NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL; + static __NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL; static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL; #endif HANDLE hProcess = NULL; LPCVOID src; - SIZE_T size; WCHAR *buffer = NULL; #ifdef _WIN64 LPVOID ppeb32 = NULL; @@ -492,7 +111,7 @@ psutil_get_process_data(long pid, #ifdef _WIN64 /* 64 bit case. Check if the target is a 32 bit process running in WoW64 * mode. */ - status = psutil_NtQueryInformationProcess( + status = NtQueryInformationProcess( hProcess, ProcessWow64Information, &ppeb32, @@ -512,18 +131,22 @@ psutil_get_process_data(long pid, // read PEB if (!ReadProcessMemory(hProcess, ppeb32, &peb32, sizeof(peb32), NULL)) { - PyErr_SetFromOSErrnoWithSyscall("ReadProcessMemory"); + // May fail with ERROR_PARTIAL_COPY, see: + // https://github.com/giampaolo/psutil/issues/875 + PyErr_SetFromWindowsErr(0); goto error; } // read process parameters if (!ReadProcessMemory(hProcess, - UlongToPtr(peb32.ProcessParameters), - &procParameters32, - sizeof(procParameters32), - NULL)) { - PyErr_SetFromOSErrnoWithSyscall( - "ReadProcessMemory(ProcessParameters)"); + UlongToPtr(peb32.ProcessParameters), + &procParameters32, + sizeof(procParameters32), + NULL)) + { + // May fail with ERROR_PARTIAL_COPY, see: + // https://github.com/giampaolo/psutil/issues/875 + PyErr_SetFromWindowsErr(0); goto error; } @@ -576,41 +199,44 @@ psutil_get_process_data(long pid, } } - if (! NT_SUCCESS( - NtWow64QueryInformationProcess64( + status = NtWow64QueryInformationProcess64( hProcess, ProcessBasicInformation, &pbi64, sizeof(pbi64), - NULL))) - { - PyErr_SetFromOSErrnoWithSyscall( - "NtWow64QueryInformationProcess64(ProcessBasicInformation)"); + NULL); + if (!NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtWow64QueryInformationProcess64(ProcessBasicInformation)" + ); goto error; } // read peb - if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( - hProcess, - pbi64.PebBaseAddress, - &peb64, - sizeof(peb64), - NULL))) - { - PyErr_SetFromOSErrnoWithSyscall("NtWow64ReadVirtualMemory64"); + status = NtWow64ReadVirtualMemory64( + hProcess, + pbi64.PebBaseAddress, + &peb64, + sizeof(peb64), + NULL); + if (!NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr(status, "NtWow64ReadVirtualMemory64"); goto error; } // read process parameters - if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( + status = NtWow64ReadVirtualMemory64( hProcess, peb64.ProcessParameters, &procParameters64, sizeof(procParameters64), - NULL))) - { - PyErr_SetFromOSErrnoWithSyscall( - "NtWow64ReadVirtualMemory64(ProcessParameters)"); + NULL); + if (!NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtWow64ReadVirtualMemory64(ProcessParameters)" + ); goto error; } @@ -635,7 +261,7 @@ psutil_get_process_data(long pid, PEB_ peb; RTL_USER_PROCESS_PARAMETERS_ procParameters; - status = psutil_NtQueryInformationProcess( + status = NtQueryInformationProcess( hProcess, ProcessBasicInformation, &pbi, @@ -654,8 +280,11 @@ psutil_get_process_data(long pid, pbi.PebBaseAddress, &peb, sizeof(peb), - NULL)) { - PyErr_SetFromOSErrnoWithSyscall("ReadProcessMemory"); + NULL)) + { + // May fail with ERROR_PARTIAL_COPY, see: + // https://github.com/giampaolo/psutil/issues/875 + PyErr_SetFromWindowsErr(0); goto error; } @@ -664,9 +293,11 @@ psutil_get_process_data(long pid, peb.ProcessParameters, &procParameters, sizeof(procParameters), - NULL)) { - PyErr_SetFromOSErrnoWithSyscall( - "ReadProcessMemory(ProcessParameters)"); + NULL)) + { + // May fail with ERROR_PARTIAL_COPY, see: + // https://github.com/giampaolo/psutil/issues/875 + PyErr_SetFromWindowsErr(0); goto error; } @@ -705,20 +336,22 @@ psutil_get_process_data(long pid, #ifndef _WIN64 if (weAreWow64 && !theyAreWow64) { - if (! NT_SUCCESS(NtWow64ReadVirtualMemory64( + status = NtWow64ReadVirtualMemory64( hProcess, src64, buffer, size, - NULL))) - { - PyErr_SetFromOSErrnoWithSyscall("NtWow64ReadVirtualMemory64"); + NULL); + if (!NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr(status, "NtWow64ReadVirtualMemory64"); goto error; } } else #endif if (!ReadProcessMemory(hProcess, src, buffer, size, NULL)) { - PyErr_SetFromOSErrnoWithSyscall("ReadProcessMemory"); + // May fail with ERROR_PARTIAL_COPY, see: + // https://github.com/giampaolo/psutil/issues/875 + PyErr_SetFromWindowsErr(0); goto error; } @@ -744,8 +377,8 @@ error: * AccessDenied. Requires Windows 8.1+. */ static int -psutil_cmdline_query_proc(long pid, WCHAR **pdata, SIZE_T *psize) { - HANDLE hProcess; +psutil_cmdline_query_proc(DWORD pid, WCHAR **pdata, SIZE_T *psize) { + HANDLE hProcess = NULL; ULONG bufLen = 0; NTSTATUS status; char * buffer = NULL; @@ -765,16 +398,15 @@ psutil_cmdline_query_proc(long pid, WCHAR **pdata, SIZE_T *psize) { goto error; // get the right buf size - status = psutil_NtQueryInformationProcess( + status = NtQueryInformationProcess( hProcess, ProcessCommandLineInformation, NULL, 0, &bufLen); - // 0xC0000225 == STATUS_NOT_FOUND, see: // https://github.com/giampaolo/psutil/issues/1501 - if (status == 0xC0000225) { + if (status == STATUS_NOT_FOUND) { AccessDenied("NtQueryInformationProcess(ProcessBasicInformation) -> " "STATUS_NOT_FOUND translated into PermissionError"); goto error; @@ -796,7 +428,7 @@ psutil_cmdline_query_proc(long pid, WCHAR **pdata, SIZE_T *psize) { } // get the cmdline - status = psutil_NtQueryInformationProcess( + status = NtQueryInformationProcess( hProcess, ProcessCommandLineInformation, buffer, @@ -838,7 +470,7 @@ error: * with given pid or NULL on error. */ PyObject * -psutil_get_cmdline(long pid, int use_peb) { +psutil_get_cmdline(DWORD pid, int use_peb) { PyObject *ret = NULL; WCHAR *data = NULL; SIZE_T size; @@ -899,7 +531,7 @@ out: PyObject * -psutil_get_cwd(long pid) { +psutil_get_cwd(DWORD pid) { PyObject *ret = NULL; WCHAR *data = NULL; SIZE_T size; @@ -923,7 +555,7 @@ out: * process with given pid or NULL on error. */ PyObject * -psutil_get_environ(long pid) { +psutil_get_environ(DWORD pid) { PyObject *ret = NULL; WCHAR *data = NULL; SIZE_T size; @@ -943,10 +575,11 @@ out: /* * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure - * fills the structure with various process information by using - * NtQuerySystemInformation. + * fills the structure with various process information in one shot + * by using NtQuerySystemInformation. * We use this as a fallback when faster functions fail with access - * denied. This is slower because it iterates over all processes. + * denied. This is slower because it iterates over all processes + * but it doesn't require any privilege (also work for PID 0). * On success return 1, else 0 with Python exception already set. */ int @@ -966,7 +599,7 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, } while (TRUE) { - status = psutil_NtQuerySystemInformation( + status = NtQuerySystemInformation( SystemProcessInformation, buffer, bufferSize, @@ -997,14 +630,14 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, process = PSUTIL_FIRST_PROCESS(buffer); do { - if (process->UniqueProcessId == (HANDLE)pid) { + if ((ULONG_PTR)process->UniqueProcessId == pid) { *retProcess = process; *retBuffer = buffer; return 1; } - } while ( (process = PSUTIL_NEXT_PROCESS(process)) ); + } while ((process = PSUTIL_NEXT_PROCESS(process))); - NoSuchProcess(""); + NoSuchProcess("NtQuerySystemInformation (no PID found)"); goto error; error: @@ -1012,3 +645,89 @@ error: free(buffer); return 0; } + + +/* + * Get various process information by using NtQuerySystemInformation. + * We use this as a fallback when faster functions fail with access + * denied. This is slower because it iterates over all processes. + * Returned tuple includes the following process info: + * + * - num_threads() + * - ctx_switches() + * - num_handles() (fallback) + * - cpu_times() (fallback) + * - create_time() (fallback) + * - io_counters() (fallback) + * - memory_info() (fallback) + */ +PyObject * +psutil_proc_info(PyObject *self, PyObject *args) { + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + ULONG i; + ULONG ctx_switches = 0; + double user_time; + double kernel_time; + double create_time; + PyObject *py_retlist; + + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + return NULL; + if (! psutil_get_proc_info(pid, &process, &buffer)) + return NULL; + + for (i = 0; i < process->NumberOfThreads; i++) + ctx_switches += process->Threads[i].ContextSwitches; + user_time = (double)process->UserTime.HighPart * HI_T + \ + (double)process->UserTime.LowPart * LO_T; + kernel_time = (double)process->KernelTime.HighPart * HI_T + \ + (double)process->KernelTime.LowPart * LO_T; + + // Convert the LARGE_INTEGER union to a Unix time. + // It's the best I could find by googling and borrowing code here + // and there. The time returned has a precision of 1 second. + if (0 == pid || 4 == pid) { + // the python module will translate this into BOOT_TIME later + create_time = 0; + } + else { + create_time = psutil_LargeIntegerToUnixTime(process->CreateTime); + } + + py_retlist = Py_BuildValue( +#if defined(_WIN64) + "kkdddiKKKKKK" "kKKKKKKKKK", +#else + "kkdddiKKKKKK" "kIIIIIIIII", +#endif + process->HandleCount, // num handles + ctx_switches, // num ctx switches + user_time, // cpu user time + kernel_time, // cpu kernel time + create_time, // create time + (int)process->NumberOfThreads, // num threads + // IO counters + process->ReadOperationCount.QuadPart, // io rcount + process->WriteOperationCount.QuadPart, // io wcount + process->ReadTransferCount.QuadPart, // io rbytes + process->WriteTransferCount.QuadPart, // io wbytes + process->OtherOperationCount.QuadPart, // io others count + process->OtherTransferCount.QuadPart, // io others bytes + // memory + process->PageFaultCount, // num page faults + process->PeakWorkingSetSize, // peak wset + process->WorkingSetSize, // wset + process->QuotaPeakPagedPoolUsage, // peak paged pool + process->QuotaPagedPoolUsage, // paged pool + process->QuotaPeakNonPagedPoolUsage, // peak non paged pool + process->QuotaNonPagedPoolUsage, // non paged pool + process->PagefileUsage, // pagefile + process->PeakPagefileUsage, // peak pagefile + process->PrivatePageCount // private + ); + + free(buffer); + return py_retlist; +} diff --git a/third_party/python/psutil/psutil/arch/windows/process_info.h b/third_party/python/psutil/psutil/arch/windows/process_info.h index 4278c4df9e0f..5e89ddebdf1b 100644 --- a/third_party/python/psutil/psutil/arch/windows/process_info.h +++ b/third_party/python/psutil/psutil/arch/windows/process_info.h @@ -4,28 +4,18 @@ * found in the LICENSE file. */ -#if !defined(__PROCESS_INFO_H) -#define __PROCESS_INFO_H - #include -#include -#include "security.h" -#include "ntextapi.h" -#define HANDLE_TO_PYNUM(handle) PyLong_FromUnsignedLong((unsigned long) handle) -#define PYNUM_TO_HANDLE(obj) ((HANDLE)PyLong_AsUnsignedLong(obj)) +#define PSUTIL_FIRST_PROCESS(Processes) ( \ + (PSYSTEM_PROCESS_INFORMATION)(Processes)) +#define PSUTIL_NEXT_PROCESS(Process) ( \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ + (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL) -DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs); -HANDLE psutil_handle_from_pid(DWORD pid, DWORD dwDesiredAccess); -int psutil_pid_is_running(DWORD pid); int psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retBuffer); - -int psutil_assert_pid_exists(DWORD pid, char *err); -int psutil_assert_pid_not_exists(DWORD pid, char *err); - -PyObject* psutil_get_cmdline(long pid, int use_peb); -PyObject* psutil_get_cwd(long pid); -PyObject* psutil_get_environ(long pid); - -#endif +PyObject* psutil_get_cmdline(DWORD pid, int use_peb); +PyObject* psutil_get_cwd(DWORD pid); +PyObject* psutil_get_environ(DWORD pid); +PyObject* psutil_proc_info(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/process_utils.c b/third_party/python/psutil/psutil/arch/windows/process_utils.c new file mode 100644 index 000000000000..f9d2f2f93dc5 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/process_utils.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper process functions. + */ + +#include +#include +#include // EnumProcesses + +#include "../../_psutil_common.h" +#include "process_utils.h" + + +DWORD * +psutil_get_pids(DWORD *numberOfReturnedPIDs) { + // Win32 SDK says the only way to know if our process array + // wasn't large enough is to check the returned size and make + // sure that it doesn't match the size of the array. + // If it does we allocate a larger array and try again + + // Stores the actual array + DWORD *procArray = NULL; + DWORD procArrayByteSz; + int procArraySz = 0; + + // Stores the byte size of the returned array from enumprocesses + DWORD enumReturnSz = 0; + + do { + procArraySz += 1024; + if (procArray != NULL) + free(procArray); + procArrayByteSz = procArraySz * sizeof(DWORD); + procArray = malloc(procArrayByteSz); + if (procArray == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) { + free(procArray); + PyErr_SetFromWindowsErr(0); + return NULL; + } + } while (enumReturnSz == procArraySz * sizeof(DWORD)); + + // The number of elements is the returned size / size of each element + *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); + + return procArray; +} + + +// Return 1 if PID exists, 0 if not, -1 on error. +int +psutil_pid_in_pids(DWORD pid) { + DWORD *proclist = NULL; + DWORD numberOfReturnedPIDs; + DWORD i; + + proclist = psutil_get_pids(&numberOfReturnedPIDs); + if (proclist == NULL) + return -1; + for (i = 0; i < numberOfReturnedPIDs; i++) { + if (proclist[i] == pid) { + free(proclist); + return 1; + } + } + free(proclist); + return 0; +} + + +// Given a process handle checks whether it's actually running. If it +// does return the handle, else return NULL with Python exception set. +// This is needed because OpenProcess API sucks. +HANDLE +psutil_check_phandle(HANDLE hProcess, DWORD pid) { + DWORD exitCode; + + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // Yeah, this is the actual error code in case of + // "no such process". + NoSuchProcess("OpenProcess"); + return NULL; + } + PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); + return NULL; + } + + if (GetExitCodeProcess(hProcess, &exitCode)) { + // XXX - maybe STILL_ACTIVE is not fully reliable as per: + // http://stackoverflow.com/questions/1591342/#comment47830782_1591379 + if (exitCode == STILL_ACTIVE) { + return hProcess; + } + if (psutil_pid_in_pids(pid) == 1) { + return hProcess; + } + CloseHandle(hProcess); + NoSuchProcess("GetExitCodeProcess != STILL_ACTIVE"); + return NULL; + } + + if (GetLastError() == ERROR_ACCESS_DENIED) { + psutil_debug("GetExitCodeProcess -> ERROR_ACCESS_DENIED (ignored)"); + SetLastError(0); + return hProcess; + } + PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess"); + CloseHandle(hProcess); + return NULL; +} + + +// A wrapper around OpenProcess setting NSP exception if process no +// longer exists. *pid* is the process PID, *access* is the first +// argument to OpenProcess. +// Return a process handle or NULL with exception set. +HANDLE +psutil_handle_from_pid(DWORD pid, DWORD access) { + HANDLE hProcess; + + if (pid == 0) { + // otherwise we'd get NoSuchProcess + return AccessDenied("automatically set for PID 0"); + } + + hProcess = OpenProcess(access, FALSE, pid); + + if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED)) { + PyErr_SetFromOSErrnoWithSyscall("OpenProcess"); + return NULL; + } + + hProcess = psutil_check_phandle(hProcess, pid); + return hProcess; +} + + +// Check for PID existance. Return 1 if pid exists, 0 if not, -1 on error. +int +psutil_pid_is_running(DWORD pid) { + HANDLE hProcess; + + // Special case for PID 0 System Idle Process + if (pid == 0) + return 1; + if (pid < 0) + return 0; + + hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + + // Access denied means there's a process to deny access to. + if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED)) + return 1; + + hProcess = psutil_check_phandle(hProcess, pid); + if (hProcess != NULL) { + CloseHandle(hProcess); + return 1; + } + + CloseHandle(hProcess); + PyErr_Clear(); + return psutil_pid_in_pids(pid); +} diff --git a/third_party/python/psutil/psutil/arch/windows/process_utils.h b/third_party/python/psutil/psutil/arch/windows/process_utils.h new file mode 100644 index 000000000000..a7171c5ca4c8 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/process_utils.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs); +HANDLE psutil_handle_from_pid(DWORD pid, DWORD dwDesiredAccess); +int psutil_pid_is_running(DWORD pid); +int psutil_assert_pid_exists(DWORD pid, char *err); +int psutil_assert_pid_not_exists(DWORD pid, char *err); diff --git a/third_party/python/psutil/psutil/arch/windows/security.c b/third_party/python/psutil/psutil/arch/windows/security.c index 4e2c7435b22c..7e400a254137 100644 --- a/third_party/python/psutil/psutil/arch/windows/security.c +++ b/third_party/python/psutil/psutil/arch/windows/security.c @@ -9,6 +9,7 @@ #include #include + #include "../../_psutil_common.h" @@ -119,7 +120,6 @@ psutil_print_err() { int psutil_set_se_debug() { HANDLE hToken; - int err = 1; if ((hToken = psutil_get_thisproc_token()) == NULL) { // "return 1;" to get an exception diff --git a/third_party/python/psutil/psutil/arch/windows/services.c b/third_party/python/psutil/psutil/arch/windows/services.c index 92458494b4ac..a91cb8f79701 100644 --- a/third_party/python/psutil/psutil/arch/windows/services.c +++ b/third_party/python/psutil/psutil/arch/windows/services.c @@ -8,8 +8,9 @@ #include #include -#include "services.h" #include "../../_psutil_common.h" +#include "services.h" + // ================================================================== // utils @@ -18,7 +19,6 @@ SC_HANDLE psutil_get_service_handler(char *service_name, DWORD scm_access, DWORD access) { - ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL; SC_HANDLE sc = NULL; SC_HANDLE hService = NULL; @@ -193,8 +193,6 @@ psutil_winservice_query_config(PyObject *self, PyObject *args) { SC_HANDLE hService = NULL; BOOL ok; DWORD bytesNeeded = 0; - DWORD resumeHandle = 0; - DWORD dwBytes = 0; QUERY_SERVICE_CONFIGW *qsc = NULL; PyObject *py_tuple = NULL; PyObject *py_unicode_display_name = NULL; @@ -284,8 +282,6 @@ psutil_winservice_query_status(PyObject *self, PyObject *args) { SC_HANDLE hService = NULL; BOOL ok; DWORD bytesNeeded = 0; - DWORD resumeHandle = 0; - DWORD dwBytes = 0; SERVICE_STATUS_PROCESS *ssp = NULL; PyObject *py_tuple = NULL; @@ -355,8 +351,6 @@ psutil_winservice_query_descr(PyObject *self, PyObject *args) { ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL; BOOL ok; DWORD bytesNeeded = 0; - DWORD resumeHandle = 0; - DWORD dwBytes = 0; SC_HANDLE hService = NULL; SERVICE_DESCRIPTIONW *scd = NULL; char *service_name; diff --git a/third_party/python/psutil/psutil/arch/windows/services.h b/third_party/python/psutil/psutil/arch/windows/services.h index 286ed232c90d..ebcfa5ef590a 100644 --- a/third_party/python/psutil/psutil/arch/windows/services.h +++ b/third_party/python/psutil/psutil/arch/windows/services.h @@ -8,7 +8,7 @@ #include SC_HANDLE psutil_get_service_handle( -char service_name, DWORD scm_access, DWORD access); + char service_name, DWORD scm_access, DWORD access); PyObject *psutil_winservice_enumerate(PyObject *self, PyObject *args); PyObject *psutil_winservice_query_config(PyObject *self, PyObject *args); PyObject *psutil_winservice_query_status(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/socks.c b/third_party/python/psutil/psutil/arch/windows/socks.c new file mode 100644 index 000000000000..5e4c2df802a7 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/socks.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Fixes clash between winsock2.h and windows.h +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#include "../../_psutil_common.h" +#include "process_utils.h" + + +#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) +#define STATUS_UNSUCCESSFUL 0xC0000001 + +ULONG g_TcpTableSize = 0; +ULONG g_UdpTableSize = 0; + + +// Note about GetExtended[Tcp|Udp]Table syscalls: due to other processes +// being active on the machine, it's possible that the size of the table +// increases between the moment we query the size and the moment we query +// the data. Therefore we retry if that happens. See: +// https://github.com/giampaolo/psutil/pull/1335 +// https://github.com/giampaolo/psutil/issues/1294 +// A global and ever increasing size is used in order to avoid calling +// GetExtended[Tcp|Udp]Table twice per call (faster). + + +static PVOID __GetExtendedTcpTable(ULONG family) { + DWORD err; + PVOID table; + ULONG size; + TCP_TABLE_CLASS class = TCP_TABLE_OWNER_PID_ALL; + + size = g_TcpTableSize; + if (size == 0) { + GetExtendedTcpTable(NULL, &size, FALSE, family, class, 0); + // reserve 25% more space + size = size + (size / 2 / 2); + g_TcpTableSize = size; + } + + table = malloc(size); + if (table == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = GetExtendedTcpTable(table, &size, FALSE, family, class, 0); + if (err == NO_ERROR) + return table; + + free(table); + if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) { + psutil_debug("GetExtendedTcpTable: retry with different bufsize"); + g_TcpTableSize = 0; + return __GetExtendedTcpTable(family); + } + + PyErr_SetString(PyExc_RuntimeError, "GetExtendedTcpTable failed"); + return NULL; +} + + +static PVOID __GetExtendedUdpTable(ULONG family) { + DWORD err; + PVOID table; + ULONG size; + UDP_TABLE_CLASS class = UDP_TABLE_OWNER_PID; + + size = g_UdpTableSize; + if (size == 0) { + GetExtendedUdpTable(NULL, &size, FALSE, family, class, 0); + // reserve 25% more space + size = size + (size / 2 / 2); + g_UdpTableSize = size; + } + + table = malloc(size); + if (table == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = GetExtendedUdpTable(table, &size, FALSE, family, class, 0); + if (err == NO_ERROR) + return table; + + free(table); + if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) { + psutil_debug("GetExtendedUdpTable: retry with different bufsize"); + g_UdpTableSize = 0; + return __GetExtendedUdpTable(family); + } + + PyErr_SetString(PyExc_RuntimeError, "GetExtendedUdpTable failed"); + return NULL; +} + + +#define psutil_conn_decref_objs() \ + Py_DECREF(_AF_INET); \ + Py_DECREF(_AF_INET6);\ + Py_DECREF(_SOCK_STREAM);\ + Py_DECREF(_SOCK_DGRAM); + + +/* + * Return a list of network connections opened by a process + */ +PyObject * +psutil_net_connections(PyObject *self, PyObject *args) { + static long null_address[4] = { 0, 0, 0, 0 }; + DWORD pid; + int pid_return; + PVOID table = NULL; + PMIB_TCPTABLE_OWNER_PID tcp4Table; + PMIB_UDPTABLE_OWNER_PID udp4Table; + PMIB_TCP6TABLE_OWNER_PID tcp6Table; + PMIB_UDP6TABLE_OWNER_PID udp6Table; + ULONG i; + CHAR addressBufferLocal[65]; + CHAR addressBufferRemote[65]; + + PyObject *py_retlist = NULL; + PyObject *py_conn_tuple = NULL; + PyObject *py_af_filter = NULL; + PyObject *py_type_filter = NULL; + PyObject *py_addr_tuple_local = NULL; + PyObject *py_addr_tuple_remote = NULL; + PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); + PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); + PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); + PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); + + if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, &py_af_filter, + &py_type_filter)) + { + goto error; + } + + if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { + psutil_conn_decref_objs(); + PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); + return NULL; + } + + if (pid != -1) { + pid_return = psutil_pid_is_running(pid); + if (pid_return == 0) { + psutil_conn_decref_objs(); + return NoSuchProcess("psutil_pid_is_running"); + } + else if (pid_return == -1) { + psutil_conn_decref_objs(); + return NULL; + } + } + + py_retlist = PyList_New(0); + if (py_retlist == NULL) { + psutil_conn_decref_objs(); + return NULL; + } + + // TCP IPv4 + + if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && + (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1)) + { + table = NULL; + py_conn_tuple = NULL; + py_addr_tuple_local = NULL; + py_addr_tuple_remote = NULL; + + table = __GetExtendedTcpTable(AF_INET); + if (table == NULL) + goto error; + tcp4Table = table; + for (i = 0; i < tcp4Table->dwNumEntries; i++) { + if (pid != -1) { + if (tcp4Table->table[i].dwOwningPid != pid) { + continue; + } + } + + if (tcp4Table->table[i].dwLocalAddr != 0 || + tcp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; + RtlIpv4AddressToStringA(&addr, addressBufferLocal); + py_addr_tuple_local = Py_BuildValue( + "(si)", + addressBufferLocal, + BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); + } + else { + py_addr_tuple_local = PyTuple_New(0); + } + + if (py_addr_tuple_local == NULL) + goto error; + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((tcp4Table->table[i].dwRemoteAddr != 0 || + tcp4Table->table[i].dwRemotePort != 0) && + (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; + RtlIpv4AddressToStringA(&addr, addressBufferRemote); + py_addr_tuple_remote = Py_BuildValue( + "(si)", + addressBufferRemote, + BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); + } + else + { + py_addr_tuple_remote = PyTuple_New(0); + } + + if (py_addr_tuple_remote == NULL) + goto error; + + py_conn_tuple = Py_BuildValue( + "(iiiNNiI)", + -1, + AF_INET, + SOCK_STREAM, + py_addr_tuple_local, + py_addr_tuple_remote, + tcp4Table->table[i].dwState, + tcp4Table->table[i].dwOwningPid); + if (!py_conn_tuple) + goto error; + if (PyList_Append(py_retlist, py_conn_tuple)) + goto error; + Py_CLEAR(py_conn_tuple); + } + + free(table); + table = NULL; + } + + // TCP IPv6 + if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && + (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1) && + (RtlIpv6AddressToStringA != NULL)) + { + table = NULL; + py_conn_tuple = NULL; + py_addr_tuple_local = NULL; + py_addr_tuple_remote = NULL; + + table = __GetExtendedTcpTable(AF_INET6); + if (table == NULL) + goto error; + tcp6Table = table; + for (i = 0; i < tcp6Table->dwNumEntries; i++) + { + if (pid != -1) { + if (tcp6Table->table[i].dwOwningPid != pid) { + continue; + } + } + + if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) + != 0 || tcp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); + RtlIpv6AddressToStringA(&addr, addressBufferLocal); + py_addr_tuple_local = Py_BuildValue( + "(si)", + addressBufferLocal, + BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); + } + else { + py_addr_tuple_local = PyTuple_New(0); + } + + if (py_addr_tuple_local == NULL) + goto error; + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) + != 0 || + tcp6Table->table[i].dwRemotePort != 0) && + (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); + RtlIpv6AddressToStringA(&addr, addressBufferRemote); + py_addr_tuple_remote = Py_BuildValue( + "(si)", + addressBufferRemote, + BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); + } + else { + py_addr_tuple_remote = PyTuple_New(0); + } + + if (py_addr_tuple_remote == NULL) + goto error; + + py_conn_tuple = Py_BuildValue( + "(iiiNNiI)", + -1, + AF_INET6, + SOCK_STREAM, + py_addr_tuple_local, + py_addr_tuple_remote, + tcp6Table->table[i].dwState, + tcp6Table->table[i].dwOwningPid); + if (!py_conn_tuple) + goto error; + if (PyList_Append(py_retlist, py_conn_tuple)) + goto error; + Py_CLEAR(py_conn_tuple); + } + + free(table); + table = NULL; + } + + // UDP IPv4 + + if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) && + (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1)) + { + table = NULL; + py_conn_tuple = NULL; + py_addr_tuple_local = NULL; + py_addr_tuple_remote = NULL; + table = __GetExtendedUdpTable(AF_INET); + if (table == NULL) + goto error; + udp4Table = table; + for (i = 0; i < udp4Table->dwNumEntries; i++) + { + if (pid != -1) { + if (udp4Table->table[i].dwOwningPid != pid) { + continue; + } + } + + if (udp4Table->table[i].dwLocalAddr != 0 || + udp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; + RtlIpv4AddressToStringA(&addr, addressBufferLocal); + py_addr_tuple_local = Py_BuildValue( + "(si)", + addressBufferLocal, + BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); + } + else { + py_addr_tuple_local = PyTuple_New(0); + } + + if (py_addr_tuple_local == NULL) + goto error; + + py_conn_tuple = Py_BuildValue( + "(iiiNNiI)", + -1, + AF_INET, + SOCK_DGRAM, + py_addr_tuple_local, + PyTuple_New(0), + PSUTIL_CONN_NONE, + udp4Table->table[i].dwOwningPid); + if (!py_conn_tuple) + goto error; + if (PyList_Append(py_retlist, py_conn_tuple)) + goto error; + Py_CLEAR(py_conn_tuple); + } + + free(table); + table = NULL; + } + + // UDP IPv6 + + if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) && + (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1) && + (RtlIpv6AddressToStringA != NULL)) + { + table = NULL; + py_conn_tuple = NULL; + py_addr_tuple_local = NULL; + py_addr_tuple_remote = NULL; + table = __GetExtendedUdpTable(AF_INET6); + if (table == NULL) + goto error; + udp6Table = table; + for (i = 0; i < udp6Table->dwNumEntries; i++) { + if (pid != -1) { + if (udp6Table->table[i].dwOwningPid != pid) { + continue; + } + } + + if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) + != 0 || udp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); + RtlIpv6AddressToStringA(&addr, addressBufferLocal); + py_addr_tuple_local = Py_BuildValue( + "(si)", + addressBufferLocal, + BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); + } + else { + py_addr_tuple_local = PyTuple_New(0); + } + + if (py_addr_tuple_local == NULL) + goto error; + + py_conn_tuple = Py_BuildValue( + "(iiiNNiI)", + -1, + AF_INET6, + SOCK_DGRAM, + py_addr_tuple_local, + PyTuple_New(0), + PSUTIL_CONN_NONE, + udp6Table->table[i].dwOwningPid); + if (!py_conn_tuple) + goto error; + if (PyList_Append(py_retlist, py_conn_tuple)) + goto error; + Py_CLEAR(py_conn_tuple); + } + + free(table); + table = NULL; + } + + psutil_conn_decref_objs(); + return py_retlist; + +error: + psutil_conn_decref_objs(); + Py_XDECREF(py_conn_tuple); + Py_XDECREF(py_addr_tuple_local); + Py_XDECREF(py_addr_tuple_remote); + Py_DECREF(py_retlist); + if (table != NULL) + free(table); + return NULL; +} diff --git a/third_party/python/psutil/psutil/arch/windows/socks.h b/third_party/python/psutil/psutil/arch/windows/socks.h new file mode 100644 index 000000000000..cd9ba58dcbc8 --- /dev/null +++ b/third_party/python/psutil/psutil/arch/windows/socks.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Jeff Tang. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_net_connections(PyObject *self, PyObject *args); diff --git a/third_party/python/psutil/psutil/arch/windows/wmi.c b/third_party/python/psutil/psutil/arch/windows/wmi.c index 5858a9e08359..42a70df76673 100644 --- a/third_party/python/psutil/psutil/arch/windows/wmi.c +++ b/third_party/python/psutil/psutil/arch/windows/wmi.c @@ -10,8 +10,6 @@ #include #include -#include "../../_psutil_common.h" - // We use an exponentially weighted moving average, just like Unix systems do // https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation @@ -22,8 +20,8 @@ // This formula comes from linux's include/linux/sched/loadavg.h // https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23 #define LOADAVG_FACTOR_1F 0.9200444146293232478931553241 -#define LOADAVG_FACTOR_5F 0.6592406302004437462547604110 -#define LOADAVG_FACTOR_15F 0.2865047968601901003248854266 +#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501 +#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394 // The time interval in seconds between taking load counts, same as Linux #define SAMPLING_INTERVAL 5 @@ -98,13 +96,13 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) { Py_RETURN_NONE; error: - PyErr_SetExcFromWindowsErr(PyExc_OSError, 0); + PyErr_SetFromWindowsErr(0); return NULL; } /* - * Gets the emulated 1 minute, 5 minute and 15 minute load averages + * Gets the emulated 1 minute, 5 minute and 15 minute load averages * (processor queue length) for the system. * `init_loadavg_counter` must be called before this function to engage the * mechanism that records load values. @@ -112,4 +110,4 @@ error: PyObject * psutil_get_loadavg(PyObject *self, PyObject *args) { return Py_BuildValue("(ddd)", load_avg_1m, load_avg_5m, load_avg_15m); -} \ No newline at end of file +} diff --git a/third_party/python/psutil/psutil/arch/windows/wmi.h b/third_party/python/psutil/psutil/arch/windows/wmi.h index 0210f2d699ee..311242a393d8 100644 --- a/third_party/python/psutil/psutil/arch/windows/wmi.h +++ b/third_party/python/psutil/psutil/arch/windows/wmi.h @@ -1,11 +1,9 @@ /* - * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - - #include PyObject* psutil_init_loadavg_counter(); diff --git a/third_party/python/psutil/psutil/tests/README.rst b/third_party/python/psutil/psutil/tests/README.rst index 9cefb77573a6..9b870d5b1b8f 100644 --- a/third_party/python/psutil/psutil/tests/README.rst +++ b/third_party/python/psutil/psutil/tests/README.rst @@ -4,13 +4,12 @@ Instructions for running tests * There are two ways of running tests. As a "user", if psutil is already installed and you just want to test it works:: - python -m psutil.tests --install-deps # install test deps python -m psutil.tests - As a "developer", if you have a copy of the source code and you whish to hack + As a "developer", if you have a copy of the source code and you wish to hack on psutil:: - make setup-dev-env # install test deps (+ other things) + make setup-dev-env # install missing third-party deps make test * To run tests on all supported Python versions install tox diff --git a/third_party/python/psutil/psutil/tests/__init__.py b/third_party/python/psutil/psutil/tests/__init__.py index 796817195f6a..3e4dc88066ca 100644 --- a/third_party/python/psutil/psutil/tests/__init__.py +++ b/third_party/python/psutil/psutil/tests/__init__.py @@ -32,15 +32,18 @@ import traceback import warnings from socket import AF_INET from socket import AF_INET6 -from socket import SOCK_DGRAM from socket import SOCK_STREAM import psutil +from psutil import AIX from psutil import MACOS from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._common import supports_ipv6 +from psutil._compat import ChildProcessError +from psutil._compat import FileExistsError +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil._compat import unicode @@ -68,7 +71,8 @@ __all__ = [ # constants 'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'MEMORY_TOLERANCE', 'NO_RETRIES', 'PYPY', 'PYTHON_EXE', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX', - 'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'VALID_PROC_STATUSES', + 'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'CIRRUS', 'CI_TESTING', + 'VALID_PROC_STATUSES', "HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS", "HAS_IONICE", "HAS_MEMORY_MAPS", "HAS_PROC_CPU_NUM", "HAS_RLIMIT", "HAS_SENSORS_BATTERY", "HAS_BATTERY", "HAS_SENSORS_FANS", @@ -91,7 +95,7 @@ __all__ = [ # sync primitives 'call_until', 'wait_for_pid', 'wait_for_file', # network - 'check_connection_ntuple', 'check_net_address', + 'check_net_address', 'get_free_port', 'unix_socket_path', 'bind_socket', 'bind_unix_socket', 'tcp_socketpair', 'unix_socketpair', 'create_sockets', # compat @@ -109,13 +113,11 @@ __all__ = [ TOX = os.getenv('TOX') or '' in ('1', 'true') PYPY = '__pypy__' in sys.builtin_module_names -WIN_VISTA = (6, 0, 0) if WINDOWS else None -# whether we're running this test suite on Travis (https://travis-ci.org/) +# whether we're running this test suite on a Continuous Integration service TRAVIS = bool(os.environ.get('TRAVIS')) -# whether we're running this test suite on Appveyor for Windows -# (http://www.appveyor.com/) APPVEYOR = bool(os.environ.get('APPVEYOR')) -PYPY = '__pypy__' in sys.builtin_module_names +CIRRUS = bool(os.environ.get('CIRRUS')) +CI_TESTING = TRAVIS or APPVEYOR or CIRRUS # --- configurable defaults @@ -124,7 +126,7 @@ NO_RETRIES = 10 # bytes tolerance for system-wide memory related tests MEMORY_TOLERANCE = 500 * 1024 # 500KB # the timeout used in functions which have to wait -GLOBAL_TIMEOUT = 3 if TRAVIS or APPVEYOR else 0.5 +GLOBAL_TIMEOUT = 5 # be more tolerant if we're on travis / appveyor in order to avoid # false positives if TRAVIS or APPVEYOR: @@ -150,9 +152,10 @@ ASCII_FS = sys.getfilesystemencoding().lower() in ('ascii', 'us-ascii') # --- paths -ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +ROOT_DIR = os.path.realpath( + os.path.join(os.path.dirname(__file__), '..', '..')) SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts') -HERE = os.path.abspath(os.path.dirname(__file__)) +HERE = os.path.realpath(os.path.dirname(__file__)) # --- support @@ -175,6 +178,7 @@ except Exception: HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans") HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures") HAS_THREADS = hasattr(psutil.Process, "threads") +SKIP_SYSCONS = (MACOS or AIX) and os.getuid() != 0 # --- misc @@ -200,9 +204,6 @@ def _get_py_exe(): return exe else: exe = os.path.realpath(sys.executable) - if WINDOWS: - # avoid subprocess warnings - exe = exe.replace('\\', '\\\\') assert os.path.exists(exe), exe return exe @@ -212,7 +213,6 @@ DEVNULL = open(os.devnull, 'r+') VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil) if x.startswith('STATUS_')] AF_UNIX = getattr(socket, "AF_UNIX", object()) -SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) _subprocesses_started = set() _pids_started = set() @@ -361,7 +361,7 @@ def create_proc_children_pair(): s += "f.write(str(os.getpid()));" s += "f.close();" s += "time.sleep(60);" - p = subprocess.Popen(['%s', '-c', s]) + p = subprocess.Popen([r'%s', '-c', s]) p.wait() """ % (_TESTFN2, PYTHON_EXE)) # On Windows if we create a subprocess with CREATE_NO_WINDOW flag @@ -403,7 +403,7 @@ def create_zombie_proc(): with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock: sock.settimeout(GLOBAL_TIMEOUT) sock.bind(unix_file) - sock.listen(1) + sock.listen(5) pyrun(src) conn, _ = sock.accept() try: @@ -449,7 +449,10 @@ def sh(cmd, **kwds): kwds.setdefault("creationflags", flags) p = subprocess.Popen(cmd, **kwds) _subprocesses_started.add(p) - stdout, stderr = p.communicate() + if PY3: + stdout, stderr = p.communicate(timeout=GLOBAL_TIMEOUT) + else: + stdout, stderr = p.communicate() if p.returncode != 0: raise RuntimeError(stderr) if stderr: @@ -514,9 +517,8 @@ def reap_children(recursive=False): # Wait for the process to terminate, to avoid zombies. try: subp.wait() - except OSError as err: - if err.errno != errno.ECHILD: - raise + except ChildProcessError: + pass # Terminate started pids. while _pids_started: @@ -710,13 +712,12 @@ def safe_rmpath(path): while time.time() < stop_at: try: return fun() + except FileNotFoundError: + pass except WindowsError as _: err = _ - if err.errno != errno.ENOENT: - raise - else: - warn("ignoring %s" % (str(err))) - time.sleep(0.01) + warn("ignoring %s" % (str(err))) + time.sleep(0.01) raise err try: @@ -729,18 +730,16 @@ def safe_rmpath(path): fun() else: retry_fun(fun) - except OSError as err: - if err.errno != errno.ENOENT: - raise + except FileNotFoundError: + pass def safe_mkdir(dir): "Convenience function for creating a directory" try: os.mkdir(dir) - except OSError as err: - if err.errno != errno.EEXIST: - raise + except FileExistsError: + pass @contextlib.contextmanager @@ -868,7 +867,6 @@ def skip_on_not_implemented(only_if=None): def get_free_port(host='127.0.0.1'): """Return an unused TCP port.""" with contextlib.closing(socket.socket()) as sock: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, 0)) return sock.getsockname()[1] @@ -895,10 +893,11 @@ def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None): addr = ("", 0) sock = socket.socket(family, type) try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if os.name not in ('nt', 'cygwin'): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(addr) if type == socket.SOCK_STREAM: - sock.listen(10) + sock.listen(5) return sock except Exception: sock.close() @@ -913,7 +912,7 @@ def bind_unix_socket(name, type=socket.SOCK_STREAM): try: sock.bind(name) if type == socket.SOCK_STREAM: - sock.listen(10) + sock.listen(5) except Exception: sock.close() raise @@ -926,7 +925,7 @@ def tcp_socketpair(family, addr=("", 0)): """ with contextlib.closing(socket.socket(family, SOCK_STREAM)) as ll: ll.bind(addr) - ll.listen(10) + ll.listen(5) addr = ll.getsockname() c = socket.socket(family, SOCK_STREAM) try: @@ -1022,77 +1021,6 @@ def check_net_address(addr, family): raise ValueError("unknown family %r", family) -def check_connection_ntuple(conn): - """Check validity of a connection namedtuple.""" - # check ntuple - assert len(conn) in (6, 7), conn - has_pid = len(conn) == 7 - has_fd = getattr(conn, 'fd', -1) != -1 - assert conn[0] == conn.fd - assert conn[1] == conn.family - assert conn[2] == conn.type - assert conn[3] == conn.laddr - assert conn[4] == conn.raddr - assert conn[5] == conn.status - if has_pid: - assert conn[6] == conn.pid - - # check fd - if has_fd: - assert conn.fd >= 0, conn - if hasattr(socket, 'fromfd') and not WINDOWS: - try: - dupsock = socket.fromfd(conn.fd, conn.family, conn.type) - except (socket.error, OSError) as err: - if err.args[0] != errno.EBADF: - raise - else: - with contextlib.closing(dupsock): - assert dupsock.family == conn.family - assert dupsock.type == conn.type - - # check family - assert conn.family in (AF_INET, AF_INET6, AF_UNIX), repr(conn.family) - if conn.family in (AF_INET, AF_INET6): - # actually try to bind the local socket; ignore IPv6 - # sockets as their address might be represented as - # an IPv4-mapped-address (e.g. "::127.0.0.1") - # and that's rejected by bind() - if conn.family == AF_INET: - s = socket.socket(conn.family, conn.type) - with contextlib.closing(s): - try: - s.bind((conn.laddr[0], 0)) - except socket.error as err: - if err.errno != errno.EADDRNOTAVAIL: - raise - elif conn.family == AF_UNIX: - assert conn.status == psutil.CONN_NONE, conn.status - - # check type (SOCK_SEQPACKET may happen in case of AF_UNIX socks) - assert conn.type in (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET), \ - repr(conn.type) - if conn.type == SOCK_DGRAM: - assert conn.status == psutil.CONN_NONE, conn.status - - # check laddr (IP address and port sanity) - for addr in (conn.laddr, conn.raddr): - if conn.family in (AF_INET, AF_INET6): - assert isinstance(addr, tuple), addr - if not addr: - continue - assert isinstance(addr.port, int), addr.port - assert 0 <= addr.port <= 65535, addr.port - check_net_address(addr.ip, conn.family) - elif conn.family == AF_UNIX: - assert isinstance(addr, str), addr - - # check status - assert isinstance(conn.status, str), conn - valids = [getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_')] - assert conn.status in valids, conn - - # =================================================================== # --- compatibility # =================================================================== @@ -1182,9 +1110,12 @@ else: ext = ".dll" dst = tempfile.mktemp(prefix=dst_prefix, suffix=ext) libs = [x.path for x in psutil.Process().memory_maps() if - os.path.splitext(x.path)[1].lower() == ext and + x.path.lower().endswith(ext) and 'python' in os.path.basename(x.path).lower() and 'wow64' not in x.path.lower()] + if PYPY and not libs: + libs = [x.path for x in psutil.Process().memory_maps() if + 'pypy' in os.path.basename(x.path).lower()] src = random.choice(libs) shutil.copyfile(src, dst) cfile = None diff --git a/third_party/python/psutil/psutil/tests/__main__.py b/third_party/python/psutil/psutil/tests/__main__.py index 68710d44f50b..d5cd02eb1c28 100755 --- a/third_party/python/psutil/psutil/tests/__main__.py +++ b/third_party/python/psutil/psutil/tests/__main__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -6,89 +6,8 @@ """ Run unit tests. This is invoked by: - $ python -m psutil.tests """ -import contextlib -import optparse -import os -import sys -import tempfile -try: - from urllib.request import urlopen # py3 -except ImportError: - from urllib2 import urlopen - -from psutil.tests import PYTHON_EXE -from psutil.tests.runner import run - - -HERE = os.path.abspath(os.path.dirname(__file__)) -GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -TEST_DEPS = [] -if sys.version_info[:2] == (2, 6): - TEST_DEPS.extend(["ipaddress", "unittest2", "argparse", "mock==1.0.1"]) -elif sys.version_info[:2] == (2, 7) or sys.version_info[:2] <= (3, 2): - TEST_DEPS.extend(["ipaddress", "mock"]) - - -def install_pip(): - try: - import pip # NOQA - except ImportError: - import ssl - f = tempfile.NamedTemporaryFile(suffix='.py') - with contextlib.closing(f): - print("downloading %s to %s" % (GET_PIP_URL, f.name)) - if hasattr(ssl, '_create_unverified_context'): - ctx = ssl._create_unverified_context() - else: - ctx = None - kwargs = dict(context=ctx) if ctx else {} - req = urlopen(GET_PIP_URL, **kwargs) - data = req.read() - f.write(data) - f.flush() - - print("installing pip") - code = os.system('%s %s --user' % (PYTHON_EXE, f.name)) - return code - - -def install_test_deps(deps=None): - """Install test dependencies via pip.""" - if deps is None: - deps = TEST_DEPS - deps = set(deps) - if deps: - is_venv = hasattr(sys, 'real_prefix') - opts = "--user" if not is_venv else "" - install_pip() - code = os.system('%s -m pip install %s --upgrade %s' % ( - PYTHON_EXE, opts, " ".join(deps))) - return code - - -def main(): - usage = "%s -m psutil.tests [opts]" % PYTHON_EXE - parser = optparse.OptionParser(usage=usage, description="run unit tests") - parser.add_option("-i", "--install-deps", - action="store_true", default=False, - help="don't print status messages to stdout") - - opts, args = parser.parse_args() - if opts.install_deps: - install_pip() - install_test_deps() - else: - for dep in TEST_DEPS: - try: - __import__(dep.split("==")[0]) - except ImportError: - sys.exit("%r lib is not installed; run %s -m psutil.tests " - "--install-deps" % (dep, PYTHON_EXE)) - run() - - +from .runner import main main() diff --git a/third_party/python/psutil/psutil/tests/runner.py b/third_party/python/psutil/psutil/tests/runner.py index 1a28aa435227..2e9264bd4281 100755 --- a/third_party/python/psutil/psutil/tests/runner.py +++ b/third_party/python/psutil/psutil/tests/runner.py @@ -1,16 +1,18 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """ -Unit test runner, providing colourized output and printing failures -on KeyboardInterrupt. +Unit test runner, providing new features on top of unittest module: +- colourized output (error, skip) +- print failures/tracebacks on CTRL+C +- re-run failed tests only (make test-failed) """ from __future__ import print_function -import atexit +import optparse import os import sys import unittest @@ -23,7 +25,9 @@ except ImportError: ctypes = None import psutil -from psutil._common import memoize +from psutil._common import hilite +from psutil._common import print_color +from psutil._common import term_supports_colors from psutil.tests import safe_rmpath from psutil.tests import TOX @@ -31,95 +35,37 @@ from psutil.tests import TOX HERE = os.path.abspath(os.path.dirname(__file__)) VERBOSITY = 1 if TOX else 2 FAILED_TESTS_FNAME = '.failed-tests.txt' -if os.name == 'posix': - GREEN = 1 - RED = 2 - BROWN = 94 -else: - GREEN = 2 - RED = 4 - BROWN = 6 - DEFAULT_COLOR = 7 -def term_supports_colors(file=sys.stdout): - if os.name == 'nt': - return ctypes is not None - try: - import curses - assert file.isatty() - curses.setupterm() - assert curses.tigetnum("colors") > 0 - except Exception: - return False - else: - return True - - -def hilite(s, color, bold=False): - """Return an highlighted version of 'string'.""" - attr = [] - if color == GREEN: - attr.append('32') - elif color == RED: - attr.append('91') - elif color == BROWN: - attr.append('33') - else: - raise ValueError("unrecognized color") - if bold: - attr.append('1') - return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) - - -@memoize -def _stderr_handle(): - GetStdHandle = ctypes.windll.Kernel32.GetStdHandle - STD_ERROR_HANDLE_ID = ctypes.c_ulong(0xfffffff4) - GetStdHandle.restype = ctypes.c_ulong - handle = GetStdHandle(STD_ERROR_HANDLE_ID) - atexit.register(ctypes.windll.Kernel32.CloseHandle, handle) - return handle - - -def win_colorprint(printer, s, color, bold=False): - if bold and color <= 7: - color += 8 - handle = _stderr_handle() - SetConsoleTextAttribute = ctypes.windll.Kernel32.SetConsoleTextAttribute - SetConsoleTextAttribute(handle, color) - try: - printer(s) - finally: - SetConsoleTextAttribute(handle, DEFAULT_COLOR) +# ===================================================================== +# --- unittest subclasses +# ===================================================================== class ColouredResult(TextTestResult): - def _color_print(self, s, color, bold=False): - if os.name == 'posix': - self.stream.writeln(hilite(s, color, bold=bold)) - else: - win_colorprint(self.stream.writeln, s, color, bold=bold) + def _print_color(self, s, color, bold=False): + file = sys.stderr if color == "red" else sys.stdout + print_color(s, color, bold=bold, file=file) def addSuccess(self, test): TestResult.addSuccess(self, test) - self._color_print("OK", GREEN) + self._print_color("OK", "green") def addError(self, test, err): TestResult.addError(self, test, err) - self._color_print("ERROR", RED, bold=True) + self._print_color("ERROR", "red", bold=True) def addFailure(self, test, err): TestResult.addFailure(self, test, err) - self._color_print("FAIL", RED) + self._print_color("FAIL", "red") def addSkip(self, test, reason): TestResult.addSkip(self, test, reason) - self._color_print("skipped: %s" % reason, BROWN) + self._print_color("skipped: %s" % reason, "brown") def printErrorList(self, flavour, errors): - flavour = hilite(flavour, RED, bold=flavour == 'ERROR') + flavour = hilite(flavour, "red", bold=flavour == 'ERROR') TextTestResult.printErrorList(self, flavour, errors) @@ -133,6 +79,11 @@ class ColouredRunner(TextTestRunner): return self.result +# ===================================================================== +# --- public API +# ===================================================================== + + def setup_tests(): if 'PSUTIL_TESTING' not in os.environ: # This won't work on Windows but set_testing() below will do it. @@ -177,6 +128,7 @@ def save_failed_tests(result): with open(FAILED_TESTS_FNAME, 'wt') as f: for t in result.errors + result.failures: tname = str(t[0]) + unittest.defaultTestLoader.loadTestsFromName(tname) f.write(tname + '\n') @@ -194,3 +146,17 @@ def run(name=None, last_failed=False): save_failed_tests(result) success = result.wasSuccessful() sys.exit(0 if success else 1) + + +def main(): + usage = "python3 -m psutil.tests [opts]" + parser = optparse.OptionParser(usage=usage, description="run unit tests") + parser.add_option("--last-failed", + action="store_true", default=False, + help="only run last failed tests") + opts, args = parser.parse_args() + run(last_failed=opts.last_failed) + + +if __name__ == '__main__': + main() diff --git a/third_party/python/psutil/psutil/tests/test_aix.py b/third_party/python/psutil/psutil/tests/test_aix.py index 1757e3e5d3ae..7171232e08f1 100755 --- a/third_party/python/psutil/psutil/tests/test_aix.py +++ b/third_party/python/psutil/psutil/tests/test_aix.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola' # Copyright (c) 2017, Arnon Yaari diff --git a/third_party/python/psutil/psutil/tests/test_bsd.py b/third_party/python/psutil/psutil/tests/test_bsd.py index 5df8ad298239..899875d076dc 100755 --- a/third_party/python/psutil/psutil/tests/test_bsd.py +++ b/third_party/python/psutil/psutil/tests/test_bsd.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -72,7 +72,7 @@ def muse(field): @unittest.skipIf(not BSD, "BSD only") -class BSDSpecificTestCase(unittest.TestCase): +class BSDTestCase(unittest.TestCase): """Generic tests common to all BSD variants.""" @classmethod @@ -148,7 +148,7 @@ class BSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not FREEBSD, "FREEBSD only") -class FreeBSDSpecificTestCase(unittest.TestCase): +class FreeBSDProcessTestCase(unittest.TestCase): @classmethod def setUpClass(cls): @@ -158,21 +158,8 @@ class FreeBSDSpecificTestCase(unittest.TestCase): def tearDownClass(cls): reap_children() - @staticmethod - def parse_swapinfo(): - # the last line is always the total - output = sh("swapinfo -k").splitlines()[-1] - parts = re.split(r'\s+', output) - - if not parts: - raise ValueError("Can't parse swapinfo: %s" % output) - - # the size is in 1k units, so multiply by 1024 - total, used, free = (int(p) * 1024 for p in parts[1:4]) - return total, used, free - @retry_on_failure() - def test_proc_memory_maps(self): + def test_memory_maps(self): out = sh('procstat -v %s' % self.pid) maps = psutil.Process(self.pid).memory_maps(grouped=False) lines = out.split('\n')[1:] @@ -186,17 +173,17 @@ class FreeBSDSpecificTestCase(unittest.TestCase): if not map.path.startswith('['): self.assertEqual(fields[10], map.path) - def test_proc_exe(self): + def test_exe(self): out = sh('procstat -b %s' % self.pid) self.assertEqual(psutil.Process(self.pid).exe(), out.split('\n')[1].split()[-1]) - def test_proc_cmdline(self): + def test_cmdline(self): out = sh('procstat -c %s' % self.pid) self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), ' '.join(out.split('\n')[1].split()[2:])) - def test_proc_uids_gids(self): + def test_uids_gids(self): out = sh('procstat -s %s' % self.pid) euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] p = psutil.Process(self.pid) @@ -210,7 +197,7 @@ class FreeBSDSpecificTestCase(unittest.TestCase): self.assertEqual(gids.saved, int(sgid)) @retry_on_failure() - def test_proc_ctx_switches(self): + def test_ctx_switches(self): tested = [] out = sh('procstat -r %s' % self.pid) p = psutil.Process(self.pid) @@ -230,7 +217,7 @@ class FreeBSDSpecificTestCase(unittest.TestCase): raise RuntimeError("couldn't find lines match in procstat out") @retry_on_failure() - def test_proc_cpu_times(self): + def test_cpu_times(self): tested = [] out = sh('procstat -r %s' % self.pid) p = psutil.Process(self.pid) @@ -249,11 +236,31 @@ class FreeBSDSpecificTestCase(unittest.TestCase): if len(tested) != 2: raise RuntimeError("couldn't find lines match in procstat out") + +@unittest.skipIf(not FREEBSD, "FREEBSD only") +class FreeBSDSystemTestCase(unittest.TestCase): + + @staticmethod + def parse_swapinfo(): + # the last line is always the total + output = sh("swapinfo -k").splitlines()[-1] + parts = re.split(r'\s+', output) + + if not parts: + raise ValueError("Can't parse swapinfo: %s" % output) + + # the size is in 1k units, so multiply by 1024 + total, used, free = (int(p) * 1024 for p in parts[1:4]) + return total, used, free + def test_cpu_frequency_against_sysctl(self): # Currently only cpu 0 is frequency is supported in FreeBSD # All other cores use the same frequency. sensor = "dev.cpu.0.freq" - sysctl_result = int(sysctl(sensor)) + try: + sysctl_result = int(sysctl(sensor)) + except RuntimeError: + self.skipTest("frequencies not supported by kernel") self.assertEqual(psutil.cpu_freq().current, sysctl_result) sensor = "dev.cpu.0.freq_levels" @@ -365,9 +372,11 @@ class FreeBSDSpecificTestCase(unittest.TestCase): self.assertAlmostEqual(psutil.cpu_stats().soft_interrupts, sysctl('vm.stats.sys.v_soft'), delta=1000) + @retry_on_failure() def test_cpu_stats_syscalls(self): + # pretty high tolerance but it looks like it's OK. self.assertAlmostEqual(psutil.cpu_stats().syscalls, - sysctl('vm.stats.sys.v_syscall'), delta=1000) + sysctl('vm.stats.sys.v_syscall'), delta=200000) # def test_cpu_stats_traps(self): # self.assertAlmostEqual(psutil.cpu_stats().traps, @@ -450,7 +459,10 @@ class FreeBSDSpecificTestCase(unittest.TestCase): for cpu in range(num_cpus): sensor = "dev.cpu.%s.temperature" % cpu # sysctl returns a string in the format 46.0C - sysctl_result = int(float(sysctl(sensor)[:-1])) + try: + sysctl_result = int(float(sysctl(sensor)[:-1])) + except RuntimeError: + self.skipTest("temperatures not supported by kernel") self.assertAlmostEqual( psutil.sensors_temperatures()["coretemp"][cpu].current, sysctl_result, delta=10) @@ -467,7 +479,7 @@ class FreeBSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not OPENBSD, "OPENBSD only") -class OpenBSDSpecificTestCase(unittest.TestCase): +class OpenBSDTestCase(unittest.TestCase): def test_boot_time(self): s = sysctl('kern.boottime') @@ -482,11 +494,11 @@ class OpenBSDSpecificTestCase(unittest.TestCase): @unittest.skipIf(not NETBSD, "NETBSD only") -class NetBSDSpecificTestCase(unittest.TestCase): +class NetBSDTestCase(unittest.TestCase): @staticmethod def parse_meminfo(look_for): - with open('/proc/meminfo', 'rb') as f: + with open('/proc/meminfo', 'rt') as f: for line in f: if line.startswith(look_for): return int(line.split()[1]) * 1024 diff --git a/third_party/python/psutil/psutil/tests/test_connections.py b/third_party/python/psutil/psutil/tests/test_connections.py index 68eea7845d38..972ac9d58ccb 100755 --- a/third_party/python/psutil/psutil/tests/test_connections.py +++ b/third_party/python/psutil/psutil/tests/test_connections.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -6,6 +6,8 @@ """Tests for net_connections() and Process.connections() APIs.""" +import contextlib +import errno import os import socket import textwrap @@ -29,14 +31,17 @@ from psutil._compat import PY3 from psutil.tests import AF_UNIX from psutil.tests import bind_socket from psutil.tests import bind_unix_socket -from psutil.tests import check_connection_ntuple +from psutil.tests import check_net_address +from psutil.tests import CIRRUS from psutil.tests import create_sockets +from psutil.tests import enum from psutil.tests import get_free_port from psutil.tests import HAS_CONNECTIONS_UNIX from psutil.tests import pyrun from psutil.tests import reap_children from psutil.tests import safe_rmpath from psutil.tests import skip_on_access_denied +from psutil.tests import SKIP_SYSCONS from psutil.tests import tcp_socketpair from psutil.tests import TESTFN from psutil.tests import TRAVIS @@ -47,30 +52,147 @@ from psutil.tests import wait_for_file thisproc = psutil.Process() +SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) class Base(object): def setUp(self): safe_rmpath(TESTFN) - if not NETBSD: - # NetBSD opens a UNIX socket to /var/log/run. + if not (NETBSD or FREEBSD): + # process opens a UNIX socket to /var/log/run. cons = thisproc.connections(kind='all') assert not cons, cons def tearDown(self): safe_rmpath(TESTFN) reap_children() - if not NETBSD: + if not (FREEBSD or NETBSD): # Make sure we closed all resources. # NetBSD opens a UNIX socket to /var/log/run. cons = thisproc.connections(kind='all') assert not cons, cons + def compare_procsys_connections(self, pid, proc_cons, kind='all'): + """Given a process PID and its list of connections compare + those against system-wide connections retrieved via + psutil.net_connections. + """ + try: + sys_cons = psutil.net_connections(kind=kind) + except psutil.AccessDenied: + # On MACOS, system-wide connections are retrieved by iterating + # over all processes + if MACOS: + return + else: + raise + # Filter for this proc PID and exlucde PIDs from the tuple. + sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] + sys_cons.sort() + proc_cons.sort() + self.assertEqual(proc_cons, sys_cons) + + def check_connection_ntuple(self, conn): + """Check validity of a connection namedtuple.""" + def check_ntuple(conn): + has_pid = len(conn) == 7 + self.assertIn(len(conn), (6, 7)) + self.assertEqual(conn[0], conn.fd) + self.assertEqual(conn[1], conn.family) + self.assertEqual(conn[2], conn.type) + self.assertEqual(conn[3], conn.laddr) + self.assertEqual(conn[4], conn.raddr) + self.assertEqual(conn[5], conn.status) + if has_pid: + self.assertEqual(conn[6], conn.pid) + + def check_family(conn): + self.assertIn(conn.family, (AF_INET, AF_INET6, AF_UNIX)) + if enum is not None: + assert isinstance(conn.family, enum.IntEnum), conn + else: + assert isinstance(conn.family, int), conn + if conn.family == AF_INET: + # actually try to bind the local socket; ignore IPv6 + # sockets as their address might be represented as + # an IPv4-mapped-address (e.g. "::127.0.0.1") + # and that's rejected by bind() + s = socket.socket(conn.family, conn.type) + with contextlib.closing(s): + try: + s.bind((conn.laddr[0], 0)) + except socket.error as err: + if err.errno != errno.EADDRNOTAVAIL: + raise + elif conn.family == AF_UNIX: + self.assertEqual(conn.status, psutil.CONN_NONE) + + def check_type(conn): + # SOCK_SEQPACKET may happen in case of AF_UNIX socks + self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) + if enum is not None: + assert isinstance(conn.type, enum.IntEnum), conn + else: + assert isinstance(conn.type, int), conn + if conn.type == SOCK_DGRAM: + self.assertEqual(conn.status, psutil.CONN_NONE) + + def check_addrs(conn): + # check IP address and port sanity + for addr in (conn.laddr, conn.raddr): + if conn.family in (AF_INET, AF_INET6): + self.assertIsInstance(addr, tuple) + if not addr: + continue + self.assertIsInstance(addr.port, int) + assert 0 <= addr.port <= 65535, addr.port + check_net_address(addr.ip, conn.family) + elif conn.family == AF_UNIX: + self.assertIsInstance(addr, str) + + def check_status(conn): + self.assertIsInstance(conn.status, str) + valids = [getattr(psutil, x) for x in dir(psutil) + if x.startswith('CONN_')] + self.assertIn(conn.status, valids) + if conn.family in (AF_INET, AF_INET6) and conn.type == SOCK_STREAM: + self.assertNotEqual(conn.status, psutil.CONN_NONE) + else: + self.assertEqual(conn.status, psutil.CONN_NONE) + + check_ntuple(conn) + check_family(conn) + check_type(conn) + check_addrs(conn) + check_status(conn) + + +class TestBase(Base, unittest.TestCase): + + @unittest.skipIf(SKIP_SYSCONS, "requires root") + def test_system(self): + with create_sockets(): + for conn in psutil.net_connections(kind='all'): + self.check_connection_ntuple(conn) + + def test_process(self): + with create_sockets(): + for conn in psutil.Process().connections(kind='all'): + self.check_connection_ntuple(conn) + + def test_invalid_kind(self): + self.assertRaises(ValueError, thisproc.connections, kind='???') + self.assertRaises(ValueError, psutil.net_connections, kind='???') + + +class TestUnconnectedSockets(Base, unittest.TestCase): + """Tests sockets which are open but not connected to anything.""" + def get_conn_from_sock(self, sock): cons = thisproc.connections(kind='all') smap = dict([(c.fd, c) for c in cons]) - if NETBSD: + if NETBSD or FREEBSD: # NetBSD opens a UNIX socket to /var/log/run # so there may be more connections. return smap[sock.fileno()] @@ -80,14 +202,13 @@ class Base(object): self.assertEqual(smap[sock.fileno()].fd, sock.fileno()) return cons[0] - def check_socket(self, sock, conn=None): + def check_socket(self, sock): """Given a socket, makes sure it matches the one obtained via psutil. It assumes this process created one connection only (the one supposed to be checked). """ - if conn is None: - conn = self.get_conn_from_sock(sock) - check_connection_ntuple(conn) + conn = self.get_conn_from_sock(sock) + self.check_connection_ntuple(conn) # fd, family, type if conn.fd != -1: @@ -113,38 +234,9 @@ class Base(object): # XXX Solaris can't retrieve system-wide UNIX sockets if sock.family == AF_UNIX and HAS_CONNECTIONS_UNIX: cons = thisproc.connections(kind='all') - self.compare_procsys_connections(os.getpid(), cons) + self.compare_procsys_connections(os.getpid(), cons, kind='all') return conn - def compare_procsys_connections(self, pid, proc_cons, kind='all'): - """Given a process PID and its list of connections compare - those against system-wide connections retrieved via - psutil.net_connections. - """ - try: - sys_cons = psutil.net_connections(kind=kind) - except psutil.AccessDenied: - # On MACOS, system-wide connections are retrieved by iterating - # over all processes - if MACOS: - return - else: - raise - # Filter for this proc PID and exlucde PIDs from the tuple. - sys_cons = [c[:-1] for c in sys_cons if c.pid == pid] - sys_cons.sort() - proc_cons.sort() - self.assertEqual(proc_cons, sys_cons) - - -# ===================================================================== -# --- Test unconnected sockets -# ===================================================================== - - -class TestUnconnectedSockets(Base, unittest.TestCase): - """Tests sockets which are open but not connected to anything.""" - def test_tcp_v4(self): addr = ("127.0.0.1", get_free_port()) with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock: @@ -192,12 +284,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase): self.assertEqual(conn.status, psutil.CONN_NONE) -# ===================================================================== -# --- Test connected sockets -# ===================================================================== - - -class TestConnectedSocketPairs(Base, unittest.TestCase): +class TestConnectedSocket(Base, unittest.TestCase): """Test socket pairs which are are actually connected to each other. """ @@ -232,11 +319,14 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): cons = thisproc.connections(kind='unix') assert not (cons[0].laddr and cons[0].raddr) assert not (cons[1].laddr and cons[1].raddr) - if NETBSD: + if NETBSD or FREEBSD: # On NetBSD creating a UNIX socket will cause # a UNIX connection to /var/run/log. cons = [c for c in cons if c.raddr != '/var/run/log'] - self.assertEqual(len(cons), 2) + if CIRRUS: + cons = [c for c in cons if c.fd in + (server.fileno(), client.fileno())] + self.assertEqual(len(cons), 2, msg=cons) if LINUX or FREEBSD or SUNOS: # remote path is never set self.assertEqual(cons[0].raddr, "") @@ -257,12 +347,58 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): server.close() client.close() + +class TestFilters(Base, unittest.TestCase): + + def test_filters(self): + def check(kind, families, types): + for conn in thisproc.connections(kind=kind): + self.assertIn(conn.family, families) + self.assertIn(conn.type, types) + if not SKIP_SYSCONS: + for conn in psutil.net_connections(kind=kind): + self.assertIn(conn.family, families) + self.assertIn(conn.type, types) + + with create_sockets(): + check('all', + [AF_INET, AF_INET6, AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) + check('inet', + [AF_INET, AF_INET6], + [SOCK_STREAM, SOCK_DGRAM]) + check('inet4', + [AF_INET], + [SOCK_STREAM, SOCK_DGRAM]) + check('tcp', + [AF_INET, AF_INET6], + [SOCK_STREAM]) + check('tcp4', + [AF_INET], + [SOCK_STREAM]) + check('tcp6', + [AF_INET6], + [SOCK_STREAM]) + check('udp', + [AF_INET, AF_INET6], + [SOCK_DGRAM]) + check('udp4', + [AF_INET], + [SOCK_DGRAM]) + check('udp6', + [AF_INET6], + [SOCK_DGRAM]) + if HAS_CONNECTIONS_UNIX: + check('unix', + [AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) + @skip_on_access_denied(only_if=MACOS) def test_combos(self): def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6") - check_connection_ntuple(conn) + self.check_connection_ntuple(conn) self.assertEqual(conn.family, family) self.assertEqual(conn.type, type) self.assertEqual(conn.laddr, laddr) @@ -284,7 +420,7 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): import socket, time s = socket.socket($family, socket.SOCK_STREAM) s.bind(('$addr', 0)) - s.listen(1) + s.listen(5) with open('$testfn', 'w') as f: f.write(str(s.getsockname()[:2])) time.sleep(60) @@ -352,13 +488,8 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): psutil.CONN_NONE, ("all", "inet", "inet6", "udp", "udp6")) - # err - self.assertRaises(ValueError, p.connections, kind='???') - - def test_multi_sockets_filtering(self): - with create_sockets() as socks: - cons = thisproc.connections(kind='all') - self.assertEqual(len(cons), len(socks)) + def test_count(self): + with create_sockets(): # tcp cons = thisproc.connections(kind='tcp') self.assertEqual(len(cons), 2 if supports_ipv6() else 1) @@ -406,8 +537,9 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): for conn in cons: self.assertEqual(conn.family, AF_INET6) self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) - # unix - if HAS_CONNECTIONS_UNIX: + # Skipped on BSD becayse by default the Python process + # creates a UNIX socket to '/var/run/log'. + if HAS_CONNECTIONS_UNIX and not (FREEBSD or NETBSD): cons = thisproc.connections(kind='unix') self.assertEqual(len(cons), 3) for conn in cons: @@ -415,23 +547,17 @@ class TestConnectedSocketPairs(Base, unittest.TestCase): self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) -# ===================================================================== -# --- Miscellaneous tests -# ===================================================================== - - +@unittest.skipIf(SKIP_SYSCONS, "requires root") class TestSystemWideConnections(Base, unittest.TestCase): """Tests for net_connections().""" - @skip_on_access_denied() def test_it(self): def check(cons, families, types_): - AF_UNIX = getattr(socket, 'AF_UNIX', object()) for conn in cons: self.assertIn(conn.family, families, msg=conn) if conn.family != AF_UNIX: self.assertIn(conn.type, types_, msg=conn) - check_connection_ntuple(conn) + self.check_connection_ntuple(conn) with create_sockets(): from psutil._common import conn_tmap @@ -444,16 +570,6 @@ class TestSystemWideConnections(Base, unittest.TestCase): self.assertEqual(len(cons), len(set(cons))) check(cons, families, types_) - self.assertRaises(ValueError, psutil.net_connections, kind='???') - - @skip_on_access_denied() - def test_multi_socks(self): - with create_sockets() as socks: - cons = [x for x in psutil.net_connections(kind='all') - if x.pid == os.getpid()] - self.assertEqual(len(cons), len(socks)) - - @skip_on_access_denied() # See: https://travis-ci.org/giampaolo/psutil/jobs/237566297 @unittest.skipIf(MACOS and TRAVIS, "unreliable on MACOS + TRAVIS") def test_multi_sockets_procs(self): @@ -495,11 +611,6 @@ class TestSystemWideConnections(Base, unittest.TestCase): self.assertEqual(len(p.connections('all')), expected) -# ===================================================================== -# --- Miscellaneous tests -# ===================================================================== - - class TestMisc(unittest.TestCase): def test_connection_constants(self): diff --git a/third_party/python/psutil/psutil/tests/test_contracts.py b/third_party/python/psutil/psutil/tests/test_contracts.py index 20da8241f337..312f17d9a39d 100755 --- a/third_party/python/psutil/psutil/tests/test_contracts.py +++ b/third_party/python/psutil/psutil/tests/test_contracts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -14,8 +14,6 @@ import os import stat import time import traceback -import warnings -from contextlib import closing from psutil import AIX from psutil import BSD @@ -29,20 +27,19 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._compat import long -from psutil.tests import bind_unix_socket -from psutil.tests import check_connection_ntuple +from psutil.tests import create_sockets +from psutil.tests import enum from psutil.tests import get_kernel_version -from psutil.tests import HAS_CONNECTIONS_UNIX +from psutil.tests import HAS_CPU_FREQ from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_RLIMIT from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import is_namedtuple from psutil.tests import safe_rmpath -from psutil.tests import skip_on_access_denied +from psutil.tests import SKIP_SYSCONS from psutil.tests import TESTFN from psutil.tests import unittest -from psutil.tests import unix_socket_path from psutil.tests import VALID_PROC_STATUSES from psutil.tests import warn import psutil @@ -52,19 +49,10 @@ import psutil # --- APIs availability # =================================================================== +# Make sure code reflects what doc promises in terms of APIs +# availability. -class TestAvailability(unittest.TestCase): - """Make sure code reflects what doc promises in terms of APIs - availability. - """ - - def test_cpu_affinity(self): - hasit = LINUX or WINDOWS or FREEBSD - self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), hasit) - - def test_win_service(self): - self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) - self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) +class TestAvailConstantsAPIs(unittest.TestCase): def test_PROCFS_PATH(self): self.assertEqual(hasattr(psutil, "PROCFS_PATH"), @@ -79,13 +67,20 @@ class TestAvailability(unittest.TestCase): ae(hasattr(psutil, "NORMAL_PRIORITY_CLASS"), WINDOWS) ae(hasattr(psutil, "REALTIME_PRIORITY_CLASS"), WINDOWS) - def test_linux_ioprio(self): + def test_linux_ioprio_linux(self): ae = self.assertEqual ae(hasattr(psutil, "IOPRIO_CLASS_NONE"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_RT"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_BE"), LINUX) ae(hasattr(psutil, "IOPRIO_CLASS_IDLE"), LINUX) + def test_linux_ioprio_windows(self): + ae = self.assertEqual + ae(hasattr(psutil, "IOPRIO_HIGH"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_NORMAL"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS) + ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS) + def test_linux_rlimit(self): ae = self.assertEqual hasit = LINUX and get_kernel_version() >= (2, 6, 36) @@ -110,6 +105,15 @@ class TestAvailability(unittest.TestCase): ae(hasattr(psutil, "RLIMIT_RTTIME"), hasit) ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit) + +class TestAvailSystemAPIs(unittest.TestCase): + + def test_win_service_iter(self): + self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) + + def test_win_service_get(self): + self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) + def test_cpu_freq(self): linux = (LINUX and (os.path.exists("/sys/devices/system/cpu/cpufreq") or @@ -128,71 +132,58 @@ class TestAvailability(unittest.TestCase): self.assertEqual(hasattr(psutil, "sensors_battery"), LINUX or WINDOWS or FREEBSD or MACOS) - def test_proc_environ(self): + +class TestAvailProcessAPIs(unittest.TestCase): + + def test_environ(self): self.assertEqual(hasattr(psutil.Process, "environ"), LINUX or MACOS or WINDOWS or AIX or SUNOS) - def test_proc_uids(self): + def test_uids(self): self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) - def test_proc_gids(self): + def test_gids(self): self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) - def test_proc_terminal(self): + def test_terminal(self): self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX) - def test_proc_ionice(self): + def test_ionice(self): self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS) - def test_proc_rlimit(self): + def test_rlimit(self): self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX) - def test_proc_io_counters(self): + def test_io_counters(self): hasit = hasattr(psutil.Process, "io_counters") self.assertEqual(hasit, False if MACOS or SUNOS else True) - def test_proc_num_fds(self): + def test_num_fds(self): self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX) - def test_proc_num_handles(self): + def test_num_handles(self): self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS) - def test_proc_cpu_affinity(self): + def test_cpu_affinity(self): self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), LINUX or WINDOWS or FREEBSD) - def test_proc_cpu_num(self): + def test_cpu_num(self): self.assertEqual(hasattr(psutil.Process, "cpu_num"), LINUX or FREEBSD or SUNOS) - def test_proc_memory_maps(self): + def test_memory_maps(self): hasit = hasattr(psutil.Process, "memory_maps") self.assertEqual( hasit, False if OPENBSD or NETBSD or AIX or MACOS else True) -# =================================================================== -# --- Test deprecations -# =================================================================== - - -class TestDeprecations(unittest.TestCase): - - def test_memory_info_ex(self): - with warnings.catch_warnings(record=True) as ws: - psutil.Process().memory_info_ex() - w = ws[0] - self.assertIsInstance(w.category(), DeprecationWarning) - self.assertIn("memory_info_ex() is deprecated", str(w.message)) - self.assertIn("use memory_info() instead", str(w.message)) - - # =================================================================== # --- System API types # =================================================================== -class TestSystem(unittest.TestCase): +class TestSystemAPITypes(unittest.TestCase): """Check the return types of system related APIs. Mainly we want to test we never return unicode on Python 2, see: https://github.com/giampaolo/psutil/issues/1039 @@ -205,18 +196,40 @@ class TestSystem(unittest.TestCase): def tearDown(self): safe_rmpath(TESTFN) - def test_cpu_times(self): - # Duplicate of test_system.py. Keep it anyway. - ret = psutil.cpu_times() - assert is_namedtuple(ret) - for n in ret: - self.assertIsInstance(n, float) - self.assertGreaterEqual(n, 0) + def assert_ntuple_of_nums(self, nt, type_=float, gezero=True): + assert is_namedtuple(nt) + for n in nt: + self.assertIsInstance(n, type_) + if gezero: + self.assertGreaterEqual(n, 0) - def test_io_counters(self): + def test_cpu_times(self): + self.assert_ntuple_of_nums(psutil.cpu_times()) + for nt in psutil.cpu_times(percpu=True): + self.assert_ntuple_of_nums(nt) + + def test_cpu_percent(self): + self.assertIsInstance(psutil.cpu_percent(interval=None), float) + self.assertIsInstance(psutil.cpu_percent(interval=0.00001), float) + + def test_cpu_times_percent(self): + self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None)) + self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001)) + + def test_cpu_count(self): + self.assertIsInstance(psutil.cpu_count(), int) + + @unittest.skipIf(not HAS_CPU_FREQ, "not supported") + def test_cpu_freq(self): + if psutil.cpu_freq() is None: + raise self.skipTest("cpu_freq() returns None") + self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int, long)) + + def test_disk_io_counters(self): # Duplicate of test_system.py. Keep it anyway. - for k in psutil.disk_io_counters(perdisk=True): + for k, v in psutil.disk_io_counters(perdisk=True).items(): self.assertIsInstance(k, str) + self.assert_ntuple_of_nums(v, type_=(int, long)) def test_disk_partitions(self): # Duplicate of test_system.py. Keep it anyway. @@ -226,30 +239,38 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(disk.fstype, str) self.assertIsInstance(disk.opts, str) - @unittest.skipIf(not POSIX, 'POSIX only') - @unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets") - @skip_on_access_denied(only_if=MACOS) + @unittest.skipIf(SKIP_SYSCONS, "requires root") def test_net_connections(self): - with unix_socket_path() as name: - with closing(bind_unix_socket(name)): - cons = psutil.net_connections(kind='unix') - assert cons - for conn in cons: - self.assertIsInstance(conn.laddr, str) + with create_sockets(): + ret = psutil.net_connections('all') + self.assertEqual(len(ret), len(set(ret))) + for conn in ret: + assert is_namedtuple(conn) def test_net_if_addrs(self): # Duplicate of test_system.py. Keep it anyway. for ifname, addrs in psutil.net_if_addrs().items(): self.assertIsInstance(ifname, str) for addr in addrs: + if enum is not None: + assert isinstance(addr.family, enum.IntEnum), addr + else: + assert isinstance(addr.family, int), addr self.assertIsInstance(addr.address, str) self.assertIsInstance(addr.netmask, (str, type(None))) self.assertIsInstance(addr.broadcast, (str, type(None))) def test_net_if_stats(self): # Duplicate of test_system.py. Keep it anyway. - for ifname, _ in psutil.net_if_stats().items(): + for ifname, info in psutil.net_if_stats().items(): self.assertIsInstance(ifname, str) + self.assertIsInstance(info.isup, bool) + if enum is not None: + self.assertIsInstance(info.duplex, enum.IntEnum) + else: + self.assertIsInstance(info.duplex, int) + self.assertIsInstance(info.speed, int) + self.assertIsInstance(info.mtu, int) @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): @@ -264,6 +285,7 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(name, str) for unit in units: self.assertIsInstance(unit.label, str) + self.assertIsInstance(unit.current, (float, int, type(None))) @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures(self): @@ -272,6 +294,13 @@ class TestSystem(unittest.TestCase): self.assertIsInstance(name, str) for unit in units: self.assertIsInstance(unit.label, str) + self.assertIsInstance(unit.current, (float, int, type(None))) + self.assertIsInstance(unit.high, (float, int, type(None))) + self.assertIsInstance(unit.critical, (float, int, type(None))) + + def test_boot_time(self): + # Duplicate of test_system.py. Keep it anyway. + self.assertIsInstance(psutil.boot_time(), float) def test_users(self): # Duplicate of test_system.py. Keep it anyway. @@ -385,6 +414,8 @@ class TestFetchAllProcesses(unittest.TestCase): if not ret: self.assertEqual(ret, '') else: + if WINDOWS and not ret.endswith('.exe'): + return # May be "Registry", "MemCompression", ... assert os.path.isabs(ret), ret # Note: os.stat() may return False even if the file is there # hence we skip the test, see: @@ -457,16 +488,20 @@ class TestFetchAllProcesses(unittest.TestCase): self.assertGreaterEqual(field, 0) def ionice(self, ret, proc): - if POSIX: - assert is_namedtuple(ret) - for field in ret: - self.assertIsInstance(field, int) if LINUX: + self.assertIsInstance(ret.ioclass, int) + self.assertIsInstance(ret.value, int) self.assertGreaterEqual(ret.ioclass, 0) self.assertGreaterEqual(ret.value, 0) - else: + else: # Windows, Cygwin + choices = [ + psutil.IOPRIO_VERYLOW, + psutil.IOPRIO_LOW, + psutil.IOPRIO_NORMAL, + psutil.IOPRIO_HIGH] + self.assertIsInstance(ret, int) self.assertGreaterEqual(ret, 0) - self.assertIn(ret, (0, 1, 2)) + self.assertIn(ret, choices) def num_threads(self, ret, proc): self.assertIsInstance(ret, int) @@ -507,13 +542,7 @@ class TestFetchAllProcesses(unittest.TestCase): for value in ret: self.assertIsInstance(value, (int, long)) self.assertGreaterEqual(value, 0) - if POSIX and not AIX and ret.vms != 0: - # VMS is always supposed to be the highest - for name in ret._fields: - if name != 'vms': - value = getattr(ret, name) - self.assertGreater(ret.vms, value, msg=ret) - elif WINDOWS: + if WINDOWS: self.assertGreaterEqual(ret.peak_wset, ret.wset) self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool) self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool) @@ -560,9 +589,10 @@ class TestFetchAllProcesses(unittest.TestCase): self.assertGreaterEqual(ret, 0) def connections(self, ret, proc): - self.assertEqual(len(ret), len(set(ret))) - for conn in ret: - check_connection_ntuple(conn) + with create_sockets(): + self.assertEqual(len(ret), len(set(ret))) + for conn in ret: + assert is_namedtuple(conn) def cwd(self, ret, proc): if ret: # 'ret' can be None or empty diff --git a/third_party/python/psutil/psutil/tests/test_linux.py b/third_party/python/psutil/psutil/tests/test_linux.py index d732e90d7e63..e51f8bd573ba 100755 --- a/third_party/python/psutil/psutil/tests/test_linux.py +++ b/third_party/python/psutil/psutil/tests/test_linux.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -10,6 +10,7 @@ from __future__ import division import collections import contextlib import errno +import glob import io import os import re @@ -24,6 +25,7 @@ import warnings import psutil from psutil import LINUX from psutil._compat import basestring +from psutil._compat import FileNotFoundError from psutil._compat import PY3 from psutil._compat import u from psutil.tests import call_until @@ -54,7 +56,7 @@ SIOCGIFCONF = 0x8912 SIOCGIFHWADDR = 0x8927 if LINUX: SECTOR_SIZE = 512 - +EMPTY_TEMPERATURES = not glob.glob('/sys/class/hwmon/hwmon*') # ===================================================================== # --- utils @@ -311,9 +313,10 @@ class TestSystemVirtualMemory(unittest.TestCase): self.assertEqual(ret.available, 0) self.assertEqual(ret.slab, 0) + @retry_on_failure() def test_avail_old_percent(self): # Make sure that our calculation of avail mem for old kernels - # is off by max 10%. + # is off by max 15%. from psutil._pslinux import calculate_avail_vmem from psutil._pslinux import open_binary @@ -327,7 +330,7 @@ class TestSystemVirtualMemory(unittest.TestCase): if b'MemAvailable:' in mems: b = mems[b'MemAvailable:'] diff_percent = abs(a - b) / a * 100 - self.assertLess(diff_percent, 10) + self.assertLess(diff_percent, 15) def test_avail_old_comes_from_kernel(self): # Make sure "MemAvailable:" coluimn is used instead of relying @@ -704,15 +707,12 @@ class TestSystemCPUFrequency(unittest.TestCase): if path.startswith("/sys/devices/system/cpu/cpufreq/policy"): return False else: - flags.append(None) return orig_exists(path) - flags = [] orig_exists = os.path.exists with mock.patch("os.path.exists", side_effect=path_exists_mock, create=True): assert psutil.cpu_freq() - self.assertEqual(len(flags), psutil.cpu_count(logical=True)) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_use_cpuinfo(self): @@ -843,25 +843,6 @@ class TestSystemCPUFrequency(unittest.TestCase): freq = psutil.cpu_freq() self.assertEqual(freq.current, 200) - # Also test that NotImplementedError is raised in case no - # current freq file is present. - - def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): - raise IOError(errno.ENOENT, "") - elif name.endswith('/cpuinfo_cur_freq'): - raise IOError(errno.ENOENT, "") - elif name == '/proc/cpuinfo': - raise IOError(errno.ENOENT, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('os.path.exists', return_value=True): - self.assertRaises(NotImplementedError, psutil.cpu_freq) - @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUStats(unittest.TestCase): @@ -1080,10 +1061,9 @@ class TestSystemDiskPartitions(unittest.TestCase): try: with mock.patch('os.path.realpath', return_value='/non/existent') as m: - with self.assertRaises(OSError) as cm: + with self.assertRaises(FileNotFoundError): psutil.disk_partitions() assert m.called - self.assertEqual(cm.exception.errno, errno.ENOENT) finally: psutil.PROCFS_PATH = "/proc" @@ -1568,24 +1548,6 @@ class TestSensorsBattery(unittest.TestCase): @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsTemperatures(unittest.TestCase): - @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") - def test_emulate_eio_error(self): - def open_mock(name, *args, **kwargs): - if name.endswith("_input"): - raise OSError(errno.EIO, "") - elif name.endswith("temp"): - raise OSError(errno.EIO, "") - else: - return orig_open(name, *args, **kwargs) - - orig_open = open - patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, side_effect=open_mock) as m: - with warnings.catch_warnings(record=True) as ws: - self.assertEqual(psutil.sensors_temperatures(), {}) - assert m.called - self.assertIn("ignoring", str(ws[0].message)) - def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): @@ -1636,6 +1598,7 @@ class TestSensorsTemperatures(unittest.TestCase): elif path == '/sys/class/thermal/thermal_zone0/trip_point*': return ['/sys/class/thermal/thermal_zone1/trip_point_0_type', '/sys/class/thermal/thermal_zone1/trip_point_0_temp'] + return [] orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' @@ -1857,6 +1820,16 @@ class TestProcess(unittest.TestCase): self.assertEqual(p.cmdline(), ['foo', 'bar', '']) assert m.called + def test_cmdline_mixed_separators(self): + # https://github.com/giampaolo/psutil/issues/ + # 1179#issuecomment-552984549 + p = psutil.Process() + fake_file = io.StringIO(u('foo\x20bar\x00')) + with mock.patch('psutil._common.open', + return_value=fake_file, create=True) as m: + self.assertEqual(p.cmdline(), ['foo', 'bar']) + assert m.called + def test_readlink_path_deleted_mocked(self): with mock.patch('psutil._pslinux.os.readlink', return_value='/home/foo (deleted)'): @@ -1920,9 +1893,8 @@ class TestProcess(unittest.TestCase): '/proc/%s/smaps' % os.getpid(), IOError(errno.ENOENT, "")) as m: p = psutil.Process() - with self.assertRaises(IOError) as err: + with self.assertRaises(FileNotFoundError): p.memory_maps() - self.assertEqual(err.exception.errno, errno.ENOENT) assert m.called @unittest.skipIf(not HAS_RLIMIT, "not supported") @@ -1994,6 +1966,9 @@ class TestProcess(unittest.TestCase): "0", # cnswap "0", # exit_signal "6", # processor + "0", # rt priority + "0", # policy + "7", # delayacct_blkio_ticks ] content = " ".join(args).encode() with mock_open_content('/proc/%s/stat' % os.getpid(), content): @@ -2008,6 +1983,7 @@ class TestProcess(unittest.TestCase): self.assertEqual(cpu.system, 3 / CLOCK_TICKS) self.assertEqual(cpu.children_user, 4 / CLOCK_TICKS) self.assertEqual(cpu.children_system, 5 / CLOCK_TICKS) + self.assertEqual(cpu.iowait, 7 / CLOCK_TICKS) self.assertEqual(p.cpu_num(), 6) def test_status_file_parsing(self): diff --git a/third_party/python/psutil/psutil/tests/test_memory_leaks.py b/third_party/python/psutil/psutil/tests/test_memory_leaks.py index 543dbf7100f5..f9cad70fd33e 100755 --- a/third_party/python/psutil/psutil/tests/test_memory_leaks.py +++ b/third_party/python/psutil/psutil/tests/test_memory_leaks.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,10 +11,11 @@ checking whether process memory usage keeps increasing between calls or over time. Note that this may produce false positives (especially on Windows for some reason). +PyPy appears to be completely unstable for this framework, probably +because of how its JIT handles memory, so tests are skipped. """ from __future__ import print_function -import errno import functools import gc import os @@ -24,6 +25,7 @@ import time import psutil import psutil._common +from psutil import FREEBSD from psutil import LINUX from psutil import MACOS from psutil import OPENBSD @@ -31,12 +33,13 @@ from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS from psutil._common import bytes2human +from psutil._compat import ProcessLookupError from psutil._compat import xrange +from psutil.tests import CIRRUS from psutil.tests import create_sockets from psutil.tests import get_test_subprocess from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_CPU_FREQ -from psutil.tests import HAS_GETLOADAVG from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE from psutil.tests import HAS_MEMORY_MAPS @@ -47,6 +50,7 @@ from psutil.tests import HAS_RLIMIT from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES +from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import safe_rmpath from psutil.tests import skip_on_access_denied @@ -55,14 +59,14 @@ from psutil.tests import TRAVIS from psutil.tests import unittest +# configurable opts LOOPS = 1000 MEMORY_TOLERANCE = 4096 RETRY_FOR = 3 +SKIP_PYTHON_IMPL = True -SKIP_PYTHON_IMPL = True if TRAVIS else False cext = psutil._psplatform.cext thisproc = psutil.Process() -SKIP_PYTHON_IMPL = True if TRAVIS else False # =================================================================== @@ -75,6 +79,7 @@ def skip_if_linux(): "worthless on LINUX (pure python)") +@unittest.skipIf(PYPY, "unreliable on PYPY") class TestMemLeak(unittest.TestCase): """Base framework class which calls a function many times and produces a failure if process memory usage keeps increasing @@ -365,14 +370,14 @@ class TestProcessObjectLeaks(TestMemLeak): self.execute(cext.proc_info, os.getpid()) +@unittest.skipIf(not WINDOWS, "WINDOWS only") class TestProcessDualImplementation(TestMemLeak): - if WINDOWS: - def test_cmdline_peb_true(self): - self.execute(cext.proc_cmdline, os.getpid(), use_peb=True) + def test_cmdline_peb_true(self): + self.execute(cext.proc_cmdline, os.getpid(), use_peb=True) - def test_cmdline_peb_false(self): - self.execute(cext.proc_cmdline, os.getpid(), use_peb=False) + def test_cmdline_peb_false(self): + self.execute(cext.proc_cmdline, os.getpid(), use_peb=False) class TestTerminatedProcessLeaks(TestProcessObjectLeaks): @@ -423,9 +428,8 @@ class TestTerminatedProcessLeaks(TestProcessObjectLeaks): def call(): try: return cext.proc_info(self.proc.pid) - except OSError as err: - if err.errno != errno.ESRCH: - raise + except ProcessLookupError: + pass self.execute(call) @@ -476,7 +480,7 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_cpu_freq(self): self.execute(psutil.cpu_freq) - @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + @unittest.skipIf(not WINDOWS, "WINDOWS only") def test_getloadavg(self): self.execute(psutil.getloadavg) @@ -486,8 +490,7 @@ class TestModuleFunctionsLeaks(TestMemLeak): self.execute(psutil.virtual_memory) # TODO: remove this skip when this gets fixed - @unittest.skipIf(SUNOS, - "worthless on SUNOS (uses a subprocess)") + @unittest.skipIf(SUNOS, "worthless on SUNOS (uses a subprocess)") def test_swap_memory(self): self.execute(psutil.swap_memory) @@ -520,14 +523,14 @@ class TestModuleFunctionsLeaks(TestMemLeak): # --- net - @unittest.skipIf(TRAVIS and MACOS, "false positive on travis") + @unittest.skipIf(TRAVIS and MACOS, "false positive on TRAVIS + MACOS") + @unittest.skipIf(CIRRUS and FREEBSD, "false positive on CIRRUS + FREEBSD") @skip_if_linux() @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): self.execute(psutil.net_io_counters, nowrap=False) - @unittest.skipIf(LINUX, - "worthless on Linux (pure python)") + @skip_if_linux() @unittest.skipIf(MACOS and os.getuid() != 0, "need root access") def test_net_connections(self): with create_sockets(): @@ -565,7 +568,6 @@ class TestModuleFunctionsLeaks(TestMemLeak): def test_boot_time(self): self.execute(psutil.boot_time) - # XXX - on Windows this produces a false positive @unittest.skipIf(WINDOWS, "XXX produces a false positive on Windows") def test_users(self): self.execute(psutil.users) diff --git a/third_party/python/psutil/psutil/tests/test_misc.py b/third_party/python/psutil/psutil/tests/test_misc.py index 29e1e41e600d..c20cd9413b6a 100755 --- a/third_party/python/psutil/psutil/tests/test_misc.py +++ b/third_party/python/psutil/psutil/tests/test_misc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -19,7 +19,9 @@ import pickle import socket import stat +from psutil import FREEBSD from psutil import LINUX +from psutil import NETBSD from psutil import POSIX from psutil import WINDOWS from psutil._common import memoize @@ -34,6 +36,7 @@ from psutil.tests import bind_socket from psutil.tests import bind_unix_socket from psutil.tests import call_until from psutil.tests import chdir +from psutil.tests import CI_TESTING from psutil.tests import create_proc_children_pair from psutil.tests import create_sockets from psutil.tests import create_zombie_proc @@ -179,7 +182,8 @@ class TestMisc(unittest.TestCase): for name in dir_psutil: if name in ('callable', 'error', 'namedtuple', 'tests', 'long', 'test', 'NUM_CPUS', 'BOOT_TIME', - 'TOTAL_PHYMEM'): + 'TOTAL_PHYMEM', 'PermissionError', + 'ProcessLookupError'): continue if not name.startswith('_'): try: @@ -710,9 +714,7 @@ class TestScripts(unittest.TestCase): def test_procinfo(self): self.assert_stdout('procinfo.py', str(os.getpid())) - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") + @unittest.skipIf(CI_TESTING and not psutil.users(), "no users") def test_who(self): self.assert_stdout('who.py') @@ -765,11 +767,15 @@ class TestScripts(unittest.TestCase): @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") def test_temperatures(self): + if not psutil.sensors_temperatures(): + self.skipTest("no temperatures") self.assert_stdout('temperatures.py') @unittest.skipIf(not HAS_SENSORS_FANS, "not supported") @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") def test_fans(self): + if not psutil.sensors_fans(): + self.skipTest("no fans") self.assert_stdout('fans.py') @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported") @@ -1011,6 +1017,8 @@ class TestNetUtils(unittest.TestCase): self.assertNotEqual(client.getsockname(), addr) @unittest.skipIf(not POSIX, "POSIX only") + @unittest.skipIf(NETBSD or FREEBSD, + "/var/run/log UNIX socket opened by default") def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() diff --git a/third_party/python/psutil/psutil/tests/test_osx.py b/third_party/python/psutil/psutil/tests/test_osx.py index 723b255eea1f..e4e77f93537e 100755 --- a/third_party/python/psutil/psutil/tests/test_osx.py +++ b/third_party/python/psutil/psutil/tests/test_osx.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/psutil/tests/test_posix.py b/third_party/python/psutil/psutil/tests/test_posix.py index 348366ad7b99..a96b310ffe7f 100755 --- a/third_party/python/psutil/psutil/tests/test_posix.py +++ b/third_party/python/psutil/psutil/tests/test_posix.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -22,7 +22,7 @@ from psutil import MACOS from psutil import OPENBSD from psutil import POSIX from psutil import SUNOS -from psutil.tests import APPVEYOR +from psutil.tests import CI_TESTING from psutil.tests import get_kernel_version from psutil.tests import get_test_subprocess from psutil.tests import HAS_NET_IO_COUNTERS @@ -58,8 +58,7 @@ def ps(fmt, pid=None): cmd.append('ax') if SUNOS: - fmt_map = {'command', 'comm', - 'start', 'stime'} + fmt_map = set(('command', 'comm', 'start', 'stime')) fmt = fmt_map.get(fmt, fmt) cmd.extend(['-o', fmt]) @@ -365,13 +364,13 @@ class TestSystemAPIs(unittest.TestCase): "couldn't find %s nic in 'ifconfig -a' output\n%s" % ( nic, output)) - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") + @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") @retry_on_failure() def test_users(self): out = sh("who") lines = out.split('\n') + if not lines: + raise self.skipTest("no users on this system") users = [x.split()[0] for x in lines] terminals = [x.split()[1] for x in lines] self.assertEqual(len(users), len(psutil.users())) diff --git a/third_party/python/psutil/psutil/tests/test_process.py b/third_party/python/psutil/psutil/tests/test_process.py index 512a84371fe7..987bdf38bb69 100755 --- a/third_party/python/psutil/psutil/tests/test_process.py +++ b/third_party/python/psutil/psutil/tests/test_process.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -37,6 +37,7 @@ from psutil._compat import long from psutil._compat import PY3 from psutil.tests import APPVEYOR from psutil.tests import call_until +from psutil.tests import CIRRUS from psutil.tests import copyload_shared_lib from psutil.tests import create_exe from psutil.tests import create_proc_children_pair @@ -251,6 +252,8 @@ class TestProcess(unittest.TestCase): assert (times.user > 0.0) or (times.system > 0.0), times assert (times.children_user >= 0.0), times assert (times.children_system >= 0.0), times + if LINUX: + assert times.iowait >= 0.0, times # make sure returned values can be pretty printed with strftime for name in times._fields: time.strftime("%H:%M:%S", time.localtime(getattr(times, name))) @@ -295,7 +298,7 @@ class TestProcess(unittest.TestCase): time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time())) @unittest.skipIf(not POSIX, 'POSIX only') - @unittest.skipIf(TRAVIS, 'not reliable on TRAVIS') + @unittest.skipIf(TRAVIS or CIRRUS, 'not reliable on TRAVIS/CIRRUS') def test_terminal(self): terminal = psutil.Process().terminal() if sys.stdin.isatty() or sys.stdout.isatty(): @@ -308,7 +311,6 @@ class TestProcess(unittest.TestCase): @skip_on_not_implemented(only_if=LINUX) def test_io_counters(self): p = psutil.Process() - # test reads io1 = p.io_counters() with open(PYTHON_EXE, 'rb') as f: @@ -724,6 +726,7 @@ class TestProcess(unittest.TestCase): else: raise + @unittest.skipIf(PYPY, "broken on PYPY") def test_long_cmdline(self): create_exe(TESTFN) self.addCleanup(safe_rmpath, TESTFN) @@ -738,6 +741,7 @@ class TestProcess(unittest.TestCase): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) + @unittest.skipIf(PYPY, "unreliable on PYPY") def test_long_name(self): long_name = TESTFN + ("0123456789" * 2) create_exe(long_name) @@ -749,6 +753,7 @@ class TestProcess(unittest.TestCase): # XXX @unittest.skipIf(SUNOS, "broken on SUNOS") @unittest.skipIf(AIX, "broken on AIX") + @unittest.skipIf(PYPY, "broken on PYPY") def test_prog_w_funky_name(self): # Test that name(), exe() and cmdline() correctly handle programs # with funky chars such as spaces and ")", see: @@ -937,6 +942,8 @@ class TestProcess(unittest.TestCase): self.addCleanup(p.cpu_affinity, initial) # All possible CPU set combinations. + if len(initial) > 12: + initial = initial[:12] # ...otherwise it will take forever combos = [] for l in range(0, len(initial) + 1): for subset in itertools.combinations(initial, l): @@ -961,13 +968,12 @@ class TestProcess(unittest.TestCase): f.flush() # give the kernel some time to see the new file files = call_until(p.open_files, "len(ret) != %i" % len(files)) - for file in files: - if file.path == TESTFN: - if LINUX: + filenames = [os.path.normcase(x.path) for x in files] + self.assertIn(os.path.normcase(TESTFN), filenames) + if LINUX: + for file in files: + if file.path == TESTFN: self.assertEqual(file.position, 1024) - break - else: - self.fail("no file found; files=%s" % repr(files)) for file in files: assert os.path.isfile(file.path), file @@ -977,12 +983,12 @@ class TestProcess(unittest.TestCase): p = psutil.Process(sproc.pid) for x in range(100): - filenames = [x.path for x in p.open_files()] + filenames = [os.path.normcase(x.path) for x in p.open_files()] if TESTFN in filenames: break time.sleep(.01) else: - self.assertIn(TESTFN, filenames) + self.assertIn(os.path.normcase(TESTFN), filenames) for file in filenames: assert os.path.isfile(file), file @@ -992,14 +998,16 @@ class TestProcess(unittest.TestCase): @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") def test_open_files_2(self): # test fd and path fields + normcase = os.path.normcase with open(TESTFN, 'w') as fileobj: p = psutil.Process() for file in p.open_files(): - if file.path == fileobj.name or file.fd == fileobj.fileno(): + if normcase(file.path) == normcase(fileobj.name) or \ + file.fd == fileobj.fileno(): break else: self.fail("no file found; files=%s" % repr(p.open_files())) - self.assertEqual(file.path, fileobj.name) + self.assertEqual(normcase(file.path), normcase(fileobj.name)) if WINDOWS: self.assertEqual(file.fd, -1) else: @@ -1078,16 +1086,13 @@ class TestProcess(unittest.TestCase): side_effect=psutil.NoSuchProcess(0, 'foo')): self.assertIsNone(p.parent()) + @retry_on_failure() def test_parents(self): assert psutil.Process().parents() p1, p2 = create_proc_children_pair() self.assertEqual(p1.parents()[0], psutil.Process()) self.assertEqual(p2.parents()[0], p1) self.assertEqual(p2.parents()[1], psutil.Process()) - if POSIX: - lowest_pid = psutil.pids()[0] - self.assertEqual(p1.parents()[-1].pid, lowest_pid) - self.assertEqual(p2.parents()[-1].pid, lowest_pid) def test_children(self): reap_children(recursive=True) @@ -1322,6 +1327,16 @@ class TestProcess(unittest.TestCase): except NotImplementedError: pass else: + # NtQuerySystemInformation succeeds if process is gone. + if WINDOWS and name in ('exe', 'name'): + normcase = os.path.normcase + if name == 'exe': + self.assertEqual(normcase(ret), normcase(PYTHON_EXE)) + else: + self.assertEqual( + normcase(ret), + normcase(os.path.basename(PYTHON_EXE))) + continue self.fail( "NoSuchProcess exception not raised for %r, retval=%s" % ( name, ret)) diff --git a/third_party/python/psutil/psutil/tests/test_sunos.py b/third_party/python/psutil/psutil/tests/test_sunos.py index 94405d41be9b..e3beb625bf53 100755 --- a/third_party/python/psutil/psutil/tests/test_sunos.py +++ b/third_party/python/psutil/psutil/tests/test_sunos.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/psutil/tests/test_system.py b/third_party/python/psutil/psutil/tests/test_system.py index eb9016f08af1..3834209fcb36 100755 --- a/third_party/python/psutil/psutil/tests/test_system.py +++ b/third_party/python/psutil/psutil/tests/test_system.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -29,10 +29,11 @@ from psutil import OPENBSD from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS +from psutil._compat import FileNotFoundError from psutil._compat import long -from psutil.tests import APPVEYOR from psutil.tests import ASCII_FS from psutil.tests import check_net_address +from psutil.tests import CI_TESTING from psutil.tests import DEVNULL from psutil.tests import enum from psutil.tests import get_test_subprocess @@ -44,6 +45,7 @@ from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import mock +from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import retry_on_failure from psutil.tests import safe_rmpath @@ -58,8 +60,7 @@ from psutil.tests import unittest # =================================================================== -class TestSystemAPIs(unittest.TestCase): - """Tests for system-related APIs.""" +class TestProcessAPIs(unittest.TestCase): def setUp(self): safe_rmpath(TESTFN) @@ -84,7 +85,7 @@ class TestSystemAPIs(unittest.TestCase): with self.assertRaises(psutil.AccessDenied): list(psutil.process_iter()) - def test_prcess_iter_w_params(self): + def test_prcess_iter_w_attrs(self): for p in psutil.process_iter(attrs=['pid']): self.assertEqual(list(p.info.keys()), ['pid']) with self.assertRaises(ValueError): @@ -104,6 +105,8 @@ class TestSystemAPIs(unittest.TestCase): self.assertGreaterEqual(p.info['pid'], 0) assert m.called + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs(self): def callback(p): pids.append(p.pid) @@ -159,6 +162,8 @@ class TestSystemAPIs(unittest.TestCase): for p in gone: self.assertTrue(hasattr(p, 'returncode')) + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs_no_timeout(self): sproc1 = get_test_subprocess() sproc2 = get_test_subprocess() @@ -168,12 +173,67 @@ class TestSystemAPIs(unittest.TestCase): p.terminate() gone, alive = psutil.wait_procs(procs) + def test_pid_exists(self): + sproc = get_test_subprocess() + self.assertTrue(psutil.pid_exists(sproc.pid)) + p = psutil.Process(sproc.pid) + p.kill() + p.wait() + self.assertFalse(psutil.pid_exists(sproc.pid)) + self.assertFalse(psutil.pid_exists(-1)) + self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) + + def test_pid_exists_2(self): + reap_children() + pids = psutil.pids() + for pid in pids: + try: + assert psutil.pid_exists(pid) + except AssertionError: + # in case the process disappeared in meantime fail only + # if it is no longer in psutil.pids() + time.sleep(.1) + if pid in psutil.pids(): + self.fail(pid) + pids = range(max(pids) + 5000, max(pids) + 6000) + for pid in pids: + self.assertFalse(psutil.pid_exists(pid), msg=pid) + + def test_pids(self): + pidslist = psutil.pids() + procslist = [x.pid for x in psutil.process_iter()] + # make sure every pid is unique + self.assertEqual(sorted(set(pidslist)), pidslist) + self.assertEqual(pidslist, procslist) + + +class TestMiscAPIs(unittest.TestCase): + def test_boot_time(self): bt = psutil.boot_time() self.assertIsInstance(bt, float) self.assertGreater(bt, 0) self.assertLess(bt, time.time()) + @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") + def test_users(self): + users = psutil.users() + self.assertNotEqual(users, []) + for user in users: + assert user.name, user + self.assertIsInstance(user.name, str) + self.assertIsInstance(user.terminal, (str, type(None))) + if user.host is not None: + self.assertIsInstance(user.host, (str, type(None))) + user.terminal + user.host + assert user.started > 0.0, user + datetime.datetime.fromtimestamp(user.started) + if WINDOWS or OPENBSD: + self.assertIsNone(user.pid) + else: + psutil.Process(user.pid) + @unittest.skipIf(not POSIX, 'POSIX only') def test_PAGESIZE(self): # pagesize is used internally to perform different calculations @@ -182,6 +242,55 @@ class TestSystemAPIs(unittest.TestCase): import resource self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize()) + def test_test(self): + # test for psutil.test() function + stdout = sys.stdout + sys.stdout = DEVNULL + try: + psutil.test() + finally: + sys.stdout = stdout + + def test_os_constants(self): + names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD", + "NETBSD", "BSD", "SUNOS"] + for name in names: + self.assertIsInstance(getattr(psutil, name), bool, msg=name) + + if os.name == 'posix': + assert psutil.POSIX + assert not psutil.WINDOWS + names.remove("POSIX") + if "linux" in sys.platform.lower(): + assert psutil.LINUX + names.remove("LINUX") + elif "bsd" in sys.platform.lower(): + assert psutil.BSD + self.assertEqual([psutil.FREEBSD, psutil.OPENBSD, + psutil.NETBSD].count(True), 1) + names.remove("BSD") + names.remove("FREEBSD") + names.remove("OPENBSD") + names.remove("NETBSD") + elif "sunos" in sys.platform.lower() or \ + "solaris" in sys.platform.lower(): + assert psutil.SUNOS + names.remove("SUNOS") + elif "darwin" in sys.platform.lower(): + assert psutil.MACOS + names.remove("MACOS") + else: + assert psutil.WINDOWS + assert not psutil.POSIX + names.remove("WINDOWS") + + # assert all other constants are set to False + for name in names: + self.assertIs(getattr(psutil, name), False, msg=name) + + +class TestMemoryAPIs(unittest.TestCase): + def test_virtual_memory(self): mem = psutil.virtual_memory() assert mem.total > 0, mem @@ -216,50 +325,12 @@ class TestSystemAPIs(unittest.TestCase): assert mem.sin >= 0, mem assert mem.sout >= 0, mem - def test_pid_exists(self): - sproc = get_test_subprocess() - self.assertTrue(psutil.pid_exists(sproc.pid)) - p = psutil.Process(sproc.pid) - p.kill() - p.wait() - self.assertFalse(psutil.pid_exists(sproc.pid)) - self.assertFalse(psutil.pid_exists(-1)) - self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) - def test_pid_exists_2(self): - reap_children() - pids = psutil.pids() - for pid in pids: - try: - assert psutil.pid_exists(pid) - except AssertionError: - # in case the process disappeared in meantime fail only - # if it is no longer in psutil.pids() - time.sleep(.1) - if pid in psutil.pids(): - self.fail(pid) - pids = range(max(pids) + 5000, max(pids) + 6000) - for pid in pids: - self.assertFalse(psutil.pid_exists(pid), msg=pid) +class TestCpuAPIs(unittest.TestCase): - def test_pids(self): - pidslist = psutil.pids() - procslist = [x.pid for x in psutil.process_iter()] - # make sure every pid is unique - self.assertEqual(sorted(set(pidslist)), pidslist) - self.assertEqual(pidslist, procslist) - - def test_test(self): - # test for psutil.test() function - stdout = sys.stdout - sys.stdout = DEVNULL - try: - psutil.test() - finally: - sys.stdout = stdout - - def test_cpu_count(self): + def test_cpu_count_logical(self): logical = psutil.cpu_count() + self.assertIsNotNone(logical) self.assertEqual(logical, len(psutil.cpu_times(percpu=True))) self.assertGreaterEqual(logical, 1) # @@ -268,7 +339,12 @@ class TestSystemAPIs(unittest.TestCase): cpuinfo_data = fd.read() if "physical id" not in cpuinfo_data: raise unittest.SkipTest("cpuinfo doesn't include physical id") + + def test_cpu_count_physical(self): + logical = psutil.cpu_count() physical = psutil.cpu_count(logical=False) + if physical is None: + raise self.skipTest("physical cpu_count() is None") if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista self.assertIsNone(physical) else: @@ -363,17 +439,16 @@ class TestSystemAPIs(unittest.TestCase): # Simulate some work load then make sure time have increased # between calls. tot1 = psutil.cpu_times(percpu=True) - stop_at = time.time() + 0.1 + giveup_at = time.time() + 1 while True: - if time.time() >= stop_at: - break - tot2 = psutil.cpu_times(percpu=True) - for t1, t2 in zip(tot1, tot2): - t1, t2 = sum(t1), sum(t2) - difference = t2 - t1 - if difference >= 0.05: - return - self.fail() + if time.time() >= giveup_at: + return self.fail("timeout") + tot2 = psutil.cpu_times(percpu=True) + for t1, t2 in zip(tot1, tot2): + t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2) + difference = t2 - t1 + if difference >= 0.05: + return def test_cpu_times_comparison(self): # Make sure the sum of all per cpu times is almost equal to @@ -445,6 +520,55 @@ class TestSystemAPIs(unittest.TestCase): for percent in cpu: self._test_cpu_percent(percent, None, None) + def test_cpu_stats(self): + # Tested more extensively in per-platform test modules. + infos = psutil.cpu_stats() + self.assertEqual( + infos._fields, + ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls')) + for name in infos._fields: + value = getattr(infos, name) + self.assertGreaterEqual(value, 0) + # on AIX, ctx_switches is always 0 + if not AIX and name in ('ctx_switches', 'interrupts'): + self.assertGreater(value, 0) + + @unittest.skipIf(not HAS_CPU_FREQ, "not suported") + def test_cpu_freq(self): + def check_ls(ls): + for nt in ls: + self.assertEqual(nt._fields, ('current', 'min', 'max')) + if nt.max != 0.0: + self.assertLessEqual(nt.current, nt.max) + for name in nt._fields: + value = getattr(nt, name) + self.assertIsInstance(value, (int, long, float)) + self.assertGreaterEqual(value, 0) + + ls = psutil.cpu_freq(percpu=True) + if TRAVIS and not ls: + raise self.skipTest("skipped on Travis") + if FREEBSD and not ls: + raise self.skipTest("returns empty list on FreeBSD") + + assert ls, ls + check_ls([psutil.cpu_freq(percpu=False)]) + + if LINUX: + self.assertEqual(len(ls), psutil.cpu_count()) + + @unittest.skipIf(not HAS_GETLOADAVG, "not supported") + def test_getloadavg(self): + loadavg = psutil.getloadavg() + assert len(loadavg) == 3 + + for load in loadavg: + self.assertIsInstance(load, float) + self.assertGreaterEqual(load, 0.0) + + +class TestDiskAPIs(unittest.TestCase): + def test_disk_usage(self): usage = psutil.disk_usage(os.getcwd()) self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent')) @@ -468,9 +592,8 @@ class TestSystemAPIs(unittest.TestCase): # if path does not exist OSError ENOENT is expected across # all platforms fname = tempfile.mktemp() - with self.assertRaises(OSError) as exc: + with self.assertRaises(FileNotFoundError): psutil.disk_usage(fname) - self.assertEqual(exc.exception.errno, errno.ENOENT) def test_disk_usage_unicode(self): # See: https://github.com/giampaolo/psutil/issues/416 @@ -501,18 +624,15 @@ class TestSystemAPIs(unittest.TestCase): # we cannot make any assumption about this, see: # http://goo.gl/p9c43 disk.device - if SUNOS or TRAVIS: - # on solaris apparently mount points can also be files - assert os.path.exists(disk.mountpoint), disk - else: - assert os.path.isdir(disk.mountpoint), disk + # on modern systems mount points can also be files + assert os.path.exists(disk.mountpoint), disk assert disk.fstype, disk # all = True ls = psutil.disk_partitions(all=True) self.assertTrue(ls, msg=ls) for disk in psutil.disk_partitions(all=True): - if not WINDOWS: + if not WINDOWS and disk.mountpoint: try: os.stat(disk.mountpoint) except OSError as err: @@ -535,10 +655,54 @@ class TestSystemAPIs(unittest.TestCase): mount = find_mount_point(__file__) mounts = [x.mountpoint.lower() for x in - psutil.disk_partitions(all=True)] + psutil.disk_partitions(all=True) if x.mountpoint] self.assertIn(mount, mounts) psutil.disk_usage(mount) + @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), + '/proc/diskstats not available on this linux version') + @unittest.skipIf(CI_TESTING and not psutil.disk_io_counters(), + "unreliable on CI") # no visible disks + def test_disk_io_counters(self): + def check_ntuple(nt): + self.assertEqual(nt[0], nt.read_count) + self.assertEqual(nt[1], nt.write_count) + self.assertEqual(nt[2], nt.read_bytes) + self.assertEqual(nt[3], nt.write_bytes) + if not (OPENBSD or NETBSD): + self.assertEqual(nt[4], nt.read_time) + self.assertEqual(nt[5], nt.write_time) + if LINUX: + self.assertEqual(nt[6], nt.read_merged_count) + self.assertEqual(nt[7], nt.write_merged_count) + self.assertEqual(nt[8], nt.busy_time) + elif FREEBSD: + self.assertEqual(nt[6], nt.busy_time) + for name in nt._fields: + assert getattr(nt, name) >= 0, nt + + ret = psutil.disk_io_counters(perdisk=False) + assert ret is not None, "no disks on this system?" + check_ntuple(ret) + ret = psutil.disk_io_counters(perdisk=True) + # make sure there are no duplicates + self.assertEqual(len(ret), len(set(ret))) + for key in ret: + assert key, key + check_ntuple(ret[key]) + + def test_disk_io_counters_no_disks(self): + # Emulate a case where no disks are installed, see: + # https://github.com/giampaolo/psutil/issues/1062 + with mock.patch('psutil._psplatform.disk_io_counters', + return_value={}) as m: + self.assertIsNone(psutil.disk_io_counters(perdisk=False)) + self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) + assert m.called + + +class TestNetAPIs(unittest.TestCase): + @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): def check_ntuple(nt): @@ -681,150 +845,8 @@ class TestSystemAPIs(unittest.TestCase): self.assertEqual(ret, {}) assert m.called - @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this linux version') - @unittest.skipIf(APPVEYOR and psutil.disk_io_counters() is None, - "unreliable on APPVEYOR") # no visible disks - def test_disk_io_counters(self): - def check_ntuple(nt): - self.assertEqual(nt[0], nt.read_count) - self.assertEqual(nt[1], nt.write_count) - self.assertEqual(nt[2], nt.read_bytes) - self.assertEqual(nt[3], nt.write_bytes) - if not (OPENBSD or NETBSD): - self.assertEqual(nt[4], nt.read_time) - self.assertEqual(nt[5], nt.write_time) - if LINUX: - self.assertEqual(nt[6], nt.read_merged_count) - self.assertEqual(nt[7], nt.write_merged_count) - self.assertEqual(nt[8], nt.busy_time) - elif FREEBSD: - self.assertEqual(nt[6], nt.busy_time) - for name in nt._fields: - assert getattr(nt, name) >= 0, nt - ret = psutil.disk_io_counters(perdisk=False) - assert ret is not None, "no disks on this system?" - check_ntuple(ret) - ret = psutil.disk_io_counters(perdisk=True) - # make sure there are no duplicates - self.assertEqual(len(ret), len(set(ret))) - for key in ret: - assert key, key - check_ntuple(ret[key]) - - def test_disk_io_counters_no_disks(self): - # Emulate a case where no disks are installed, see: - # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch('psutil._psplatform.disk_io_counters', - return_value={}) as m: - self.assertIsNone(psutil.disk_io_counters(perdisk=False)) - self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) - assert m.called - - # can't find users on APPVEYOR or TRAVIS - @unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(), - "unreliable on APPVEYOR or TRAVIS") - def test_users(self): - users = psutil.users() - self.assertNotEqual(users, []) - for user in users: - assert user.name, user - self.assertIsInstance(user.name, str) - self.assertIsInstance(user.terminal, (str, type(None))) - if user.host is not None: - self.assertIsInstance(user.host, (str, type(None))) - user.terminal - user.host - assert user.started > 0.0, user - datetime.datetime.fromtimestamp(user.started) - if WINDOWS or OPENBSD: - self.assertIsNone(user.pid) - else: - psutil.Process(user.pid) - - def test_cpu_stats(self): - # Tested more extensively in per-platform test modules. - infos = psutil.cpu_stats() - self.assertEqual( - infos._fields, - ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls')) - for name in infos._fields: - value = getattr(infos, name) - self.assertGreaterEqual(value, 0) - # on AIX, ctx_switches is always 0 - if not AIX and name in ('ctx_switches', 'interrupts'): - self.assertGreater(value, 0) - - @unittest.skipIf(not HAS_CPU_FREQ, "not suported") - def test_cpu_freq(self): - def check_ls(ls): - for nt in ls: - self.assertEqual(nt._fields, ('current', 'min', 'max')) - if nt.max != 0.0: - self.assertLessEqual(nt.current, nt.max) - for name in nt._fields: - value = getattr(nt, name) - self.assertIsInstance(value, (int, long, float)) - self.assertGreaterEqual(value, 0) - - ls = psutil.cpu_freq(percpu=True) - if TRAVIS and not ls: - raise self.skipTest("skipped on Travis") - if FREEBSD and not ls: - raise self.skipTest("returns empty list on FreeBSD") - - assert ls, ls - check_ls([psutil.cpu_freq(percpu=False)]) - - if LINUX: - self.assertEqual(len(ls), psutil.cpu_count()) - - @unittest.skipIf(not HAS_GETLOADAVG, "not supported") - def test_getloadavg(self): - loadavg = psutil.getloadavg() - assert len(loadavg) == 3 - - for load in loadavg: - self.assertIsInstance(load, float) - self.assertGreaterEqual(load, 0.0) - - def test_os_constants(self): - names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD", - "NETBSD", "BSD", "SUNOS"] - for name in names: - self.assertIsInstance(getattr(psutil, name), bool, msg=name) - - if os.name == 'posix': - assert psutil.POSIX - assert not psutil.WINDOWS - names.remove("POSIX") - if "linux" in sys.platform.lower(): - assert psutil.LINUX - names.remove("LINUX") - elif "bsd" in sys.platform.lower(): - assert psutil.BSD - self.assertEqual([psutil.FREEBSD, psutil.OPENBSD, - psutil.NETBSD].count(True), 1) - names.remove("BSD") - names.remove("FREEBSD") - names.remove("OPENBSD") - names.remove("NETBSD") - elif "sunos" in sys.platform.lower() or \ - "solaris" in sys.platform.lower(): - assert psutil.SUNOS - names.remove("SUNOS") - elif "darwin" in sys.platform.lower(): - assert psutil.MACOS - names.remove("MACOS") - else: - assert psutil.WINDOWS - assert not psutil.POSIX - names.remove("WINDOWS") - - # assert all other constants are set to False - for name in names: - self.assertIs(getattr(psutil, name), False, msg=name) +class TestSensorsAPIs(unittest.TestCase): @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures(self): diff --git a/third_party/python/psutil/psutil/tests/test_unicode.py b/third_party/python/psutil/psutil/tests/test_unicode.py index f7115e3d4ccc..ac2d4f69e0aa 100755 --- a/third_party/python/psutil/psutil/tests/test_unicode.py +++ b/third_party/python/psutil/psutil/tests/test_unicode.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -9,7 +9,32 @@ Notes about unicode handling in psutil ====================================== -In psutil these are the APIs returning or dealing with a string +Starting from version 5.3.0 psutil adds unicode support, see: +https://github.com/giampaolo/psutil/issues/1040 +The notes below apply to *any* API returning a string such as +process exe(), cwd() or username(): + +* all strings are encoded by using the OS filesystem encoding + (sys.getfilesystemencoding()) which varies depending on the platform + (e.g. "UTF-8" on macOS, "mbcs" on Win) +* no API call is supposed to crash with UnicodeDecodeError +* instead, in case of badly encoded data returned by the OS, the + following error handlers are used to replace the corrupted characters in + the string: + * Python 3: sys.getfilesystemencodeerrors() (PY 3.6+) or + "surrogatescape" on POSIX and "replace" on Windows + * Python 2: "replace" +* on Python 2 all APIs return bytes (str type), never unicode +* on Python 2, you can go back to unicode by doing: + + >>> unicode(p.exe(), sys.getdefaultencoding(), errors="replace") + +For a detailed explanation of how psutil handles unicode see #1040. + +Tests +===== + +List of APIs returning or dealing with a string: ('not tested' means they are not tested to deal with non-ASCII strings): * Process.cmdline() @@ -46,10 +71,6 @@ etc.) and make sure that: * psutil never crashes with UnicodeDecodeError * the returned path matches - -For a detailed explanation of how psutil handles unicode see: -- https://github.com/giampaolo/psutil/issues/1040 -- http://psutil.readthedocs.io/#unicode """ import os @@ -65,6 +86,7 @@ from psutil import WINDOWS from psutil._compat import PY3 from psutil._compat import u from psutil.tests import APPVEYOR +from psutil.tests import CIRRUS from psutil.tests import ASCII_FS from psutil.tests import bind_unix_socket from psutil.tests import chdir @@ -74,7 +96,6 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_CONNECTIONS_UNIX from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_MEMORY_MAPS -from psutil.tests import mock from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import safe_mkdir @@ -165,20 +186,12 @@ class _BaseFSAPIsTests(object): exe = p.exe() self.assertIsInstance(exe, str) if self.expect_exact_path_match(): - self.assertEqual(exe, self.funky_name) + self.assertEqual(os.path.normcase(exe), + os.path.normcase(self.funky_name)) def test_proc_name(self): subp = get_test_subprocess(cmd=[self.funky_name]) - if WINDOWS: - # On Windows name() is determined from exe() first, because - # it's faster; we want to overcome the internal optimization - # and test name() instead of exe(). - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as m: - name = psutil.Process(subp.pid).name() - assert m.called - else: - name = psutil.Process(subp.pid).name() + name = psutil.Process(subp.pid).name() self.assertIsInstance(name, str) if self.expect_exact_path_match(): self.assertEqual(name, os.path.basename(self.funky_name)) @@ -203,6 +216,7 @@ class _BaseFSAPIsTests(object): if self.expect_exact_path_match(): self.assertEqual(cwd, dname) + @unittest.skipIf(PYPY and WINDOWS, "fails on PYPY + WINDOWS") def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) @@ -232,7 +246,7 @@ class _BaseFSAPIsTests(object): conn = psutil.Process().connections('unix')[0] self.assertIsInstance(conn.laddr, str) # AF_UNIX addr not set on OpenBSD - if not OPENBSD: + if not OPENBSD and not CIRRUS: # XXX self.assertEqual(conn.laddr, name) @unittest.skipIf(not POSIX, "POSIX only") @@ -270,6 +284,8 @@ class _BaseFSAPIsTests(object): @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") @unittest.skipIf(not PY3, "ctypes does not support unicode on PY2") + @unittest.skipIf(PYPY and WINDOWS, + "copyload_shared_lib() unsupported on PYPY + WINDOWS") def test_memory_maps(self): # XXX: on Python 2, using ctypes.CDLL with a unicode path # opens a message box which blocks the test run. @@ -299,17 +315,15 @@ class TestFSAPIs(_BaseFSAPIsTests, unittest.TestCase): def expect_exact_path_match(cls): # Do not expect psutil to correctly handle unicode paths on # Python 2 if os.listdir() is not able either. - if PY3: - return True - else: - here = '.' if isinstance(cls.funky_name, str) else u('.') - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return cls.funky_name in os.listdir(here) + here = '.' if isinstance(cls.funky_name, str) else u('.') + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return cls.funky_name in os.listdir(here) @unittest.skipIf(PYPY and TRAVIS, "unreliable on PYPY + TRAVIS") @unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO +@unittest.skipIf(PYPY, "unreliable on PYPY") @unittest.skipIf(not subprocess_supports_unicode(INVALID_NAME), "subprocess can't deal with invalid unicode") class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase): @@ -322,19 +336,6 @@ class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase): return True -@unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestWinProcessName(unittest.TestCase): - - def test_name_type(self): - # On Windows name() is determined from exe() first, because - # it's faster; we want to overcome the internal optimization - # and test name() instead of exe(). - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as m: - self.assertIsInstance(psutil.Process().name(), str) - assert m.called - - # =================================================================== # Non fs APIs # =================================================================== @@ -347,6 +348,7 @@ class TestNonFSAPIS(unittest.TestCase): reap_children() @unittest.skipIf(not HAS_ENVIRON, "not supported") + @unittest.skipIf(PYPY and WINDOWS, "segfaults on PYPY + WINDOWS") def test_proc_environ(self): # Note: differently from others, this test does not deal # with fs paths. On Python 2 subprocess module is broken as diff --git a/third_party/python/psutil/psutil/tests/test_windows.py b/third_party/python/psutil/psutil/tests/test_windows.py index 5a998dd4ff29..f68885d0ca3d 100755 --- a/third_party/python/psutil/psutil/tests/test_windows.py +++ b/third_party/python/psutil/psutil/tests/test_windows.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: UTF-8 -* # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. @@ -21,26 +21,26 @@ import warnings import psutil from psutil import WINDOWS +from psutil._compat import FileNotFoundError from psutil.tests import APPVEYOR from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import mock from psutil.tests import PY3 +from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import unittest -with warnings.catch_warnings(): - warnings.simplefilter("ignore") - try: + +if WINDOWS and not PYPY: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") import win32api # requires "pip install pypiwin32" import win32con import win32process import wmi # requires "pip install wmi" / "make setup-dev-env" - except ImportError: - if os.name == 'nt': - raise cext = psutil._psplatform.cext @@ -63,13 +63,18 @@ def wrap_exceptions(fun): return wrapper +@unittest.skipIf(PYPY, "pywin32 not available on PYPY") # skip whole module +class TestCase(unittest.TestCase): + pass + + # =================================================================== # System APIs # =================================================================== @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestCpuAPIs(unittest.TestCase): +class TestCpuAPIs(TestCase): @unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ, 'NUMBER_OF_PROCESSORS env var is not available') @@ -108,7 +113,7 @@ class TestCpuAPIs(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSystemAPIs(unittest.TestCase): +class TestSystemAPIs(TestCase): def test_nic_names(self): out = sh('ipconfig /all') @@ -161,12 +166,9 @@ class TestSystemAPIs(unittest.TestCase): break try: usage = psutil.disk_usage(ps_part.mountpoint) - except OSError as err: - if err.errno == errno.ENOENT: - # usually this is the floppy - break - else: - raise + except FileNotFoundError: + # usually this is the floppy + break self.assertEqual(usage.total, int(wmi_part.Size)) wmi_free = int(wmi_part.FreeSpace) self.assertEqual(usage.free, wmi_free) @@ -215,7 +217,7 @@ class TestSystemAPIs(unittest.TestCase): wmi_btime_str, "%Y%m%d%H%M%S") psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time()) diff = abs((wmi_btime_dt - psutil_dt).total_seconds()) - self.assertLessEqual(diff, 1) + self.assertLessEqual(diff, 3) def test_boot_time_fluctuation(self): # https://github.com/giampaolo/psutil/issues/1007 @@ -235,7 +237,7 @@ class TestSystemAPIs(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSensorsBattery(unittest.TestCase): +class TestSensorsBattery(TestCase): def test_has_battery(self): if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: @@ -296,7 +298,7 @@ class TestSensorsBattery(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcess(unittest.TestCase): +class TestProcess(TestCase): @classmethod def setUpClass(cls): @@ -331,20 +333,6 @@ class TestProcess(unittest.TestCase): p = psutil.Process(self.pid) self.assertRaises(ValueError, p.send_signal, signal.SIGINT) - def test_exe_and_name(self): - for p in psutil.process_iter(): - # On Windows name() is never supposed to raise AccessDenied, - # see https://github.com/giampaolo/psutil/issues/627 - try: - name = p.name() - except psutil.NoSuchProcess: - pass - else: - try: - self.assertEqual(os.path.basename(p.exe()), name) - except psutil.Error: - continue - def test_num_handles_increment(self): p = psutil.Process(os.getpid()) before = p.num_handles() @@ -519,9 +507,26 @@ class TestProcess(unittest.TestCase): psutil_value = psutil.Process(self.pid).num_handles() self.assertEqual(psutil_value, sys_value) + def test_error_partial_copy(self): + # https://github.com/giampaolo/psutil/issues/875 + exc = WindowsError() + exc.winerror = 299 + with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc): + with mock.patch("time.sleep") as m: + p = psutil.Process() + self.assertRaises(psutil.AccessDenied, p.cwd) + self.assertGreaterEqual(m.call_count, 5) + + def test_exe(self): + # NtQuerySystemInformation succeeds if process is gone. Make sure + # it raises NSP for a non existent pid. + pid = psutil.pids()[-1] + 99999 + proc = psutil._psplatform.Process(pid) + self.assertRaises(psutil.NoSuchProcess, proc.exe) + @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcessWMI(unittest.TestCase): +class TestProcessWMI(TestCase): """Compare Process API results with WMI.""" @classmethod @@ -587,7 +592,7 @@ class TestProcessWMI(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestDualProcessImplementation(unittest.TestCase): +class TestDualProcessImplementation(TestCase): """ Certain APIs on Windows have 2 internal implementations, one based on documented Windows APIs, another one based @@ -605,16 +610,6 @@ class TestDualProcessImplementation(unittest.TestCase): @classmethod def tearDownClass(cls): reap_children() - # --- - # same tests as above but mimicks the AccessDenied failure of - # the first (fast) method failing with AD. - - def test_name(self): - name = psutil.Process(self.pid).name() - with mock.patch("psutil._psplatform.cext.proc_exe", - side_effect=psutil.AccessDenied(os.getpid())) as fun: - self.assertEqual(psutil.Process(self.pid).name(), name) - assert fun.called def test_memory_info(self): mem_1 = psutil.Process(self.pid).memory_info() @@ -630,14 +625,14 @@ class TestDualProcessImplementation(unittest.TestCase): def test_create_time(self): ctime = psutil.Process(self.pid).create_time() - with mock.patch("psutil._psplatform.cext.proc_create_time", + with mock.patch("psutil._psplatform.cext.proc_times", side_effect=OSError(errno.EPERM, "msg")) as fun: self.assertEqual(psutil.Process(self.pid).create_time(), ctime) assert fun.called def test_cpu_times(self): cpu_times_1 = psutil.Process(self.pid).cpu_times() - with mock.patch("psutil._psplatform.cext.proc_cpu_times", + with mock.patch("psutil._psplatform.cext.proc_times", side_effect=OSError(errno.EPERM, "msg")) as fun: cpu_times_2 = psutil.Process(self.pid).cpu_times() assert fun.called @@ -680,7 +675,7 @@ class TestDualProcessImplementation(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class RemoteProcessTestCase(unittest.TestCase): +class RemoteProcessTestCase(TestCase): """Certain functions require calling ReadProcessMemory. This trivially works when called on the current process. Check that this works on other processes, especially when they @@ -776,7 +771,7 @@ class RemoteProcessTestCase(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestServices(unittest.TestCase): +class TestServices(TestCase): def test_win_service_iter(self): valid_statuses = set([ diff --git a/third_party/python/psutil/scripts/battery.py b/third_party/python/psutil/scripts/battery.py index abbad8785a34..0da2b9588900 100755 --- a/third_party/python/psutil/scripts/battery.py +++ b/third_party/python/psutil/scripts/battery.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/cpu_distribution.py b/third_party/python/psutil/scripts/cpu_distribution.py index c509c7328836..08997797c3d0 100755 --- a/third_party/python/psutil/scripts/cpu_distribution.py +++ b/third_party/python/psutil/scripts/cpu_distribution.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -84,7 +84,7 @@ def main(): # processes procs = collections.defaultdict(list) - for p in psutil.process_iter(attrs=['name', 'cpu_num']): + for p in psutil.process_iter(['name', 'cpu_num']): procs[p.info['cpu_num']].append(p.info['name'][:5]) curr_line = 3 diff --git a/third_party/python/psutil/scripts/disk_usage.py b/third_party/python/psutil/scripts/disk_usage.py index 1860401fdacf..901dbf8c226c 100755 --- a/third_party/python/psutil/scripts/disk_usage.py +++ b/third_party/python/psutil/scripts/disk_usage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/fans.py b/third_party/python/psutil/scripts/fans.py index 7a0ccf91d50e..179af6312c6f 100755 --- a/third_party/python/psutil/scripts/fans.py +++ b/third_party/python/psutil/scripts/fans.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/free.py b/third_party/python/psutil/scripts/free.py index 82e962ffc926..000323c5ddcf 100755 --- a/third_party/python/psutil/scripts/free.py +++ b/third_party/python/psutil/scripts/free.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/ifconfig.py b/third_party/python/psutil/scripts/ifconfig.py index ad62a44f794e..cfd02f0dafa3 100755 --- a/third_party/python/psutil/scripts/ifconfig.py +++ b/third_party/python/psutil/scripts/ifconfig.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/internal/.git-pre-commit b/third_party/python/psutil/scripts/internal/.git-pre-commit index c7d7d1ff59a0..e60092538d3d 100755 --- a/third_party/python/psutil/scripts/internal/.git-pre-commit +++ b/third_party/python/psutil/scripts/internal/.git-pre-commit @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -77,6 +77,8 @@ def main(): out = sh("git diff --cached --name-only") py_files = [x for x in out.split('\n') if x.endswith('.py') and os.path.exists(x)] + c_files = [x for x in out.split('\n') if x.endswith(('.c', '.h')) and + os.path.exists(x)] lineno = 0 kw = {'encoding': 'utf8'} if sys.version_info[0] == 3 else {} @@ -100,7 +102,7 @@ def main(): print("%s:%s %s" % (path, lineno, line)) return exit("commit aborted: bare except clause") - # flake8 + # Python linter if py_files: try: import flake8 # NOQA @@ -108,12 +110,22 @@ def main(): return exit("commit aborted: flake8 is not installed; " "run 'make setup-dev-env'") - # XXX: we should scape spaces and possibly other amenities here + # XXX: we should escape spaces and possibly other amenities here ret = subprocess.call( "%s -m flake8 %s" % (sys.executable, " ".join(py_files)), shell=True) if ret != 0: return exit("commit aborted: python code is not flake8 compliant") + # C linter + if c_files: + # XXX: we should escape spaces and possibly other amenities here + cmd = "%s scripts/internal/clinter.py %s" % ( + sys.executable, " ".join(c_files)) + print(cmd) + ret = subprocess.call(cmd, shell=True) + if ret != 0: + return exit("commit aborted: C code didn't pass style check") + main() diff --git a/third_party/python/psutil/scripts/internal/bench_oneshot.py b/third_party/python/psutil/scripts/internal/bench_oneshot.py index 28ad4bac829b..436bdd6b0187 100755 --- a/third_party/python/psutil/scripts/internal/bench_oneshot.py +++ b/third_party/python/psutil/scripts/internal/bench_oneshot.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/internal/bench_oneshot_2.py b/third_party/python/psutil/scripts/internal/bench_oneshot_2.py old mode 100644 new mode 100755 index a25d1806e4f6..3867391b4036 --- a/third_party/python/psutil/scripts/internal/bench_oneshot_2.py +++ b/third_party/python/psutil/scripts/internal/bench_oneshot_2.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -11,7 +11,7 @@ supposed to be more precise. import sys -import perf # requires "pip install perf" +import pyperf # requires "pip install pyperf" import psutil from bench_oneshot import names @@ -37,7 +37,7 @@ def add_cmdline_args(cmd, args): def main(): - runner = perf.Runner() + runner = pyperf.Runner() args = runner.parse_args() if not args.worker: diff --git a/third_party/python/psutil/scripts/internal/check_broken_links.py b/third_party/python/psutil/scripts/internal/check_broken_links.py index 3d108d810d62..1a0761161329 100755 --- a/third_party/python/psutil/scripts/internal/check_broken_links.py +++ b/third_party/python/psutil/scripts/internal/check_broken_links.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola', Himanshu Shekhar. # All rights reserved. Use of this source code is governed by a @@ -160,7 +160,7 @@ def parse_c(fname): def parse_generic(fname): - with open(fname) as f: + with open(fname, 'rt', errors='ignore') as f: text = f.read() return find_urls(text) @@ -174,8 +174,8 @@ def get_urls(fname): elif fname.endswith('.c') or fname.endswith('.h'): return parse_c(fname) else: - with open(fname) as f: - if f.readline().strip().startswith('#!/usr/bin/env python'): + with open(fname, 'rt', errors='ignore') as f: + if f.readline().strip().startswith('#!/usr/bin/env python3'): return parse_py(fname) return parse_generic(fname) diff --git a/third_party/python/psutil/scripts/internal/clinter.py b/third_party/python/psutil/scripts/internal/clinter.py new file mode 100755 index 000000000000..1d4ba9b14bf6 --- /dev/null +++ b/third_party/python/psutil/scripts/internal/clinter.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""A super simple linter to check C syntax.""" + +from __future__ import print_function +import argparse +import sys + + +warned = False + + +def warn(path, line, lineno, msg): + global warned + warned = True + print("%s:%s: %s" % (path, lineno, msg), file=sys.stderr) + + +def check_line(path, line, idx, lines): + s = line + lineno = idx + 1 + eof = lineno == len(lines) + if s.endswith(' \n'): + warn(path, line, lineno, "extra space at EOL") + elif '\t' in line: + warn(path, line, lineno, "line has a tab") + elif s.endswith('\r\n'): + warn(path, line, lineno, "Windows line ending") + # end of global block, e.g. "}newfunction...": + elif s == "}\n": + if not eof: + nextline = lines[idx + 1] + # "#" is a pre-processor line + if nextline != '\n' and \ + nextline.strip()[0] != '#' and \ + nextline.strip()[:2] != '*/': + warn(path, line, lineno, "expected 1 blank line") + + sls = s.lstrip() + if sls.startswith('//') and sls[2] != ' ' and line.strip() != '//': + warn(path, line, lineno, "no space after // comment") + + # e.g. "if(..." after keywords + keywords = ("if", "else", "while", "do", "enum", "for") + for kw in keywords: + if sls.startswith(kw + '('): + warn(path, line, lineno, "missing space between %r and '('" % kw) + # eof + if eof: + if not line.endswith('\n'): + warn(path, line, lineno, "no blank line at EOF") + + +def process(path): + with open(path, 'rt') as f: + lines = f.readlines() + for idx, line in enumerate(lines): + check_line(path, line, idx, lines) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('paths', nargs='+', help='path(s) to a file(s)') + args = parser.parse_args() + for path in args.paths: + process(path) + if warned: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/third_party/python/psutil/scripts/internal/fix_flake8.py b/third_party/python/psutil/scripts/internal/fix_flake8.py new file mode 100755 index 000000000000..7cde608bbadd --- /dev/null +++ b/third_party/python/psutil/scripts/internal/fix_flake8.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Fix some flake8 errors by rewriting files for which flake8 emitted +an error/warning. Usage (from the root dir): + + $ python3 -m flake8 --exit-zero | python3 scripts/fix_flake8.py +""" + +import sys +import tempfile +import shutil +from collections import defaultdict +from collections import namedtuple +from pprint import pprint as pp # NOQA + + +ntentry = namedtuple('ntentry', 'msg, issue, lineno, pos, descr') + + +# ===================================================================== +# utils +# ===================================================================== + + +def enter_pdb(): + from pdb import set_trace as st # trick GIT commit hook + sys.stdin = open('/dev/tty') + st() + + +def read_lines(fname): + with open(fname, 'rt') as f: + return f.readlines() + + +def read_line(fname, lineno): + with open(fname, 'rt') as f: + for i, line in enumerate(f, 1): + if i == lineno: + return line + raise ValueError("lineno too big") + + +def write_file(fname, newlines): + with tempfile.NamedTemporaryFile('wt', delete=False) as f: + for line in newlines: + f.write(line) + shutil.move(f.name, fname) + + +# ===================================================================== +# handlers +# ===================================================================== + + +def handle_f401(fname, lineno): + """ This is 'module imported but not used' + Able to handle this case: + import os + + ...but not this: + from os import listdir + """ + line = read_line(fname, lineno).strip() + if line.startswith('import '): + return True + else: + mods = line.partition(' import ')[2] # everything after import + return ',' not in mods + + +# ===================================================================== +# converters +# ===================================================================== + + +def remove_lines(fname, entries): + """Check if we should remove lines, then do it. + Return the numner of lines removed. + """ + to_remove = [] + for entry in entries: + msg, issue, lineno, pos, descr = entry + # 'module imported but not used' + if issue == 'F401' and handle_f401(fname, lineno): + to_remove.append(lineno) + # 'blank line(s) at end of file' + elif issue == 'W391': + lines = read_lines(fname) + i = len(lines) - 1 + while lines[i] == '\n': + to_remove.append(i + 1) + i -= 1 + # 'too many blank lines' + elif issue == 'E303': + howmany = descr.replace('(', '').replace(')', '') + howmany = int(howmany[-1]) + for x in range(lineno - howmany, lineno): + to_remove.append(x) + + if to_remove: + newlines = [] + for i, line in enumerate(read_lines(fname), 1): + if i not in to_remove: + newlines.append(line) + print("removing line(s) from %s" % fname) + write_file(fname, newlines) + + return len(to_remove) + + +def add_lines(fname, entries): + """Check if we should remove lines, then do it. + Return the numner of lines removed. + """ + EXPECTED_BLANK_LINES = ( + 'E302', # 'expected 2 blank limes, found 1' + 'E305') # ìexpected 2 blank lines after class or fun definition' + to_add = {} + for entry in entries: + msg, issue, lineno, pos, descr = entry + if issue in EXPECTED_BLANK_LINES: + howmany = 2 if descr.endswith('0') else 1 + to_add[lineno] = howmany + + if to_add: + newlines = [] + for i, line in enumerate(read_lines(fname), 1): + if i in to_add: + newlines.append('\n' * to_add[i]) + newlines.append(line) + print("adding line(s) to %s" % fname) + write_file(fname, newlines) + + return len(to_add) + + +# ===================================================================== +# main +# ===================================================================== + + +def build_table(): + table = defaultdict(list) + for line in sys.stdin: + line = line.strip() + if not line: + break + fields = line.split(':') + fname, lineno, pos = fields[:3] + issue = fields[3].split()[0] + descr = fields[3].strip().partition(' ')[2] + lineno, pos = int(lineno), int(pos) + table[fname].append(ntentry(line, issue, lineno, pos, descr)) + return table + + +def main(): + table = build_table() + + # remove lines (unused imports) + removed = 0 + for fname, entries in table.items(): + removed += remove_lines(fname, entries) + if removed: + print("%s lines were removed from some file(s); please re-run" % + removed) + return + + # add lines (missing \n between functions/classes) + added = 0 + for fname, entries in table.items(): + added += add_lines(fname, entries) + if added: + print("%s lines were added from some file(s); please re-run" % + added) + return + + +main() diff --git a/third_party/python/psutil/scripts/internal/generate_manifest.py b/third_party/python/psutil/scripts/internal/generate_manifest.py index f6cf0eaa054f..c0be6d99d978 100755 --- a/third_party/python/psutil/scripts/internal/generate_manifest.py +++ b/third_party/python/psutil/scripts/internal/generate_manifest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/internal/print_access_denied.py b/third_party/python/psutil/scripts/internal/print_access_denied.py old mode 100644 new mode 100755 index 1519c94b17e8..81d192f0c754 --- a/third_party/python/psutil/scripts/internal/print_access_denied.py +++ b/third_party/python/psutil/scripts/internal/print_access_denied.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -47,9 +47,10 @@ Totals: access-denied=1744, calls=10020, processes=334 from __future__ import print_function, division from collections import defaultdict +import time import psutil -from scriptutils import hilite +from psutil._common import print_color def main(): @@ -59,6 +60,7 @@ def main(): tot_calls = 0 signaler = object() d = defaultdict(int) + start = time.time() for p in psutil.process_iter(attrs=[], ad_value=signaler): tot_procs += 1 for methname, value in p.info.items(): @@ -68,20 +70,22 @@ def main(): d[methname] += 1 else: d[methname] += 0 + elapsed = time.time() - start # print templ = "%-20s %-5s %-9s %s" s = templ % ("API", "AD", "Percent", "Outcome") - print(hilite(s, ok=None, bold=True)) - for methname, ads in sorted(d.items(), key=lambda x: x[1]): + print_color(s, color=None, bold=True) + for methname, ads in sorted(d.items(), key=lambda x: (x[1], x[0])): perc = (ads / tot_procs) * 100 outcome = "SUCCESS" if not ads else "ACCESS DENIED" s = templ % (methname, ads, "%6.1f%%" % perc, outcome) - s = hilite(s, ok=not ads) - print(s) + print_color(s, "red" if ads else None) + tot_perc = round((tot_ads / tot_calls) * 100, 1) print("-" * 50) - print("Totals: access-denied=%s, calls=%s, processes=%s" % ( - tot_ads, tot_calls, tot_procs)) + print("Totals: access-denied=%s (%s%%), calls=%s, processes=%s, " + "elapsed=%ss" % (tot_ads, tot_perc, tot_calls, tot_procs, + round(elapsed, 2))) if __name__ == '__main__': diff --git a/third_party/python/psutil/scripts/internal/print_announce.py b/third_party/python/psutil/scripts/internal/print_announce.py index 7fbe74f3d654..9569c3674d8c 100755 --- a/third_party/python/psutil/scripts/internal/print_announce.py +++ b/third_party/python/psutil/scripts/internal/print_announce.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/internal/print_api_speed.py b/third_party/python/psutil/scripts/internal/print_api_speed.py old mode 100644 new mode 100755 index a99293c458c3..e39a1baa7b96 --- a/third_party/python/psutil/scripts/internal/print_api_speed.py +++ b/third_party/python/psutil/scripts/internal/print_api_speed.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -9,53 +9,45 @@ $ make print_api_speed SYSTEM APIS SECONDS ---------------------------------- -boot_time 0.000140 -cpu_count 0.000016 -cpu_count (cores) 0.000312 -cpu_freq 0.000811 -cpu_percent 0.000138 -cpu_stats 0.000165 -cpu_times 0.000140 +cpu_count 0.000014 +disk_usage 0.000027 +cpu_times 0.000037 +cpu_percent 0.000045 ... PROCESS APIS SECONDS ---------------------------------- -children 0.007246 -cmdline 0.000069 -connections 0.000072 -cpu_affinity 0.000012 -cpu_num 0.000035 -cpu_percent 0.000042 -cpu_times 0.000031 +create_time 0.000001 +nice 0.000005 +cwd 0.000011 +cpu_affinity 0.000011 +ionice 0.000013 +... """ from __future__ import print_function, division from timeit import default_timer as timer -import argparse import inspect import os import psutil -from scriptutils import hilite +from psutil._common import print_color -SORT_BY_TIME = False if psutil.POSIX else True -TOP_SLOWEST = 7 timings = [] templ = "%-25s %s" def print_timings(): - slower = [] - timings.sort(key=lambda x: x[1 if SORT_BY_TIME else 0]) - for x in sorted(timings, key=lambda x: x[1], reverse=1)[:TOP_SLOWEST]: - slower.append(x[0]) + timings.sort(key=lambda x: x[1]) + i = 0 while timings[:]: title, elapsed = timings.pop(0) s = templ % (title, "%f" % elapsed) - if title in slower: - s = hilite(s, ok=False) - print(s) + if i > len(timings) - 5: + print_color(s, color="red") + else: + print(s) def timecall(title, fun, *args, **kw): @@ -65,11 +57,7 @@ def timecall(title, fun, *args, **kw): timings.append((title, elapsed)) -def titlestr(s): - return hilite(s, ok=None, bold=True) - - -def run(): +def main(): # --- system public_apis = [] @@ -83,7 +71,7 @@ def run(): if name not in ignore: public_apis.append(name) - print(titlestr(templ % ("SYSTEM APIS", "SECONDS"))) + print_color(templ % ("SYSTEM APIS", "SECONDS"), color=None, bold=True) print("-" * 34) for name in public_apis: fun = getattr(psutil, name) @@ -99,7 +87,7 @@ def run(): # --- process print("") - print(titlestr(templ % ("PROCESS APIS", "SECONDS"))) + print_color(templ % ("PROCESS APIS", "SECONDS"), color=None, bold=True) print("-" * 34) ignore = ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', 'as_dict', 'parent', 'parents', 'memory_info_ex', 'oneshot', @@ -114,18 +102,5 @@ def run(): print_timings() -def main(): - global SORT_BY_TIME, TOP_SLOWEST - parser = argparse.ArgumentParser(description='Benchmark all API calls') - parser.add_argument('-t', '--time', required=False, default=SORT_BY_TIME, - action='store_true', help="sort by timings") - parser.add_argument('-s', '--slowest', required=False, default=TOP_SLOWEST, - help="highlight the top N slowest APIs") - args = parser.parse_args() - SORT_BY_TIME = bool(args.time) - TOP_SLOWEST = int(args.slowest) - run() - - if __name__ == '__main__': main() diff --git a/third_party/python/psutil/scripts/internal/print_timeline.py b/third_party/python/psutil/scripts/internal/print_timeline.py old mode 100644 new mode 100755 index 4bfe76b332f2..64608b26ba59 --- a/third_party/python/psutil/scripts/internal/print_timeline.py +++ b/third_party/python/psutil/scripts/internal/print_timeline.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -30,7 +30,7 @@ def get_tag_date(tag): def main(): releases = [] - out = sh("git tags") + out = sh("git tag") for line in out.split('\n'): tag = line.split(' ')[0] ver = tag.replace('release-', '') diff --git a/third_party/python/psutil/scripts/internal/purge_installation.py b/third_party/python/psutil/scripts/internal/purge_installation.py index d9301719550d..50c00463cf39 100755 --- a/third_party/python/psutil/scripts/internal/purge_installation.py +++ b/third_party/python/psutil/scripts/internal/purge_installation.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/internal/scriptutils.py b/third_party/python/psutil/scripts/internal/scriptutils.py deleted file mode 100644 index 3ee416f82583..000000000000 --- a/third_party/python/psutil/scripts/internal/scriptutils.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Utils shared by all files in scripts/internal.""" - -from __future__ import print_function -import sys - -from psutil._compat import lru_cache - - -__all__ = ['hilite', 'printerr', 'exit'] - - -@lru_cache() -def _term_supports_colors(file=sys.stdout): - try: - import curses - assert file.isatty() - curses.setupterm() - assert curses.tigetnum("colors") > 0 - except Exception: - return False - else: - return True - - -def hilite(s, ok=True, bold=False): - """Return an highlighted version of 'string'.""" - if not _term_supports_colors(): - return s - attr = [] - if ok is None: # no color - pass - elif ok: # green - attr.append('32') - else: # red - attr.append('31') - if bold: - attr.append('1') - return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) - - -def printerr(s): - print(hilite(s, ok=False), file=sys.stderr) - - -def exit(msg=""): - if msg: - printerr(msg) - sys.exit(1) diff --git a/third_party/python/psutil/scripts/internal/download_exes.py b/third_party/python/psutil/scripts/internal/win_download_wheels.py similarity index 93% rename from third_party/python/psutil/scripts/internal/download_exes.py rename to third_party/python/psutil/scripts/internal/win_download_wheels.py index 1b72a177753b..3720dd96c07b 100755 --- a/third_party/python/psutil/scripts/internal/download_exes.py +++ b/third_party/python/psutil/scripts/internal/win_download_wheels.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -19,14 +19,15 @@ import errno import os import requests import shutil +import sys from psutil import __version__ as PSUTIL_VERSION from psutil._common import bytes2human -from scriptutils import printerr, exit +from psutil._common import print_color BASE_URL = 'https://ci.appveyor.com/api' -PY_VERSIONS = ['2.7', '3.5', '3.6', '3.7'] +PY_VERSIONS = ['2.7', '3.5', '3.6', '3.7', '3.8'] TIMEOUT = 30 COLORS = True @@ -81,7 +82,8 @@ def get_file_urls(options): file_url = job_url + '/' + item['fileName'] urls.append(file_url) if not urls: - exit("no artifacts found") + print_color("no artifacts found", 'ret') + sys.exit(1) else: for url in sorted(urls, key=lambda x: os.path.basename(x)): yield url @@ -111,7 +113,7 @@ def run(options): try: local_fname = fut.result() except Exception: - printerr("error while downloading %s" % (url)) + print_color("error while downloading %s" % (url), 'red') raise else: completed += 1 diff --git a/third_party/python/psutil/scripts/internal/winmake.py b/third_party/python/psutil/scripts/internal/winmake.py index 75b4c348ae6c..4d3fa3184bc1 100755 --- a/third_party/python/psutil/scripts/internal/winmake.py +++ b/third_party/python/psutil/scripts/internal/winmake.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -12,9 +12,11 @@ that they should be deemed illegal! """ from __future__ import print_function +import argparse +import atexit +import ctypes import errno import fnmatch -import functools import os import shutil import site @@ -29,19 +31,19 @@ if APPVEYOR: PYTHON = sys.executable else: PYTHON = os.getenv('PYTHON', sys.executable) -TEST_SCRIPT = 'psutil\\tests\\__main__.py' +TEST_SCRIPT = 'psutil\\tests\\runner.py' GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" PY3 = sys.version_info[0] == 3 HERE = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.realpath(os.path.join(HERE, "..", "..")) +PYPY = '__pypy__' in sys.builtin_module_names DEPS = [ "coverage", "flake8", "nose", "pdbpp", - "perf", "pip", - "pypiwin32==219" if sys.version_info[:2] <= (3, 4) else "pypiwin32", + "pyperf", "pyreadline", "setuptools", "wheel", @@ -54,11 +56,24 @@ if sys.version_info[:2] <= (2, 7): DEPS.append('mock') if sys.version_info[:2] <= (3, 2): DEPS.append('ipaddress') +if PYPY: + pass +elif sys.version_info[:2] <= (3, 4): + DEPS.append("pypiwin32==219") +else: + DEPS.append("pypiwin32") _cmds = {} if PY3: basestring = str +GREEN = 2 +LIGHTBLUE = 3 +YELLOW = 6 +RED = 4 +DEFAULT_COLOR = 7 + + # =================================================================== # utils # =================================================================== @@ -84,6 +99,26 @@ def safe_print(text, file=sys.stdout, flush=False): file.write("\n") +def stderr_handle(): + GetStdHandle = ctypes.windll.Kernel32.GetStdHandle + STD_ERROR_HANDLE_ID = ctypes.c_ulong(0xfffffff4) + GetStdHandle.restype = ctypes.c_ulong + handle = GetStdHandle(STD_ERROR_HANDLE_ID) + atexit.register(ctypes.windll.Kernel32.CloseHandle, handle) + return handle + + +def win_colorprint(s, color=LIGHTBLUE): + color += 8 # bold + handle = stderr_handle() + SetConsoleTextAttribute = ctypes.windll.Kernel32.SetConsoleTextAttribute + SetConsoleTextAttribute(handle, color) + try: + print(s) + finally: + SetConsoleTextAttribute(handle, DEFAULT_COLOR) + + def sh(cmd, nolog=False): if not nolog: safe_print("cmd: " + cmd) @@ -93,15 +128,6 @@ def sh(cmd, nolog=False): sys.exit(p.returncode) -def cmd(fun): - @functools.wraps(fun) - def wrapper(*args, **kwds): - return fun(*args, **kwds) - - _cmds[fun.__name__] = fun.__doc__ - return wrapper - - def rm(pattern, directory=False): """Recursively remove a file or dir by pattern.""" def safe_remove(path): @@ -195,51 +221,61 @@ def test_setup(): # =================================================================== -@cmd -def help(): - """Print this help""" - safe_print('Run "make [-p ] " where is one of:') - for name in sorted(_cmds): - safe_print( - " %-20s %s" % (name.replace('_', '-'), _cmds[name] or '')) - sys.exit(1) - - -@cmd def build(): """Build / compile""" # Make sure setuptools is installed (needed for 'develop' / # edit mode). sh('%s -c "import setuptools"' % PYTHON) - sh("%s setup.py build" % PYTHON) + + # Print coloured warnings in real time. + cmd = [PYTHON, "setup.py", "build"] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + for line in iter(p.stdout.readline, b''): + if PY3: + line = line.decode() + line = line.strip() + if 'warning' in line: + win_colorprint(line, YELLOW) + elif 'error' in line: + win_colorprint(line, RED) + else: + print(line) + # retcode = p.poll() + p.communicate() + if p.returncode: + win_colorprint("failure", RED) + sys.exit(p.returncode) + finally: + p.terminate() + p.wait() + # Copies compiled *.pyd files in ./psutil directory in order to # allow "import psutil" when using the interactive interpreter # from within this directory. sh("%s setup.py build_ext -i" % PYTHON) # Make sure it actually worked. sh('%s -c "import psutil"' % PYTHON) + win_colorprint("build + import successful", GREEN) -@cmd def wheel(): """Create wheel file.""" build() sh("%s setup.py bdist_wheel" % PYTHON) -@cmd def upload_wheels(): """Upload wheel files on PyPI.""" build() sh("%s -m twine upload dist/*.whl" % PYTHON) -@cmd def install_pip(): """Install pip""" try: - import pip # NOQA - except ImportError: + sh('%s -c "import pip"' % PYTHON) + except SystemExit: if PY3: from urllib.request import urlopen else: @@ -264,15 +300,12 @@ def install_pip(): os.remove(tfile) -@cmd def install(): """Install in develop / edit mode""" - install_git_hooks() build() sh("%s setup.py develop" % PYTHON) -@cmd def uninstall(): """Uninstall psutil""" # Uninstalling psutil on Windows seems to be tricky. @@ -299,9 +332,26 @@ def uninstall(): for name in os.listdir(dir): if name.startswith('psutil'): rm(os.path.join(dir, name)) + elif name == 'easy-install.pth': + # easy_install can add a line (installation path) into + # easy-install.pth; that line alters sys.path. + path = os.path.join(dir, name) + with open(path, 'rt') as f: + lines = f.readlines() + hasit = False + for line in lines: + if 'psutil' in line: + hasit = True + break + if hasit: + with open(path, 'wt') as f: + for line in lines: + if 'psutil' not in line: + f.write(line) + else: + print("removed line %r from %r" % (line, path)) -@cmd def clean(): """Deletes dev files""" recursive_rm( @@ -329,7 +379,6 @@ def clean(): safe_rmtree("tmp") -@cmd def setup_dev_env(): """Install useful deps""" install_pip() @@ -337,8 +386,7 @@ def setup_dev_env(): sh("%s -m pip install -U %s" % (PYTHON, " ".join(DEPS))) -@cmd -def flake8(): +def lint(): """Run flake8 against all py files""" py_files = subprocess.check_output("git ls-files") if PY3: @@ -348,22 +396,15 @@ def flake8(): sh("%s -m flake8 %s" % (PYTHON, py_files), nolog=True) -@cmd -def test(): +def test(script=TEST_SCRIPT): """Run tests""" - try: - arg = sys.argv[2] - except IndexError: - arg = TEST_SCRIPT - install() test_setup() - cmdline = "%s %s" % (PYTHON, arg) + cmdline = "%s %s" % (PYTHON, script) safe_print(cmdline) sh(cmdline) -@cmd def coverage(): """Run coverage tests.""" # Note: coverage options are controlled by .coveragerc file @@ -375,7 +416,6 @@ def coverage(): sh("%s -m webbrowser -t htmlcov/index.html" % PYTHON) -@cmd def test_process(): """Run process tests""" install() @@ -383,7 +423,6 @@ def test_process(): sh("%s psutil\\tests\\test_process.py" % PYTHON) -@cmd def test_system(): """Run system tests""" install() @@ -391,7 +430,6 @@ def test_system(): sh("%s psutil\\tests\\test_system.py" % PYTHON) -@cmd def test_platform(): """Run windows only tests""" install() @@ -399,7 +437,6 @@ def test_platform(): sh("%s psutil\\tests\\test_windows.py" % PYTHON) -@cmd def test_misc(): """Run misc tests""" install() @@ -407,7 +444,6 @@ def test_misc(): sh("%s psutil\\tests\\test_misc.py" % PYTHON) -@cmd def test_unicode(): """Run unicode tests""" install() @@ -415,7 +451,6 @@ def test_unicode(): sh("%s psutil\\tests\\test_unicode.py" % PYTHON) -@cmd def test_connections(): """Run connections tests""" install() @@ -423,7 +458,6 @@ def test_connections(): sh("%s psutil\\tests\\test_connections.py" % PYTHON) -@cmd def test_contracts(): """Run contracts tests""" install() @@ -431,16 +465,13 @@ def test_contracts(): sh("%s psutil\\tests\\test_contracts.py" % PYTHON) -@cmd -def test_by_name(): +def test_by_name(name): """Run test by name""" - name = sys.argv[2] install() test_setup() sh("%s -m unittest -v %s" % (PYTHON, name)) -@cmd def test_failed(): """Re-run tests which failed on last run.""" install() @@ -449,20 +480,6 @@ def test_failed(): PYTHON)) -@cmd -def test_script(): - """Quick way to test a script""" - try: - safe_print(sys.argv) - name = sys.argv[2] - except IndexError: - sys.exit('second arg missing') - install() - test_setup() - sh("%s %s" % (PYTHON, name)) - - -@cmd def test_memleaks(): """Run memory leaks tests""" install() @@ -470,7 +487,6 @@ def test_memleaks(): sh("%s psutil\\tests\\test_memory_leaks.py" % PYTHON) -@cmd def install_git_hooks(): """Install GIT pre-commit hook.""" if os.path.isdir('.git'): @@ -482,74 +498,110 @@ def install_git_hooks(): d.write(s.read()) -@cmd def bench_oneshot(): """Benchmarks for oneshot() ctx manager (see #799).""" sh("%s -Wa scripts\\internal\\bench_oneshot.py" % PYTHON) -@cmd def bench_oneshot_2(): """Same as above but using perf module (supposed to be more precise).""" sh("%s -Wa scripts\\internal\\bench_oneshot_2.py" % PYTHON) -@cmd def print_access_denied(): """Print AD exceptions raised by all Process methods.""" + install() + test_setup() sh("%s -Wa scripts\\internal\\print_access_denied.py" % PYTHON) -@cmd def print_api_speed(): """Benchmark all API calls.""" + install() + test_setup() sh("%s -Wa scripts\\internal\\print_api_speed.py" % PYTHON) -def set_python(s): - global PYTHON - if os.path.isabs(s): - PYTHON = s - else: - # try to look for a python installation - orig = s - s = s.replace('.', '') - vers = ('26', '27', '34', '35', '36', '37', - '26-64', '27-64', '34-64', '35-64', '36-64', '37-64') - for v in vers: - if s == v: - path = r'C:\\python%s\python.exe' % s - if os.path.isfile(path): - print(path) - PYTHON = path - os.putenv('PYTHON', path) - return - return sys.exit( - "can't find any python installation matching %r" % orig) - - -def parse_cmdline(): - if '-p' in sys.argv: - try: - pos = sys.argv.index('-p') - sys.argv.pop(pos) - py = sys.argv.pop(pos) - except IndexError: - return help() - set_python(py) +def get_python(path): + if not path: + return sys.executable + if os.path.isabs(path): + return path + # try to look for a python installation given a shortcut name + path = path.replace('.', '') + vers = ('26', '27', '36', '37', '38', + '26-64', '27-64', '36-64', '37-64', '38-64' + '26-32', '27-32', '36-32', '37-32', '38-32') + for v in vers: + pypath = r'C:\\python%s\python.exe' % v + if path in pypath and os.path.isfile(pypath): + return pypath def main(): - parse_cmdline() - try: - cmd = sys.argv[1].replace('-', '_') - except IndexError: - return help() - if cmd in _cmds: - fun = getattr(sys.modules[__name__], cmd) - fun() - else: - help() + global PYTHON + parser = argparse.ArgumentParser() + # option shared by all commands + parser.add_argument( + '-p', '--python', + help="use python executable path") + sp = parser.add_subparsers(dest='command', title='targets') + sp.add_parser('bench-oneshot', help="benchmarks for oneshot()") + sp.add_parser('bench-oneshot_2', help="benchmarks for oneshot() (perf)") + sp.add_parser('build', help="build") + sp.add_parser('clean', help="deletes dev files") + sp.add_parser('coverage', help="run coverage tests.") + sp.add_parser('help', help="print this help") + sp.add_parser('install', help="build + install in develop/edit mode") + sp.add_parser('install-git-hooks', help="install GIT pre-commit hook") + sp.add_parser('install-pip', help="install pip") + sp.add_parser('lint', help="run flake8 against all py files") + sp.add_parser('print-access-denied', help="print AD exceptions") + sp.add_parser('print-api-speed', help="benchmark all API calls") + sp.add_parser('setup-dev-env', help="install deps") + test = sp.add_parser('test', help="[ARG] run tests") + test_by_name = sp.add_parser('test-by-name', help=" run test by name") + sp.add_parser('test-connections', help="run connections tests") + sp.add_parser('test-contracts', help="run contracts tests") + sp.add_parser('test-failed', help="re-run tests which failed on last run") + sp.add_parser('test-memleaks', help="run memory leaks tests") + sp.add_parser('test-misc', help="run misc tests") + sp.add_parser('test-platform', help="run windows only tests") + sp.add_parser('test-process', help="run process tests") + sp.add_parser('test-system', help="run system tests") + sp.add_parser('test-unicode', help="run unicode tests") + sp.add_parser('uninstall', help="uninstall psutil") + sp.add_parser('upload-wheels', help="upload wheel files on PyPI") + sp.add_parser('wheel', help="create wheel file") + + for p in (test, test_by_name): + p.add_argument('arg', type=str, nargs='?', default="", help="arg") + args = parser.parse_args() + + # set python exe + PYTHON = get_python(args.python) + if not PYTHON: + return sys.exit( + "can't find any python installation matching %r" % args.python) + os.putenv('PYTHON', PYTHON) + win_colorprint("using " + PYTHON) + + if not args.command or args.command == 'help': + parser.print_help(sys.stderr) + sys.exit(1) + + fname = args.command.replace('-', '_') + fun = getattr(sys.modules[__name__], fname) # err if fun not defined + funargs = [] + # mandatory args + if args.command in ('test-by-name', 'test-script'): + if not args.arg: + sys.exit('command needs an argument') + funargs = [args.arg] + # optional args + if args.command == 'test' and args.arg: + funargs = [args.arg] + fun(*funargs) if __name__ == '__main__': diff --git a/third_party/python/psutil/scripts/iotop.py b/third_party/python/psutil/scripts/iotop.py index 6a5d3fa96e5a..c3afd0715162 100755 --- a/third_party/python/psutil/scripts/iotop.py +++ b/third_party/python/psutil/scripts/iotop.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/killall.py b/third_party/python/psutil/scripts/killall.py index f9cc9201856f..7bbcd75a88d3 100755 --- a/third_party/python/psutil/scripts/killall.py +++ b/third_party/python/psutil/scripts/killall.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/meminfo.py b/third_party/python/psutil/scripts/meminfo.py index 0b15ff2b75ea..550fcd0128f5 100755 --- a/third_party/python/psutil/scripts/meminfo.py +++ b/third_party/python/psutil/scripts/meminfo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/netstat.py b/third_party/python/psutil/scripts/netstat.py index 490b429f232e..fe3bfe402c53 100755 --- a/third_party/python/psutil/scripts/netstat.py +++ b/third_party/python/psutil/scripts/netstat.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -41,7 +41,7 @@ def main(): "Proto", "Local address", "Remote address", "Status", "PID", "Program name")) proc_names = {} - for p in psutil.process_iter(attrs=['pid', 'name']): + for p in psutil.process_iter(['pid', 'name']): proc_names[p.info['pid']] = p.info['name'] for c in psutil.net_connections(kind='inet'): laddr = "%s:%s" % (c.laddr) diff --git a/third_party/python/psutil/scripts/nettop.py b/third_party/python/psutil/scripts/nettop.py index 45b8879faca0..ce647c9dd391 100755 --- a/third_party/python/psutil/scripts/nettop.py +++ b/third_party/python/psutil/scripts/nettop.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $ # diff --git a/third_party/python/psutil/scripts/pidof.py b/third_party/python/psutil/scripts/pidof.py index bcb8a2e6da14..ee18aae4c5b5 100755 --- a/third_party/python/psutil/scripts/pidof.py +++ b/third_party/python/psutil/scripts/pidof.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -18,7 +18,7 @@ import sys def pidof(pgname): pids = [] - for proc in psutil.process_iter(attrs=['name', 'cmdline']): + for proc in psutil.process_iter(['name', 'cmdline']): # search for matches in the process name and cmdline if proc.info['name'] == pgname or \ proc.info['cmdline'] and proc.info['cmdline'][0] == pgname: diff --git a/third_party/python/psutil/scripts/pmap.py b/third_party/python/psutil/scripts/pmap.py index 300f23e9d275..5f7246a159bf 100755 --- a/third_party/python/psutil/scripts/pmap.py +++ b/third_party/python/psutil/scripts/pmap.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/procinfo.py b/third_party/python/psutil/scripts/procinfo.py index 161b50575fca..01974513f035 100755 --- a/third_party/python/psutil/scripts/procinfo.py +++ b/third_party/python/psutil/scripts/procinfo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/procsmem.py b/third_party/python/psutil/scripts/procsmem.py index f660c085fc99..259d79d42f2b 100755 --- a/third_party/python/psutil/scripts/procsmem.py +++ b/third_party/python/psutil/scripts/procsmem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -64,7 +64,7 @@ def main(): with p.oneshot(): try: mem = p.memory_full_info() - info = p.as_dict(attrs=["cmdline", "username"]) + info = p.as_dict(["cmdline", "username"]) except psutil.AccessDenied: ad_pids.append(p.pid) except psutil.NoSuchProcess: diff --git a/third_party/python/psutil/scripts/ps.py b/third_party/python/psutil/scripts/ps.py index 8467cca6f389..540c032a7f4c 100755 --- a/third_party/python/psutil/scripts/ps.py +++ b/third_party/python/psutil/scripts/ps.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/pstree.py b/third_party/python/psutil/scripts/pstree.py index 8e4c9f9572f0..0005e4a18161 100755 --- a/third_party/python/psutil/scripts/pstree.py +++ b/third_party/python/psutil/scripts/pstree.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/sensors.py b/third_party/python/psutil/scripts/sensors.py index bbf3ac90882f..726af3d2c55d 100755 --- a/third_party/python/psutil/scripts/sensors.py +++ b/third_party/python/psutil/scripts/sensors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. diff --git a/third_party/python/psutil/scripts/temperatures.py b/third_party/python/psutil/scripts/temperatures.py index 15b9156b88a5..e83df44036f6 100755 --- a/third_party/python/psutil/scripts/temperatures.py +++ b/third_party/python/psutil/scripts/temperatures.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. diff --git a/third_party/python/psutil/scripts/top.py b/third_party/python/psutil/scripts/top.py index 69890e27ce3e..0b17471d55fd 100755 --- a/third_party/python/psutil/scripts/top.py +++ b/third_party/python/psutil/scripts/top.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/who.py b/third_party/python/psutil/scripts/who.py index 748d936c9496..c2299eb09efd 100755 --- a/third_party/python/psutil/scripts/who.py +++ b/third_party/python/psutil/scripts/who.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/scripts/winservices.py b/third_party/python/psutil/scripts/winservices.py index 677248359a1f..8792f752e4ac 100755 --- a/third_party/python/psutil/scripts/winservices.py +++ b/third_party/python/psutil/scripts/winservices.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be diff --git a/third_party/python/psutil/setup.py b/third_party/python/psutil/setup.py index 693bd89d87c5..2402a143c4bd 100755 --- a/third_party/python/psutil/setup.py +++ b/third_party/python/psutil/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -6,10 +6,14 @@ """Cross-platform lib for process and system monitoring in Python.""" +from __future__ import print_function import contextlib import io import os import platform +import re +import shutil +import struct import sys import tempfile import warnings @@ -25,12 +29,13 @@ with warnings.catch_warnings(): HERE = os.path.abspath(os.path.dirname(__file__)) -# ...so we can import _common.py +# ...so we can import _common.py and _compat.py sys.path.insert(0, os.path.join(HERE, "psutil")) from _common import AIX # NOQA from _common import BSD # NOQA from _common import FREEBSD # NOQA +from _common import hilite # NOQA from _common import LINUX # NOQA from _common import MACOS # NOQA from _common import NETBSD # NOQA @@ -38,6 +43,8 @@ from _common import OPENBSD # NOQA from _common import POSIX # NOQA from _common import SUNOS # NOQA from _common import WINDOWS # NOQA +from _compat import PY3 # NOQA +from _compat import which # NOQA macros = [] @@ -46,17 +53,19 @@ if POSIX: if BSD: macros.append(("PSUTIL_BSD", 1)) +# Needed to determine _Py_PARSE_PID in case it's missing (Python 2, PyPy). +# Taken from Lib/test/test_fcntl.py. +# XXX: not bullet proof as the (long long) case is missing. +if struct.calcsize('l') <= 8: + macros.append(('PSUTIL_SIZEOF_PID_T', '4')) # int +else: + macros.append(('PSUTIL_SIZEOF_PID_T', '8')) # long + + sources = ['psutil/_psutil_common.c'] if POSIX: sources.append('psutil/_psutil_posix.c') -tests_require = [] -if sys.version_info[:2] <= (2, 6): - tests_require.append('unittest2') -if sys.version_info[:2] <= (2, 7): - tests_require.append('mock') -if sys.version_info[:2] <= (3, 2): - tests_require.append('ipaddress') extras_require = {} if sys.version_info[:2] <= (3, 3): @@ -103,6 +112,13 @@ def silenced_output(stream_name): setattr(sys, stream_name, orig) +def missdeps(msg): + s = hilite("C compiler or Python headers are not installed ", ok=False) + s += hilite("on this system. Try to run:\n", ok=False) + s += hilite(msg, ok=False, bold=True) + print(s, file=sys.stderr) + + if WINDOWS: def get_winver(): maj, min = sys.getwindowsversion()[0:2] @@ -129,12 +145,15 @@ if WINDOWS: 'psutil._psutil_windows', sources=sources + [ 'psutil/_psutil_windows.c', + 'psutil/arch/windows/process_utils.c', 'psutil/arch/windows/process_info.c', 'psutil/arch/windows/process_handles.c', + 'psutil/arch/windows/disk.c', + 'psutil/arch/windows/net.c', + 'psutil/arch/windows/cpu.c', 'psutil/arch/windows/security.c', - 'psutil/arch/windows/inet_ntop.c', 'psutil/arch/windows/services.c', - 'psutil/arch/windows/global.c', + 'psutil/arch/windows/socks.c', 'psutil/arch/windows/wmi.c', ], define_macros=macros, @@ -142,7 +161,7 @@ if WINDOWS: "psapi", "kernel32", "advapi32", "shell32", "netapi32", "wtsapi32", "ws2_32", "PowrProf", "pdh", ], - # extra_compile_args=["/Z7"], + # extra_compile_args=["/W 4"], # extra_link_args=["/DEBUG"] ) @@ -205,20 +224,22 @@ elif LINUX: suffix='.c', delete=False, mode="wt") as f: f.write("#include ") + output_dir = tempfile.mkdtemp() try: compiler = UnixCCompiler() + # https://github.com/giampaolo/psutil/pull/1568 + if os.getenv('CC'): + compiler.set_executable('compiler_so', os.getenv('CC')) with silenced_output('stderr'): with silenced_output('stdout'): - compiler.compile([f.name]) + compiler.compile([f.name], output_dir=output_dir) except CompileError: return ("PSUTIL_ETHTOOL_MISSING_TYPES", 1) else: return None finally: - try: - os.remove(f.name) - except OSError: - pass + os.remove(f.name) + shutil.rmtree(output_dir) macros.append(("PSUTIL_LINUX", 1)) ETHTOOL_MACRO = get_ethtool_macro() @@ -240,7 +261,7 @@ elif SUNOS: ], define_macros=macros, libraries=['kstat', 'nsl', 'socket']) -# AIX + elif AIX: macros.append(("PSUTIL_AIX", 1)) ext = Extension( @@ -263,10 +284,28 @@ if POSIX: define_macros=macros, sources=sources) if SUNOS: + def get_sunos_update(): + # See https://serverfault.com/q/524883 + # for an explanation of Solaris /etc/release + with open('/etc/release') as f: + update = re.search(r'(?<=s10s_u)[0-9]{1,2}', f.readline()) + if update is None: + return 0 + else: + return int(update.group(0)) + posix_extension.libraries.append('socket') if platform.release() == '5.10': + # Detect Solaris 5.10, update >= 4, see: + # https://github.com/giampaolo/psutil/pull/1638 + if get_sunos_update() >= 4: + # MIB compliancy starts with SunOS 5.10 Update 4: + posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1)) posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c') posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1)) + else: + # Other releases are by default considered to be new mib compliant. + posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1)) elif AIX: posix_extension.sources.append('psutil/arch/aix/ifaddrs.c') @@ -295,7 +334,6 @@ def main(): license='BSD', packages=['psutil', 'psutil.tests'], ext_modules=extensions, - # see: python setup.py register --list-classifiers classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -305,9 +343,16 @@ def main(): 'Intended Audience :: System Administrators', 'License :: OSI Approved :: BSD License', 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows :: Windows NT/2000', + 'Operating System :: Microsoft :: Windows :: Windows 10', + 'Operating System :: Microsoft :: Windows :: Windows 7', + 'Operating System :: Microsoft :: Windows :: Windows 8', + 'Operating System :: Microsoft :: Windows :: Windows 8.1', + 'Operating System :: Microsoft :: Windows :: Windows Server 2003', + 'Operating System :: Microsoft :: Windows :: Windows Server 2008', + 'Operating System :: Microsoft :: Windows :: Windows Vista', 'Operating System :: Microsoft', 'Operating System :: OS Independent', + 'Operating System :: POSIX :: AIX', 'Operating System :: POSIX :: BSD :: FreeBSD', 'Operating System :: POSIX :: BSD :: NetBSD', 'Operating System :: POSIX :: BSD :: OpenBSD', @@ -326,8 +371,10 @@ def main(): 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries', 'Topic :: System :: Benchmark', + 'Topic :: System :: Hardware :: Hardware Drivers', 'Topic :: System :: Hardware', 'Topic :: System :: Monitoring', + 'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog', 'Topic :: System :: Networking :: Monitoring', 'Topic :: System :: Networking', 'Topic :: System :: Operating System', @@ -338,12 +385,41 @@ def main(): if setuptools is not None: kwargs.update( python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", - test_suite="psutil.tests.get_suite", - tests_require=tests_require, extras_require=extras_require, zip_safe=False, ) - setup(**kwargs) + success = False + try: + setup(**kwargs) + success = True + finally: + if not success and POSIX and not which('gcc'): + py3 = "3" if PY3 else "" + if LINUX: + if which('dpkg'): + missdeps("sudo apt-get install gcc python%s-dev" % py3) + elif which('rpm'): + missdeps("sudo yum install gcc python%s-devel" % py3) + elif MACOS: + print(hilite("XCode (https://developer.apple.com/xcode/) " + "is not installed"), ok=False, file=sys.stderr) + elif FREEBSD: + missdeps("pkg install gcc python%s" % py3) + elif OPENBSD: + missdeps("pkg_add -v gcc python%s" % py3) + elif NETBSD: + missdeps("pkgin install gcc python%s" % py3) + elif SUNOS: + missdeps("sudo ln -s /usr/bin/gcc /usr/local/bin/cc && " + "pkg install gcc") + elif not success and WINDOWS: + if PY3: + ur = "http://www.visualstudio.com/en-au/news/vs2015-preview-vs" + else: + ur = "http://www.microsoft.com/en-us/download/" + ur += "details.aspx?id=44266" + s = "VisualStudio is not installed; get it from %s" % ur + print(hilite(s, ok=False), file=sys.stderr) if __name__ == '__main__': diff --git a/third_party/python/psutil/tox.ini b/third_party/python/psutil/tox.ini index 947fbeda7b74..b148642ba82c 100644 --- a/third_party/python/psutil/tox.ini +++ b/third_party/python/psutil/tox.ini @@ -5,7 +5,7 @@ # directory. [tox] -envlist = py26, py27, py34, py35, py36, py37, lint +envlist = py26, py27, py34, py35, py36, py37, py38, lint [testenv] deps = @@ -18,7 +18,7 @@ deps = setenv = TOX = 1 -commands = python psutil/tests/__main__.py +commands = python psutil/tests/runner.py usedevelop = True diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in index 7aaa02001e29..b537233bbc3c 100644 --- a/third_party/python/requirements.in +++ b/third_party/python/requirements.in @@ -9,7 +9,7 @@ mozilla-version==0.3.0 pathlib2==2.3.2 pip-tools==3.9.0 pipenv==2018.5.18 -psutil==5.6.3 +psutil==5.7.0 pytest==3.6.2 python-hglib==2.4 redo==2.0.3 diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt index 021f1596b202..ee556c41205b 100644 --- a/third_party/python/requirements.txt +++ b/third_party/python/requirements.txt @@ -64,17 +64,18 @@ pluggy==0.6.0 \ --hash=sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c \ --hash=sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5 \ # via pytest -psutil==5.6.3 \ - --hash=sha256:028a1ec3c6197eadd11e7b46e8cc2f0720dc18ac6d7aabdb8e8c0d6c9704f000 \ - --hash=sha256:12542c3642909f4cd1928a2fba59e16fa27e47cbeea60928ebb62a8cbd1ce123 \ - --hash=sha256:503e4b20fa9d3342bcf58191bbc20a4a5ef79ca7df8972e6197cc14c5513e73d \ - --hash=sha256:863a85c1c0a5103a12c05a35e59d336e1d665747e531256e061213e2e90f63f3 \ - --hash=sha256:954f782608bfef9ae9f78e660e065bd8ffcfaea780f9f2c8a133bb7cb9e826d7 \ - --hash=sha256:b6e08f965a305cd84c2d07409bc16fbef4417d67b70c53b299116c5b895e3f45 \ - --hash=sha256:bc96d437dfbb8865fc8828cf363450001cb04056bbdcdd6fc152c436c8a74c61 \ - --hash=sha256:cf49178021075d47c61c03c0229ac0c60d5e2830f8cab19e2d88e579b18cdb76 \ - --hash=sha256:d5350cb66690915d60f8b233180f1e49938756fb2d501c93c44f8fb5b970cc63 \ - --hash=sha256:eba238cf1989dfff7d483c029acb0ac4fcbfc15de295d682901f0e2497e6781a +psutil==5.7.0 \ + --hash=sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058 \ + --hash=sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953 \ + --hash=sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4 \ + --hash=sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e \ + --hash=sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f \ + --hash=sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38 \ + --hash=sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e \ + --hash=sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8 \ + --hash=sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26 \ + --hash=sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5 \ + --hash=sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310 py==1.5.4 \ --hash=sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7 \ --hash=sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e \