Bug 1436857 - Upgrade psutil from version 3.1.1 to 5.4.3. draft
authorHenrik Skupin <mail@hskupin.info>
Thu, 08 Feb 2018 23:22:16 +0100
changeset 754413 d476296bb98af6f25ca05dc62986814e0f4ce376
parent 754399 38b3c1d03a594664c6b32c35533734283c258f43
push id98845
push userbmo:hskupin@gmail.com
push dateTue, 13 Feb 2018 12:36:28 +0000
bugs1436857
milestone60.0a1
Bug 1436857 - Upgrade psutil from version 3.1.1 to 5.4.3. MozReview-Commit-ID: FQTGv4jPE83
testing/mozharness/scripts/desktop_unittest.py
testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt
third_party/python/psutil/CREDITS
third_party/python/psutil/DEVGUIDE.rst
third_party/python/psutil/HISTORY.rst
third_party/python/psutil/IDEAS
third_party/python/psutil/INSTALL.rst
third_party/python/psutil/MANIFEST.in
third_party/python/psutil/Makefile
third_party/python/psutil/PKG-INFO
third_party/python/psutil/README.rst
third_party/python/psutil/TODO
third_party/python/psutil/docs/Makefile
third_party/python/psutil/docs/README
third_party/python/psutil/docs/_static/css/custom.css
third_party/python/psutil/docs/_static/logo.png
third_party/python/psutil/docs/_template/globaltoc.html
third_party/python/psutil/docs/_template/indexcontent.html
third_party/python/psutil/docs/_template/indexsidebar.html
third_party/python/psutil/docs/_template/page.html
third_party/python/psutil/docs/_themes/pydoctheme/static/pydoctheme.css
third_party/python/psutil/docs/_themes/pydoctheme/theme.conf
third_party/python/psutil/docs/conf.py
third_party/python/psutil/docs/index.rst
third_party/python/psutil/docs/make.bat
third_party/python/psutil/docs/xxx
third_party/python/psutil/examples/disk_usage.py
third_party/python/psutil/examples/free.py
third_party/python/psutil/examples/ifconfig.py
third_party/python/psutil/examples/iotop.py
third_party/python/psutil/examples/killall.py
third_party/python/psutil/examples/meminfo.py
third_party/python/psutil/examples/netstat.py
third_party/python/psutil/examples/nettop.py
third_party/python/psutil/examples/pidof.py
third_party/python/psutil/examples/pmap.py
third_party/python/psutil/examples/process_detail.py
third_party/python/psutil/examples/ps.py
third_party/python/psutil/examples/pstree.py
third_party/python/psutil/examples/top.py
third_party/python/psutil/examples/who.py
third_party/python/psutil/make.bat
third_party/python/psutil/psutil.egg-info/PKG-INFO
third_party/python/psutil/psutil.egg-info/SOURCES.txt
third_party/python/psutil/psutil.egg-info/dependency_links.txt
third_party/python/psutil/psutil.egg-info/top_level.txt
third_party/python/psutil/psutil/DEVNOTES
third_party/python/psutil/psutil/__init__.py
third_party/python/psutil/psutil/_common.py
third_party/python/psutil/psutil/_compat.py
third_party/python/psutil/psutil/_exceptions.py
third_party/python/psutil/psutil/_psaix.py
third_party/python/psutil/psutil/_psbsd.py
third_party/python/psutil/psutil/_pslinux.py
third_party/python/psutil/psutil/_psosx.py
third_party/python/psutil/psutil/_psposix.py
third_party/python/psutil/psutil/_pssunos.py
third_party/python/psutil/psutil/_psutil_bsd.c
third_party/python/psutil/psutil/_psutil_bsd.h
third_party/python/psutil/psutil/_psutil_common.c
third_party/python/psutil/psutil/_psutil_common.h
third_party/python/psutil/psutil/_psutil_linux.c
third_party/python/psutil/psutil/_psutil_linux.h
third_party/python/psutil/psutil/_psutil_osx.c
third_party/python/psutil/psutil/_psutil_osx.h
third_party/python/psutil/psutil/_psutil_posix.c
third_party/python/psutil/psutil/_psutil_posix.h
third_party/python/psutil/psutil/_psutil_sunos.c
third_party/python/psutil/psutil/_psutil_sunos.h
third_party/python/psutil/psutil/_psutil_windows.c
third_party/python/psutil/psutil/_psutil_windows.h
third_party/python/psutil/psutil/_pswindows.py
third_party/python/psutil/psutil/arch/aix/common.c
third_party/python/psutil/psutil/arch/aix/common.h
third_party/python/psutil/psutil/arch/aix/ifaddrs.c
third_party/python/psutil/psutil/arch/aix/ifaddrs.h
third_party/python/psutil/psutil/arch/aix/net_connections.c
third_party/python/psutil/psutil/arch/aix/net_connections.h
third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h
third_party/python/psutil/psutil/arch/bsd/process_info.c
third_party/python/psutil/psutil/arch/bsd/process_info.h
third_party/python/psutil/psutil/arch/freebsd/proc_socks.c
third_party/python/psutil/psutil/arch/freebsd/proc_socks.h
third_party/python/psutil/psutil/arch/freebsd/specific.c
third_party/python/psutil/psutil/arch/freebsd/specific.h
third_party/python/psutil/psutil/arch/freebsd/sys_socks.c
third_party/python/psutil/psutil/arch/freebsd/sys_socks.h
third_party/python/psutil/psutil/arch/netbsd/socks.c
third_party/python/psutil/psutil/arch/netbsd/socks.h
third_party/python/psutil/psutil/arch/netbsd/specific.c
third_party/python/psutil/psutil/arch/netbsd/specific.h
third_party/python/psutil/psutil/arch/openbsd/specific.c
third_party/python/psutil/psutil/arch/openbsd/specific.h
third_party/python/psutil/psutil/arch/osx/process_info.c
third_party/python/psutil/psutil/arch/osx/process_info.h
third_party/python/psutil/psutil/arch/solaris/environ.c
third_party/python/psutil/psutil/arch/solaris/environ.h
third_party/python/psutil/psutil/arch/solaris/v10/ifaddrs.c
third_party/python/psutil/psutil/arch/solaris/v10/ifaddrs.h
third_party/python/psutil/psutil/arch/windows/inet_ntop.c
third_party/python/psutil/psutil/arch/windows/inet_ntop.h
third_party/python/psutil/psutil/arch/windows/ntextapi.h
third_party/python/psutil/psutil/arch/windows/process_handles.c
third_party/python/psutil/psutil/arch/windows/process_handles.h
third_party/python/psutil/psutil/arch/windows/process_info.c
third_party/python/psutil/psutil/arch/windows/process_info.h
third_party/python/psutil/psutil/arch/windows/security.c
third_party/python/psutil/psutil/arch/windows/services.c
third_party/python/psutil/psutil/arch/windows/services.h
third_party/python/psutil/psutil/tests/README.rst
third_party/python/psutil/psutil/tests/__init__.py
third_party/python/psutil/psutil/tests/__main__.py
third_party/python/psutil/psutil/tests/test_bsd.py
third_party/python/psutil/psutil/tests/test_connections.py
third_party/python/psutil/psutil/tests/test_contracts.py
third_party/python/psutil/psutil/tests/test_linux.py
third_party/python/psutil/psutil/tests/test_memory_leaks.py
third_party/python/psutil/psutil/tests/test_misc.py
third_party/python/psutil/psutil/tests/test_osx.py
third_party/python/psutil/psutil/tests/test_posix.py
third_party/python/psutil/psutil/tests/test_process.py
third_party/python/psutil/psutil/tests/test_sunos.py
third_party/python/psutil/psutil/tests/test_system.py
third_party/python/psutil/psutil/tests/test_unicode.py
third_party/python/psutil/psutil/tests/test_windows.py
third_party/python/psutil/scripts/battery.py
third_party/python/psutil/scripts/cpu_distribution.py
third_party/python/psutil/scripts/disk_usage.py
third_party/python/psutil/scripts/fans.py
third_party/python/psutil/scripts/free.py
third_party/python/psutil/scripts/ifconfig.py
third_party/python/psutil/scripts/internal/README
third_party/python/psutil/scripts/internal/bench_oneshot.py
third_party/python/psutil/scripts/internal/bench_oneshot_2.py
third_party/python/psutil/scripts/internal/check_broken_links.py
third_party/python/psutil/scripts/internal/download_exes.py
third_party/python/psutil/scripts/internal/generate_manifest.py
third_party/python/psutil/scripts/internal/print_timeline.py
third_party/python/psutil/scripts/internal/winmake.py
third_party/python/psutil/scripts/iotop.py
third_party/python/psutil/scripts/killall.py
third_party/python/psutil/scripts/meminfo.py
third_party/python/psutil/scripts/netstat.py
third_party/python/psutil/scripts/nettop.py
third_party/python/psutil/scripts/pidof.py
third_party/python/psutil/scripts/pmap.py
third_party/python/psutil/scripts/procinfo.py
third_party/python/psutil/scripts/procsmem.py
third_party/python/psutil/scripts/ps.py
third_party/python/psutil/scripts/pstree.py
third_party/python/psutil/scripts/sensors.py
third_party/python/psutil/scripts/temperatures.py
third_party/python/psutil/scripts/top.py
third_party/python/psutil/scripts/who.py
third_party/python/psutil/scripts/winservices.py
third_party/python/psutil/setup.cfg
third_party/python/psutil/setup.py
third_party/python/psutil/test/README.rst
third_party/python/psutil/test/_bsd.py
third_party/python/psutil/test/_linux.py
third_party/python/psutil/test/_osx.py
third_party/python/psutil/test/_posix.py
third_party/python/psutil/test/_sunos.py
third_party/python/psutil/test/_windows.py
third_party/python/psutil/test/test_memory_leaks.py
third_party/python/psutil/test/test_psutil.py
third_party/python/psutil/tox.ini
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -332,17 +332,17 @@ class DesktopUnittest(TestingMixin, Merc
             self.abs_res_dir = abs_app_dir
         return self.abs_res_dir
 
     @PreScriptAction('create-virtualenv')
     def _pre_create_virtualenv(self, action):
         dirs = self.query_abs_dirs()
 
         self.register_virtualenv_module(name='pip>=1.5')
-        self.register_virtualenv_module('psutil==3.1.1', method='pip')
+        self.register_virtualenv_module('psutil==5.4.3', method='pip')
         self.register_virtualenv_module(name='mock')
         self.register_virtualenv_module(name='simplejson')
 
         requirements_files = [os.path.join(dirs['abs_test_install_dir'],
                               'config', 'marionette_requirements.txt')]
 
         if self._query_specified_suites('mochitest') is not None:
             # mochitest is the only thing that needs this
--- a/testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt
+++ b/testing/tools/websocketprocessbridge/websocketprocessbridge_requirements.txt
@@ -1,13 +1,13 @@
 twisted==10.2.0
 
 # websocket adapter for twisted, might be built into twisted someday
 txws==0.9.1
 
-psutil==3.1.1
+psutil==5.4.3
 
 # needed by txws, but pypi doesn't know about it
 six==1.10.0
 
 # Needed by iceserver
 ipaddr==2.1.11
 passlib==1.6.5
--- a/third_party/python/psutil/CREDITS
+++ b/third_party/python/psutil/CREDITS
@@ -15,30 +15,69 @@ Really thanks to all of you.
 Author
 ======
 
 N: Giampaolo Rodola'
 C: Italy
 E: g.rodola@gmail.com
 W: http://grodola.blogspot.com/
 
+Experts
+=======
+
+Github usernames of people to CC on github when in need of help.
+
+- NetBSD:
+    - 0-wiz-0, Thomas Klausner
+    - ryoqun, Ryo Onodera
+- OpenBSD:
+    - landryb, Landry Breuil
+- FreeBSD:
+    - glebius, Gleb Smirnoff (#1013)
+    - sunpoet, Po-Chuan Hsieh (pkg maintainer, #1105)
+    - kostikbel, Konstantin Belousov (#1105)
+- OSX:
+    - whitlockjc, Jeremy Whitlock
+- Windows:
+    - mrjefftang, Jeff Tang
+    - wj32, Wen Jia Liu
+    - fbenkstein, Frank Benkstein
+- SunOS:
+    - wiggin15, Arnon Yaari
+    - alxchk, Oleksii Shevchuk
+- AIX:
+    - wiggin15, Arnon Yaari (maintainer)
+
 Contributors
 ============
 
 N: Jay Loden
 C: NJ, USA
 E: jloden@gmail.com
 D: original co-author, initial design/bootstrap and occasional bug fixes
 W: http://www.jayloden.com
 
+N: Arnon Yaari (wiggin15)
+W: https://github.com/wiggin15
+I: 517, 607, 610, 1131, 1123, 1130, 1154, 1164, 1174, 1177
+
+N: Jeff Tang
+W: https://github.com/mrjefftang
+I: 340, 529, 616, 653, 654, 648, 641
+
 N: Jeremy Whitlock
 E: jcscoobyrs@gmail.com
 D: great help with OSX C development.
 I: 125, 150, 174, 206
 
+N: Landry Breuil
+W: https://github.com/landryb
+D: OpenBSD implementation.
+I: 615
+
 N: wj32
 E: wj32.64@gmail.com
 D: process username() and get_connections() on Windows
 I: 114, 115
 
 N: Yan Raber
 C: Bologna, Italy
 E: yanraber@gmail.com
@@ -51,16 +90,24 @@ I: 18
 
 N: Dave Daeschler
 C: USA
 E: david.daeschler@gmail.com
 W: http://daviddaeschler.com
 D: some contributions to initial design/bootstrap plus occasional bug fixing
 I: 522, 536
 
+N: Thomas Klausner
+W: https://github.com/0-wiz-0
+I: #557
+
+N: Ryo Onodera
+W: https://github.com/ryoon
+I: #557
+
 N: cjgohlke
 E: cjgohlke@gmail.com
 D: Windows 64 bit support
 I: 107
 
 N: Jeffery Kline
 E: jeffery.kline@gmail.com
 I: 130
@@ -248,32 +295,29 @@ I: 497
 N: Szigeti Gabor Niif
 E: szigeti.gabor.niif@gmail.com
 I: 446
 
 N: msabramo
 E: msabramo@gmail.com
 I: 492
 
-N: Jeff Tang
-W: https://github.com/mrjefftang
-I: 340, 529, 616, 653, 654
-
 N: Yaolong Huang
 E: airekans@gmail.com
 W: http://airekans.github.io/
 I: 530
 
 N: Anders Chrigström
 W: https://github.com/anders-chrigstrom
 I: 496
 
 N: spacewander
+W: https://github.com/spacewander
 E: spacewanderlzx@gmail.com
-I: 561
+I: 561, 603
 
 N: Sylvain Mouquet
 E: sylvain.mouquet@gmail.com
 I: 565
 
 N: karthikrev
 I: 568
 
@@ -303,8 +347,171 @@ N: John Burnett
 W: http://www.johnburnett.com/
 C: Irvine, CA, US
 I: 614
 
 N: Árni Már Jónsson
 E: Reykjavik, Iceland
 E: https://github.com/arnimarj
 I: 634
+
+N: Bart van Kleef
+W: https://github.com/bkleef
+I: 664
+
+N: Steven Winfield
+W: https://github.com/stevenwinfield
+I: 672
+
+N: sk6249
+W: https://github.com/sk6249
+I: 670
+
+N: maozguttman
+W: https://github.com/maozguttman
+I: 659
+
+N: dasumin
+W: https://github.com/dasumin
+I: 541
+
+N: Mike Sarahan
+W: https://github.com/msarahan
+I: 688
+
+N: Syohei YOSHIDA
+W: https://github.com/syohex
+I: 730
+
+N: Frank Benkstein
+W: https://github.com/fbenkstein
+I: 732, 733
+
+N: Visa Hankala
+E: visa@openbsd.org
+I: 741
+
+N: Sebastian-Gabriel Brestin
+C: Romania
+E: sebastianbrestin@gmail.com
+I: 704
+
+N: Timmy Konick
+W: https://github.com/tijko
+I: 751
+
+N: mpderbec
+W: https://github.com/mpderbec
+I: 660
+
+N: Mozilla Foundation
+D: sample code for process USS memory.
+
+N: wxwright
+W: https://github.com/wxwright
+I: 776
+
+N: Farhan Khan
+E: khanzf@gmail.com
+I: 823
+
+N: Jake Omann
+E: https://github.com/jomann09
+I: 816, 775
+
+N: Jeremy Humble
+W: https://github.com/jhumble
+I: 863
+
+N: Ilya Georgievsky
+W: https://github.com/xBeAsTx
+I: 870
+
+N: Yago Jesus
+W: https://github.com/YJesus
+I: 798
+
+N: Andre Caron
+C: Montreal, QC, Canada
+E: andre.l.caron@gmail.com
+W: https://github.com/AndreLouisCaron
+I: 880
+
+N: ewedlund
+W: https://github.com/ewedlund
+I: 874
+
+N: Arcadiy Ivanov
+W: https://github.com/arcivanov
+I: 919
+
+N: Max Bélanger
+W: https://github.com/maxbelanger
+I: 936, 1133
+
+N: Pierre Fersing
+C: France
+E: pierre.fersing@bleemeo.com
+I: 950
+
+N: Thiago Borges Abdnur
+W: https://github.com/bolaum
+I: 959
+
+N: Nicolas Hennion
+W: https://github.com/nicolargo
+I: 974
+
+N: Baruch Siach
+W: https://github.com/baruchsiach
+I: 872
+
+N: Danek Duvall
+W: https://github.com/dhduvall
+I: 1002
+
+N: Alexander Hasselhuhn
+C: Germany
+W: https://github.com/alexanha
+
+N: Himanshu Shekhar
+W: https://github.com/himanshub16
+I: 1036
+
+N: Yannick Gingras
+W: https://github.com/ygingras
+I: 1057
+
+N: Gleb Smirnoff
+W: https://github.com/glebius
+I: 1042, 1079
+
+N: Oleksii Shevchuk
+W: https://github.com/alxchk
+I: 1077, 1093, 1091
+
+N: Prodesire
+W: https://github.com/Prodesire
+I: 1138
+
+N: Sebastian Saip
+W: https://github.com/ssaip
+I: 1141
+
+N: Jakub Bacic
+W: https://github.com/jakub-bacic
+I: 1127
+
+N: Akos Kiss
+W: https://github.com/akosthekiss
+I: 1150
+
+N: Adrian Page
+W: https://github.com/adpag
+I: 1159, 1160, 1161
+
+N: Matthew Long
+W: https://github.com/matray
+I: 1167
+
+N: janderbrain
+W: https://github.com/janderbrain
+I: 1169
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/DEVGUIDE.rst
@@ -0,0 +1,209 @@
+=======================
+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:
+
+.. code-block:: bash
+
+  make setup-dev-env
+
+- run tests:
+
+.. code-block:: bash
+
+  make test
+
+- bear in mind that ``make``
+  (see `Makefile <https://github.com/giampaolo/psutil/blob/master/Makefile>`_)
+  is the designated tool to run tests, build, install etc. and that it is also
+  available on Windows
+  (see `make.bat <https://github.com/giampaolo/psutil/blob/master/make.bat>`_).
+- do not use ``sudo``; ``make install`` installs psutil as a limited user in
+  "edit" mode; also ``make setup-dev-env`` installs deps as a limited user.
+- use `make help` to see the list of available commands.
+
+============
+Coding style
+============
+
+- python code strictly follows `PEP 8 <https://www.python.org/dev/peps/pep-0008/>`_
+  styling guides and this is enforced by ``make install-git-hooks``.
+- C code strictly follows `PEP 7 <https://www.python.org/dev/peps/pep-0007/>`_
+  styling guides.
+
+========
+Makefile
+========
+
+Some useful make commands:
+
+.. code-block:: bash
+
+    make install        # install
+    make setup-dev-env  # install useful dev libs (pyflakes, unittest2, etc.)
+    make test           # run unit tests
+    make test-memleaks  # run memory leak tests
+    make test-coverage  # run test coverage
+    make flake8         # run PEP8 linter
+
+There are some differences between ``make`` on UNIX and Windows.
+For instance, to run a specific Python version. On UNIX:
+
+.. code-block:: bash
+
+    make test PYTHON=python3.5
+
+On Windows:
+
+.. code-block:: bat
+
+    set PYTHON=C:\python35\python.exe && make test
+
+...or:
+
+.. code-block:: bat
+
+    make -p 35 test
+
+If you want to modify psutil and run a script on the fly which uses it do
+(on UNIX):
+
+.. code-block:: bash
+
+    make test TSCRIPT=foo.py
+
+On Windows:
+
+.. code-block:: bat
+
+    set TSCRIPT=foo.py && make test
+
+====================
+Adding a new feature
+====================
+
+Usually the files involved when adding a new functionality are:
+
+.. code-block:: bash
+
+    psutil/__init__.py                   # main psutil namespace
+    psutil/_ps{platform}.py              # python platform wrapper
+    psutil/_psutil_{platform}.c          # C platform extension
+    psutil/_psutil_{platform}.h          # C header file
+    psutil/tests/test_process|system.py  # main test suite
+    psutil/tests/test_{platform}.py      # platform specific test suite
+
+Typical process occurring when adding a new functionality (API):
+
+- define the new function in ``psutil/__init__.py``.
+- write the platform specific implementation in ``psutil/_ps{platform}.py``
+  (e.g. ``psutil/_pslinux.py``).
+- if the change requires C, write the C implementation in
+  ``psutil/_psutil_{platform}.c`` (e.g. ``psutil/_psutil_linux.c``).
+- write a generic test in ``psutil/tests/test_system.py`` or
+  ``psutil/tests/test_process.py``.
+- if possible, write a platform specific test in
+  ``psutil/tests/test_{platform}.py`` (e.g. ``test_linux.py``).
+  This usually means testing the return value of the new feature against
+  a system CLI tool.
+- update doc in ``doc/index.py``.
+- update ``HISTORY.rst``.
+- update ``README.rst`` (if necessary).
+- make a pull request.
+
+===================
+Make a pull request
+===================
+
+- fork psutil
+- create your feature branch (``git checkout -b my-new-feature``)
+- commit your changes (``git commit -am 'add some feature'``)
+- push to the branch (``git push origin my-new-feature``)
+- create a new pull request
+
+======================
+Continuous integration
+======================
+
+All of the services listed below are automatically run on ``git push``.
+
+Unit tests
+----------
+
+Tests are automatically run for every GIT push on **Linux**, **OSX** and
+**Windows** by using:
+
+- `Travis <https://travis-ci.org/giampaolo/psutil>`_ (Linux, OSX)
+- `Appveyor <https://ci.appveyor.com/project/giampaolo/psutil>`_ (Windows)
+
+Test files controlling these are
+`.travis.yml <https://github.com/giampaolo/psutil/blob/master/.travis.yml>`_
+and
+`appveyor.yml <https://github.com/giampaolo/psutil/blob/master/appveyor.yml>`_.
+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/%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)
+
+OSX, BSD, AIX and Solaris are currently tested manually (sigh!).
+
+Test coverage
+-------------
+
+Test coverage is provided by `coveralls.io <https://coveralls.io/github/giampaolo/psutil>`_,
+it is controlled via `.travis.yml <https://github.com/giampaolo/psutil/blob/master/.travis.yml>`_
+and it is updated on every git push.
+An icon in the home page (README) always shows the last coverage percentage:
+
+.. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github
+    :target: https://coveralls.io/github/giampaolo/psutil?branch=master
+    :alt: Test coverage (coverall.io)
+
+=============
+Documentation
+=============
+
+- doc source code is written in a single file: `/docs/index.rst <https://raw.githubusercontent.com/giampaolo/psutil/master/docs/index.rst>`_.
+- it uses `RsT syntax <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_
+  and it's built with `sphinx <http://sphinx-doc.org/>`_.
+- doc can be built with ``make setup-dev-env; cd docs; make html``.
+- public doc is hosted on http://psutil.readthedocs.io/
+
+=======================
+Releasing a new version
+=======================
+
+These are notes for myself (Giampaolo):
+
+- ``make release``
+- post announce (``make print-announce``) on psutil and python-announce mailing
+  lists, twitter, g+, blog.
+
+=============
+FreeBSD notes
+=============
+
+- setup:
+
+.. code-block:: bash
+
+    pkg install python python3 gcc git vim screen bash
+    chsh -s /usr/local/bin/bash user  # set bash as default shell
+
+- ``/usr/src`` contains the source codes for all installed CLI tools (grep in it).
--- a/third_party/python/psutil/HISTORY.rst
+++ b/third_party/python/psutil/HISTORY.rst
@@ -1,245 +1,931 @@
-Bug tracker at https://github.com/giampaolo/psutil/issues
-
-3.1.1 - 2015-07-15
-==================
+*Bug tracker at https://github.com/giampaolo/psutil/issues*
+
+5.4.3
+=====
+
+*2018-01-01*
+
+**Enhancements**
+
+- 775_: disk_partitions() on Windows return mount points.
 
 **Bug fixes**
 
-- #645: [Linux] psutil.cpu_times_percent() may produce negative results.
-- #656: 'from psutil import *' does not work.
-
-
-3.1.0 - 2015-07-15
-==================
+- 1193_: pids() may return False on OSX.
+
+5.4.2
+=====
+
+*2017-12-07*
+
+**Enhancements**
+
+- 1173_: introduced PSUTIL_DEBUG environment variable which can be set in order
+  to print useful debug messages on stderr (useful in case of nasty errors).
+- 1177_: added support for sensors_battery() on OSX.  (patch by Arnon Yaari)
+- 1183_: Process.children() is 2x faster on UNIX and 2.4x faster on Linux.
+- 1188_: deprecated method Process.memory_info_ex() now warns by using
+  FutureWarning instead of DeprecationWarning.
+
+**Bug fixes**
+
+- 1152_: [Windows] disk_io_counters() may return an empty dict.
+- 1169_: [Linux] users() "hostname" returns username instead.  (patch by
+  janderbrain)
+- 1172_: [Windows] `make test` does not work.
+- 1179_: [Linux] Process.cmdline() is now able to splits cmdline args for
+  misbehaving processes which overwrite /proc/pid/cmdline and use spaces
+  instead of null bytes as args separator.
+- 1181_: [OSX] Process.memory_maps() may raise ENOENT.
+- 1187_: [OSX] pids() does not return PID 0 on recent OSX versions.
+
+5.4.1
+=====
+
+*2017-11-08*
 
 **Enhancements**
 
-- #534: [Linux] disk_partitions() added support for ZFS filesystems.
-- #646: continuous tests integration for Windows with
-  https://ci.appveyor.com/project/giampaolo/psutil.
-- #647: new dev guide:
-  https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst
-- #651: continuous code quality test integration with
-  https://scrutinizer-ci.com/g/giampaolo/psutil/
+- 1164_: [AIX] add support for Process.num_ctx_switches().  (patch by Arnon
+  Yaari)
+- 1053_: abandon Python 3.3 support (psutil still works but it's no longer
+  tested).
+
+**Bug fixes**
+
+- 1150_: [Windows] when a process is terminate()d now the exit code is set to
+  SIGTERM instead of 0.  (patch by Akos Kiss)
+- 1151_: python -m psutil.tests fail
+- 1154_: [AIX] psutil won't compile on AIX 6.1.0.  (patch by Arnon Yaari)
+- 1167_: [Windows] net_io_counter() packets count now include also non-unicast
+  packets.  (patch by Matthew Long)
+
+5.4.0
+=====
+
+*2017-10-12*
+
+**Enhancements**
+
+- 1123_: [AIX] added support for AIX platform.  (patch by Arnon Yaari)
 
 **Bug fixes**
 
-- #340: [Windows] Process.open_files() no longer hangs. Instead it uses a
-  thred which times out and skips the file handle in case it's taking too long
-  to be retrieved.  (patch by Jeff Tang, PR #597)
-- #627: [Windows] Process.name() no longer raises AccessDenied for pids owned
-  by another user.
-- #636: [Windows] Process.memory_info() raise AccessDenied.
-- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it
-  will affect os.getpid()'s process group instead of PID 0.
-- #639: [Linux] Process.cmdline() can be truncated.
-- #640: [Linux] *connections functions may swallow errors and return an
-  incomplete list of connnections.
-- #642: repr() of exceptions is incorrect.
-- #653: [Windows] Add inet_ntop function for Windows XP to support IPv6.
-- #641: [Windows] Replace deprecated string functions with safe equivalents.
-
-
-3.0.1 - 2015-06-18
-==================
+- 1009_: [Linux] sensors_temperatures() may crash with IOError.
+- 1012_: [Windows] disk_io_counters()'s read_time and write_time were expressed
+  in tens of micro seconds instead of milliseconds.
+- 1127_: [OSX] invalid reference counting in Process.open_files() may lead to
+  segfault.  (patch by Jakub Bacic)
+- 1129_: [Linux] sensors_fans() may crash with IOError.  (patch by Sebastian
+  Saip)
+- 1131_: [SunOS] fix compilation warnings.  (patch by Arnon Yaari)
+- 1133_: [Windows] can't compile on newer versions of Visual Studio 2017 15.4.
+  (patch by Max Bélanger)
+- 1138_: [Linux] can't compile on CentOS 5.0 and RedHat 5.0.
+  (patch by Prodesire)
+
+5.3.1
+=====
+
+*2017-09-10*
+
+**Enhancements**
+
+- 1124_: documentation moved to http://psutil.readthedocs.io
+
+**Bug fixes**
+
+- 1105_: [FreeBSD] psutil does not compile on FreeBSD 12.
+- 1125_: [BSD] net_connections() raises TypeError.
+
+**Compatibility notes**
+
+- 1120_: .exe files for Windows are no longer uploaded on PYPI as per PEP-527;
+  only wheels are provided.
+
+5.3.0
+=====
+
+*2017-09-01*
+
+**Enhancements**
+
+- 802_: disk_io_counters() and net_io_counters() numbers no longer wrap
+  (restart from 0). Introduced a new "nowrap" argument.
+- 928_: psutil.net_connections() and psutil.Process.connections() "laddr" and
+  "raddr" are now named tuples.
+- 1015_: swap_memory() now relies on /proc/meminfo instead of sysinfo() syscall
+  so that it can be used in conjunction with PROCFS_PATH in order to retrieve
+  memory info about Linux containers such as Docker and Heroku.
+- 1022_: psutil.users() provides a new "pid" field.
+- 1025_: process_iter() accepts two new parameters in order to invoke
+  Process.as_dict(): "attrs" and "ad_value". With this you can iterate over all
+  processes in one shot without needing to catch NoSuchProcess and do list/dict
+  comprehensions.
+- 1040_: implemented full unicode support.
+- 1051_: disk_usage() on Python 3 is now able to accept bytes.
+- 1058_: test suite now enables all warnings by default.
+- 1060_: source distribution is dynamically generated so that it only includes
+  relevant files.
+- 1079_: [FreeBSD] net_connections()'s fd number is now being set for real
+  (instead of -1).  (patch by Gleb Smirnoff)
+- 1091_: [SunOS] implemented Process.environ().  (patch by Oleksii Shevchuk)
 
 **Bug fixes**
 
-- #632: [Linux] better error message if cannot parse process UNIX connections.
-- #634: [Linux] Proces.cmdline() does not include empty string arguments.
-- #635: [UNIX] crash on module import if 'enum' package is installed on python
-  < 3.4.
-
-
-3.0.0 - 2015-06-13
-==================
+- 989_: [Windows] boot_time() may return a negative value.
+- 1007_: [Windows] boot_time() can have a 1 sec fluctuation between calls; the
+  value of the first call is now cached so that boot_time() always returns the
+  same value if fluctuation is <= 1 second.
+- 1013_: [FreeBSD] psutil.net_connections() may return incorrect PID.  (patch
+  by Gleb Smirnoff)
+- 1014_: [Linux] Process class can mask legitimate ENOENT exceptions as
+  NoSuchProcess.
+- 1016_: disk_io_counters() raises RuntimeError on a system with no disks.
+- 1017_: net_io_counters() raises RuntimeError on a system with no network
+  cards installed.
+- 1021_: [Linux] open_files() may erroneously raise NoSuchProcess instead of
+  skipping a file which gets deleted while open files are retrieved.
+- 1029_: [OSX, FreeBSD] Process.connections('unix') on Python 3 doesn't
+  properly handle unicode paths and may raise UnicodeDecodeError.
+- 1033_: [OSX, FreeBSD] memory leak for net_connections() and
+  Process.connections() when retrieving UNIX sockets (kind='unix').
+- 1040_: fixed many unicode related issues such as UnicodeDecodeError on
+  Python 3 + UNIX and invalid encoded data on Windows.
+- 1042_: [FreeBSD] psutil won't compile on FreeBSD 12.
+- 1044_: [OSX] different Process methods incorrectly raise AccessDenied for
+  zombie processes.
+- 1046_: [Windows] disk_partitions() on Windows overrides user's SetErrorMode.
+- 1047_: [Windows] Process username(): memory leak in case exception is thrown.
+- 1048_: [Windows] users()'s host field report an invalid IP address.
+- 1050_: [Windows] Process.memory_maps memory() leaks memory.
+- 1055_: cpu_count() is no longer cached; this is useful on systems such as
+  Linux where CPUs can be disabled at runtime. This also reflects on
+  Process.cpu_percent() which no longer uses the cache.
+- 1058_: fixed Python warnings.
+- 1062_: disk_io_counters() and net_io_counters() raise TypeError if no disks
+  or NICs are installed on the system.
+- 1063_: [NetBSD] net_connections() may list incorrect sockets.
+- 1064_: [NetBSD] swap_memory() may segfault in case of error.
+- 1065_: [OpenBSD] Process.cmdline() may raise SystemError.
+- 1067_: [NetBSD] Process.cmdline() leaks memory if process has terminated.
+- 1069_: [FreeBSD] Process.cpu_num() may return 255 for certain kernel
+  processes.
+- 1071_: [Linux] cpu_freq() may raise IOError on old RedHat distros.
+- 1074_: [FreeBSD] sensors_battery() raises OSError in case of no battery.
+- 1075_: [Windows] net_if_addrs(): inet_ntop() return value is not checked.
+- 1077_: [SunOS] net_if_addrs() shows garbage addresses on SunOS 5.10.
+  (patch by Oleksii Shevchuk)
+- 1077_: [SunOS] net_connections() does not work on SunOS 5.10. (patch by
+  Oleksii Shevchuk)
+- 1079_: [FreeBSD] net_connections() didn't list locally connected sockets.
+  (patch by Gleb Smirnoff)
+- 1085_: cpu_count() return value is now checked and forced to None if <= 1.
+- 1087_: Process.cpu_percent() guard against cpu_count() returning None and
+  assumes 1 instead.
+- 1093_: [SunOS] memory_maps() shows wrong 64 bit addresses.
+- 1094_: [Windows] psutil.pid_exists() may lie. Also, all process APIs relying
+  on OpenProcess Windows API now check whether the PID is actually running.
+- 1098_: [Windows] Process.wait() may erroneously return sooner, when the PID
+  is still alive.
+- 1099_: [Windows] Process.terminate() may raise AccessDenied even if the
+  process already died.
+- 1101_: [Linux] sensors_temperatures() may raise ENODEV.
+
+**Porting notes**
+
+- 1039_: returned types consolidation:
+  - Windows / Process.cpu_times(): fields #3 and #4 were int instead of float
+  - Linux / FreeBSD: connections('unix'): raddr is now set to "" instead of
+    None
+  - OpenBSD: connections('unix'): laddr and raddr are now set to "" instead of
+    None
+- 1040_: all strings are encoded by using OS fs encoding.
+- 1040_: the following Windows APIs on Python 2 now return a string instead of
+  unicode:
+  - Process.memory_maps().path
+  - WindowsService.bin_path()
+  - WindowsService.description()
+  - WindowsService.display_name()
+  - WindowsService.username()
+
+5.2.2
+=====
+
+*2017-04-10*
+
+**Bug fixes**
+
+- 1000_: fixed some setup.py warnings.
+- 1002_: [SunOS] remove C macro which will not be available on new Solaris
+  versions. (patch by Danek Duvall)
+- 1004_: [Linux] Process.io_counters() may raise ValueError.
+- 1006_: [Linux] cpu_freq() may return None on some Linux versions does not
+  support the function; now the function is not declared instead.
+- 1009_: [Linux] sensors_temperatures() may raise OSError.
+- 1010_: [Linux] virtual_memory() may raise ValueError on Ubuntu 14.04.
+
+5.2.1
+=====
+
+*2017-03-24*
+
+**Bug fixes**
+
+- 981_: [Linux] cpu_freq() may return an empty list.
+- 993_: [Windows] Process.memory_maps() on Python 3 may raise
+  UnicodeDecodeError.
+- 996_: [Linux] sensors_temperatures() may not show all temperatures.
+- 997_: [FreeBSD] virtual_memory() may fail due to missing sysctl parameter on
+  FreeBSD 12.
+
+5.2.0
+=====
+
+*2017-03-05*
 
 **Enhancements**
 
-- #250: new psutil.net_if_stats() returning NIC statistics (isup, duplex,
-  speed, MTU).
-- #376: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig.
-- #469: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants
-  returned by psutil.Process' ionice() and nice() methods are enums instead of
-  plain integers.
-- #581: add .gitignore. (patch by Gabi Davar)
-- #582: connection constants returned by psutil.net_connections() and
-  psutil.Process.connections() were turned from int to enums on Python > 3.4.
-- #587: Move native extension into the package.
-- #589: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
-  not only lists.
-- #594: all deprecated APIs were removed.
-- #599: [Windows] process name() can now be determined for all processes even
-  when running as a limited user.
-- #602: pre-commit GIT hook.
-- #629: enhanced support for py.test and nose test discovery and tests run.
-- #616: [Windows] Add inet_ntop function for Windows XP.
+- 971_: [Linux] Add psutil.sensors_fans() function.  (patch by Nicolas Hennion)
+- 976_: [Windows] Process.io_counters() has 2 new fields: *other_count* and
+  *other_bytes*.
+- 976_: [Linux] Process.io_counters() has 2 new fields: *read_chars* and
+  *write_chars*.
+
+**Bug fixes**
+
+- 872_: [Linux] can now compile on Linux by using MUSL C library.
+- 985_: [Windows] Fix a crash in `Process.open_files` when the worker thread for `NtQueryObject` times out.
+- 986_: [Linux] Process.cwd() may raise NoSuchProcess instead of ZombieProcess.
+
+5.1.3
+=====
+
+**Bug fixes**
+
+- 971_: [Linux] sensors_temperatures() didn't work on CentOS 7.
+- 973_: cpu_percent() may raise ZeroDivisionError.
+
+5.1.2
+=====
+
+*2017-02-03*
+
+**Bug fixes**
+
+- 966_: [Linux] sensors_battery().power_plugged may erroneously return None on
+  Python 3.
+- 968_: [Linux] disk_io_counters() raises TypeError on python 3.
+- 970_: [Linux] sensors_battery()'s name and label fields on Python 3 are bytes
+  instead of str.
+
+5.1.1
+=====
+
+*2017-02-03*
+
+**Enhancements**
+
+- 966_: [Linux] sensors_battery().percent is a float and is more precise.
+
+**Bug fixes**
+
+- 964_: [Windows] Process.username() and psutil.users() may return badly
+  decoding character on Python 3.
+- 965_: [Linux] disk_io_counters() may miscalculate sector size and report the
+  wrong number of bytes read and written.
+- 966_: [Linux] sensors_battery() may fail with "no such file error".
+- 966_: [Linux] sensors_battery().power_plugged may lie.
+
+5.1.0
+=====
+
+*2017-02-01*
+
+**Enhancements**
+
+- 357_: added psutil.Process.cpu_num() (what CPU a process is on).
+- 371_: added psutil.sensors_temperatures() (Linux only).
+- 941_: added psutil.cpu_freq() (CPU frequency).
+- 955_: added psutil.sensors_battery() (Linux, Windows, only).
+- 956_: cpu_affinity([]) can now be used as an alias to set affinity against
+  all eligible CPUs.
 
 **Bug fixes**
 
-- #428: [all UNIXes except Linux] correct handling of zombie processes;
-  introduced new ZombieProcess exception class.
-- #512: [BSD] fix segfault in net_connections().
-- #555: [Linux] psutil.users() correctly handles ":0" as an alias for
-  "localhost"
-- #579: [Windows] Fixed open_files() for PID>64K.
-- #579: [Windows] fixed many compiler warnings.
-- #585: [FreeBSD] net_connections() may raise KeyError.
-- #586: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU
-  number is provided.
-- #593: [FreeBSD] Process().memory_maps() segfaults.
-- #606: Process.parent() may swallow NoSuchProcess exceptions.
-- #611: [SunOS] net_io_counters has send and received swapped
-- #614: [Linux]: cpu_count(logical=False) return the number of physical CPUs
-  instead of physical cores.
-- #618: [SunOS] swap tests fail on Solaris when run as normal user
-- #628: [Linux] Process.name() truncates process name in case it contains
-  spaces or parentheses.
-
-
-2.2.1 - 2015-02-02
-==================
+- 687_: [Linux] pid_exists() no longer returns True if passed a process thread
+  ID.
+- 948_: cannot install psutil with PYTHONOPTIMIZE=2.
+- 950_: [Windows] Process.cpu_percent() was calculated incorrectly and showed
+  higher number than real usage.
+- 951_: [Windows] the uploaded wheels for Python 3.6 64 bit didn't work.
+- 959_: psutil exception objects could not be pickled.
+- 960_: Popen.wait() did not return the correct negative exit status if process
+  is ``kill()``ed by a signal.
+- 961_: [Windows] WindowsService.description() may fail with
+  ERROR_MUI_FILE_NOT_FOUND.
+
+5.0.1
+=====
+
+*2016-12-21*
+
+**Enhancements**
+
+- 939_: tar.gz distribution went from 1.8M to 258K.
+- 811_: [Windows] provide a more meaningful error message if trying to use
+  psutil on unsupported Windows XP.
+
+**Bug fixes**
+
+- 609_: [SunOS] psutil does not compile on Solaris 10.
+- 936_: [Windows] fix compilation error on VS 2013 (patch by Max Bélanger).
+- 940_: [Linux] cpu_percent() and cpu_times_percent() was calculated
+  incorrectly as "iowait", "guest" and "guest_nice" times were not properly
+  taken into account.
+- 944_: [OpenBSD] psutil.pids() was omitting PID 0.
+
+5.0.0
+=====
+
+*2016-11-06*
+
+**Enhncements**
+
+- 799_: new Process.oneshot() context manager making Process methods around
+  +2x faster in general and from +2x to +6x faster on Windows.
+- 943_: better error message in case of version conflict on import.
+
+**Bug fixes**
+
+- 932_: [NetBSD] net_connections() and Process.connections() may fail without
+  raising an exception.
+- 933_: [Windows] memory leak in cpu_stats() and WindowsService.description().
+
+4.4.2
+=====
+
+*2016-10-26*
+
+**Bug fixes**
+
+- 931_: psutil no longer compiles on Solaris.
+
+4.4.1
+=====
+
+*2016-10-25*
+
+**Bug fixes**
+
+- 927_: ``Popen.__del__`` may cause maximum recursion depth error.
+
+4.4.0
+=====
+
+*2016-10-23*
+
+**Enhancements**
+
+- 874_: [Windows] net_if_addrs() returns also the netmask.
+- 887_: [Linux] virtual_memory()'s 'available' and 'used' values are more
+  precise and match "free" cmdline utility.  "available" also takes into
+  account LCX containers preventing "available" to overflow "total".
+- 891_: procinfo.py script has been updated and provides a lot more info.
+
+**Bug fixes**
+
+- 514_: [OSX] possibly fix Process.memory_maps() segfault (critical!).
+- 783_: [OSX] Process.status() may erroneously return "running" for zombie
+  processes.
+- 798_: [Windows] Process.open_files() returns and empty list on Windows 10.
+- 825_: [Linux] cpu_affinity; fix possible double close and use of unopened
+  socket.
+- 880_: [Windows] Handle race condition inside psutil_net_connections.
+- 885_: ValueError is raised if a negative integer is passed to cpu_percent()
+  functions.
+- 892_: [Linux] Process.cpu_affinity([-1]) raise SystemError with no error
+  set; now ValueError is raised.
+- 906_: [BSD] disk_partitions(all=False) returned an empty list. Now the
+  argument is ignored and all partitions are always returned.
+- 907_: [FreeBSD] Process.exe() may fail with OSError(ENOENT).
+- 908_: [OSX, BSD] different process methods could errounesuly mask the real
+  error for high-privileged PIDs and raise NoSuchProcess and AccessDenied
+  instead of OSError and RuntimeError.
+- 909_: [OSX] Process open_files() and connections() methods may raise
+  OSError with no exception set if process is gone.
+- 916_: [OSX] fix many compilation warnings.
+
+4.3.1
+=====
+
+*2016-09-01*
+
+**Enhancements**
+
+- 881_: "make install" now works also when using a virtual env.
 
 **Bug fixes**
 
-- #496: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references"
-  (patch by Bruno Binet)
-
-
-2.2.0 - 2015-01-06
-==================
+- 854_: Process.as_dict() raises ValueError if passed an erroneous attrs name.
+- 857_: [SunOS] Process cpu_times(), cpu_percent(), threads() amd memory_maps()
+  may raise RuntimeError if attempting to query a 64bit process with a 32bit
+  python. "Null" values are returned as a fallback.
+- 858_: Process.as_dict() should not return memory_info_ex() because it's
+  deprecated.
+- 863_: [Windows] memory_map truncates addresses above 32 bits
+- 866_: [Windows] win_service_iter() and services in general are not able to
+  handle unicode service names / descriptions.
+- 869_: [Windows] Process.wait() may raise TimeoutExpired with wrong timeout
+  unit (ms instead of sec).
+- 870_: [Windows] Handle leak inside psutil_get_process_data.
+
+4.3.0
+=====
+
+*2016-06-18*
+
+**Enhancements**
+
+- 819_: [Linux] different speedup improvements:
+  Process.ppid() is 20% faster
+  Process.status() is 28% faster
+  Process.name() is 25% faster
+  Process.num_threads is 20% faster on Python 3
+
+**Bug fixes**
+
+- 810_: [Windows] Windows wheels are incompatible with pip 7.1.2.
+- 812_: [NetBSD] fix compilation on NetBSD-5.x.
+- 823_: [NetBSD] virtual_memory() raises TypeError on Python 3.
+- 829_: [UNIX] psutil.disk_usage() percent field takes root reserved space
+  into account.
+- 816_: [Windows] fixed net_io_counter() values wrapping after 4.3GB in
+  Windows Vista (NT 6.0) and above using 64bit values from newer win APIs.
+
+4.2.0
+=====
+
+*2016-05-14*
 
 **Enhancements**
 
-- #521: drop support for Python 2.4 and 2.5.
-- #553: new examples/pstree.py script.
-- #564: C extension version mismatch in case the user messed up with psutil
-  installation or with sys.path is now detected at import time.
-- #568: New examples/pidof.py script.
-- #569: [FreeBSD] add support for process CPU affinity.
+- 795_: [Windows] new APIs to deal with Windows services: win_service_iter()
+  and win_service_get().
+- 800_: [Linux] psutil.virtual_memory() returns a new "shared" memory field.
+- 819_: [Linux] speedup /proc parsing:
+  - Process.ppid() is 20% faster
+  - Process.status() is 28% faster
+  - Process.name() is 25% faster
+  - Process.num_threads is 20% faster on Python 3
+
+**Bug fixes**
+
+- 797_: [Linux] net_if_stats() may raise OSError for certain NIC cards.
+- 813_: Process.as_dict() should ignore extraneous attribute names which gets
+  attached to the Process instance.
+
+4.1.0
+=====
+
+*2016-03-12*
+
+**Enhancements**
+
+- 777_: [Linux] Process.open_files() on Linux return 3 new fields: position,
+  mode and flags.
+- 779_: Process.cpu_times() returns two new fields, 'children_user' and
+  'children_system' (always set to 0 on OSX and Windows).
+- 789_: [Windows] psutil.cpu_times() return two new fields: "interrupt" and
+  "dpc". Same for psutil.cpu_times_percent().
+- 792_: new psutil.cpu_stats() function returning number of CPU ctx switches
+  interrupts, soft interrupts and syscalls.
+
+**Bug fixes**
+
+- 774_: [FreeBSD] net_io_counters() dropout is no longer set to 0 if the kernel
+  provides it.
+- 776_: [Linux] Process.cpu_affinity() may erroneously raise NoSuchProcess.
+  (patch by wxwright)
+- 780_: [OSX] psutil does not compile with some gcc versions.
+- 786_: net_if_addrs() may report incomplete MAC addresses.
+- 788_: [NetBSD] virtual_memory()'s buffers and shared values were set to 0.
+- 790_: [OSX] psutil won't compile on OSX 10.4.
+
+4.0.0
+=====
+
+*2016-02-17*
+
+**Enhancements**
+
+- 523_: [Linux, FreeBSD] disk_io_counters() return a new "busy_time" field.
+- 660_: [Windows] make.bat is smarter in finding alternative VS install
+  locations.  (patch by mpderbec)
+- 732_: Process.environ().  (patch by Frank Benkstein)
+- 753_: [Linux, OSX, Windows] Process USS and PSS (Linux) "real" memory stats.
+  (patch by Eric Rahm)
+- 755_: Process.memory_percent() "memtype" parameter.
+- 758_: tests now live in psutil namespace.
+- 760_: expose OS constants (psutil.LINUX, psutil.OSX, etc.)
+- 756_: [Linux] disk_io_counters() return 2 new fields: read_merged_count and
+  write_merged_count.
+- 762_: new scripts/procsmem.py script.
 
 **Bug fixes**
 
-- #496: [Solaris] can't import psutil.
-- #547: [UNIX] Process.username() may raise KeyError if UID can't be resolved.
-- #551: [Windows] get rid of the unicode hack for net_io_counters() NIC names.
-- #556: [Linux] lots of file handles were left open.
-- #561: [Linux] net_connections() might skip some legitimate UNIX sockets.
-  (patch by spacewander)
-- #565: [Windows] use proper encoding for psutil.Process.username() and
-  psutil.users(). (patch by Sylvain Mouquet)
-- #567: [Linux] in the alternative implementation of CPU affinity PyList_Append
-  and Py_BuildValue return values are not checked.
-- #569: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False).
-- #571: [Linux] Process.open_files() might swallow AccessDenied exceptions and
-  return an incomplete list of open files.
-
-
-2.1.3 - 2014-09-26
-==================
-
-- #536: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error.
-
-
-2.1.2 - 2014-09-21
-==================
+- 685_: [Linux] virtual_memory() provides wrong results on systems with a lot
+  of physical memory.
+- 704_: [Solaris] psutil does not compile on Solaris sparc.
+- 734_: on Python 3 invalid UTF-8 data is not correctly handled for process
+  name(), cwd(), exe(), cmdline() and open_files() methods resulting in
+  UnicodeDecodeError exceptions. 'surrogateescape' error handler is now
+  used as a workaround for replacing the corrupted data.
+- 737_: [Windows] when the bitness of psutil and the target process was
+  different cmdline() and cwd() could return a wrong result or incorrectly
+  report an AccessDenied error.
+- 741_: [OpenBSD] psutil does not compile on mips64.
+- 751_: [Linux] fixed call to Py_DECREF on possible Null object.
+- 754_: [Linux] cmdline() can be wrong in case of zombie process.
+- 759_: [Linux] Process.memory_maps() may return paths ending with " (deleted)"
+- 761_: [Windows] psutil.boot_time() wraps to 0 after 49 days.
+- 764_: [NetBSD] fix compilation on NetBSD-6.x.
+- 766_: [Linux] net_connections() can't handle malformed /proc/net/unix file.
+- 767_: [Linux] disk_io_counters() may raise ValueError on 2.6 kernels and it's
+  broken on 2.4 kernels.
+- 770_: [NetBSD] disk_io_counters() metrics didn't update.
+
+3.4.2
+=====
+
+*2016-01-20*
+
+**Enhancements**
+
+- 728_: [Solaris] exposed psutil.PROCFS_PATH constant to change the default
+  location of /proc filesystem.
+
+**Bug fixes**
+
+- 724_: [FreeBSD] psutil.virtual_memory().total is incorrect.
+- 730_: [FreeBSD] psutil.virtual_memory() crashes.
+
+3.4.1
+=====
+
+*2016-01-15*
 
 **Enhancements**
 
-- #407: project moved from Google Code to Github; code moved from Mercurial
-  to Git.
-- #492: use tox to run tests on multiple python versions.  (patch by msabramo)
-- #505: [Windows] distribution as wheel packages.
-- #511: new examples/ps.py sample code.
+- 557_: [NetBSD] added NetBSD support.  (contributed by Ryo Onodera and
+  Thomas Klausner)
+- 708_: [Linux] psutil.net_connections() and Process.connections() on Python 2
+  can be up to 3x faster in case of many connections.
+  Also psutil.Process.memory_maps() is slightly faster.
+- 718_: process_iter() is now thread safe.
+
+**Bug fixes**
+
+- 714_: [OpenBSD] virtual_memory().cached value was always set to 0.
+- 715_: don't crash at import time if cpu_times() fail for some reason.
+- 717_: [Linux] Process.open_files fails if deleted files still visible.
+- 722_: [Linux] swap_memory() no longer crashes if sin/sout can't be determined
+  due to missing /proc/vmstat.
+- 724_: [FreeBSD] virtual_memory().total is slightly incorrect.
+
+3.3.0
+=====
+
+*2015-11-25*
+
+**Enhancements**
+
+- 558_: [Linux] exposed psutil.PROCFS_PATH constant to change the default
+  location of /proc filesystem.
+- 615_: [OpenBSD] added OpenBSD support.  (contributed by Landry Breuil)
+
+**Bug fixes**
+
+- 692_: [UNIX] Process.name() is no longer cached as it may change.
+
+3.2.2
+=====
+
+*2015-10-04*
+
+**Bug fixes**
+
+- 517_: [SunOS] net_io_counters failed to detect network interfaces
+  correctly on Solaris 10
+- 541_: [FreeBSD] disk_io_counters r/w times were expressed in seconds instead
+  of milliseconds.  (patch by dasumin)
+- 610_: [SunOS] fix build and tests on Solaris 10
+- 623_: [Linux] process or system connections raises ValueError if IPv6 is not
+  supported by the system.
+- 678_: [Linux] can't install psutil due to bug in setup.py.
+- 688_: [Windows] compilation fails with MSVC 2015, Python 3.5. (patch by
+  Mike Sarahan)
+
+3.2.1
+=====
+
+*2015-09-03*
+
+**Bug fixes**
+
+- 677_: [Linux] can't install psutil due to bug in setup.py.
+
+3.2.0
+=====
+
+*2015-09-02*
+
+**Enhancements**
+
+- 644_: [Windows] added support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals
+  to use with Process.send_signal().
+- 648_: CI test integration for OSX. (patch by Jeff Tang)
+- 663_: [UNIX] net_if_addrs() now returns point-to-point (VPNs) addresses.
+- 655_: [Windows] different issues regarding unicode handling were fixed. On
+  Python 2 all APIs returning a string will now return an encoded version of it
+  by using sys.getfilesystemencoding() codec. The APIs involved are:
+  - psutil.net_if_addrs()
+  - psutil.net_if_stats()
+  - psutil.net_io_counters()
+  - psutil.Process.cmdline()
+  - psutil.Process.name()
+  - psutil.Process.username()
+  - psutil.users()
 
 **Bug fixes**
 
-- #340: [Windows] Process.get_open_files() no longer hangs.  (patch by
-  Jeff Tang)
-- #501: [Windows] disk_io_counters() may return negative values.
-- #503: [Linux] in rare conditions Process exe(), open_files() and
-  connections() methods can raise OSError(ESRCH) instead of NoSuchProcess.
-- #504: [Linux] can't build RPM packages via setup.py
-- #506: [Linux] python 2.4 support was broken.
-- #522: [Linux] Process.cpu_affinity() might return EINVAL.  (patch by David
-  Daeschler)
-- #529: [Windows] Process.exe() may raise unhandled WindowsError exception
-  for PIDs 0 and 4.  (patch by Jeff Tang)
-- #530: [Linux] psutil.disk_io_counters() may crash on old Linux distros
-  (< 2.6.5)  (patch by Yaolong Huang)
-- #533: [Linux] Process.memory_maps() may raise TypeError on old Linux distros.
-
-
-2.1.1 - 2014-04-30
-==================
+- 513_: [Linux] fixed integer overflow for RLIM_INFINITY.
+- 641_: [Windows] fixed many compilation warnings.  (patch by Jeff Tang)
+- 652_: [Windows] net_if_addrs() UnicodeDecodeError in case of non-ASCII NIC
+  names.
+- 655_: [Windows] net_if_stats() UnicodeDecodeError in case of non-ASCII NIC
+  names.
+- 659_: [Linux] compilation error on Suse 10. (patch by maozguttman)
+- 664_: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef)
+- 670_: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names.
+  (patch by sk6249)
+- 672_: [Windows] compilation fails if using Windows SDK v8.0. (patch by
+  Steven Winfield)
+- 675_: [Linux] net_connections(); UnicodeDecodeError may occur when listing
+  UNIX sockets.
+
+3.1.1
+=====
+
+*2015-07-15*
+
+**Bug fixes**
+
+- 603_: [Linux] ionice_set value range is incorrect.  (patch by spacewander)
+- 645_: [Linux] psutil.cpu_times_percent() may produce negative results.
+- 656_: 'from psutil import *' does not work.
+
+3.1.0
+=====
+
+*2015-07-15*
+
+**Enhancements**
+
+- 534_: [Linux] disk_partitions() added support for ZFS filesystems.
+- 646_: continuous tests integration for Windows with
+  https://ci.appveyor.com/project/giampaolo/psutil.
+- 647_: new dev guide:
+  https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst
+- 651_: continuous code quality test integration with scrutinizer-ci.com
+
+**Bug fixes**
+
+- 340_: [Windows] Process.open_files() no longer hangs. Instead it uses a
+  thred which times out and skips the file handle in case it's taking too long
+  to be retrieved.  (patch by Jeff Tang, PR #597)
+- 627_: [Windows] Process.name() no longer raises AccessDenied for pids owned
+  by another user.
+- 636_: [Windows] Process.memory_info() raise AccessDenied.
+- 637_: [UNIX] raise exception if trying to send signal to Process PID 0 as it
+  will affect os.getpid()'s process group instead of PID 0.
+- 639_: [Linux] Process.cmdline() can be truncated.
+- 640_: [Linux] *connections functions may swallow errors and return an
+  incomplete list of connnections.
+- 642_: repr() of exceptions is incorrect.
+- 653_: [Windows] Add inet_ntop function for Windows XP to support IPv6.
+- 641_: [Windows] Replace deprecated string functions with safe equivalents.
+
+3.0.1
+=====
+
+*2015-06-18*
+
+**Bug fixes**
+
+- 632_: [Linux] better error message if cannot parse process UNIX connections.
+- 634_: [Linux] Proces.cmdline() does not include empty string arguments.
+- 635_: [UNIX] crash on module import if 'enum' package is installed on python
+  < 3.4.
+
+3.0.0
+=====
+
+*2015-06-13*
+
+**Enhancements**
+
+- 250_: new psutil.net_if_stats() returning NIC statistics (isup, duplex,
+  speed, MTU).
+- 376_: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig.
+- 469_: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants
+  returned by psutil.Process' ionice() and nice() methods are enums instead of
+  plain integers.
+- 581_: add .gitignore. (patch by Gabi Davar)
+- 582_: connection constants returned by psutil.net_connections() and
+  psutil.Process.connections() were turned from int to enums on Python > 3.4.
+- 587_: Move native extension into the package.
+- 589_: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
+  not only lists.
+- 594_: all deprecated APIs were removed.
+- 599_: [Windows] process name() can now be determined for all processes even
+  when running as a limited user.
+- 602_: pre-commit GIT hook.
+- 629_: enhanced support for py.test and nose test discovery and tests run.
+- 616_: [Windows] Add inet_ntop function for Windows XP.
 
 **Bug fixes**
 
-- #446: [Windows] fix encoding error when using net_io_counters() on Python 3.
-  (patch by Szigeti Gabor Niif)
-- #460: [Windows] net_io_counters() wraps after 4G.
-- #491: [Linux] psutil.net_connections() exceptions. (patch by Alexander Grothe)
-
-
-2.1.0 - 2014-04-08
-==================
+- 428_: [all UNIXes except Linux] correct handling of zombie processes;
+  introduced new ZombieProcess exception class.
+- 512_: [BSD] fix segfault in net_connections().
+- 555_: [Linux] psutil.users() correctly handles ":0" as an alias for
+  "localhost"
+- 579_: [Windows] Fixed open_files() for PID>64K.
+- 579_: [Windows] fixed many compiler warnings.
+- 585_: [FreeBSD] net_connections() may raise KeyError.
+- 586_: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU
+  number is provided.
+- 593_: [FreeBSD] Process().memory_maps() segfaults.
+- 606_: Process.parent() may swallow NoSuchProcess exceptions.
+- 611_: [SunOS] net_io_counters has send and received swapped
+- 614_: [Linux]: cpu_count(logical=False) return the number of physical CPUs
+  instead of physical cores.
+- 618_: [SunOS] swap tests fail on Solaris when run as normal user
+- 628_: [Linux] Process.name() truncates process name in case it contains
+  spaces or parentheses.
+
+2.2.1
+=====
+
+*2015-02-02*
+
+**Bug fixes**
+
+- 496_: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references"
+  (patch by Bruno Binet)
+
+2.2.0
+=====
+
+*2015-01-06*
 
 **Enhancements**
 
-- #387: system-wide open connections a-la netstat.
+- 521_: drop support for Python 2.4 and 2.5.
+- 553_: new examples/pstree.py script.
+- 564_: C extension version mismatch in case the user messed up with psutil
+  installation or with sys.path is now detected at import time.
+- 568_: New examples/pidof.py script.
+- 569_: [FreeBSD] add support for process CPU affinity.
 
 **Bug fixes**
 
-- #421: [Solaris] psutil does not compile on SunOS 5.10 (patch by Naveed
-  Roudsari)
-- #489: [Linux] psutil.disk_partitions() return an empty list.
-
-
-2.0.0 - 2014-03-10
-==================
+- 496_: [Solaris] can't import psutil.
+- 547_: [UNIX] Process.username() may raise KeyError if UID can't be resolved.
+- 551_: [Windows] get rid of the unicode hack for net_io_counters() NIC names.
+- 556_: [Linux] lots of file handles were left open.
+- 561_: [Linux] net_connections() might skip some legitimate UNIX sockets.
+  (patch by spacewander)
+- 565_: [Windows] use proper encoding for psutil.Process.username() and
+  psutil.users(). (patch by Sylvain Mouquet)
+- 567_: [Linux] in the alternative implementation of CPU affinity PyList_Append
+  and Py_BuildValue return values are not checked.
+- 569_: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False).
+- 571_: [Linux] Process.open_files() might swallow AccessDenied exceptions and
+  return an incomplete list of open files.
+
+2.1.3
+=====
+
+*2014-09-26*
+
+- 536_: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error.
+
+2.1.2
+=====
+
+*2014-09-21*
 
 **Enhancements**
 
-- #424: [Windows] installer for Python 3.X 64 bit.
-- #427: number of logical and physical CPUs (psutil.cpu_count()).
-- #447: psutil.wait_procs() timeout parameter is now optional.
-- #452: make Process instances hashable and usable with set()s.
-- #453: tests on Python < 2.7 require unittest2 module.
-- #459: add a make file for running tests and other repetitive tasks (also
+- 407_: project moved from Google Code to Github; code moved from Mercurial
+  to Git.
+- 492_: use tox to run tests on multiple python versions.  (patch by msabramo)
+- 505_: [Windows] distribution as wheel packages.
+- 511_: new examples/ps.py sample code.
+
+**Bug fixes**
+
+- 340_: [Windows] Process.get_open_files() no longer hangs.  (patch by
+  Jeff Tang)
+- 501_: [Windows] disk_io_counters() may return negative values.
+- 503_: [Linux] in rare conditions Process exe(), open_files() and
+  connections() methods can raise OSError(ESRCH) instead of NoSuchProcess.
+- 504_: [Linux] can't build RPM packages via setup.py
+- 506_: [Linux] python 2.4 support was broken.
+- 522_: [Linux] Process.cpu_affinity() might return EINVAL.  (patch by David
+  Daeschler)
+- 529_: [Windows] Process.exe() may raise unhandled WindowsError exception
+  for PIDs 0 and 4.  (patch by Jeff Tang)
+- 530_: [Linux] psutil.disk_io_counters() may crash on old Linux distros
+  (< 2.6.5)  (patch by Yaolong Huang)
+- 533_: [Linux] Process.memory_maps() may raise TypeError on old Linux distros.
+
+2.1.1
+=====
+
+*2014-04-30*
+
+**Bug fixes**
+
+- 446_: [Windows] fix encoding error when using net_io_counters() on Python 3.
+  (patch by Szigeti Gabor Niif)
+- 460_: [Windows] net_io_counters() wraps after 4G.
+- 491_: [Linux] psutil.net_connections() exceptions. (patch by Alexander Grothe)
+
+2.1.0
+=====
+
+*2014-04-08*
+
+**Enhancements**
+
+- 387_: system-wide open connections a-la netstat.
+
+**Bug fixes**
+
+- 421_: [Solaris] psutil does not compile on SunOS 5.10 (patch by Naveed
+  Roudsari)
+- 489_: [Linux] psutil.disk_partitions() return an empty list.
+
+2.0.0
+=====
+
+*2014-03-10*
+
+**Enhancements**
+
+- 424_: [Windows] installer for Python 3.X 64 bit.
+- 427_: number of logical and physical CPUs (psutil.cpu_count()).
+- 447_: psutil.wait_procs() timeout parameter is now optional.
+- 452_: make Process instances hashable and usable with set()s.
+- 453_: tests on Python < 2.7 require unittest2 module.
+- 459_: add a make file for running tests and other repetitive tasks (also
   on Windows).
-- #463: make timeout parameter of cpu_percent* functions default to 0.0 'cause
+- 463_: make timeout parameter of cpu_percent* functions default to 0.0 'cause
   it's a common trap to introduce slowdowns.
-- #468: move documentation to readthedocs.com.
-- #477: process cpu_percent() is about 30% faster.  (suggested by crusaderky)
-- #478: [Linux] almost all APIs are about 30% faster on Python 3.X.
-- #479: long deprecated psutil.error module is gone; exception classes now
+- 468_: move documentation to readthedocs.com.
+- 477_: process cpu_percent() is about 30% faster.  (suggested by crusaderky)
+- 478_: [Linux] almost all APIs are about 30% faster on Python 3.X.
+- 479_: long deprecated psutil.error module is gone; exception classes now
   live in "psutil" namespace only.
 
 **Bug fixes**
 
-- #193: psutil.Popen constructor can throw an exception if the spawned process
+- 193_: psutil.Popen constructor can throw an exception if the spawned process
   terminates quickly.
-- #340: [Windows] process get_open_files() no longer hangs.  (patch by
+- 340_: [Windows] process get_open_files() no longer hangs.  (patch by
   jtang@vahna.net)
-- #443: [Linux] fix a potential overflow issue for Process.set_cpu_affinity()
+- 443_: [Linux] fix a potential overflow issue for Process.set_cpu_affinity()
   on systems with more than 64 CPUs.
-- #448: [Windows] get_children() and ppid() memory leak (patch by Ulrich
+- 448_: [Windows] get_children() and ppid() memory leak (patch by Ulrich
   Klank).
-- #457: [POSIX] pid_exists() always returns True for PID 0.
-- #461: namedtuples are not pickle-able.
-- #466: [Linux] process exe improper null bytes handling.  (patch by
+- 457_: [POSIX] pid_exists() always returns True for PID 0.
+- 461_: namedtuples are not pickle-able.
+- 466_: [Linux] process exe improper null bytes handling.  (patch by
   Gautam Singh)
-- #470: wait_procs() might not wait.  (patch by crusaderky)
-- #471: [Windows] process exe improper unicode handling. (patch by
+- 470_: wait_procs() might not wait.  (patch by crusaderky)
+- 471_: [Windows] process exe improper unicode handling. (patch by
   alex@mroja.net)
-- #473: psutil.Popen.wait() does not set returncode attribute.
-- #474: [Windows] Process.cpu_percent() is no longer capped at 100%.
-- #476: [Linux] encoding error for process name and cmdline.
+- 473_: psutil.Popen.wait() does not set returncode attribute.
+- 474_: [Windows] Process.cpu_percent() is no longer capped at 100%.
+- 476_: [Linux] encoding error for process name and cmdline.
 
 **API changes**
 
 For the sake of consistency a lot of psutil APIs have been renamed.
 In most cases accessing the old names will work but it will cause a
 DeprecationWarning.
 
 - psutil.* module level constants have being replaced by functions:
@@ -356,264 +1042,276 @@ DeprecationWarning.
   +---------------+-----------------+
 
 - timeout parameter of cpu_percent* functions defaults to 0.0 instead of 0.1.
 - long deprecated psutil.error module is gone; exception classes now live in
   "psutil" namespace only.
 - Process instances' "retcode" attribute returned by psutil.wait_procs() has
   been renamed to "returncode" for consistency with subprocess.Popen.
 
-
-1.2.1 - 2013-11-25
-==================
+1.2.1
+=====
+
+*2013-11-25*
 
 **Bug fixes**
 
-- #348: [Windows XP] fixed "ImportError: DLL load failed" occurring on module
+- 348_: [Windows XP] fixed "ImportError: DLL load failed" occurring on module
   import.
-- #425: [Solaris] crash on import due to failure at determining BOOT_TIME.
-- #443: [Linux] can't set CPU affinity on systems with more than 64 cores.
-
-
-1.2.0 - 2013-11-20
-==================
+- 425_: [Solaris] crash on import due to failure at determining BOOT_TIME.
+- 443_: [Linux] can't set CPU affinity on systems with more than 64 cores.
+
+1.2.0
+=====
+
+*2013-11-20*
 
 **Enhancements**
 
-- #439: assume os.getpid() if no argument is passed to psutil.Process
+- 439_: assume os.getpid() if no argument is passed to psutil.Process
   constructor.
-- #440: new psutil.wait_procs() utility function which waits for multiple
+- 440_: new psutil.wait_procs() utility function which waits for multiple
   processes to terminate.
 
 **Bug fixes**
 
-- #348: [Windows XP/Vista] fix "ImportError: DLL load failed" occurring on
+- 348_: [Windows XP/Vista] fix "ImportError: DLL load failed" occurring on
   module import.
 
-
-1.1.3 - 2013-11-07
-==================
+1.1.3
+=====
+
+*2013-11-07*
 
 **Bug fixes**
 
-- #442: [Linux] psutil won't compile on certain version of Linux because of
+- 442_: [Linux] psutil won't compile on certain version of Linux because of
   missing prlimit(2) syscall.
 
-
-1.1.2 - 2013-10-22
-==================
+1.1.2
+=====
+
+*2013-10-22*
 
 **Bug fixes**
 
-- #442: [Linux] psutil won't compile on Debian 6.0 because of missing
+- 442_: [Linux] psutil won't compile on Debian 6.0 because of missing
   prlimit(2) syscall.
 
-
-1.1.1 - 2013-10-08
-==================
+1.1.1
+=====
+
+*2013-10-08*
 
 **Bug fixes**
 
-- #442: [Linux] psutil won't compile on kernels < 2.6.36 due to missing
+- 442_: [Linux] psutil won't compile on kernels < 2.6.36 due to missing
   prlimit(2) syscall.
 
-
-1.1.0 - 2013-09-28
-==================
+1.1.0
+=====
+
+*2013-09-28*
 
 **Enhancements**
 
-- #410: host tar.gz and windows binary files are on PYPI.
-- #412: [Linux] get/set process resource limits.
-- #415: [Windows] Process.get_children() is an order of magnitude faster.
-- #426: [Windows] Process.name is an order of magnitude faster.
-- #431: [UNIX] Process.name is slightly faster because it unnecessarily
+- 410_: host tar.gz and windows binary files are on PYPI.
+- 412_: [Linux] get/set process resource limits.
+- 415_: [Windows] Process.get_children() is an order of magnitude faster.
+- 426_: [Windows] Process.name is an order of magnitude faster.
+- 431_: [UNIX] Process.name is slightly faster because it unnecessarily
   retrieved also process cmdline.
 
 **Bug fixes**
 
-- #391: [Windows] psutil.cpu_times_percent() returns negative percentages.
-- #408: STATUS_* and CONN_* constants don't properly serialize on JSON.
-- #411: [Windows] examples/disk_usage.py may pop-up a GUI error.
-- #413: [Windows] Process.get_memory_info() leaks memory.
-- #414: [Windows] Process.exe on Windows XP may raise ERROR_INVALID_PARAMETER.
-- #416: psutil.disk_usage() doesn't work well with unicode path names.
-- #430: [Linux] process IO counters report wrong number of r/w syscalls.
-- #435: [Linux] psutil.net_io_counters() might report erreneous NIC names.
-- #436: [Linux] psutil.net_io_counters() reports a wrong 'dropin' value.
+- 391_: [Windows] psutil.cpu_times_percent() returns negative percentages.
+- 408_: STATUS_* and CONN_* constants don't properly serialize on JSON.
+- 411_: [Windows] examples/disk_usage.py may pop-up a GUI error.
+- 413_: [Windows] Process.get_memory_info() leaks memory.
+- 414_: [Windows] Process.exe on Windows XP may raise ERROR_INVALID_PARAMETER.
+- 416_: psutil.disk_usage() doesn't work well with unicode path names.
+- 430_: [Linux] process IO counters report wrong number of r/w syscalls.
+- 435_: [Linux] psutil.net_io_counters() might report erreneous NIC names.
+- 436_: [Linux] psutil.net_io_counters() reports a wrong 'dropin' value.
 
 **API changes**
 
-- #408: turn STATUS_* and CONN_* constants into plain Python strings.
-
-
-1.0.1 - 2013-07-12
-==================
+- 408_: turn STATUS_* and CONN_* constants into plain Python strings.
+
+1.0.1
+=====
+
+*2013-07-12*
 
 **Bug fixes**
 
-- #405: network_io_counters(pernic=True) no longer works as intended in 1.0.0.
-
-
-1.0.0 - 2013-07-10
-==================
+- 405_: network_io_counters(pernic=True) no longer works as intended in 1.0.0.
+
+1.0.0
+=====
+
+*2013-07-10*
 
 **Enhancements**
 
-- #18:  Solaris support (yay!)  (thanks Justin Venus)
-- #367: Process.get_connections() 'status' strings are now constants.
-- #380: test suite exits with non-zero on failure.  (patch by floppymaster)
-- #391: introduce unittest2 facilities and provide workarounds if unittest2
+- 18_:  Solaris support (yay!)  (thanks Justin Venus)
+- 367_: Process.get_connections() 'status' strings are now constants.
+- 380_: test suite exits with non-zero on failure.  (patch by floppymaster)
+- 391_: introduce unittest2 facilities and provide workarounds if unittest2
   is not installed (python < 2.7).
 
 **Bug fixes**
 
-- #374: [Windows] negative memory usage reported if process uses a lot of
+- 374_: [Windows] negative memory usage reported if process uses a lot of
   memory.
-- #379: [Linux] Process.get_memory_maps() may raise ValueError.
-- #394: [OSX] Mapped memory regions report incorrect file name.
-- #404: [Linux] sched_*affinity() are implicitly declared. (patch by Arfrever)
+- 379_: [Linux] Process.get_memory_maps() may raise ValueError.
+- 394_: [OSX] Mapped memory regions report incorrect file name.
+- 404_: [Linux] sched_*affinity() are implicitly declared. (patch by Arfrever)
 
 **API changes**
 
 - Process.get_connections() 'status' field is no longer a string but a
   constant object (psutil.CONN_*).
 - Process.get_connections() 'local_address' and 'remote_address' fields
   renamed to 'laddr' and 'raddr'.
 - psutil.network_io_counters() renamed to psutil.net_io_counters().
 
-
-0.7.1 - 2013-05-03
-==================
+0.7.1
+=====
+
+*2013-05-03*
 
 **Bug fixes**
 
-- #325: [BSD] psutil.virtual_memory() can raise SystemError.
+- 325_: [BSD] psutil.virtual_memory() can raise SystemError.
   (patch by Jan Beich)
-- #370: [BSD] Process.get_connections() requires root.  (patch by John Baldwin)
-- #372: [BSD] different process methods raise NoSuchProcess instead of
+- 370_: [BSD] Process.get_connections() requires root.  (patch by John Baldwin)
+- 372_: [BSD] different process methods raise NoSuchProcess instead of
   AccessDenied.
 
-
-0.7.0 - 2013-04-12
-==================
+0.7.0
+=====
+
+*2013-04-12*
 
 **Enhancements**
 
-- #233: code migrated to Mercurial (yay!)
-- #246: psutil.error module is deprecated and scheduled for removal.
-- #328: [Windows] process IO nice/priority support.
-- #359: psutil.get_boot_time()
-- #361: [Linux] psutil.cpu_times() now includes new 'steal', 'guest' and
+- 233_: code migrated to Mercurial (yay!)
+- 246_: psutil.error module is deprecated and scheduled for removal.
+- 328_: [Windows] process IO nice/priority support.
+- 359_: psutil.get_boot_time()
+- 361_: [Linux] psutil.cpu_times() now includes new 'steal', 'guest' and
   'guest_nice' fields available on recent Linux kernels.
   Also, psutil.cpu_percent() is more accurate.
-- #362: cpu_times_percent() (per-CPU-time utilization as a percentage)
+- 362_: cpu_times_percent() (per-CPU-time utilization as a percentage)
 
 **Bug fixes**
 
-- #234: [Windows] disk_io_counters() fails to list certain disks.
-- #264: [Windows] use of psutil.disk_partitions() may cause a message box to
+- 234_: [Windows] disk_io_counters() fails to list certain disks.
+- 264_: [Windows] use of psutil.disk_partitions() may cause a message box to
   appear.
-- #313: [Linux] psutil.virtual_memory() and psutil.swap_memory() can crash on
+- 313_: [Linux] psutil.virtual_memory() and psutil.swap_memory() can crash on
   certain exotic Linux flavors having an incomplete /proc interface.
   If that's the case we now set the unretrievable stats to 0 and raise a
   RuntimeWarning.
-- #315: [OSX] fix some compilation warnings.
-- #317: [Windows] cannot set process CPU affinity above 31 cores.
-- #319: [Linux] process get_memory_maps() raises KeyError 'Anonymous' on Debian
+- 315_: [OSX] fix some compilation warnings.
+- 317_: [Windows] cannot set process CPU affinity above 31 cores.
+- 319_: [Linux] process get_memory_maps() raises KeyError 'Anonymous' on Debian
   squeeze.
-- #321: [UNIX] Process.ppid property is no longer cached as the kernel may set
+- 321_: [UNIX] Process.ppid property is no longer cached as the kernel may set
   the ppid to 1 in case of a zombie process.
-- #323: [OSX] disk_io_counters()'s read_time and write_time parameters were
+- 323_: [OSX] disk_io_counters()'s read_time and write_time parameters were
   reporting microseconds not milliseconds.  (patch by Gregory Szorc)
-- #331: Process cmdline is no longer cached after first acces as it may change.
-- #333: [OSX] Leak of Mach ports on OS X (patch by rsesek@google.com)
-- #337: [Linux] process methods not working because of a poor /proc
+- 331_: Process cmdline is no longer cached after first acces as it may change.
+- 333_: [OSX] Leak of Mach ports on OS X (patch by rsesek@google.com)
+- 337_: [Linux] process methods not working because of a poor /proc
   implementation will raise NotImplementedError rather than RuntimeError
   and Process.as_dict() will not blow up.  (patch by Curtin1060)
-- #338: [Linux] disk_io_counters() fails to find some disks.
-- #339: [FreeBSD] get_pid_list() can allocate all the memory on system.
-- #341: [Linux] psutil might crash on import due to error in retrieving system
+- 338_: [Linux] disk_io_counters() fails to find some disks.
+- 339_: [FreeBSD] get_pid_list() can allocate all the memory on system.
+- 341_: [Linux] psutil might crash on import due to error in retrieving system
   terminals map.
-- #344: [FreeBSD] swap_memory() might return incorrect results due to
+- 344_: [FreeBSD] swap_memory() might return incorrect results due to
   kvm_open(3) not being called. (patch by Jean Sebastien)
-- #338: [Linux] disk_io_counters() fails to find some disks.
-- #351: [Windows] if psutil is compiled with mingw32 (provided installers for
+- 338_: [Linux] disk_io_counters() fails to find some disks.
+- 351_: [Windows] if psutil is compiled with mingw32 (provided installers for
   py2.4 and py2.5 are) disk_io_counters() will fail. (Patch by m.malycha)
-- #353: [OSX] get_users() returns an empty list on OSX 10.8.
-- #356: Process.parent now checks whether parent PID has been reused in which
+- 353_: [OSX] get_users() returns an empty list on OSX 10.8.
+- 356_: Process.parent now checks whether parent PID has been reused in which
   case returns None.
-- #365: Process.set_nice() should check PID has not been reused by another
+- 365_: Process.set_nice() should check PID has not been reused by another
   process.
-- #366: [FreeBSD] get_memory_maps(), get_num_fds(), get_open_files() and
+- 366_: [FreeBSD] get_memory_maps(), get_num_fds(), get_open_files() and
   getcwd() Process methods raise RuntimeError instead of AccessDenied.
 
 **API changes**
 
 - Process.cmdline property is no longer cached after first access.
 - Process.ppid property is no longer cached after first access.
 - [Linux] Process methods not working because of a poor /proc implementation
   will raise NotImplementedError instead of RuntimeError.
 - psutil.error module is deprecated and scheduled for removal.
 
-
-0.6.1 - 2012-08-16
-==================
+0.6.1
+=====
+
+*2012-08-16*
 
 **Enhancements**
 
-- #316: process cmdline property now makes a better job at guessing the process
+- 316_: process cmdline property now makes a better job at guessing the process
   executable from the cmdline.
 
 **Bug fixes**
 
-- #316: process exe was resolved in case it was a symlink.
-- #318: python 2.4 compatibility was broken.
+- 316_: process exe was resolved in case it was a symlink.
+- 318_: python 2.4 compatibility was broken.
 
 **API changes**
 
 - process exe can now return an empty string instead of raising AccessDenied.
 - process exe is no longer resolved in case it's a symlink.
 
-
-0.6.0 - 2012-08-13
-==================
+0.6.0
+=====
+
+*2012-08-13*
 
 **Enhancements**
 
-- #216: [POSIX] get_connections() UNIX sockets support.
-- #220: [FreeBSD] get_connections() has been rewritten in C and no longer
+- 216_: [POSIX] get_connections() UNIX sockets support.
+- 220_: [FreeBSD] get_connections() has been rewritten in C and no longer
   requires lsof.
-- #222: [OSX] add support for process cwd.
-- #261: process extended memory info.
-- #295: [OSX] process executable path is now determined by asking the OS
+- 222_: [OSX] add support for process cwd.
+- 261_: process extended memory info.
+- 295_: [OSX] process executable path is now determined by asking the OS
   instead of being guessed from process cmdline.
-- #297: [OSX] the Process methods below were always raising AccessDenied for
+- 297_: [OSX] the Process methods below were always raising AccessDenied for
   any process except the current one. Now this is no longer true. Also
   they are 2.5x faster.
   - name
   - get_memory_info()
   - get_memory_percent()
   - get_cpu_times()
   - get_cpu_percent()
   - get_num_threads()
-- #300: examples/pmap.py script.
-- #301: process_iter() now yields processes sorted by their PIDs.
-- #302: process number of voluntary and involuntary context switches.
-- #303: [Windows] the Process methods below were always raising AccessDenied
+- 300_: examples/pmap.py script.
+- 301_: process_iter() now yields processes sorted by their PIDs.
+- 302_: process number of voluntary and involuntary context switches.
+- 303_: [Windows] the Process methods below were always raising AccessDenied
   for any process not owned by current user. Now this is no longer true:
   - create_time
   - get_cpu_times()
   - get_cpu_percent()
   - get_memory_info()
   - get_memory_percent()
   - get_num_handles()
   - get_io_counters()
-- #305: add examples/netstat.py script.
-- #311: system memory functions has been refactorized and rewritten and now
+- 305_: add examples/netstat.py script.
+- 311_: system memory functions has been refactorized and rewritten and now
   provide a more detailed and consistent representation of the system
   memory. New psutil.virtual_memory() function provides the following
   memory amounts:
   - total
   - available
   - percent
   - used
   - active [POSIX]
@@ -626,291 +1324,298 @@ 0.6.0 - 2012-08-13
   - total
   - used
   - free
   - percent
   - sin (no. of bytes the system has swapped in from disk (cumulative))
   - sout (no. of bytes the system has swapped out from disk (cumulative))
   All old memory-related functions are deprecated.
   Also two new example scripts were added:  free.py and meminfo.py.
-- #312: psutil.network_io_counters() namedtuple includes 4 new fields:
+- 312_: psutil.network_io_counters() namedtuple includes 4 new fields:
   errin, errout dropin and dropout, reflecting the number of packets
   dropped and with errors.
 
-**Bugfixes**
-
-- #298: [OSX and BSD] memory leak in get_num_fds().
-- #299: potential memory leak every time PyList_New(0) is used.
-- #303: [Windows] potential heap corruption in get_num_threads() and
+**Bug fixes**
+
+- 298_: [OSX and BSD] memory leak in get_num_fds().
+- 299_: potential memory leak every time PyList_New(0) is used.
+- 303_: [Windows] potential heap corruption in get_num_threads() and
   get_status() Process methods.
-- #305: [FreeBSD] psutil can't compile on FreeBSD 9 due to removal of utmp.h.
-- #306: at C level, errors are not checked when invoking Py* functions which
+- 305_: [FreeBSD] psutil can't compile on FreeBSD 9 due to removal of utmp.h.
+- 306_: at C level, errors are not checked when invoking Py* functions which
   create or manipulate Python objects leading to potential memory related
   errors and/or segmentation faults.
-- #307: [FreeBSD] values returned by psutil.network_io_counters() are wrong.
-- #308: [BSD / Windows] psutil.virtmem_usage() wasn't actually returning
+- 307_: [FreeBSD] values returned by psutil.network_io_counters() are wrong.
+- 308_: [BSD / Windows] psutil.virtmem_usage() wasn't actually returning
   information about swap memory usage as it was supposed to do. It does
   now.
-- #309: get_open_files() might not return files which can not be accessed
+- 309_: get_open_files() might not return files which can not be accessed
   due to limited permissions. AccessDenied is now raised instead.
 
 **API changes**
 
 - psutil.phymem_usage() is deprecated       (use psutil.virtual_memory())
 - psutil.virtmem_usage() is deprecated      (use psutil.swap_memory())
 - psutil.phymem_buffers() on Linux is deprecated  (use psutil.virtual_memory())
 - psutil.cached_phymem() on Linux is deprecated   (use psutil.virtual_memory())
 - [Windows and BSD] psutil.virtmem_usage() now returns information about swap
   memory instead of virtual memory.
 
-
-0.5.1 - 2012-06-29
-==================
+0.5.1
+=====
+
+*2012-06-29*
 
 **Enhancements**
 
-- #293: [Windows] process executable path is now determined by asking the OS
+- 293_: [Windows] process executable path is now determined by asking the OS
   instead of being guessed from process cmdline.
 
-**Bugfixes**
-
-- #292: [Linux] race condition in process files/threads/connections.
-- #294: [Windows] Process CPU affinity is only able to set CPU #0.
-
-
-0.5.0 - 2012-06-27
-==================
+**Bug fixes**
+
+- 292_: [Linux] race condition in process files/threads/connections.
+- 294_: [Windows] Process CPU affinity is only able to set CPU #0.
+
+0.5.0
+=====
+
+*2012-06-27*
 
 **Enhancements**
 
-- #195: [Windows] number of handles opened by process.
-- #209: psutil.disk_partitions() now provides also mount options.
-- #229: list users currently connected on the system (psutil.get_users()).
-- #238: [Linux, Windows] process CPU affinity (get and set).
-- #242: Process.get_children(recursive=True): return all process
+- 195_: [Windows] number of handles opened by process.
+- 209_: psutil.disk_partitions() now provides also mount options.
+- 229_: list users currently connected on the system (psutil.get_users()).
+- 238_: [Linux, Windows] process CPU affinity (get and set).
+- 242_: Process.get_children(recursive=True): return all process
   descendants.
-- #245: [POSIX] Process.wait() incrementally consumes less CPU cycles.
-- #257: [Windows] removed Windows 2000 support.
-- #258: [Linux] Process.get_memory_info() is now 0.5x faster.
-- #260: process's mapped memory regions. (Windows patch by wj32.64, OSX patch
+- 245_: [POSIX] Process.wait() incrementally consumes less CPU cycles.
+- 257_: [Windows] removed Windows 2000 support.
+- 258_: [Linux] Process.get_memory_info() is now 0.5x faster.
+- 260_: process's mapped memory regions. (Windows patch by wj32.64, OSX patch
   by Jeremy Whitlock)
-- #262: [Windows] psutil.disk_partitions() was slow due to inspecting the
+- 262_: [Windows] psutil.disk_partitions() was slow due to inspecting the
   floppy disk drive also when "all" argument was False.
-- #273: psutil.get_process_list() is deprecated.
-- #274: psutil no longer requires 2to3 at installation time in order to work
+- 273_: psutil.get_process_list() is deprecated.
+- 274_: psutil no longer requires 2to3 at installation time in order to work
   with Python 3.
-- #278: new Process.as_dict() method.
-- #281: ppid, name, exe, cmdline and create_time properties of Process class
+- 278_: new Process.as_dict() method.
+- 281_: ppid, name, exe, cmdline and create_time properties of Process class
   are now cached after being accessed.
-- #282: psutil.STATUS_* constants can now be compared by using their string
+- 282_: psutil.STATUS_* constants can now be compared by using their string
   representation.
-- #283: speedup Process.is_running() by caching its return value in case the
+- 283_: speedup Process.is_running() by caching its return value in case the
   process is terminated.
-- #284: [POSIX] per-process number of opened file descriptors.
-- #287: psutil.process_iter() now caches Process instances between calls.
-- #290: Process.nice property is deprecated in favor of new get_nice() and
+- 284_: [POSIX] per-process number of opened file descriptors.
+- 287_: psutil.process_iter() now caches Process instances between calls.
+- 290_: Process.nice property is deprecated in favor of new get_nice() and
   set_nice() methods.
 
-**Bugfixes**
-
-- #193: psutil.Popen constructor can throw an exception if the spawned process
+**Bug fixes**
+
+- 193_: psutil.Popen constructor can throw an exception if the spawned process
   terminates quickly.
-- #240: [OSX] incorrect use of free() for Process.get_connections().
-- #244: [POSIX] Process.wait() can hog CPU resources if called against a
+- 240_: [OSX] incorrect use of free() for Process.get_connections().
+- 244_: [POSIX] Process.wait() can hog CPU resources if called against a
   process which is not our children.
-- #248: [Linux] psutil.network_io_counters() might return erroneous NIC names.
-- #252: [Windows] process getcwd() erroneously raise NoSuchProcess for
+- 248_: [Linux] psutil.network_io_counters() might return erroneous NIC names.
+- 252_: [Windows] process getcwd() erroneously raise NoSuchProcess for
   processes owned by another user.  It now raises AccessDenied instead.
-- #266: [Windows] psutil.get_pid_list() only shows 1024 processes.
+- 266_: [Windows] psutil.get_pid_list() only shows 1024 processes.
   (patch by Amoser)
-- #267: [OSX] Process.get_connections() - an erroneous remote address was
+- 267_: [OSX] Process.get_connections() - an erroneous remote address was
   returned. (Patch by Amoser)
-- #272: [Linux] Porcess.get_open_files() - potential race condition can lead to
+- 272_: [Linux] Porcess.get_open_files() - potential race condition can lead to
   unexpected NoSuchProcess exception.  Also, we can get incorrect reports
   of not absolutized path names.
-- #275: [Linux] Process.get_io_counters() erroneously raise NoSuchProcess on
+- 275_: [Linux] Process.get_io_counters() erroneously raise NoSuchProcess on
   old Linux versions. Where not available it now raises
   NotImplementedError.
-- #286: Process.is_running() doesn't actually check whether PID has been
+- 286_: Process.is_running() doesn't actually check whether PID has been
   reused.
-- #314: Process.get_children() can sometimes return non-children.
+- 314_: Process.get_children() can sometimes return non-children.
 
 **API changes**
 
 - Process.nice property is deprecated in favor of new get_nice() and set_nice()
   methods.
 - psutil.get_process_list() is deprecated.
 - ppid, name, exe, cmdline and create_time properties of Process class are now
   cached after being accessed, meaning NoSuchProcess will no longer be raised
   in case the process is gone in the meantime.
 - psutil.STATUS_* constants can now be compared by using their string
   representation.
 
-
-0.4.1 - 2011-12-14
-==================
-
-**Bugfixes**
-
-- #228: some example scripts were not working with python 3.
-- #230: [Windows / OSX] memory leak in Process.get_connections().
-- #232: [Linux] psutil.phymem_usage() can report erroneous values which are
+0.4.1
+=====
+
+*2011-12-14*
+
+**Bug fixes**
+
+- 228_: some example scripts were not working with python 3.
+- 230_: [Windows / OSX] memory leak in Process.get_connections().
+- 232_: [Linux] psutil.phymem_usage() can report erroneous values which are
   different than "free" command.
-- #236: [Windows] memory/handle leak in Process's get_memory_info(),
+- 236_: [Windows] memory/handle leak in Process's get_memory_info(),
   suspend() and resume() methods.
 
-
-0.4.0 - 2011-10-29
-==================
+0.4.0
+=====
+
+*2011-10-29*
 
 **Enhancements**
 
-- #150: network I/O counters. (OSX and Windows patch by Jeremy Whitlock)
-- #154: [FreeBSD] add support for process getcwd()
-- #157: [Windows] provide installer for Python 3.2 64-bit.
-- #198: Process.wait(timeout=0) can now be used to make wait() return
+- 150_: network I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+- 154_: [FreeBSD] add support for process getcwd()
+- 157_: [Windows] provide installer for Python 3.2 64-bit.
+- 198_: Process.wait(timeout=0) can now be used to make wait() return
   immediately.
-- #206: disk I/O counters. (OSX and Windows patch by Jeremy Whitlock)
-- #213: examples/iotop.py script.
-- #217: Process.get_connections() now has a "kind" argument to filter
+- 206_: disk I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+- 213_: examples/iotop.py script.
+- 217_: Process.get_connections() now has a "kind" argument to filter
   for connections with different criteria.
-- #221: [FreeBSD] Process.get_open_files has been rewritten in C and no longer
+- 221_: [FreeBSD] Process.get_open_files has been rewritten in C and no longer
   relies on lsof.
-- #223: examples/top.py script.
-- #227: examples/nettop.py script.
-
-**Bugfixes**
-
-- #135: [OSX] psutil cannot create Process object.
-- #144: [Linux] no longer support 0 special PID.
-- #188: [Linux] psutil import error on Linux ARM architectures.
-- #194: [POSIX] psutil.Process.get_cpu_percent() now reports a percentage over
+- 223_: examples/top.py script.
+- 227_: examples/nettop.py script.
+
+**Bug fixes**
+
+- 135_: [OSX] psutil cannot create Process object.
+- 144_: [Linux] no longer support 0 special PID.
+- 188_: [Linux] psutil import error on Linux ARM architectures.
+- 194_: [POSIX] psutil.Process.get_cpu_percent() now reports a percentage over
   100 on multicore processors.
-- #197: [Linux] Process.get_connections() is broken on platforms not
+- 197_: [Linux] Process.get_connections() is broken on platforms not
   supporting IPv6.
-- #200: [Linux] psutil.NUM_CPUS not working on armel and sparc architectures
+- 200_: [Linux] psutil.NUM_CPUS not working on armel and sparc architectures
   and causing crash on module import.
-- #201: [Linux] Process.get_connections() is broken on big-endian
+- 201_: [Linux] Process.get_connections() is broken on big-endian
   architectures.
-- #211: Process instance can unexpectedly raise NoSuchProcess if tested for
+- 211_: Process instance can unexpectedly raise NoSuchProcess if tested for
   equality with another object.
-- #218: [Linux] crash at import time on Debian 64-bit because of a missing
+- 218_: [Linux] crash at import time on Debian 64-bit because of a missing
   line in /proc/meminfo.
-- #226: [FreeBSD] crash at import time on FreeBSD 7 and minor.
-
-
-0.3.0 - 2011-07-08
-==================
+- 226_: [FreeBSD] crash at import time on FreeBSD 7 and minor.
+
+0.3.0
+=====
+
+*2011-07-08*
 
 **Enhancements**
 
-- #125: system per-cpu percentage utilization and times.
-- #163: per-process associated terminal (TTY).
-- #171: added get_phymem() and get_virtmem() functions returning system
+- 125_: system per-cpu percentage utilization and times.
+- 163_: per-process associated terminal (TTY).
+- 171_: added get_phymem() and get_virtmem() functions returning system
   memory information (total, used, free) and memory percent usage.
   total_* avail_* and used_* memory functions are deprecated.
-- #172: disk usage statistics.
-- #174: mounted disk partitions.
-- #179: setuptools is now used in setup.py
-
-**Bugfixes**
-
-- #159: SetSeDebug() does not close handles or unset impersonation on return.
-- #164: [Windows] wait function raises a TimeoutException when a process
+- 172_: disk usage statistics.
+- 174_: mounted disk partitions.
+- 179_: setuptools is now used in setup.py
+
+**Bug fixes**
+
+- 159_: SetSeDebug() does not close handles or unset impersonation on return.
+- 164_: [Windows] wait function raises a TimeoutException when a process
   returns -1 .
-- #165: process.status raises an unhandled exception.
-- #166: get_memory_info() leaks handles hogging system resources.
-- #168: psutil.cpu_percent() returns erroneous results when used in
+- 165_: process.status raises an unhandled exception.
+- 166_: get_memory_info() leaks handles hogging system resources.
+- 168_: psutil.cpu_percent() returns erroneous results when used in
   non-blocking mode.  (patch by Philip Roberts)
-- #178: OSX - Process.get_threads() leaks memory
-- #180: [Windows] Process's get_num_threads() and get_threads() methods can
+- 178_: OSX - Process.get_threads() leaks memory
+- 180_: [Windows] Process's get_num_threads() and get_threads() methods can
   raise NoSuchProcess exception while process still exists.
 
-
-0.2.1 - 2011-03-20
-==================
+0.2.1
+=====
+
+*2011-03-20*
 
 **Enhancements**
 
-- #64: per-process I/O counters.
-- #116: per-process wait() (wait for process to terminate and return its exit
+- 64_: per-process I/O counters.
+- 116_: per-process wait() (wait for process to terminate and return its exit
   code).
-- #134: per-process get_threads() returning information (id, user and kernel
+- 134_: per-process get_threads() returning information (id, user and kernel
   times) about threads opened by process.
-- #136: process executable path on FreeBSD is now determined by asking the
+- 136_: process executable path on FreeBSD is now determined by asking the
   kernel instead of guessing it from cmdline[0].
-- #137: per-process real, effective and saved user and group ids.
-- #140: system boot time.
-- #142: per-process get and set niceness (priority).
-- #143: per-process status.
-- #147: per-process I/O nice (priority) - Linux only.
-- #148: psutil.Popen class which tidies up subprocess.Popen and psutil.Process
+- 137_: per-process real, effective and saved user and group ids.
+- 140_: system boot time.
+- 142_: per-process get and set niceness (priority).
+- 143_: per-process status.
+- 147_: per-process I/O nice (priority) - Linux only.
+- 148_: psutil.Popen class which tidies up subprocess.Popen and psutil.Process
   in a unique interface.
-- #152: [OSX] get_process_open_files() implementation has been rewritten
+- 152_: [OSX] get_process_open_files() implementation has been rewritten
   in C and no longer relies on lsof resulting in a 3x speedup.
-- #153: [OSX] get_process_connection() implementation has been rewritten
+- 153_: [OSX] get_process_connection() implementation has been rewritten
   in C and no longer relies on lsof resulting in a 3x speedup.
 
-**Bugfixes**
-
-- #83:  process cmdline is empty on OSX 64-bit.
-- #130: a race condition can cause IOError exception be raised on
+**Bug fixes**
+
+- 83_:  process cmdline is empty on OSX 64-bit.
+- 130_: a race condition can cause IOError exception be raised on
   Linux if process disappears between open() and subsequent read() calls.
-- #145: WindowsError was raised instead of psutil.AccessDenied when using
+- 145_: WindowsError was raised instead of psutil.AccessDenied when using
   process resume() or suspend() on Windows.
-- #146: 'exe' property on Linux can raise TypeError if path contains NULL
+- 146_: 'exe' property on Linux can raise TypeError if path contains NULL
   bytes.
-- #151: exe and getcwd() for PID 0 on Linux return inconsistent data.
+- 151_: exe and getcwd() for PID 0 on Linux return inconsistent data.
 
 **API changes**
 
 - Process "uid" and "gid" properties are deprecated in favor of "uids" and
   "gids" properties.
 
-
-0.2.0 - 2010-11-13
-==================
+0.2.0
+=====
+
+*2010-11-13*
 
 **Enhancements**
 
-- #79: per-process open files.
-- #88: total system physical cached memory.
-- #88: total system physical memory buffers used by the kernel.
-- #91: per-process send_signal() and terminate() methods.
-- #95: NoSuchProcess and AccessDenied exception classes now provide "pid",
+- 79_: per-process open files.
+- 88_: total system physical cached memory.
+- 88_: total system physical memory buffers used by the kernel.
+- 91_: per-process send_signal() and terminate() methods.
+- 95_: NoSuchProcess and AccessDenied exception classes now provide "pid",
   "name" and "msg" attributes.
-- #97: per-process children.
-- #98: Process.get_cpu_times() and Process.get_memory_info now return
+- 97_: per-process children.
+- 98_: Process.get_cpu_times() and Process.get_memory_info now return
   a namedtuple instead of a tuple.
-- #103: per-process opened TCP and UDP connections.
-- #107: add support for Windows 64 bit. (patch by cjgohlke)
-- #111: per-process executable name.
-- #113: exception messages now include process name and pid.
-- #114: process username Windows implementation has been rewritten in pure
+- 103_: per-process opened TCP and UDP connections.
+- 107_: add support for Windows 64 bit. (patch by cjgohlke)
+- 111_: per-process executable name.
+- 113_: exception messages now include process name and pid.
+- 114_: process username Windows implementation has been rewritten in pure
   C and no longer uses WMI resulting in a big speedup. Also, pywin32 is no
   longer required as a third-party dependancy. (patch by wj32)
-- #117: added support for Windows 2000.
-- #123: psutil.cpu_percent() and psutil.Process.cpu_percent() accept a
+- 117_: added support for Windows 2000.
+- 123_: psutil.cpu_percent() and psutil.Process.cpu_percent() accept a
   new 'interval' parameter.
-- #129: per-process number of threads.
-
-**Bugfixes**
-
-- #80: fixed warnings when installing psutil with easy_install.
-- #81: psutil fails to compile with Visual Studio.
-- #94: suspend() raises OSError instead of AccessDenied.
-- #86: psutil didn't compile against FreeBSD 6.x.
-- #102: orphaned process handles obtained by using OpenProcess in C were
+- 129_: per-process number of threads.
+
+**Bug fixes**
+
+- 80_: fixed warnings when installing psutil with easy_install.
+- 81_: psutil fails to compile with Visual Studio.
+- 94_: suspend() raises OSError instead of AccessDenied.
+- 86_: psutil didn't compile against FreeBSD 6.x.
+- 102_: orphaned process handles obtained by using OpenProcess in C were
   left behind every time Process class was instantiated.
-- #111: path and name Process properties report truncated or erroneous
+- 111_: path and name Process properties report truncated or erroneous
   values on UNIX.
-- #120: cpu_percent() always returning 100% on OS X.
-- #112: uid and gid properties don't change if process changes effective
+- 120_: cpu_percent() always returning 100% on OS X.
+- 112_: uid and gid properties don't change if process changes effective
   user/group id at some point.
-- #126: ppid, uid, gid, name, exe, cmdline and create_time properties are
+- 126_: ppid, uid, gid, name, exe, cmdline and create_time properties are
   no longer cached and correctly raise NoSuchProcess exception if the process
   disappears.
 
 **API changes**
 
 - psutil.Process.path property is deprecated and works as an alias for "exe"
   property.
 - psutil.Process.kill(): signal argument was removed - to send a signal to the
@@ -920,99 +1625,1596 @@ 0.2.0 - 2010-11-13
 - New psutil.Process methods: get_open_files(), get_connections(),
   send_signal() and terminate().
 - ppid, uid, gid, name, exe, cmdline and create_time properties are no longer
   cached and raise NoSuchProcess exception if process disappears.
 - psutil.cpu_percent() no longer returns immediately (see issue 123).
 - psutil.Process.get_cpu_percent() and psutil.cpu_percent() no longer returns
   immediately by default (see issue 123).
 
-
-0.1.3 - 2010-03-02
-==================
+0.1.3
+=====
+
+*2010-03-02*
 
 **Enhancements**
 
-- #14: per-process username
-- #51: per-process current working directory (Windows and Linux only)
-- #59: Process.is_running() is now 10 times faster
-- #61: added supoprt for FreeBSD 64 bit
-- #71: implemented suspend/resume process
-- #75: python 3 support
-
-**Bugfixes**
-
-- #36: process cpu_times() and memory_info() functions succeeded also for dead
+- 14_: per-process username
+- 51_: per-process current working directory (Windows and Linux only)
+- 59_: Process.is_running() is now 10 times faster
+- 61_: added supoprt for FreeBSD 64 bit
+- 71_: implemented suspend/resume process
+- 75_: python 3 support
+
+**Bug fixes**
+
+- 36_: process cpu_times() and memory_info() functions succeeded also for dead
   processes while a NoSuchProcess exception is supposed to be raised.
-- #48: incorrect size for mib array defined in getcmdargs for BSD
-- #49: possible memory leak due to missing free() on error condition on
-- #50: fixed getcmdargs() memory fragmentation on BSD
-- #55: test_pid_4 was failing on Windows Vista
-- #57: some unit tests were failing on systems where no swap memory is
+- 48_: incorrect size for mib array defined in getcmdargs for BSD
+- 49_: possible memory leak due to missing free() on error condition on
+- 50_: fixed getcmdargs() memory fragmentation on BSD
+- 55_: test_pid_4 was failing on Windows Vista
+- 57_: some unit tests were failing on systems where no swap memory is
   available
-- #58: is_running() is now called before kill() to make sure we are going
+- 58_: is_running() is now called before kill() to make sure we are going
   to kill the correct process.
-- #73: virtual memory size reported on OS X includes shared library size
-- #77: NoSuchProcess wasn't raised on Process.create_time if kill() was
+- 73_: virtual memory size reported on OS X includes shared library size
+- 77_: NoSuchProcess wasn't raised on Process.create_time if kill() was
   used first.
 
-
-0.1.2 - 2009-05-06
-==================
+0.1.2
+=====
+
+*2009-05-06*
 
 **Enhancements**
 
-- #32: Per-process CPU user/kernel times
-- #33: Process create time
-- #34: Per-process CPU utilization percentage
-- #38: Per-process memory usage (bytes)
-- #41: Per-process memory utilization (percent)
-- #39: System uptime
-- #43: Total system virtual memory
-- #46: Total system physical memory
-- #44: Total system used/free virtual and physical memory
-
-**Bugfixes**
-
-- #36: [Windows] NoSuchProcess not raised when accessing timing methods.
-- #40: test_get_cpu_times() failing on FreeBSD and OS X.
-- #42: [Windows] get_memory_percent() raises AccessDenied.
-
-
-0.1.1 - 2009-03-06
-==================
+- 32_: Per-process CPU user/kernel times
+- 33_: Process create time
+- 34_: Per-process CPU utilization percentage
+- 38_: Per-process memory usage (bytes)
+- 41_: Per-process memory utilization (percent)
+- 39_: System uptime
+- 43_: Total system virtual memory
+- 46_: Total system physical memory
+- 44_: Total system used/free virtual and physical memory
+
+**Bug fixes**
+
+- 36_: [Windows] NoSuchProcess not raised when accessing timing methods.
+- 40_: test_get_cpu_times() failing on FreeBSD and OS X.
+- 42_: [Windows] get_memory_percent() raises AccessDenied.
+
+0.1.1
+=====
+
+*2009-03-06*
 
 **Enhancements**
 
-- #4: FreeBSD support for all functions of psutil
-- #9: Process.uid and Process.gid now retrieve process UID and GID.
-- #11: Support for parent/ppid - Process.parent property returns a
+- 4_: FreeBSD support for all functions of psutil
+- 9_: Process.uid and Process.gid now retrieve process UID and GID.
+- 11_: Support for parent/ppid - Process.parent property returns a
   Process object representing the parent process, and Process.ppid returns
   the parent PID.
-- #12 & 15:
+- 12_ & 15:
   NoSuchProcess exception now raised when creating an object
   for a nonexistent process, or when retrieving information about a process
   that has gone away.
-- #21: AccessDenied exception created for raising access denied errors
+- 21_: AccessDenied exception created for raising access denied errors
   from OSError or WindowsError on individual platforms.
-- #26: psutil.process_iter() function to iterate over processes as
+- 26_: psutil.process_iter() function to iterate over processes as
   Process objects with a generator.
-- #?:  Process objects can now also be compared with == operator for equality
+- Process objects can now also be compared with == operator for equality
   (PID, name, command line are compared).
 
-**Bugfixes**
-
-- #16: [Windows] Special case for "System Idle Process" (PID 0) which
+**Bug fixes**
+
+- 16_: [Windows] Special case for "System Idle Process" (PID 0) which
   otherwise would return an "invalid parameter" exception.
-- #17: get_process_list() ignores NoSuchProcess and AccessDenied
+- 17_: get_process_list() ignores NoSuchProcess and AccessDenied
   exceptions during building of the list.
-- #22: [Windows] Process(0).kill() was failing with an unset exception.
-- #23: Special case for pid_exists(0)
-- #24: [Windows] Process(0).kill() now raises AccessDenied exception instead
+- 22_: [Windows] Process(0).kill() was failing with an unset exception.
+- 23_: Special case for pid_exists(0)
+- 24_: [Windows] Process(0).kill() now raises AccessDenied exception instead
   of WindowsError.
-- #30: psutil.get_pid_list() was returning two instances of PID 0 on OSX and
-  FreeBSD platforms.
-
-
-0.1.0 - 2009-01-27
-==================
-
-- Initial release.
+- 30_: psutil.get_pid_list() was returning two ins
+
+.. _1: https://github.com/giampaolo/psutil/issues/1
+.. _2: https://github.com/giampaolo/psutil/issues/2
+.. _3: https://github.com/giampaolo/psutil/issues/3
+.. _4: https://github.com/giampaolo/psutil/issues/4
+.. _5: https://github.com/giampaolo/psutil/issues/5
+.. _6: https://github.com/giampaolo/psutil/issues/6
+.. _7: https://github.com/giampaolo/psutil/issues/7
+.. _8: https://github.com/giampaolo/psutil/issues/8
+.. _9: https://github.com/giampaolo/psutil/issues/9
+.. _10: https://github.com/giampaolo/psutil/issues/10
+.. _11: https://github.com/giampaolo/psutil/issues/11
+.. _12: https://github.com/giampaolo/psutil/issues/12
+.. _13: https://github.com/giampaolo/psutil/issues/13
+.. _14: https://github.com/giampaolo/psutil/issues/14
+.. _15: https://github.com/giampaolo/psutil/issues/15
+.. _16: https://github.com/giampaolo/psutil/issues/16
+.. _17: https://github.com/giampaolo/psutil/issues/17
+.. _18: https://github.com/giampaolo/psutil/issues/18
+.. _19: https://github.com/giampaolo/psutil/issues/19
+.. _20: https://github.com/giampaolo/psutil/issues/20
+.. _21: https://github.com/giampaolo/psutil/issues/21
+.. _22: https://github.com/giampaolo/psutil/issues/22
+.. _23: https://github.com/giampaolo/psutil/issues/23
+.. _24: https://github.com/giampaolo/psutil/issues/24
+.. _25: https://github.com/giampaolo/psutil/issues/25
+.. _26: https://github.com/giampaolo/psutil/issues/26
+.. _27: https://github.com/giampaolo/psutil/issues/27
+.. _28: https://github.com/giampaolo/psutil/issues/28
+.. _29: https://github.com/giampaolo/psutil/issues/29
+.. _30: https://github.com/giampaolo/psutil/issues/30
+.. _31: https://github.com/giampaolo/psutil/issues/31
+.. _32: https://github.com/giampaolo/psutil/issues/32
+.. _33: https://github.com/giampaolo/psutil/issues/33
+.. _34: https://github.com/giampaolo/psutil/issues/34
+.. _35: https://github.com/giampaolo/psutil/issues/35
+.. _36: https://github.com/giampaolo/psutil/issues/36
+.. _37: https://github.com/giampaolo/psutil/issues/37
+.. _38: https://github.com/giampaolo/psutil/issues/38
+.. _39: https://github.com/giampaolo/psutil/issues/39
+.. _40: https://github.com/giampaolo/psutil/issues/40
+.. _41: https://github.com/giampaolo/psutil/issues/41
+.. _42: https://github.com/giampaolo/psutil/issues/42
+.. _43: https://github.com/giampaolo/psutil/issues/43
+.. _44: https://github.com/giampaolo/psutil/issues/44
+.. _45: https://github.com/giampaolo/psutil/issues/45
+.. _46: https://github.com/giampaolo/psutil/issues/46
+.. _47: https://github.com/giampaolo/psutil/issues/47
+.. _48: https://github.com/giampaolo/psutil/issues/48
+.. _49: https://github.com/giampaolo/psutil/issues/49
+.. _50: https://github.com/giampaolo/psutil/issues/50
+.. _51: https://github.com/giampaolo/psutil/issues/51
+.. _52: https://github.com/giampaolo/psutil/issues/52
+.. _53: https://github.com/giampaolo/psutil/issues/53
+.. _54: https://github.com/giampaolo/psutil/issues/54
+.. _55: https://github.com/giampaolo/psutil/issues/55
+.. _56: https://github.com/giampaolo/psutil/issues/56
+.. _57: https://github.com/giampaolo/psutil/issues/57
+.. _58: https://github.com/giampaolo/psutil/issues/58
+.. _59: https://github.com/giampaolo/psutil/issues/59
+.. _60: https://github.com/giampaolo/psutil/issues/60
+.. _61: https://github.com/giampaolo/psutil/issues/61
+.. _62: https://github.com/giampaolo/psutil/issues/62
+.. _63: https://github.com/giampaolo/psutil/issues/63
+.. _64: https://github.com/giampaolo/psutil/issues/64
+.. _65: https://github.com/giampaolo/psutil/issues/65
+.. _66: https://github.com/giampaolo/psutil/issues/66
+.. _67: https://github.com/giampaolo/psutil/issues/67
+.. _68: https://github.com/giampaolo/psutil/issues/68
+.. _69: https://github.com/giampaolo/psutil/issues/69
+.. _70: https://github.com/giampaolo/psutil/issues/70
+.. _71: https://github.com/giampaolo/psutil/issues/71
+.. _72: https://github.com/giampaolo/psutil/issues/72
+.. _73: https://github.com/giampaolo/psutil/issues/73
+.. _74: https://github.com/giampaolo/psutil/issues/74
+.. _75: https://github.com/giampaolo/psutil/issues/75
+.. _76: https://github.com/giampaolo/psutil/issues/76
+.. _77: https://github.com/giampaolo/psutil/issues/77
+.. _78: https://github.com/giampaolo/psutil/issues/78
+.. _79: https://github.com/giampaolo/psutil/issues/79
+.. _80: https://github.com/giampaolo/psutil/issues/80
+.. _81: https://github.com/giampaolo/psutil/issues/81
+.. _82: https://github.com/giampaolo/psutil/issues/82
+.. _83: https://github.com/giampaolo/psutil/issues/83
+.. _84: https://github.com/giampaolo/psutil/issues/84
+.. _85: https://github.com/giampaolo/psutil/issues/85
+.. _86: https://github.com/giampaolo/psutil/issues/86
+.. _87: https://github.com/giampaolo/psutil/issues/87
+.. _88: https://github.com/giampaolo/psutil/issues/88
+.. _89: https://github.com/giampaolo/psutil/issues/89
+.. _90: https://github.com/giampaolo/psutil/issues/90
+.. _91: https://github.com/giampaolo/psutil/issues/91
+.. _92: https://github.com/giampaolo/psutil/issues/92
+.. _93: https://github.com/giampaolo/psutil/issues/93
+.. _94: https://github.com/giampaolo/psutil/issues/94
+.. _95: https://github.com/giampaolo/psutil/issues/95
+.. _96: https://github.com/giampaolo/psutil/issues/96
+.. _97: https://github.com/giampaolo/psutil/issues/97
+.. _98: https://github.com/giampaolo/psutil/issues/98
+.. _99: https://github.com/giampaolo/psutil/issues/99
+.. _100: https://github.com/giampaolo/psutil/issues/100
+.. _101: https://github.com/giampaolo/psutil/issues/101
+.. _102: https://github.com/giampaolo/psutil/issues/102
+.. _103: https://github.com/giampaolo/psutil/issues/103
+.. _104: https://github.com/giampaolo/psutil/issues/104
+.. _105: https://github.com/giampaolo/psutil/issues/105
+.. _106: https://github.com/giampaolo/psutil/issues/106
+.. _107: https://github.com/giampaolo/psutil/issues/107
+.. _108: https://github.com/giampaolo/psutil/issues/108
+.. _109: https://github.com/giampaolo/psutil/issues/109
+.. _110: https://github.com/giampaolo/psutil/issues/110
+.. _111: https://github.com/giampaolo/psutil/issues/111
+.. _112: https://github.com/giampaolo/psutil/issues/112
+.. _113: https://github.com/giampaolo/psutil/issues/113
+.. _114: https://github.com/giampaolo/psutil/issues/114
+.. _115: https://github.com/giampaolo/psutil/issues/115
+.. _116: https://github.com/giampaolo/psutil/issues/116
+.. _117: https://github.com/giampaolo/psutil/issues/117
+.. _118: https://github.com/giampaolo/psutil/issues/118
+.. _119: https://github.com/giampaolo/psutil/issues/119
+.. _120: https://github.com/giampaolo/psutil/issues/120
+.. _121: https://github.com/giampaolo/psutil/issues/121
+.. _122: https://github.com/giampaolo/psutil/issues/122
+.. _123: https://github.com/giampaolo/psutil/issues/123
+.. _124: https://github.com/giampaolo/psutil/issues/124
+.. _125: https://github.com/giampaolo/psutil/issues/125
+.. _126: https://github.com/giampaolo/psutil/issues/126
+.. _127: https://github.com/giampaolo/psutil/issues/127
+.. _128: https://github.com/giampaolo/psutil/issues/128
+.. _129: https://github.com/giampaolo/psutil/issues/129
+.. _130: https://github.com/giampaolo/psutil/issues/130
+.. _131: https://github.com/giampaolo/psutil/issues/131
+.. _132: https://github.com/giampaolo/psutil/issues/132
+.. _133: https://github.com/giampaolo/psutil/issues/133
+.. _134: https://github.com/giampaolo/psutil/issues/134
+.. _135: https://github.com/giampaolo/psutil/issues/135
+.. _136: https://github.com/giampaolo/psutil/issues/136
+.. _137: https://github.com/giampaolo/psutil/issues/137
+.. _138: https://github.com/giampaolo/psutil/issues/138
+.. _139: https://github.com/giampaolo/psutil/issues/139
+.. _140: https://github.com/giampaolo/psutil/issues/140
+.. _141: https://github.com/giampaolo/psutil/issues/141
+.. _142: https://github.com/giampaolo/psutil/issues/142
+.. _143: https://github.com/giampaolo/psutil/issues/143
+.. _144: https://github.com/giampaolo/psutil/issues/144
+.. _145: https://github.com/giampaolo/psutil/issues/145
+.. _146: https://github.com/giampaolo/psutil/issues/146
+.. _147: https://github.com/giampaolo/psutil/issues/147
+.. _148: https://github.com/giampaolo/psutil/issues/148
+.. _149: https://github.com/giampaolo/psutil/issues/149
+.. _150: https://github.com/giampaolo/psutil/issues/150
+.. _151: https://github.com/giampaolo/psutil/issues/151
+.. _152: https://github.com/giampaolo/psutil/issues/152
+.. _153: https://github.com/giampaolo/psutil/issues/153
+.. _154: https://github.com/giampaolo/psutil/issues/154
+.. _155: https://github.com/giampaolo/psutil/issues/155
+.. _156: https://github.com/giampaolo/psutil/issues/156
+.. _157: https://github.com/giampaolo/psutil/issues/157
+.. _158: https://github.com/giampaolo/psutil/issues/158
+.. _159: https://github.com/giampaolo/psutil/issues/159
+.. _160: https://github.com/giampaolo/psutil/issues/160
+.. _161: https://github.com/giampaolo/psutil/issues/161
+.. _162: https://github.com/giampaolo/psutil/issues/162
+.. _163: https://github.com/giampaolo/psutil/issues/163
+.. _164: https://github.com/giampaolo/psutil/issues/164
+.. _165: https://github.com/giampaolo/psutil/issues/165
+.. _166: https://github.com/giampaolo/psutil/issues/166
+.. _167: https://github.com/giampaolo/psutil/issues/167
+.. _168: https://github.com/giampaolo/psutil/issues/168
+.. _169: https://github.com/giampaolo/psutil/issues/169
+.. _170: https://github.com/giampaolo/psutil/issues/170
+.. _171: https://github.com/giampaolo/psutil/issues/171
+.. _172: https://github.com/giampaolo/psutil/issues/172
+.. _173: https://github.com/giampaolo/psutil/issues/173
+.. _174: https://github.com/giampaolo/psutil/issues/174
+.. _175: https://github.com/giampaolo/psutil/issues/175
+.. _176: https://github.com/giampaolo/psutil/issues/176
+.. _177: https://github.com/giampaolo/psutil/issues/177
+.. _178: https://github.com/giampaolo/psutil/issues/178
+.. _179: https://github.com/giampaolo/psutil/issues/179
+.. _180: https://github.com/giampaolo/psutil/issues/180
+.. _181: https://github.com/giampaolo/psutil/issues/181
+.. _182: https://github.com/giampaolo/psutil/issues/182
+.. _183: https://github.com/giampaolo/psutil/issues/183
+.. _184: https://github.com/giampaolo/psutil/issues/184
+.. _185: https://github.com/giampaolo/psutil/issues/185
+.. _186: https://github.com/giampaolo/psutil/issues/186
+.. _187: https://github.com/giampaolo/psutil/issues/187
+.. _188: https://github.com/giampaolo/psutil/issues/188
+.. _189: https://github.com/giampaolo/psutil/issues/189
+.. _190: https://github.com/giampaolo/psutil/issues/190
+.. _191: https://github.com/giampaolo/psutil/issues/191
+.. _192: https://github.com/giampaolo/psutil/issues/192
+.. _193: https://github.com/giampaolo/psutil/issues/193
+.. _194: https://github.com/giampaolo/psutil/issues/194
+.. _195: https://github.com/giampaolo/psutil/issues/195
+.. _196: https://github.com/giampaolo/psutil/issues/196
+.. _197: https://github.com/giampaolo/psutil/issues/197
+.. _198: https://github.com/giampaolo/psutil/issues/198
+.. _199: https://github.com/giampaolo/psutil/issues/199
+.. _200: https://github.com/giampaolo/psutil/issues/200
+.. _201: https://github.com/giampaolo/psutil/issues/201
+.. _202: https://github.com/giampaolo/psutil/issues/202
+.. _203: https://github.com/giampaolo/psutil/issues/203
+.. _204: https://github.com/giampaolo/psutil/issues/204
+.. _205: https://github.com/giampaolo/psutil/issues/205
+.. _206: https://github.com/giampaolo/psutil/issues/206
+.. _207: https://github.com/giampaolo/psutil/issues/207
+.. _208: https://github.com/giampaolo/psutil/issues/208
+.. _209: https://github.com/giampaolo/psutil/issues/209
+.. _210: https://github.com/giampaolo/psutil/issues/210
+.. _211: https://github.com/giampaolo/psutil/issues/211
+.. _212: https://github.com/giampaolo/psutil/issues/212
+.. _213: https://github.com/giampaolo/psutil/issues/213
+.. _214: https://github.com/giampaolo/psutil/issues/214
+.. _215: https://github.com/giampaolo/psutil/issues/215
+.. _216: https://github.com/giampaolo/psutil/issues/216
+.. _217: https://github.com/giampaolo/psutil/issues/217
+.. _218: https://github.com/giampaolo/psutil/issues/218
+.. _219: https://github.com/giampaolo/psutil/issues/219
+.. _220: https://github.com/giampaolo/psutil/issues/220
+.. _221: https://github.com/giampaolo/psutil/issues/221
+.. _222: https://github.com/giampaolo/psutil/issues/222
+.. _223: https://github.com/giampaolo/psutil/issues/223
+.. _224: https://github.com/giampaolo/psutil/issues/224
+.. _225: https://github.com/giampaolo/psutil/issues/225
+.. _226: https://github.com/giampaolo/psutil/issues/226
+.. _227: https://github.com/giampaolo/psutil/issues/227
+.. _228: https://github.com/giampaolo/psutil/issues/228
+.. _229: https://github.com/giampaolo/psutil/issues/229
+.. _230: https://github.com/giampaolo/psutil/issues/230
+.. _231: https://github.com/giampaolo/psutil/issues/231
+.. _232: https://github.com/giampaolo/psutil/issues/232
+.. _233: https://github.com/giampaolo/psutil/issues/233
+.. _234: https://github.com/giampaolo/psutil/issues/234
+.. _235: https://github.com/giampaolo/psutil/issues/235
+.. _236: https://github.com/giampaolo/psutil/issues/236
+.. _237: https://github.com/giampaolo/psutil/issues/237
+.. _238: https://github.com/giampaolo/psutil/issues/238
+.. _239: https://github.com/giampaolo/psutil/issues/239
+.. _240: https://github.com/giampaolo/psutil/issues/240
+.. _241: https://github.com/giampaolo/psutil/issues/241
+.. _242: https://github.com/giampaolo/psutil/issues/242
+.. _243: https://github.com/giampaolo/psutil/issues/243
+.. _244: https://github.com/giampaolo/psutil/issues/244
+.. _245: https://github.com/giampaolo/psutil/issues/245
+.. _246: https://github.com/giampaolo/psutil/issues/246
+.. _247: https://github.com/giampaolo/psutil/issues/247
+.. _248: https://github.com/giampaolo/psutil/issues/248
+.. _249: https://github.com/giampaolo/psutil/issues/249
+.. _250: https://github.com/giampaolo/psutil/issues/250
+.. _251: https://github.com/giampaolo/psutil/issues/251
+.. _252: https://github.com/giampaolo/psutil/issues/252
+.. _253: https://github.com/giampaolo/psutil/issues/253
+.. _254: https://github.com/giampaolo/psutil/issues/254
+.. _255: https://github.com/giampaolo/psutil/issues/255
+.. _256: https://github.com/giampaolo/psutil/issues/256
+.. _257: https://github.com/giampaolo/psutil/issues/257
+.. _258: https://github.com/giampaolo/psutil/issues/258
+.. _259: https://github.com/giampaolo/psutil/issues/259
+.. _260: https://github.com/giampaolo/psutil/issues/260
+.. _261: https://github.com/giampaolo/psutil/issues/261
+.. _262: https://github.com/giampaolo/psutil/issues/262
+.. _263: https://github.com/giampaolo/psutil/issues/263
+.. _264: https://github.com/giampaolo/psutil/issues/264
+.. _265: https://github.com/giampaolo/psutil/issues/265
+.. _266: https://github.com/giampaolo/psutil/issues/266
+.. _267: https://github.com/giampaolo/psutil/issues/267
+.. _268: https://github.com/giampaolo/psutil/issues/268
+.. _269: https://github.com/giampaolo/psutil/issues/269
+.. _270: https://github.com/giampaolo/psutil/issues/270
+.. _271: https://github.com/giampaolo/psutil/issues/271
+.. _272: https://github.com/giampaolo/psutil/issues/272
+.. _273: https://github.com/giampaolo/psutil/issues/273
+.. _274: https://github.com/giampaolo/psutil/issues/274
+.. _275: https://github.com/giampaolo/psutil/issues/275
+.. _276: https://github.com/giampaolo/psutil/issues/276
+.. _277: https://github.com/giampaolo/psutil/issues/277
+.. _278: https://github.com/giampaolo/psutil/issues/278
+.. _279: https://github.com/giampaolo/psutil/issues/279
+.. _280: https://github.com/giampaolo/psutil/issues/280
+.. _281: https://github.com/giampaolo/psutil/issues/281
+.. _282: https://github.com/giampaolo/psutil/issues/282
+.. _283: https://github.com/giampaolo/psutil/issues/283
+.. _284: https://github.com/giampaolo/psutil/issues/284
+.. _285: https://github.com/giampaolo/psutil/issues/285
+.. _286: https://github.com/giampaolo/psutil/issues/286
+.. _287: https://github.com/giampaolo/psutil/issues/287
+.. _288: https://github.com/giampaolo/psutil/issues/288
+.. _289: https://github.com/giampaolo/psutil/issues/289
+.. _290: https://github.com/giampaolo/psutil/issues/290
+.. _291: https://github.com/giampaolo/psutil/issues/291
+.. _292: https://github.com/giampaolo/psutil/issues/292
+.. _293: https://github.com/giampaolo/psutil/issues/293
+.. _294: https://github.com/giampaolo/psutil/issues/294
+.. _295: https://github.com/giampaolo/psutil/issues/295
+.. _296: https://github.com/giampaolo/psutil/issues/296
+.. _297: https://github.com/giampaolo/psutil/issues/297
+.. _298: https://github.com/giampaolo/psutil/issues/298
+.. _299: https://github.com/giampaolo/psutil/issues/299
+.. _300: https://github.com/giampaolo/psutil/issues/300
+.. _301: https://github.com/giampaolo/psutil/issues/301
+.. _302: https://github.com/giampaolo/psutil/issues/302
+.. _303: https://github.com/giampaolo/psutil/issues/303
+.. _304: https://github.com/giampaolo/psutil/issues/304
+.. _305: https://github.com/giampaolo/psutil/issues/305
+.. _306: https://github.com/giampaolo/psutil/issues/306
+.. _307: https://github.com/giampaolo/psutil/issues/307
+.. _308: https://github.com/giampaolo/psutil/issues/308
+.. _309: https://github.com/giampaolo/psutil/issues/309
+.. _310: https://github.com/giampaolo/psutil/issues/310
+.. _311: https://github.com/giampaolo/psutil/issues/311
+.. _312: https://github.com/giampaolo/psutil/issues/312
+.. _313: https://github.com/giampaolo/psutil/issues/313
+.. _314: https://github.com/giampaolo/psutil/issues/314
+.. _315: https://github.com/giampaolo/psutil/issues/315
+.. _316: https://github.com/giampaolo/psutil/issues/316
+.. _317: https://github.com/giampaolo/psutil/issues/317
+.. _318: https://github.com/giampaolo/psutil/issues/318
+.. _319: https://github.com/giampaolo/psutil/issues/319
+.. _320: https://github.com/giampaolo/psutil/issues/320
+.. _321: https://github.com/giampaolo/psutil/issues/321
+.. _322: https://github.com/giampaolo/psutil/issues/322
+.. _323: https://github.com/giampaolo/psutil/issues/323
+.. _324: https://github.com/giampaolo/psutil/issues/324
+.. _325: https://github.com/giampaolo/psutil/issues/325
+.. _326: https://github.com/giampaolo/psutil/issues/326
+.. _327: https://github.com/giampaolo/psutil/issues/327
+.. _328: https://github.com/giampaolo/psutil/issues/328
+.. _329: https://github.com/giampaolo/psutil/issues/329
+.. _330: https://github.com/giampaolo/psutil/issues/330
+.. _331: https://github.com/giampaolo/psutil/issues/331
+.. _332: https://github.com/giampaolo/psutil/issues/332
+.. _333: https://github.com/giampaolo/psutil/issues/333
+.. _334: https://github.com/giampaolo/psutil/issues/334
+.. _335: https://github.com/giampaolo/psutil/issues/335
+.. _336: https://github.com/giampaolo/psutil/issues/336
+.. _337: https://github.com/giampaolo/psutil/issues/337
+.. _338: https://github.com/giampaolo/psutil/issues/338
+.. _339: https://github.com/giampaolo/psutil/issues/339
+.. _340: https://github.com/giampaolo/psutil/issues/340
+.. _341: https://github.com/giampaolo/psutil/issues/341
+.. _342: https://github.com/giampaolo/psutil/issues/342
+.. _343: https://github.com/giampaolo/psutil/issues/343
+.. _344: https://github.com/giampaolo/psutil/issues/344
+.. _345: https://github.com/giampaolo/psutil/issues/345
+.. _346: https://github.com/giampaolo/psutil/issues/346
+.. _347: https://github.com/giampaolo/psutil/issues/347
+.. _348: https://github.com/giampaolo/psutil/issues/348
+.. _349: https://github.com/giampaolo/psutil/issues/349
+.. _350: https://github.com/giampaolo/psutil/issues/350
+.. _351: https://github.com/giampaolo/psutil/issues/351
+.. _352: https://github.com/giampaolo/psutil/issues/352
+.. _353: https://github.com/giampaolo/psutil/issues/353
+.. _354: https://github.com/giampaolo/psutil/issues/354
+.. _355: https://github.com/giampaolo/psutil/issues/355
+.. _356: https://github.com/giampaolo/psutil/issues/356
+.. _357: https://github.com/giampaolo/psutil/issues/357
+.. _358: https://github.com/giampaolo/psutil/issues/358
+.. _359: https://github.com/giampaolo/psutil/issues/359
+.. _360: https://github.com/giampaolo/psutil/issues/360
+.. _361: https://github.com/giampaolo/psutil/issues/361
+.. _362: https://github.com/giampaolo/psutil/issues/362
+.. _363: https://github.com/giampaolo/psutil/issues/363
+.. _364: https://github.com/giampaolo/psutil/issues/364
+.. _365: https://github.com/giampaolo/psutil/issues/365
+.. _366: https://github.com/giampaolo/psutil/issues/366
+.. _367: https://github.com/giampaolo/psutil/issues/367
+.. _368: https://github.com/giampaolo/psutil/issues/368
+.. _369: https://github.com/giampaolo/psutil/issues/369
+.. _370: https://github.com/giampaolo/psutil/issues/370
+.. _371: https://github.com/giampaolo/psutil/issues/371
+.. _372: https://github.com/giampaolo/psutil/issues/372
+.. _373: https://github.com/giampaolo/psutil/issues/373
+.. _374: https://github.com/giampaolo/psutil/issues/374
+.. _375: https://github.com/giampaolo/psutil/issues/375
+.. _376: https://github.com/giampaolo/psutil/issues/376
+.. _377: https://github.com/giampaolo/psutil/issues/377
+.. _378: https://github.com/giampaolo/psutil/issues/378
+.. _379: https://github.com/giampaolo/psutil/issues/379
+.. _380: https://github.com/giampaolo/psutil/issues/380
+.. _381: https://github.com/giampaolo/psutil/issues/381
+.. _382: https://github.com/giampaolo/psutil/issues/382
+.. _383: https://github.com/giampaolo/psutil/issues/383
+.. _384: https://github.com/giampaolo/psutil/issues/384
+.. _385: https://github.com/giampaolo/psutil/issues/385
+.. _386: https://github.com/giampaolo/psutil/issues/386
+.. _387: https://github.com/giampaolo/psutil/issues/387
+.. _388: https://github.com/giampaolo/psutil/issues/388
+.. _389: https://github.com/giampaolo/psutil/issues/389
+.. _390: https://github.com/giampaolo/psutil/issues/390
+.. _391: https://github.com/giampaolo/psutil/issues/391
+.. _392: https://github.com/giampaolo/psutil/issues/392
+.. _393: https://github.com/giampaolo/psutil/issues/393
+.. _394: https://github.com/giampaolo/psutil/issues/394
+.. _395: https://github.com/giampaolo/psutil/issues/395
+.. _396: https://github.com/giampaolo/psutil/issues/396
+.. _397: https://github.com/giampaolo/psutil/issues/397
+.. _398: https://github.com/giampaolo/psutil/issues/398
+.. _399: https://github.com/giampaolo/psutil/issues/399
+.. _400: https://github.com/giampaolo/psutil/issues/400
+.. _401: https://github.com/giampaolo/psutil/issues/401
+.. _402: https://github.com/giampaolo/psutil/issues/402
+.. _403: https://github.com/giampaolo/psutil/issues/403
+.. _404: https://github.com/giampaolo/psutil/issues/404
+.. _405: https://github.com/giampaolo/psutil/issues/405
+.. _406: https://github.com/giampaolo/psutil/issues/406
+.. _407: https://github.com/giampaolo/psutil/issues/407
+.. _408: https://github.com/giampaolo/psutil/issues/408
+.. _409: https://github.com/giampaolo/psutil/issues/409
+.. _410: https://github.com/giampaolo/psutil/issues/410
+.. _411: https://github.com/giampaolo/psutil/issues/411
+.. _412: https://github.com/giampaolo/psutil/issues/412
+.. _413: https://github.com/giampaolo/psutil/issues/413
+.. _414: https://github.com/giampaolo/psutil/issues/414
+.. _415: https://github.com/giampaolo/psutil/issues/415
+.. _416: https://github.com/giampaolo/psutil/issues/416
+.. _417: https://github.com/giampaolo/psutil/issues/417
+.. _418: https://github.com/giampaolo/psutil/issues/418
+.. _419: https://github.com/giampaolo/psutil/issues/419
+.. _420: https://github.com/giampaolo/psutil/issues/420
+.. _421: https://github.com/giampaolo/psutil/issues/421
+.. _422: https://github.com/giampaolo/psutil/issues/422
+.. _423: https://github.com/giampaolo/psutil/issues/423
+.. _424: https://github.com/giampaolo/psutil/issues/424
+.. _425: https://github.com/giampaolo/psutil/issues/425
+.. _426: https://github.com/giampaolo/psutil/issues/426
+.. _427: https://github.com/giampaolo/psutil/issues/427
+.. _428: https://github.com/giampaolo/psutil/issues/428
+.. _429: https://github.com/giampaolo/psutil/issues/429
+.. _430: https://github.com/giampaolo/psutil/issues/430
+.. _431: https://github.com/giampaolo/psutil/issues/431
+.. _432: https://github.com/giampaolo/psutil/issues/432
+.. _433: https://github.com/giampaolo/psutil/issues/433
+.. _434: https://github.com/giampaolo/psutil/issues/434
+.. _435: https://github.com/giampaolo/psutil/issues/435
+.. _436: https://github.com/giampaolo/psutil/issues/436
+.. _437: https://github.com/giampaolo/psutil/issues/437
+.. _438: https://github.com/giampaolo/psutil/issues/438
+.. _439: https://github.com/giampaolo/psutil/issues/439
+.. _440: https://github.com/giampaolo/psutil/issues/440
+.. _441: https://github.com/giampaolo/psutil/issues/441
+.. _442: https://github.com/giampaolo/psutil/issues/442
+.. _443: https://github.com/giampaolo/psutil/issues/443
+.. _444: https://github.com/giampaolo/psutil/issues/444
+.. _445: https://github.com/giampaolo/psutil/issues/445
+.. _446: https://github.com/giampaolo/psutil/issues/446
+.. _447: https://github.com/giampaolo/psutil/issues/447
+.. _448: https://github.com/giampaolo/psutil/issues/448
+.. _449: https://github.com/giampaolo/psutil/issues/449
+.. _450: https://github.com/giampaolo/psutil/issues/450
+.. _451: https://github.com/giampaolo/psutil/issues/451
+.. _452: https://github.com/giampaolo/psutil/issues/452
+.. _453: https://github.com/giampaolo/psutil/issues/453
+.. _454: https://github.com/giampaolo/psutil/issues/454
+.. _455: https://github.com/giampaolo/psutil/issues/455
+.. _456: https://github.com/giampaolo/psutil/issues/456
+.. _457: https://github.com/giampaolo/psutil/issues/457
+.. _458: https://github.com/giampaolo/psutil/issues/458
+.. _459: https://github.com/giampaolo/psutil/issues/459
+.. _460: https://github.com/giampaolo/psutil/issues/460
+.. _461: https://github.com/giampaolo/psutil/issues/461
+.. _462: https://github.com/giampaolo/psutil/issues/462
+.. _463: https://github.com/giampaolo/psutil/issues/463
+.. _464: https://github.com/giampaolo/psutil/issues/464
+.. _465: https://github.com/giampaolo/psutil/issues/465
+.. _466: https://github.com/giampaolo/psutil/issues/466
+.. _467: https://github.com/giampaolo/psutil/issues/467
+.. _468: https://github.com/giampaolo/psutil/issues/468
+.. _469: https://github.com/giampaolo/psutil/issues/469
+.. _470: https://github.com/giampaolo/psutil/issues/470
+.. _471: https://github.com/giampaolo/psutil/issues/471
+.. _472: https://github.com/giampaolo/psutil/issues/472
+.. _473: https://github.com/giampaolo/psutil/issues/473
+.. _474: https://github.com/giampaolo/psutil/issues/474
+.. _475: https://github.com/giampaolo/psutil/issues/475
+.. _476: https://github.com/giampaolo/psutil/issues/476
+.. _477: https://github.com/giampaolo/psutil/issues/477
+.. _478: https://github.com/giampaolo/psutil/issues/478
+.. _479: https://github.com/giampaolo/psutil/issues/479
+.. _480: https://github.com/giampaolo/psutil/issues/480
+.. _481: https://github.com/giampaolo/psutil/issues/481
+.. _482: https://github.com/giampaolo/psutil/issues/482
+.. _483: https://github.com/giampaolo/psutil/issues/483
+.. _484: https://github.com/giampaolo/psutil/issues/484
+.. _485: https://github.com/giampaolo/psutil/issues/485
+.. _486: https://github.com/giampaolo/psutil/issues/486
+.. _487: https://github.com/giampaolo/psutil/issues/487
+.. _488: https://github.com/giampaolo/psutil/issues/488
+.. _489: https://github.com/giampaolo/psutil/issues/489
+.. _490: https://github.com/giampaolo/psutil/issues/490
+.. _491: https://github.com/giampaolo/psutil/issues/491
+.. _492: https://github.com/giampaolo/psutil/issues/492
+.. _493: https://github.com/giampaolo/psutil/issues/493
+.. _494: https://github.com/giampaolo/psutil/issues/494
+.. _495: https://github.com/giampaolo/psutil/issues/495
+.. _496: https://github.com/giampaolo/psutil/issues/496
+.. _497: https://github.com/giampaolo/psutil/issues/497
+.. _498: https://github.com/giampaolo/psutil/issues/498
+.. _499: https://github.com/giampaolo/psutil/issues/499
+.. _500: https://github.com/giampaolo/psutil/issues/500
+.. _501: https://github.com/giampaolo/psutil/issues/501
+.. _502: https://github.com/giampaolo/psutil/issues/502
+.. _503: https://github.com/giampaolo/psutil/issues/503
+.. _504: https://github.com/giampaolo/psutil/issues/504
+.. _505: https://github.com/giampaolo/psutil/issues/505
+.. _506: https://github.com/giampaolo/psutil/issues/506
+.. _507: https://github.com/giampaolo/psutil/issues/507
+.. _508: https://github.com/giampaolo/psutil/issues/508
+.. _509: https://github.com/giampaolo/psutil/issues/509
+.. _510: https://github.com/giampaolo/psutil/issues/510
+.. _511: https://github.com/giampaolo/psutil/issues/511
+.. _512: https://github.com/giampaolo/psutil/issues/512
+.. _513: https://github.com/giampaolo/psutil/issues/513
+.. _514: https://github.com/giampaolo/psutil/issues/514
+.. _515: https://github.com/giampaolo/psutil/issues/515
+.. _516: https://github.com/giampaolo/psutil/issues/516
+.. _517: https://github.com/giampaolo/psutil/issues/517
+.. _518: https://github.com/giampaolo/psutil/issues/518
+.. _519: https://github.com/giampaolo/psutil/issues/519
+.. _520: https://github.com/giampaolo/psutil/issues/520
+.. _521: https://github.com/giampaolo/psutil/issues/521
+.. _522: https://github.com/giampaolo/psutil/issues/522
+.. _523: https://github.com/giampaolo/psutil/issues/523
+.. _524: https://github.com/giampaolo/psutil/issues/524
+.. _525: https://github.com/giampaolo/psutil/issues/525
+.. _526: https://github.com/giampaolo/psutil/issues/526
+.. _527: https://github.com/giampaolo/psutil/issues/527
+.. _528: https://github.com/giampaolo/psutil/issues/528
+.. _529: https://github.com/giampaolo/psutil/issues/529
+.. _530: https://github.com/giampaolo/psutil/issues/530
+.. _531: https://github.com/giampaolo/psutil/issues/531
+.. _532: https://github.com/giampaolo/psutil/issues/532
+.. _533: https://github.com/giampaolo/psutil/issues/533
+.. _534: https://github.com/giampaolo/psutil/issues/534
+.. _535: https://github.com/giampaolo/psutil/issues/535
+.. _536: https://github.com/giampaolo/psutil/issues/536
+.. _537: https://github.com/giampaolo/psutil/issues/537
+.. _538: https://github.com/giampaolo/psutil/issues/538
+.. _539: https://github.com/giampaolo/psutil/issues/539
+.. _540: https://github.com/giampaolo/psutil/issues/540
+.. _541: https://github.com/giampaolo/psutil/issues/541
+.. _542: https://github.com/giampaolo/psutil/issues/542
+.. _543: https://github.com/giampaolo/psutil/issues/543
+.. _544: https://github.com/giampaolo/psutil/issues/544
+.. _545: https://github.com/giampaolo/psutil/issues/545
+.. _546: https://github.com/giampaolo/psutil/issues/546
+.. _547: https://github.com/giampaolo/psutil/issues/547
+.. _548: https://github.com/giampaolo/psutil/issues/548
+.. _549: https://github.com/giampaolo/psutil/issues/549
+.. _550: https://github.com/giampaolo/psutil/issues/550
+.. _551: https://github.com/giampaolo/psutil/issues/551
+.. _552: https://github.com/giampaolo/psutil/issues/552
+.. _553: https://github.com/giampaolo/psutil/issues/553
+.. _554: https://github.com/giampaolo/psutil/issues/554
+.. _555: https://github.com/giampaolo/psutil/issues/555
+.. _556: https://github.com/giampaolo/psutil/issues/556
+.. _557: https://github.com/giampaolo/psutil/issues/557
+.. _558: https://github.com/giampaolo/psutil/issues/558
+.. _559: https://github.com/giampaolo/psutil/issues/559
+.. _560: https://github.com/giampaolo/psutil/issues/560
+.. _561: https://github.com/giampaolo/psutil/issues/561
+.. _562: https://github.com/giampaolo/psutil/issues/562
+.. _563: https://github.com/giampaolo/psutil/issues/563
+.. _564: https://github.com/giampaolo/psutil/issues/564
+.. _565: https://github.com/giampaolo/psutil/issues/565
+.. _566: https://github.com/giampaolo/psutil/issues/566
+.. _567: https://github.com/giampaolo/psutil/issues/567
+.. _568: https://github.com/giampaolo/psutil/issues/568
+.. _569: https://github.com/giampaolo/psutil/issues/569
+.. _570: https://github.com/giampaolo/psutil/issues/570
+.. _571: https://github.com/giampaolo/psutil/issues/571
+.. _572: https://github.com/giampaolo/psutil/issues/572
+.. _573: https://github.com/giampaolo/psutil/issues/573
+.. _574: https://github.com/giampaolo/psutil/issues/574
+.. _575: https://github.com/giampaolo/psutil/issues/575
+.. _576: https://github.com/giampaolo/psutil/issues/576
+.. _577: https://github.com/giampaolo/psutil/issues/577
+.. _578: https://github.com/giampaolo/psutil/issues/578
+.. _579: https://github.com/giampaolo/psutil/issues/579
+.. _580: https://github.com/giampaolo/psutil/issues/580
+.. _581: https://github.com/giampaolo/psutil/issues/581
+.. _582: https://github.com/giampaolo/psutil/issues/582
+.. _583: https://github.com/giampaolo/psutil/issues/583
+.. _584: https://github.com/giampaolo/psutil/issues/584
+.. _585: https://github.com/giampaolo/psutil/issues/585
+.. _586: https://github.com/giampaolo/psutil/issues/586
+.. _587: https://github.com/giampaolo/psutil/issues/587
+.. _588: https://github.com/giampaolo/psutil/issues/588
+.. _589: https://github.com/giampaolo/psutil/issues/589
+.. _590: https://github.com/giampaolo/psutil/issues/590
+.. _591: https://github.com/giampaolo/psutil/issues/591
+.. _592: https://github.com/giampaolo/psutil/issues/592
+.. _593: https://github.com/giampaolo/psutil/issues/593
+.. _594: https://github.com/giampaolo/psutil/issues/594
+.. _595: https://github.com/giampaolo/psutil/issues/595
+.. _596: https://github.com/giampaolo/psutil/issues/596
+.. _597: https://github.com/giampaolo/psutil/issues/597
+.. _598: https://github.com/giampaolo/psutil/issues/598
+.. _599: https://github.com/giampaolo/psutil/issues/599
+.. _600: https://github.com/giampaolo/psutil/issues/600
+.. _601: https://github.com/giampaolo/psutil/issues/601
+.. _602: https://github.com/giampaolo/psutil/issues/602
+.. _603: https://github.com/giampaolo/psutil/issues/603
+.. _604: https://github.com/giampaolo/psutil/issues/604
+.. _605: https://github.com/giampaolo/psutil/issues/605
+.. _606: https://github.com/giampaolo/psutil/issues/606
+.. _607: https://github.com/giampaolo/psutil/issues/607
+.. _608: https://github.com/giampaolo/psutil/issues/608
+.. _609: https://github.com/giampaolo/psutil/issues/609
+.. _610: https://github.com/giampaolo/psutil/issues/610
+.. _611: https://github.com/giampaolo/psutil/issues/611
+.. _612: https://github.com/giampaolo/psutil/issues/612
+.. _613: https://github.com/giampaolo/psutil/issues/613
+.. _614: https://github.com/giampaolo/psutil/issues/614
+.. _615: https://github.com/giampaolo/psutil/issues/615
+.. _616: https://github.com/giampaolo/psutil/issues/616
+.. _617: https://github.com/giampaolo/psutil/issues/617
+.. _618: https://github.com/giampaolo/psutil/issues/618
+.. _619: https://github.com/giampaolo/psutil/issues/619
+.. _620: https://github.com/giampaolo/psutil/issues/620
+.. _621: https://github.com/giampaolo/psutil/issues/621
+.. _622: https://github.com/giampaolo/psutil/issues/622
+.. _623: https://github.com/giampaolo/psutil/issues/623
+.. _624: https://github.com/giampaolo/psutil/issues/624
+.. _625: https://github.com/giampaolo/psutil/issues/625
+.. _626: https://github.com/giampaolo/psutil/issues/626
+.. _627: https://github.com/giampaolo/psutil/issues/627
+.. _628: https://github.com/giampaolo/psutil/issues/628
+.. _629: https://github.com/giampaolo/psutil/issues/629
+.. _630: https://github.com/giampaolo/psutil/issues/630
+.. _631: https://github.com/giampaolo/psutil/issues/631
+.. _632: https://github.com/giampaolo/psutil/issues/632
+.. _633: https://github.com/giampaolo/psutil/issues/633
+.. _634: https://github.com/giampaolo/psutil/issues/634
+.. _635: https://github.com/giampaolo/psutil/issues/635
+.. _636: https://github.com/giampaolo/psutil/issues/636
+.. _637: https://github.com/giampaolo/psutil/issues/637
+.. _638: https://github.com/giampaolo/psutil/issues/638
+.. _639: https://github.com/giampaolo/psutil/issues/639
+.. _640: https://github.com/giampaolo/psutil/issues/640
+.. _641: https://github.com/giampaolo/psutil/issues/641
+.. _642: https://github.com/giampaolo/psutil/issues/642
+.. _643: https://github.com/giampaolo/psutil/issues/643
+.. _644: https://github.com/giampaolo/psutil/issues/644
+.. _645: https://github.com/giampaolo/psutil/issues/645
+.. _646: https://github.com/giampaolo/psutil/issues/646
+.. _647: https://github.com/giampaolo/psutil/issues/647
+.. _648: https://github.com/giampaolo/psutil/issues/648
+.. _649: https://github.com/giampaolo/psutil/issues/649
+.. _650: https://github.com/giampaolo/psutil/issues/650
+.. _651: https://github.com/giampaolo/psutil/issues/651
+.. _652: https://github.com/giampaolo/psutil/issues/652
+.. _653: https://github.com/giampaolo/psutil/issues/653
+.. _654: https://github.com/giampaolo/psutil/issues/654
+.. _655: https://github.com/giampaolo/psutil/issues/655
+.. _656: https://github.com/giampaolo/psutil/issues/656
+.. _657: https://github.com/giampaolo/psutil/issues/657
+.. _658: https://github.com/giampaolo/psutil/issues/658
+.. _659: https://github.com/giampaolo/psutil/issues/659
+.. _660: https://github.com/giampaolo/psutil/issues/660
+.. _661: https://github.com/giampaolo/psutil/issues/661
+.. _662: https://github.com/giampaolo/psutil/issues/662
+.. _663: https://github.com/giampaolo/psutil/issues/663
+.. _664: https://github.com/giampaolo/psutil/issues/664
+.. _665: https://github.com/giampaolo/psutil/issues/665
+.. _666: https://github.com/giampaolo/psutil/issues/666
+.. _667: https://github.com/giampaolo/psutil/issues/667
+.. _668: https://github.com/giampaolo/psutil/issues/668
+.. _669: https://github.com/giampaolo/psutil/issues/669
+.. _670: https://github.com/giampaolo/psutil/issues/670
+.. _671: https://github.com/giampaolo/psutil/issues/671
+.. _672: https://github.com/giampaolo/psutil/issues/672
+.. _673: https://github.com/giampaolo/psutil/issues/673
+.. _674: https://github.com/giampaolo/psutil/issues/674
+.. _675: https://github.com/giampaolo/psutil/issues/675
+.. _676: https://github.com/giampaolo/psutil/issues/676
+.. _677: https://github.com/giampaolo/psutil/issues/677
+.. _678: https://github.com/giampaolo/psutil/issues/678
+.. _679: https://github.com/giampaolo/psutil/issues/679
+.. _680: https://github.com/giampaolo/psutil/issues/680
+.. _681: https://github.com/giampaolo/psutil/issues/681
+.. _682: https://github.com/giampaolo/psutil/issues/682
+.. _683: https://github.com/giampaolo/psutil/issues/683
+.. _684: https://github.com/giampaolo/psutil/issues/684
+.. _685: https://github.com/giampaolo/psutil/issues/685
+.. _686: https://github.com/giampaolo/psutil/issues/686
+.. _687: https://github.com/giampaolo/psutil/issues/687
+.. _688: https://github.com/giampaolo/psutil/issues/688
+.. _689: https://github.com/giampaolo/psutil/issues/689
+.. _690: https://github.com/giampaolo/psutil/issues/690
+.. _691: https://github.com/giampaolo/psutil/issues/691
+.. _692: https://github.com/giampaolo/psutil/issues/692
+.. _693: https://github.com/giampaolo/psutil/issues/693
+.. _694: https://github.com/giampaolo/psutil/issues/694
+.. _695: https://github.com/giampaolo/psutil/issues/695
+.. _696: https://github.com/giampaolo/psutil/issues/696
+.. _697: https://github.com/giampaolo/psutil/issues/697
+.. _698: https://github.com/giampaolo/psutil/issues/698
+.. _699: https://github.com/giampaolo/psutil/issues/699
+.. _700: https://github.com/giampaolo/psutil/issues/700
+.. _701: https://github.com/giampaolo/psutil/issues/701
+.. _702: https://github.com/giampaolo/psutil/issues/702
+.. _703: https://github.com/giampaolo/psutil/issues/703
+.. _704: https://github.com/giampaolo/psutil/issues/704
+.. _705: https://github.com/giampaolo/psutil/issues/705
+.. _706: https://github.com/giampaolo/psutil/issues/706
+.. _707: https://github.com/giampaolo/psutil/issues/707
+.. _708: https://github.com/giampaolo/psutil/issues/708
+.. _709: https://github.com/giampaolo/psutil/issues/709
+.. _710: https://github.com/giampaolo/psutil/issues/710
+.. _711: https://github.com/giampaolo/psutil/issues/711
+.. _712: https://github.com/giampaolo/psutil/issues/712
+.. _713: https://github.com/giampaolo/psutil/issues/713
+.. _714: https://github.com/giampaolo/psutil/issues/714
+.. _715: https://github.com/giampaolo/psutil/issues/715
+.. _716: https://github.com/giampaolo/psutil/issues/716
+.. _717: https://github.com/giampaolo/psutil/issues/717
+.. _718: https://github.com/giampaolo/psutil/issues/718
+.. _719: https://github.com/giampaolo/psutil/issues/719
+.. _720: https://github.com/giampaolo/psutil/issues/720
+.. _721: https://github.com/giampaolo/psutil/issues/721
+.. _722: https://github.com/giampaolo/psutil/issues/722
+.. _723: https://github.com/giampaolo/psutil/issues/723
+.. _724: https://github.com/giampaolo/psutil/issues/724
+.. _725: https://github.com/giampaolo/psutil/issues/725
+.. _726: https://github.com/giampaolo/psutil/issues/726
+.. _727: https://github.com/giampaolo/psutil/issues/727
+.. _728: https://github.com/giampaolo/psutil/issues/728
+.. _729: https://github.com/giampaolo/psutil/issues/729
+.. _730: https://github.com/giampaolo/psutil/issues/730
+.. _731: https://github.com/giampaolo/psutil/issues/731
+.. _732: https://github.com/giampaolo/psutil/issues/732
+.. _733: https://github.com/giampaolo/psutil/issues/733
+.. _734: https://github.com/giampaolo/psutil/issues/734
+.. _735: https://github.com/giampaolo/psutil/issues/735
+.. _736: https://github.com/giampaolo/psutil/issues/736
+.. _737: https://github.com/giampaolo/psutil/issues/737
+.. _738: https://github.com/giampaolo/psutil/issues/738
+.. _739: https://github.com/giampaolo/psutil/issues/739
+.. _740: https://github.com/giampaolo/psutil/issues/740
+.. _741: https://github.com/giampaolo/psutil/issues/741
+.. _742: https://github.com/giampaolo/psutil/issues/742
+.. _743: https://github.com/giampaolo/psutil/issues/743
+.. _744: https://github.com/giampaolo/psutil/issues/744
+.. _745: https://github.com/giampaolo/psutil/issues/745
+.. _746: https://github.com/giampaolo/psutil/issues/746
+.. _747: https://github.com/giampaolo/psutil/issues/747
+.. _748: https://github.com/giampaolo/psutil/issues/748
+.. _749: https://github.com/giampaolo/psutil/issues/749
+.. _750: https://github.com/giampaolo/psutil/issues/750
+.. _751: https://github.com/giampaolo/psutil/issues/751
+.. _752: https://github.com/giampaolo/psutil/issues/752
+.. _753: https://github.com/giampaolo/psutil/issues/753
+.. _754: https://github.com/giampaolo/psutil/issues/754
+.. _755: https://github.com/giampaolo/psutil/issues/755
+.. _756: https://github.com/giampaolo/psutil/issues/756
+.. _757: https://github.com/giampaolo/psutil/issues/757
+.. _758: https://github.com/giampaolo/psutil/issues/758
+.. _759: https://github.com/giampaolo/psutil/issues/759
+.. _760: https://github.com/giampaolo/psutil/issues/760
+.. _761: https://github.com/giampaolo/psutil/issues/761
+.. _762: https://github.com/giampaolo/psutil/issues/762
+.. _763: https://github.com/giampaolo/psutil/issues/763
+.. _764: https://github.com/giampaolo/psutil/issues/764
+.. _765: https://github.com/giampaolo/psutil/issues/765
+.. _766: https://github.com/giampaolo/psutil/issues/766
+.. _767: https://github.com/giampaolo/psutil/issues/767
+.. _768: https://github.com/giampaolo/psutil/issues/768
+.. _769: https://github.com/giampaolo/psutil/issues/769
+.. _770: https://github.com/giampaolo/psutil/issues/770
+.. _771: https://github.com/giampaolo/psutil/issues/771
+.. _772: https://github.com/giampaolo/psutil/issues/772
+.. _773: https://github.com/giampaolo/psutil/issues/773
+.. _774: https://github.com/giampaolo/psutil/issues/774
+.. _775: https://github.com/giampaolo/psutil/issues/775
+.. _776: https://github.com/giampaolo/psutil/issues/776
+.. _777: https://github.com/giampaolo/psutil/issues/777
+.. _778: https://github.com/giampaolo/psutil/issues/778
+.. _779: https://github.com/giampaolo/psutil/issues/779
+.. _780: https://github.com/giampaolo/psutil/issues/780
+.. _781: https://github.com/giampaolo/psutil/issues/781
+.. _782: https://github.com/giampaolo/psutil/issues/782
+.. _783: https://github.com/giampaolo/psutil/issues/783
+.. _784: https://github.com/giampaolo/psutil/issues/784
+.. _785: https://github.com/giampaolo/psutil/issues/785
+.. _786: https://github.com/giampaolo/psutil/issues/786
+.. _787: https://github.com/giampaolo/psutil/issues/787
+.. _788: https://github.com/giampaolo/psutil/issues/788
+.. _789: https://github.com/giampaolo/psutil/issues/789
+.. _790: https://github.com/giampaolo/psutil/issues/790
+.. _791: https://github.com/giampaolo/psutil/issues/791
+.. _792: https://github.com/giampaolo/psutil/issues/792
+.. _793: https://github.com/giampaolo/psutil/issues/793
+.. _794: https://github.com/giampaolo/psutil/issues/794
+.. _795: https://github.com/giampaolo/psutil/issues/795
+.. _796: https://github.com/giampaolo/psutil/issues/796
+.. _797: https://github.com/giampaolo/psutil/issues/797
+.. _798: https://github.com/giampaolo/psutil/issues/798
+.. _799: https://github.com/giampaolo/psutil/issues/799
+.. _800: https://github.com/giampaolo/psutil/issues/800
+.. _801: https://github.com/giampaolo/psutil/issues/801
+.. _802: https://github.com/giampaolo/psutil/issues/802
+.. _803: https://github.com/giampaolo/psutil/issues/803
+.. _804: https://github.com/giampaolo/psutil/issues/804
+.. _805: https://github.com/giampaolo/psutil/issues/805
+.. _806: https://github.com/giampaolo/psutil/issues/806
+.. _807: https://github.com/giampaolo/psutil/issues/807
+.. _808: https://github.com/giampaolo/psutil/issues/808
+.. _809: https://github.com/giampaolo/psutil/issues/809
+.. _810: https://github.com/giampaolo/psutil/issues/810
+.. _811: https://github.com/giampaolo/psutil/issues/811
+.. _812: https://github.com/giampaolo/psutil/issues/812
+.. _813: https://github.com/giampaolo/psutil/issues/813
+.. _814: https://github.com/giampaolo/psutil/issues/814
+.. _815: https://github.com/giampaolo/psutil/issues/815
+.. _816: https://github.com/giampaolo/psutil/issues/816
+.. _817: https://github.com/giampaolo/psutil/issues/817
+.. _818: https://github.com/giampaolo/psutil/issues/818
+.. _819: https://github.com/giampaolo/psutil/issues/819
+.. _820: https://github.com/giampaolo/psutil/issues/820
+.. _821: https://github.com/giampaolo/psutil/issues/821
+.. _822: https://github.com/giampaolo/psutil/issues/822
+.. _823: https://github.com/giampaolo/psutil/issues/823
+.. _824: https://github.com/giampaolo/psutil/issues/824
+.. _825: https://github.com/giampaolo/psutil/issues/825
+.. _826: https://github.com/giampaolo/psutil/issues/826
+.. _827: https://github.com/giampaolo/psutil/issues/827
+.. _828: https://github.com/giampaolo/psutil/issues/828
+.. _829: https://github.com/giampaolo/psutil/issues/829
+.. _830: https://github.com/giampaolo/psutil/issues/830
+.. _831: https://github.com/giampaolo/psutil/issues/831
+.. _832: https://github.com/giampaolo/psutil/issues/832
+.. _833: https://github.com/giampaolo/psutil/issues/833
+.. _834: https://github.com/giampaolo/psutil/issues/834
+.. _835: https://github.com/giampaolo/psutil/issues/835
+.. _836: https://github.com/giampaolo/psutil/issues/836
+.. _837: https://github.com/giampaolo/psutil/issues/837
+.. _838: https://github.com/giampaolo/psutil/issues/838
+.. _839: https://github.com/giampaolo/psutil/issues/839
+.. _840: https://github.com/giampaolo/psutil/issues/840
+.. _841: https://github.com/giampaolo/psutil/issues/841
+.. _842: https://github.com/giampaolo/psutil/issues/842
+.. _843: https://github.com/giampaolo/psutil/issues/843
+.. _844: https://github.com/giampaolo/psutil/issues/844
+.. _845: https://github.com/giampaolo/psutil/issues/845
+.. _846: https://github.com/giampaolo/psutil/issues/846
+.. _847: https://github.com/giampaolo/psutil/issues/847
+.. _848: https://github.com/giampaolo/psutil/issues/848
+.. _849: https://github.com/giampaolo/psutil/issues/849
+.. _850: https://github.com/giampaolo/psutil/issues/850
+.. _851: https://github.com/giampaolo/psutil/issues/851
+.. _852: https://github.com/giampaolo/psutil/issues/852
+.. _853: https://github.com/giampaolo/psutil/issues/853
+.. _854: https://github.com/giampaolo/psutil/issues/854
+.. _855: https://github.com/giampaolo/psutil/issues/855
+.. _856: https://github.com/giampaolo/psutil/issues/856
+.. _857: https://github.com/giampaolo/psutil/issues/857
+.. _858: https://github.com/giampaolo/psutil/issues/858
+.. _859: https://github.com/giampaolo/psutil/issues/859
+.. _860: https://github.com/giampaolo/psutil/issues/860
+.. _861: https://github.com/giampaolo/psutil/issues/861
+.. _862: https://github.com/giampaolo/psutil/issues/862
+.. _863: https://github.com/giampaolo/psutil/issues/863
+.. _864: https://github.com/giampaolo/psutil/issues/864
+.. _865: https://github.com/giampaolo/psutil/issues/865
+.. _866: https://github.com/giampaolo/psutil/issues/866
+.. _867: https://github.com/giampaolo/psutil/issues/867
+.. _868: https://github.com/giampaolo/psutil/issues/868
+.. _869: https://github.com/giampaolo/psutil/issues/869
+.. _870: https://github.com/giampaolo/psutil/issues/870
+.. _871: https://github.com/giampaolo/psutil/issues/871
+.. _872: https://github.com/giampaolo/psutil/issues/872
+.. _873: https://github.com/giampaolo/psutil/issues/873
+.. _874: https://github.com/giampaolo/psutil/issues/874
+.. _875: https://github.com/giampaolo/psutil/issues/875
+.. _876: https://github.com/giampaolo/psutil/issues/876
+.. _877: https://github.com/giampaolo/psutil/issues/877
+.. _878: https://github.com/giampaolo/psutil/issues/878
+.. _879: https://github.com/giampaolo/psutil/issues/879
+.. _880: https://github.com/giampaolo/psutil/issues/880
+.. _881: https://github.com/giampaolo/psutil/issues/881
+.. _882: https://github.com/giampaolo/psutil/issues/882
+.. _883: https://github.com/giampaolo/psutil/issues/883
+.. _884: https://github.com/giampaolo/psutil/issues/884
+.. _885: https://github.com/giampaolo/psutil/issues/885
+.. _886: https://github.com/giampaolo/psutil/issues/886
+.. _887: https://github.com/giampaolo/psutil/issues/887
+.. _888: https://github.com/giampaolo/psutil/issues/888
+.. _889: https://github.com/giampaolo/psutil/issues/889
+.. _890: https://github.com/giampaolo/psutil/issues/890
+.. _891: https://github.com/giampaolo/psutil/issues/891
+.. _892: https://github.com/giampaolo/psutil/issues/892
+.. _893: https://github.com/giampaolo/psutil/issues/893
+.. _894: https://github.com/giampaolo/psutil/issues/894
+.. _895: https://github.com/giampaolo/psutil/issues/895
+.. _896: https://github.com/giampaolo/psutil/issues/896
+.. _897: https://github.com/giampaolo/psutil/issues/897
+.. _898: https://github.com/giampaolo/psutil/issues/898
+.. _899: https://github.com/giampaolo/psutil/issues/899
+.. _900: https://github.com/giampaolo/psutil/issues/900
+.. _901: https://github.com/giampaolo/psutil/issues/901
+.. _902: https://github.com/giampaolo/psutil/issues/902
+.. _903: https://github.com/giampaolo/psutil/issues/903
+.. _904: https://github.com/giampaolo/psutil/issues/904
+.. _905: https://github.com/giampaolo/psutil/issues/905
+.. _906: https://github.com/giampaolo/psutil/issues/906
+.. _907: https://github.com/giampaolo/psutil/issues/907
+.. _908: https://github.com/giampaolo/psutil/issues/908
+.. _909: https://github.com/giampaolo/psutil/issues/909
+.. _910: https://github.com/giampaolo/psutil/issues/910
+.. _911: https://github.com/giampaolo/psutil/issues/911
+.. _912: https://github.com/giampaolo/psutil/issues/912
+.. _913: https://github.com/giampaolo/psutil/issues/913
+.. _914: https://github.com/giampaolo/psutil/issues/914
+.. _915: https://github.com/giampaolo/psutil/issues/915
+.. _916: https://github.com/giampaolo/psutil/issues/916
+.. _917: https://github.com/giampaolo/psutil/issues/917
+.. _918: https://github.com/giampaolo/psutil/issues/918
+.. _919: https://github.com/giampaolo/psutil/issues/919
+.. _920: https://github.com/giampaolo/psutil/issues/920
+.. _921: https://github.com/giampaolo/psutil/issues/921
+.. _922: https://github.com/giampaolo/psutil/issues/922
+.. _923: https://github.com/giampaolo/psutil/issues/923
+.. _924: https://github.com/giampaolo/psutil/issues/924
+.. _925: https://github.com/giampaolo/psutil/issues/925
+.. _926: https://github.com/giampaolo/psutil/issues/926
+.. _927: https://github.com/giampaolo/psutil/issues/927
+.. _928: https://github.com/giampaolo/psutil/issues/928
+.. _929: https://github.com/giampaolo/psutil/issues/929
+.. _930: https://github.com/giampaolo/psutil/issues/930
+.. _931: https://github.com/giampaolo/psutil/issues/931
+.. _932: https://github.com/giampaolo/psutil/issues/932
+.. _933: https://github.com/giampaolo/psutil/issues/933
+.. _934: https://github.com/giampaolo/psutil/issues/934
+.. _935: https://github.com/giampaolo/psutil/issues/935
+.. _936: https://github.com/giampaolo/psutil/issues/936
+.. _937: https://github.com/giampaolo/psutil/issues/937
+.. _938: https://github.com/giampaolo/psutil/issues/938
+.. _939: https://github.com/giampaolo/psutil/issues/939
+.. _940: https://github.com/giampaolo/psutil/issues/940
+.. _941: https://github.com/giampaolo/psutil/issues/941
+.. _942: https://github.com/giampaolo/psutil/issues/942
+.. _943: https://github.com/giampaolo/psutil/issues/943
+.. _944: https://github.com/giampaolo/psutil/issues/944
+.. _945: https://github.com/giampaolo/psutil/issues/945
+.. _946: https://github.com/giampaolo/psutil/issues/946
+.. _947: https://github.com/giampaolo/psutil/issues/947
+.. _948: https://github.com/giampaolo/psutil/issues/948
+.. _949: https://github.com/giampaolo/psutil/issues/949
+.. _950: https://github.com/giampaolo/psutil/issues/950
+.. _951: https://github.com/giampaolo/psutil/issues/951
+.. _952: https://github.com/giampaolo/psutil/issues/952
+.. _953: https://github.com/giampaolo/psutil/issues/953
+.. _954: https://github.com/giampaolo/psutil/issues/954
+.. _955: https://github.com/giampaolo/psutil/issues/955
+.. _956: https://github.com/giampaolo/psutil/issues/956
+.. _957: https://github.com/giampaolo/psutil/issues/957
+.. _958: https://github.com/giampaolo/psutil/issues/958
+.. _959: https://github.com/giampaolo/psutil/issues/959
+.. _960: https://github.com/giampaolo/psutil/issues/960
+.. _961: https://github.com/giampaolo/psutil/issues/961
+.. _962: https://github.com/giampaolo/psutil/issues/962
+.. _963: https://github.com/giampaolo/psutil/issues/963
+.. _964: https://github.com/giampaolo/psutil/issues/964
+.. _965: https://github.com/giampaolo/psutil/issues/965
+.. _966: https://github.com/giampaolo/psutil/issues/966
+.. _967: https://github.com/giampaolo/psutil/issues/967
+.. _968: https://github.com/giampaolo/psutil/issues/968
+.. _969: https://github.com/giampaolo/psutil/issues/969
+.. _970: https://github.com/giampaolo/psutil/issues/970
+.. _971: https://github.com/giampaolo/psutil/issues/971
+.. _972: https://github.com/giampaolo/psutil/issues/972
+.. _973: https://github.com/giampaolo/psutil/issues/973
+.. _974: https://github.com/giampaolo/psutil/issues/974
+.. _975: https://github.com/giampaolo/psutil/issues/975
+.. _976: https://github.com/giampaolo/psutil/issues/976
+.. _977: https://github.com/giampaolo/psutil/issues/977
+.. _978: https://github.com/giampaolo/psutil/issues/978
+.. _979: https://github.com/giampaolo/psutil/issues/979
+.. _980: https://github.com/giampaolo/psutil/issues/980
+.. _981: https://github.com/giampaolo/psutil/issues/981
+.. _982: https://github.com/giampaolo/psutil/issues/982
+.. _983: https://github.com/giampaolo/psutil/issues/983
+.. _984: https://github.com/giampaolo/psutil/issues/984
+.. _985: https://github.com/giampaolo/psutil/issues/985
+.. _986: https://github.com/giampaolo/psutil/issues/986
+.. _987: https://github.com/giampaolo/psutil/issues/987
+.. _988: https://github.com/giampaolo/psutil/issues/988
+.. _989: https://github.com/giampaolo/psutil/issues/989
+.. _990: https://github.com/giampaolo/psutil/issues/990
+.. _991: https://github.com/giampaolo/psutil/issues/991
+.. _992: https://github.com/giampaolo/psutil/issues/992
+.. _993: https://github.com/giampaolo/psutil/issues/993
+.. _994: https://github.com/giampaolo/psutil/issues/994
+.. _995: https://github.com/giampaolo/psutil/issues/995
+.. _996: https://github.com/giampaolo/psutil/issues/996
+.. _997: https://github.com/giampaolo/psutil/issues/997
+.. _998: https://github.com/giampaolo/psutil/issues/998
+.. _999: https://github.com/giampaolo/psutil/issues/999
+.. _1000: https://github.com/giampaolo/psutil/issues/1000
+.. _1001: https://github.com/giampaolo/psutil/issues/1001
+.. _1002: https://github.com/giampaolo/psutil/issues/1002
+.. _1003: https://github.com/giampaolo/psutil/issues/1003
+.. _1004: https://github.com/giampaolo/psutil/issues/1004
+.. _1005: https://github.com/giampaolo/psutil/issues/1005
+.. _1006: https://github.com/giampaolo/psutil/issues/1006
+.. _1007: https://github.com/giampaolo/psutil/issues/1007
+.. _1008: https://github.com/giampaolo/psutil/issues/1008
+.. _1009: https://github.com/giampaolo/psutil/issues/1009
+.. _1010: https://github.com/giampaolo/psutil/issues/1010
+.. _1011: https://github.com/giampaolo/psutil/issues/1011
+.. _1012: https://github.com/giampaolo/psutil/issues/1012
+.. _1013: https://github.com/giampaolo/psutil/issues/1013
+.. _1014: https://github.com/giampaolo/psutil/issues/1014
+.. _1015: https://github.com/giampaolo/psutil/issues/1015
+.. _1016: https://github.com/giampaolo/psutil/issues/1016
+.. _1017: https://github.com/giampaolo/psutil/issues/1017
+.. _1018: https://github.com/giampaolo/psutil/issues/1018
+.. _1019: https://github.com/giampaolo/psutil/issues/1019
+.. _1020: https://github.com/giampaolo/psutil/issues/1020
+.. _1021: https://github.com/giampaolo/psutil/issues/1021
+.. _1022: https://github.com/giampaolo/psutil/issues/1022
+.. _1023: https://github.com/giampaolo/psutil/issues/1023
+.. _1024: https://github.com/giampaolo/psutil/issues/1024
+.. _1025: https://github.com/giampaolo/psutil/issues/1025
+.. _1026: https://github.com/giampaolo/psutil/issues/1026
+.. _1027: https://github.com/giampaolo/psutil/issues/1027
+.. _1028: https://github.com/giampaolo/psutil/issues/1028
+.. _1029: https://github.com/giampaolo/psutil/issues/1029
+.. _1030: https://github.com/giampaolo/psutil/issues/1030
+.. _1031: https://github.com/giampaolo/psutil/issues/1031
+.. _1032: https://github.com/giampaolo/psutil/issues/1032
+.. _1033: https://github.com/giampaolo/psutil/issues/1033
+.. _1034: https://github.com/giampaolo/psutil/issues/1034
+.. _1035: https://github.com/giampaolo/psutil/issues/1035
+.. _1036: https://github.com/giampaolo/psutil/issues/1036
+.. _1037: https://github.com/giampaolo/psutil/issues/1037
+.. _1038: https://github.com/giampaolo/psutil/issues/1038
+.. _1039: https://github.com/giampaolo/psutil/issues/1039
+.. _1040: https://github.com/giampaolo/psutil/issues/1040
+.. _1041: https://github.com/giampaolo/psutil/issues/1041
+.. _1042: https://github.com/giampaolo/psutil/issues/1042
+.. _1043: https://github.com/giampaolo/psutil/issues/1043
+.. _1044: https://github.com/giampaolo/psutil/issues/1044
+.. _1045: https://github.com/giampaolo/psutil/issues/1045
+.. _1046: https://github.com/giampaolo/psutil/issues/1046
+.. _1047: https://github.com/giampaolo/psutil/issues/1047
+.. _1048: https://github.com/giampaolo/psutil/issues/1048
+.. _1049: https://github.com/giampaolo/psutil/issues/1049
+.. _1050: https://github.com/giampaolo/psutil/issues/1050
+.. _1051: https://github.com/giampaolo/psutil/issues/1051
+.. _1052: https://github.com/giampaolo/psutil/issues/1052
+.. _1053: https://github.com/giampaolo/psutil/issues/1053
+.. _1054: https://github.com/giampaolo/psutil/issues/1054
+.. _1055: https://github.com/giampaolo/psutil/issues/1055
+.. _1056: https://github.com/giampaolo/psutil/issues/1056
+.. _1057: https://github.com/giampaolo/psutil/issues/1057
+.. _1058: https://github.com/giampaolo/psutil/issues/1058
+.. _1059: https://github.com/giampaolo/psutil/issues/1059
+.. _1060: https://github.com/giampaolo/psutil/issues/1060
+.. _1061: https://github.com/giampaolo/psutil/issues/1061
+.. _1062: https://github.com/giampaolo/psutil/issues/1062
+.. _1063: https://github.com/giampaolo/psutil/issues/1063
+.. _1064: https://github.com/giampaolo/psutil/issues/1064
+.. _1065: https://github.com/giampaolo/psutil/issues/1065
+.. _1066: https://github.com/giampaolo/psutil/issues/1066
+.. _1067: https://github.com/giampaolo/psutil/issues/1067
+.. _1068: https://github.com/giampaolo/psutil/issues/1068
+.. _1069: https://github.com/giampaolo/psutil/issues/1069
+.. _1070: https://github.com/giampaolo/psutil/issues/1070
+.. _1071: https://github.com/giampaolo/psutil/issues/1071
+.. _1072: https://github.com/giampaolo/psutil/issues/1072
+.. _1073: https://github.com/giampaolo/psutil/issues/1073
+.. _1074: https://github.com/giampaolo/psutil/issues/1074
+.. _1075: https://github.com/giampaolo/psutil/issues/1075
+.. _1076: https://github.com/giampaolo/psutil/issues/1076
+.. _1077: https://github.com/giampaolo/psutil/issues/1077
+.. _1078: https://github.com/giampaolo/psutil/issues/1078
+.. _1079: https://github.com/giampaolo/psutil/issues/1079
+.. _1080: https://github.com/giampaolo/psutil/issues/1080
+.. _1081: https://github.com/giampaolo/psutil/issues/1081
+.. _1082: https://github.com/giampaolo/psutil/issues/1082
+.. _1083: https://github.com/giampaolo/psutil/issues/1083
+.. _1084: https://github.com/giampaolo/psutil/issues/1084
+.. _1085: https://github.com/giampaolo/psutil/issues/1085
+.. _1086: https://github.com/giampaolo/psutil/issues/1086
+.. _1087: https://github.com/giampaolo/psutil/issues/1087
+.. _1088: https://github.com/giampaolo/psutil/issues/1088
+.. _1089: https://github.com/giampaolo/psutil/issues/1089
+.. _1090: https://github.com/giampaolo/psutil/issues/1090
+.. _1091: https://github.com/giampaolo/psutil/issues/1091
+.. _1092: https://github.com/giampaolo/psutil/issues/1092
+.. _1093: https://github.com/giampaolo/psutil/issues/1093
+.. _1094: https://github.com/giampaolo/psutil/issues/1094
+.. _1095: https://github.com/giampaolo/psutil/issues/1095
+.. _1096: https://github.com/giampaolo/psutil/issues/1096
+.. _1097: https://github.com/giampaolo/psutil/issues/1097
+.. _1098: https://github.com/giampaolo/psutil/issues/1098
+.. _1099: https://github.com/giampaolo/psutil/issues/1099
+.. _1100: https://github.com/giampaolo/psutil/issues/1100
+.. _1101: https://github.com/giampaolo/psutil/issues/1101
+.. _1102: https://github.com/giampaolo/psutil/issues/1102
+.. _1103: https://github.com/giampaolo/psutil/issues/1103
+.. _1104: https://github.com/giampaolo/psutil/issues/1104
+.. _1105: https://github.com/giampaolo/psutil/issues/1105
+.. _1106: https://github.com/giampaolo/psutil/issues/1106
+.. _1107: https://github.com/giampaolo/psutil/issues/1107
+.. _1108: https://github.com/giampaolo/psutil/issues/1108
+.. _1109: https://github.com/giampaolo/psutil/issues/1109
+.. _1110: https://github.com/giampaolo/psutil/issues/1110
+.. _1111: https://github.com/giampaolo/psutil/issues/1111
+.. _1112: https://github.com/giampaolo/psutil/issues/1112
+.. _1113: https://github.com/giampaolo/psutil/issues/1113
+.. _1114: https://github.com/giampaolo/psutil/issues/1114
+.. _1115: https://github.com/giampaolo/psutil/issues/1115
+.. _1116: https://github.com/giampaolo/psutil/issues/1116
+.. _1117: https://github.com/giampaolo/psutil/issues/1117
+.. _1118: https://github.com/giampaolo/psutil/issues/1118
+.. _1119: https://github.com/giampaolo/psutil/issues/1119
+.. _1120: https://github.com/giampaolo/psutil/issues/1120
+.. _1121: https://github.com/giampaolo/psutil/issues/1121
+.. _1122: https://github.com/giampaolo/psutil/issues/1122
+.. _1123: https://github.com/giampaolo/psutil/issues/1123
+.. _1124: https://github.com/giampaolo/psutil/issues/1124
+.. _1125: https://github.com/giampaolo/psutil/issues/1125
+.. _1126: https://github.com/giampaolo/psutil/issues/1126
+.. _1127: https://github.com/giampaolo/psutil/issues/1127
+.. _1128: https://github.com/giampaolo/psutil/issues/1128
+.. _1129: https://github.com/giampaolo/psutil/issues/1129
+.. _1130: https://github.com/giampaolo/psutil/issues/1130
+.. _1131: https://github.com/giampaolo/psutil/issues/1131
+.. _1132: https://github.com/giampaolo/psutil/issues/1132
+.. _1133: https://github.com/giampaolo/psutil/issues/1133
+.. _1134: https://github.com/giampaolo/psutil/issues/1134
+.. _1135: https://github.com/giampaolo/psutil/issues/1135
+.. _1136: https://github.com/giampaolo/psutil/issues/1136
+.. _1137: https://github.com/giampaolo/psutil/issues/1137
+.. _1138: https://github.com/giampaolo/psutil/issues/1138
+.. _1139: https://github.com/giampaolo/psutil/issues/1139
+.. _1140: https://github.com/giampaolo/psutil/issues/1140
+.. _1141: https://github.com/giampaolo/psutil/issues/1141
+.. _1142: https://github.com/giampaolo/psutil/issues/1142
+.. _1143: https://github.com/giampaolo/psutil/issues/1143
+.. _1144: https://github.com/giampaolo/psutil/issues/1144
+.. _1145: https://github.com/giampaolo/psutil/issues/1145
+.. _1146: https://github.com/giampaolo/psutil/issues/1146
+.. _1147: https://github.com/giampaolo/psutil/issues/1147
+.. _1148: https://github.com/giampaolo/psutil/issues/1148
+.. _1149: https://github.com/giampaolo/psutil/issues/1149
+.. _1150: https://github.com/giampaolo/psutil/issues/1150
+.. _1151: https://github.com/giampaolo/psutil/issues/1151
+.. _1152: https://github.com/giampaolo/psutil/issues/1152
+.. _1153: https://github.com/giampaolo/psutil/issues/1153
+.. _1154: https://github.com/giampaolo/psutil/issues/1154
+.. _1155: https://github.com/giampaolo/psutil/issues/1155
+.. _1156: https://github.com/giampaolo/psutil/issues/1156
+.. _1157: https://github.com/giampaolo/psutil/issues/1157
+.. _1158: https://github.com/giampaolo/psutil/issues/1158
+.. _1159: https://github.com/giampaolo/psutil/issues/1159
+.. _1160: https://github.com/giampaolo/psutil/issues/1160
+.. _1161: https://github.com/giampaolo/psutil/issues/1161
+.. _1162: https://github.com/giampaolo/psutil/issues/1162
+.. _1163: https://github.com/giampaolo/psutil/issues/1163
+.. _1164: https://github.com/giampaolo/psutil/issues/1164
+.. _1165: https://github.com/giampaolo/psutil/issues/1165
+.. _1166: https://github.com/giampaolo/psutil/issues/1166
+.. _1167: https://github.com/giampaolo/psutil/issues/1167
+.. _1168: https://github.com/giampaolo/psutil/issues/1168
+.. _1169: https://github.com/giampaolo/psutil/issues/1169
+.. _1170: https://github.com/giampaolo/psutil/issues/1170
+.. _1171: https://github.com/giampaolo/psutil/issues/1171
+.. _1172: https://github.com/giampaolo/psutil/issues/1172
+.. _1173: https://github.com/giampaolo/psutil/issues/1173
+.. _1174: https://github.com/giampaolo/psutil/issues/1174
+.. _1175: https://github.com/giampaolo/psutil/issues/1175
+.. _1176: https://github.com/giampaolo/psutil/issues/1176
+.. _1177: https://github.com/giampaolo/psutil/issues/1177
+.. _1178: https://github.com/giampaolo/psutil/issues/1178
+.. _1179: https://github.com/giampaolo/psutil/issues/1179
+.. _1180: https://github.com/giampaolo/psutil/issues/1180
+.. _1181: https://github.com/giampaolo/psutil/issues/1181
+.. _1182: https://github.com/giampaolo/psutil/issues/1182
+.. _1183: https://github.com/giampaolo/psutil/issues/1183
+.. _1184: https://github.com/giampaolo/psutil/issues/1184
+.. _1185: https://github.com/giampaolo/psutil/issues/1185
+.. _1186: https://github.com/giampaolo/psutil/issues/1186
+.. _1187: https://github.com/giampaolo/psutil/issues/1187
+.. _1188: https://github.com/giampaolo/psutil/issues/1188
+.. _1189: https://github.com/giampaolo/psutil/issues/1189
+.. _1190: https://github.com/giampaolo/psutil/issues/1190
+.. _1191: https://github.com/giampaolo/psutil/issues/1191
+.. _1192: https://github.com/giampaolo/psutil/issues/1192
+.. _1193: https://github.com/giampaolo/psutil/issues/1193
+.. _1194: https://github.com/giampaolo/psutil/issues/1194
+.. _1195: https://github.com/giampaolo/psutil/issues/1195
+.. _1196: https://github.com/giampaolo/psutil/issues/1196
+.. _1197: https://github.com/giampaolo/psutil/issues/1197
+.. _1198: https://github.com/giampaolo/psutil/issues/1198
+.. _1199: https://github.com/giampaolo/psutil/issues/1199
+.. _1200: https://github.com/giampaolo/psutil/issues/1200
+.. _1201: https://github.com/giampaolo/psutil/issues/1201
+.. _1202: https://github.com/giampaolo/psutil/issues/1202
+.. _1203: https://github.com/giampaolo/psutil/issues/1203
+.. _1204: https://github.com/giampaolo/psutil/issues/1204
+.. _1205: https://github.com/giampaolo/psutil/issues/1205
+.. _1206: https://github.com/giampaolo/psutil/issues/1206
+.. _1207: https://github.com/giampaolo/psutil/issues/1207
+.. _1208: https://github.com/giampaolo/psutil/issues/1208
+.. _1209: https://github.com/giampaolo/psutil/issues/1209
+.. _1210: https://github.com/giampaolo/psutil/issues/1210
+.. _1211: https://github.com/giampaolo/psutil/issues/1211
+.. _1212: https://github.com/giampaolo/psutil/issues/1212
+.. _1213: https://github.com/giampaolo/psutil/issues/1213
+.. _1214: https://github.com/giampaolo/psutil/issues/1214
+.. _1215: https://github.com/giampaolo/psutil/issues/1215
+.. _1216: https://github.com/giampaolo/psutil/issues/1216
+.. _1217: https://github.com/giampaolo/psutil/issues/1217
+.. _1218: https://github.com/giampaolo/psutil/issues/1218
+.. _1219: https://github.com/giampaolo/psutil/issues/1219
+.. _1220: https://github.com/giampaolo/psutil/issues/1220
+.. _1221: https://github.com/giampaolo/psutil/issues/1221
+.. _1222: https://github.com/giampaolo/psutil/issues/1222
+.. _1223: https://github.com/giampaolo/psutil/issues/1223
+.. _1224: https://github.com/giampaolo/psutil/issues/1224
+.. _1225: https://github.com/giampaolo/psutil/issues/1225
+.. _1226: https://github.com/giampaolo/psutil/issues/1226
+.. _1227: https://github.com/giampaolo/psutil/issues/1227
+.. _1228: https://github.com/giampaolo/psutil/issues/1228
+.. _1229: https://github.com/giampaolo/psutil/issues/1229
+.. _1230: https://github.com/giampaolo/psutil/issues/1230
+.. _1231: https://github.com/giampaolo/psutil/issues/1231
+.. _1232: https://github.com/giampaolo/psutil/issues/1232
+.. _1233: https://github.com/giampaolo/psutil/issues/1233
+.. _1234: https://github.com/giampaolo/psutil/issues/1234
+.. _1235: https://github.com/giampaolo/psutil/issues/1235
+.. _1236: https://github.com/giampaolo/psutil/issues/1236
+.. _1237: https://github.com/giampaolo/psutil/issues/1237
+.. _1238: https://github.com/giampaolo/psutil/issues/1238
+.. _1239: https://github.com/giampaolo/psutil/issues/1239
+.. _1240: https://github.com/giampaolo/psutil/issues/1240
+.. _1241: https://github.com/giampaolo/psutil/issues/1241
+.. _1242: https://github.com/giampaolo/psutil/issues/1242
+.. _1243: https://github.com/giampaolo/psutil/issues/1243
+.. _1244: https://github.com/giampaolo/psutil/issues/1244
+.. _1245: https://github.com/giampaolo/psutil/issues/1245
+.. _1246: https://github.com/giampaolo/psutil/issues/1246
+.. _1247: https://github.com/giampaolo/psutil/issues/1247
+.. _1248: https://github.com/giampaolo/psutil/issues/1248
+.. _1249: https://github.com/giampaolo/psutil/issues/1249
+.. _1250: https://github.com/giampaolo/psutil/issues/1250
+.. _1251: https://github.com/giampaolo/psutil/issues/1251
+.. _1252: https://github.com/giampaolo/psutil/issues/1252
+.. _1253: https://github.com/giampaolo/psutil/issues/1253
+.. _1254: https://github.com/giampaolo/psutil/issues/1254
+.. _1255: https://github.com/giampaolo/psutil/issues/1255
+.. _1256: https://github.com/giampaolo/psutil/issues/1256
+.. _1257: https://github.com/giampaolo/psutil/issues/1257
+.. _1258: https://github.com/giampaolo/psutil/issues/1258
+.. _1259: https://github.com/giampaolo/psutil/issues/1259
+.. _1260: https://github.com/giampaolo/psutil/issues/1260
+.. _1261: https://github.com/giampaolo/psutil/issues/1261
+.. _1262: https://github.com/giampaolo/psutil/issues/1262
+.. _1263: https://github.com/giampaolo/psutil/issues/1263
+.. _1264: https://github.com/giampaolo/psutil/issues/1264
+.. _1265: https://github.com/giampaolo/psutil/issues/1265
+.. _1266: https://github.com/giampaolo/psutil/issues/1266
+.. _1267: https://github.com/giampaolo/psutil/issues/1267
+.. _1268: https://github.com/giampaolo/psutil/issues/1268
+.. _1269: https://github.com/giampaolo/psutil/issues/1269
+.. _1270: https://github.com/giampaolo/psutil/issues/1270
+.. _1271: https://github.com/giampaolo/psutil/issues/1271
+.. _1272: https://github.com/giampaolo/psutil/issues/1272
+.. _1273: https://github.com/giampaolo/psutil/issues/1273
+.. _1274: https://github.com/giampaolo/psutil/issues/1274
+.. _1275: https://github.com/giampaolo/psutil/issues/1275
+.. _1276: https://github.com/giampaolo/psutil/issues/1276
+.. _1277: https://github.com/giampaolo/psutil/issues/1277
+.. _1278: https://github.com/giampaolo/psutil/issues/1278
+.. _1279: https://github.com/giampaolo/psutil/issues/1279
+.. _1280: https://github.com/giampaolo/psutil/issues/1280
+.. _1281: https://github.com/giampaolo/psutil/issues/1281
+.. _1282: https://github.com/giampaolo/psutil/issues/1282
+.. _1283: https://github.com/giampaolo/psutil/issues/1283
+.. _1284: https://github.com/giampaolo/psutil/issues/1284
+.. _1285: https://github.com/giampaolo/psutil/issues/1285
+.. _1286: https://github.com/giampaolo/psutil/issues/1286
+.. _1287: https://github.com/giampaolo/psutil/issues/1287
+.. _1288: https://github.com/giampaolo/psutil/issues/1288
+.. _1289: https://github.com/giampaolo/psutil/issues/1289
+.. _1290: https://github.com/giampaolo/psutil/issues/1290
+.. _1291: https://github.com/giampaolo/psutil/issues/1291
+.. _1292: https://github.com/giampaolo/psutil/issues/1292
+.. _1293: https://github.com/giampaolo/psutil/issues/1293
+.. _1294: https://github.com/giampaolo/psutil/issues/1294
+.. _1295: https://github.com/giampaolo/psutil/issues/1295
+.. _1296: https://github.com/giampaolo/psutil/issues/1296
+.. _1297: https://github.com/giampaolo/psutil/issues/1297
+.. _1298: https://github.com/giampaolo/psutil/issues/1298
+.. _1299: https://github.com/giampaolo/psutil/issues/1299
+.. _1300: https://github.com/giampaolo/psutil/issues/1300
+.. _1301: https://github.com/giampaolo/psutil/issues/1301
+.. _1302: https://github.com/giampaolo/psutil/issues/1302
+.. _1303: https://github.com/giampaolo/psutil/issues/1303
+.. _1304: https://github.com/giampaolo/psutil/issues/1304
+.. _1305: https://github.com/giampaolo/psutil/issues/1305
+.. _1306: https://github.com/giampaolo/psutil/issues/1306
+.. _1307: https://github.com/giampaolo/psutil/issues/1307
+.. _1308: https://github.com/giampaolo/psutil/issues/1308
+.. _1309: https://github.com/giampaolo/psutil/issues/1309
+.. _1310: https://github.com/giampaolo/psutil/issues/1310
+.. _1311: https://github.com/giampaolo/psutil/issues/1311
+.. _1312: https://github.com/giampaolo/psutil/issues/1312
+.. _1313: https://github.com/giampaolo/psutil/issues/1313
+.. _1314: https://github.com/giampaolo/psutil/issues/1314
+.. _1315: https://github.com/giampaolo/psutil/issues/1315
+.. _1316: https://github.com/giampaolo/psutil/issues/1316
+.. _1317: https://github.com/giampaolo/psutil/issues/1317
+.. _1318: https://github.com/giampaolo/psutil/issues/1318
+.. _1319: https://github.com/giampaolo/psutil/issues/1319
+.. _1320: https://github.com/giampaolo/psutil/issues/1320
+.. _1321: https://github.com/giampaolo/psutil/issues/1321
+.. _1322: https://github.com/giampaolo/psutil/issues/1322
+.. _1323: https://github.com/giampaolo/psutil/issues/1323
+.. _1324: https://github.com/giampaolo/psutil/issues/1324
+.. _1325: https://github.com/giampaolo/psutil/issues/1325
+.. _1326: https://github.com/giampaolo/psutil/issues/1326
+.. _1327: https://github.com/giampaolo/psutil/issues/1327
+.. _1328: https://github.com/giampaolo/psutil/issues/1328
+.. _1329: https://github.com/giampaolo/psutil/issues/1329
+.. _1330: https://github.com/giampaolo/psutil/issues/1330
+.. _1331: https://github.com/giampaolo/psutil/issues/1331
+.. _1332: https://github.com/giampaolo/psutil/issues/1332
+.. _1333: https://github.com/giampaolo/psutil/issues/1333
+.. _1334: https://github.com/giampaolo/psutil/issues/1334
+.. _1335: https://github.com/giampaolo/psutil/issues/1335
+.. _1336: https://github.com/giampaolo/psutil/issues/1336
+.. _1337: https://github.com/giampaolo/psutil/issues/1337
+.. _1338: https://github.com/giampaolo/psutil/issues/1338
+.. _1339: https://github.com/giampaolo/psutil/issues/1339
+.. _1340: https://github.com/giampaolo/psutil/issues/1340
+.. _1341: https://github.com/giampaolo/psutil/issues/1341
+.. _1342: https://github.com/giampaolo/psutil/issues/1342
+.. _1343: https://github.com/giampaolo/psutil/issues/1343
+.. _1344: https://github.com/giampaolo/psutil/issues/1344
+.. _1345: https://github.com/giampaolo/psutil/issues/1345
+.. _1346: https://github.com/giampaolo/psutil/issues/1346
+.. _1347: https://github.com/giampaolo/psutil/issues/1347
+.. _1348: https://github.com/giampaolo/psutil/issues/1348
+.. _1349: https://github.com/giampaolo/psutil/issues/1349
+.. _1350: https://github.com/giampaolo/psutil/issues/1350
+.. _1351: https://github.com/giampaolo/psutil/issues/1351
+.. _1352: https://github.com/giampaolo/psutil/issues/1352
+.. _1353: https://github.com/giampaolo/psutil/issues/1353
+.. _1354: https://github.com/giampaolo/psutil/issues/1354
+.. _1355: https://github.com/giampaolo/psutil/issues/1355
+.. _1356: https://github.com/giampaolo/psutil/issues/1356
+.. _1357: https://github.com/giampaolo/psutil/issues/1357
+.. _1358: https://github.com/giampaolo/psutil/issues/1358
+.. _1359: https://github.com/giampaolo/psutil/issues/1359
+.. _1360: https://github.com/giampaolo/psutil/issues/1360
+.. _1361: https://github.com/giampaolo/psutil/issues/1361
+.. _1362: https://github.com/giampaolo/psutil/issues/1362
+.. _1363: https://github.com/giampaolo/psutil/issues/1363
+.. _1364: https://github.com/giampaolo/psutil/issues/1364
+.. _1365: https://github.com/giampaolo/psutil/issues/1365
+.. _1366: https://github.com/giampaolo/psutil/issues/1366
+.. _1367: https://github.com/giampaolo/psutil/issues/1367
+.. _1368: https://github.com/giampaolo/psutil/issues/1368
+.. _1369: https://github.com/giampaolo/psutil/issues/1369
+.. _1370: https://github.com/giampaolo/psutil/issues/1370
+.. _1371: https://github.com/giampaolo/psutil/issues/1371
+.. _1372: https://github.com/giampaolo/psutil/issues/1372
+.. _1373: https://github.com/giampaolo/psutil/issues/1373
+.. _1374: https://github.com/giampaolo/psutil/issues/1374
+.. _1375: https://github.com/giampaolo/psutil/issues/1375
+.. _1376: https://github.com/giampaolo/psutil/issues/1376
+.. _1377: https://github.com/giampaolo/psutil/issues/1377
+.. _1378: https://github.com/giampaolo/psutil/issues/1378
+.. _1379: https://github.com/giampaolo/psutil/issues/1379
+.. _1380: https://github.com/giampaolo/psutil/issues/1380
+.. _1381: https://github.com/giampaolo/psutil/issues/1381
+.. _1382: https://github.com/giampaolo/psutil/issues/1382
+.. _1383: https://github.com/giampaolo/psutil/issues/1383
+.. _1384: https://github.com/giampaolo/psutil/issues/1384
+.. _1385: https://github.com/giampaolo/psutil/issues/1385
+.. _1386: https://github.com/giampaolo/psutil/issues/1386
+.. _1387: https://github.com/giampaolo/psutil/issues/1387
+.. _1388: https://github.com/giampaolo/psutil/issues/1388
+.. _1389: https://github.com/giampaolo/psutil/issues/1389
+.. _1390: https://github.com/giampaolo/psutil/issues/1390
+.. _1391: https://github.com/giampaolo/psutil/issues/1391
+.. _1392: https://github.com/giampaolo/psutil/issues/1392
+.. _1393: https://github.com/giampaolo/psutil/issues/1393
+.. _1394: https://github.com/giampaolo/psutil/issues/1394
+.. _1395: https://github.com/giampaolo/psutil/issues/1395
+.. _1396: https://github.com/giampaolo/psutil/issues/1396
+.. _1397: https://github.com/giampaolo/psutil/issues/1397
+.. _1398: https://github.com/giampaolo/psutil/issues/1398
+.. _1399: https://github.com/giampaolo/psutil/issues/1399
+.. _1400: https://github.com/giampaolo/psutil/issues/1400
+.. _1401: https://github.com/giampaolo/psutil/issues/1401
+.. _1402: https://github.com/giampaolo/psutil/issues/1402
+.. _1403: https://github.com/giampaolo/psutil/issues/1403
+.. _1404: https://github.com/giampaolo/psutil/issues/1404
+.. _1405: https://github.com/giampaolo/psutil/issues/1405
+.. _1406: https://github.com/giampaolo/psutil/issues/1406
+.. _1407: https://github.com/giampaolo/psutil/issues/1407
+.. _1408: https://github.com/giampaolo/psutil/issues/1408
+.. _1409: https://github.com/giampaolo/psutil/issues/1409
+.. _1410: https://github.com/giampaolo/psutil/issues/1410
+.. _1411: https://github.com/giampaolo/psutil/issues/1411
+.. _1412: https://github.com/giampaolo/psutil/issues/1412
+.. _1413: https://github.com/giampaolo/psutil/issues/1413
+.. _1414: https://github.com/giampaolo/psutil/issues/1414
+.. _1415: https://github.com/giampaolo/psutil/issues/1415
+.. _1416: https://github.com/giampaolo/psutil/issues/1416
+.. _1417: https://github.com/giampaolo/psutil/issues/1417
+.. _1418: https://github.com/giampaolo/psutil/issues/1418
+.. _1419: https://github.com/giampaolo/psutil/issues/1419
+.. _1420: https://github.com/giampaolo/psutil/issues/1420
+.. _1421: https://github.com/giampaolo/psutil/issues/1421
+.. _1422: https://github.com/giampaolo/psutil/issues/1422
+.. _1423: https://github.com/giampaolo/psutil/issues/1423
+.. _1424: https://github.com/giampaolo/psutil/issues/1424
+.. _1425: https://github.com/giampaolo/psutil/issues/1425
+.. _1426: https://github.com/giampaolo/psutil/issues/1426
+.. _1427: https://github.com/giampaolo/psutil/issues/1427
+.. _1428: https://github.com/giampaolo/psutil/issues/1428
+.. _1429: https://github.com/giampaolo/psutil/issues/1429
+.. _1430: https://github.com/giampaolo/psutil/issues/1430
+.. _1431: https://github.com/giampaolo/psutil/issues/1431
+.. _1432: https://github.com/giampaolo/psutil/issues/1432
+.. _1433: https://github.com/giampaolo/psutil/issues/1433
+.. _1434: https://github.com/giampaolo/psutil/issues/1434
+.. _1435: https://github.com/giampaolo/psutil/issues/1435
+.. _1436: https://github.com/giampaolo/psutil/issues/1436
+.. _1437: https://github.com/giampaolo/psutil/issues/1437
+.. _1438: https://github.com/giampaolo/psutil/issues/1438
+.. _1439: https://github.com/giampaolo/psutil/issues/1439
+.. _1440: https://github.com/giampaolo/psutil/issues/1440
+.. _1441: https://github.com/giampaolo/psutil/issues/1441
+.. _1442: https://github.com/giampaolo/psutil/issues/1442
+.. _1443: https://github.com/giampaolo/psutil/issues/1443
+.. _1444: https://github.com/giampaolo/psutil/issues/1444
+.. _1445: https://github.com/giampaolo/psutil/issues/1445
+.. _1446: https://github.com/giampaolo/psutil/issues/1446
+.. _1447: https://github.com/giampaolo/psutil/issues/1447
+.. _1448: https://github.com/giampaolo/psutil/issues/1448
+.. _1449: https://github.com/giampaolo/psutil/issues/1449
+.. _1450: https://github.com/giampaolo/psutil/issues/1450
+.. _1451: https://github.com/giampaolo/psutil/issues/1451
+.. _1452: https://github.com/giampaolo/psutil/issues/1452
+.. _1453: https://github.com/giampaolo/psutil/issues/1453
+.. _1454: https://github.com/giampaolo/psutil/issues/1454
+.. _1455: https://github.com/giampaolo/psutil/issues/1455
+.. _1456: https://github.com/giampaolo/psutil/issues/1456
+.. _1457: https://github.com/giampaolo/psutil/issues/1457
+.. _1458: https://github.com/giampaolo/psutil/issues/1458
+.. _1459: https://github.com/giampaolo/psutil/issues/1459
+.. _1460: https://github.com/giampaolo/psutil/issues/1460
+.. _1461: https://github.com/giampaolo/psutil/issues/1461
+.. _1462: https://github.com/giampaolo/psutil/issues/1462
+.. _1463: https://github.com/giampaolo/psutil/issues/1463
+.. _1464: https://github.com/giampaolo/psutil/issues/1464
+.. _1465: https://github.com/giampaolo/psutil/issues/1465
+.. _1466: https://github.com/giampaolo/psutil/issues/1466
+.. _1467: https://github.com/giampaolo/psutil/issues/1467
+.. _1468: https://github.com/giampaolo/psutil/issues/1468
+.. _1469: https://github.com/giampaolo/psutil/issues/1469
+.. _1470: https://github.com/giampaolo/psutil/issues/1470
+.. _1471: https://github.com/giampaolo/psutil/issues/1471
+.. _1472: https://github.com/giampaolo/psutil/issues/1472
+.. _1473: https://github.com/giampaolo/psutil/issues/1473
+.. _1474: https://github.com/giampaolo/psutil/issues/1474
+.. _1475: https://github.com/giampaolo/psutil/issues/1475
+.. _1476: https://github.com/giampaolo/psutil/issues/1476
+.. _1477: https://github.com/giampaolo/psutil/issues/1477
+.. _1478: https://github.com/giampaolo/psutil/issues/1478
+.. _1479: https://github.com/giampaolo/psutil/issues/1479
+.. _1480: https://github.com/giampaolo/psutil/issues/1480
+.. _1481: https://github.com/giampaolo/psutil/issues/1481
+.. _1482: https://github.com/giampaolo/psutil/issues/1482
+.. _1483: https://github.com/giampaolo/psutil/issues/1483
+.. _1484: https://github.com/giampaolo/psutil/issues/1484
+.. _1485: https://github.com/giampaolo/psutil/issues/1485
+.. _1486: https://github.com/giampaolo/psutil/issues/1486
+.. _1487: https://github.com/giampaolo/psutil/issues/1487
+.. _1488: https://github.com/giampaolo/psutil/issues/1488
+.. _1489: https://github.com/giampaolo/psutil/issues/1489
+.. _1490: https://github.com/giampaolo/psutil/issues/1490
+.. _1491: https://github.com/giampaolo/psutil/issues/1491
+.. _1492: https://github.com/giampaolo/psutil/issues/1492
+.. _1493: https://github.com/giampaolo/psutil/issues/1493
+.. _1494: https://github.com/giampaolo/psutil/issues/1494
+.. _1495: https://github.com/giampaolo/psutil/issues/1495
+.. _1496: https://github.com/giampaolo/psutil/issues/1496
+.. _1497: https://github.com/giampaolo/psutil/issues/1497
+.. _1498: https://github.com/giampaolo/psutil/issues/1498
+.. _1499: https://github.com/giampaolo/psutil/issues/1499
+.. _1500: https://github.com/giampaolo/psutil/issues/1500
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/IDEAS
@@ -0,0 +1,165 @@
+TODO
+====
+
+A collection of ideas and notes about stuff to implement in future versions.
+"#NNN" occurrences refer to bug tracker issues at:
+https://github.com/giampaolo/psutil/issues
+
+PLATFORMS
+=========
+
+- #355: Android (with patch)
+- #82: Cygwin (PR at #998)
+- #276: GNU/Hurd
+- #693: Windows Nano
+- DragonFlyBSD
+- HP-UX
+
+FEATURES
+========
+
+- #371: sensors_temperatures() at least for OSX.
+
+- #669: Windows / net_if_addrs(): return broadcast addr.
+
+- #550: CPU info (frequency, architecture, threads per core, cores per socket,
+  sockets, ...)
+
+- #772: extended net_io_counters() metrics.
+
+- #900: wheels for OSX and Linux.
+
+- #922: extended net_io_stats() info.
+
+- #914: extended platform specific process info.
+
+- #898: wifi stats
+
+- #893: (BSD) process environ
+
+- #809: (BSD) per-process resource limits (rlimit()).
+
+- (UNIX) process root (different from cwd)
+
+- #782: (UNIX) process num of signals received.
+
+- (Linux) locked files via /proc/locks:
+  https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-proc-locks.html
+
+- #269: NIC rx/tx queue. This should probably go into net_if_stats().
+  Figure out on what platforms this is supported:
+  Linux: yes
+  Others: ?
+
+- Process.threads(): thread names; patch for OSX available at:
+  https://code.google.com/p/plcrashreporter/issues/detail?id=65
+  Sample code:
+  https://github.com/janmojzis/pstree/blob/master/proc_kvm.c
+
+- Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964)
+
+- (Windows) fall back on using WMIC for Process methods returning AccessDenied
+
+- #613: thread names.
+
+- #604: emulate os.getloadavg() on Windows
+
+- scripts/taskmgr-gui.py (using tk).
+
+- system-wide number of open file descriptors:
+  - https://jira.hyperic.com/browse/SIGAR-30
+
+- Number of system threads.
+  - Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684824(v=vs.85).aspx
+
+- Doc / wiki which compares similarities between UNIX cli tools and psutil.
+  Example:
+  ```
+  df -a  ->  psutil.disk_partitions
+  lsof  ->  psutil.Process.open_files() and psutil.Process.open_connections()
+  killall->  (actual script)
+  tty  ->  psutil.Process.terminal()
+  who  ->  psutil.users()
+  ```
+
+- psutil.proc_tree() something which obtains a {pid:ppid, ...} dict for
+  all running processes in one shot. This can be factored out from
+  Process.children() and exposed as a first class function.
+  PROS: on Windows we can take advantage of _psutil_windows.ppid_map()
+  which is faster than iterating over all pids and calling ppid().
+  CONS: scripts/pstree.py shows this can be easily done in the user code
+  so maybe it's not worth the addition.
+
+- advanced cmdline interface exposing the whole API and providing different
+  kind of outputs (e.g. pprinted, colorized, json).
+
+- [Linux]: process cgroups (http://en.wikipedia.org/wiki/Cgroups). They look
+  similar to prlimit() in terms of functionality but uglier (they should allow
+  limiting per-process network IO resources though, which is great). Needs
+  further reading.
+
+- Python 3.3. exposed different sched.h functions:
+  http://docs.python.org/dev/whatsnew/3.3.html#os
+  http://bugs.python.org/issue12655
+  http://docs.python.org/dev/library/os.html#interface-to-the-scheduler
+  It might be worth to take a look and figure out whether we can include some
+  of those in psutil.
+  Also, we can probably reimplement wait_pid() on POSIX which is currently
+  implemented as a busy-loop.
+
+- os.times() provides 'elapsed' times (cpu_times() might).
+
+- ...also guest_time and cguest_time on Linux.
+
+- Enrich exception classes hierarchy on Python >= 3.3 / post PEP-3151 so that:
+   - NoSuchProcess inherits from ProcessLookupError
+   - AccessDenied inherits from PermissionError
+   - TimeoutExpired inherits from TimeoutError (debatable)
+   See: http://docs.python.org/3/library/exceptions.html#os-exceptions
+
+- Process.threads() might grow an extra "id" parameter so that it can be
+   used as such:
+  ```
+  >>> p = psutil.Process(os.getpid())
+  >>> p.threads(id=psutil.current_thread_id())
+  thread(id=2539, user_time=0.03, system_time=0.02)
+  >>>
+  ```
+  Note: this leads to questions such as "should we have a custom NoSuchThread
+  exception? Also see issue #418.
+  Note #2: this would work with os.getpid() only.
+  psutil.current_thread_id() might be desirable as per issue #418 though.
+
+- should psutil.TimeoutExpired exception have a 'msg' kwarg similar to
+  NoSuchProcess and AccessDenied? Not that we need it, but currently we
+  cannot raise a TimeoutExpired exception with a specific error string.
+
+- process_iter() might grow an "attrs" parameter similar to Process.as_dict()
+  invoke the necessary methods and include the results into a "cache"
+  attribute attached to the returned Process instances so that one can avoid
+  catching NSP and AccessDenied:
+  for p in process_iter(attrs=['cpu_percent']):
+  print(p.cache['cpu_percent'])
+  This also leads questions as whether we should introduce a sorting order.
+
+- round Process.memory_percent() result?
+
+- #550: number of threads per core.
+
+BUGFIXES
+========
+
+- #600: windows / open_files(): support network file handles.
+
+REJECTED
+========
+
+- #550: threads per core
+
+RESOURCES
+=========
+
+- sigar: https://github.com/hyperic/sigar (Java)
+- zabbix: https://zabbix.org/wiki/Get_Zabbix
+- libstatgrab: http://www.i-scream.org/libstatgrab/
+- top: http://www.unixtop.org/
--- a/third_party/python/psutil/INSTALL.rst
+++ b/third_party/python/psutil/INSTALL.rst
@@ -1,116 +1,160 @@
-============================
-Installing using pip on UNIX
-============================
+Install pip
+===========
+
+pip is the easiest way to install psutil.
+It is shipped by default with Python 2.7.9+ and 3.4+. For other Python versions
+you can install it manually.
+On Linux or via wget:
+
+.. code-block:: bash
+
+    wget https://bootstrap.pypa.io/get-pip.py -O - | python
 
-The easiest way to install psutil on UNIX is by using pip (but first you might
-need to install python header files; see later).
-First install pip::
+On OSX or via curl:
+
+.. code-block:: bash
+
+    python < <(curl -s https://bootstrap.pypa.io/get-pip.py)
+
+On Windows, `download pip <https://pip.pypa.io/en/latest/installing/>`__, open
+cmd.exe and install it:
+
+.. code-block:: bat
+
+    C:\Python27\python.exe get-pip.py
 
-    $ wget https://bootstrap.pypa.io/get-pip.py
-    $ python get-pip.py
+Permission issues (UNIX)
+========================
+
+The commands below assume you're running as root.
+If you're not or you bump into permission errors you can either:
+
+* prepend ``sudo``, e.g.:
+
+.. code-block:: bash
+
+    sudo pip install psutil
+
+* install psutil for your user only (not at system level):
 
-...then run::
+.. code-block:: bash
+
+    pip install --user psutil
+
+Linux
+=====
 
-    $ pip install psutil
+Ubuntu / Debian:
+
+.. code-block:: bash
 
-You may need to install gcc and python header files first (see later).
+    sudo apt-get install gcc python-dev python-pip
+    pip install psutil
+
+RedHat / CentOS:
 
 
-=====================
-Installing on Windows
-=====================
+.. code-block:: bash
+
+    sudo yum install gcc python-devel python-pip
+    pip install psutil
+
+If you're on Python 3 use ``python3-dev`` and ``python3-pip`` instead.
 
-Just get the right installer for your Python version and architecture from:
-https://pypi.python.org/pypi/psutil/#downloads
-Since wheels installers are also available you may also use pip.
+OSX
+===
 
+Install `Xcode <https://developer.apple.com/downloads/?name=Xcode>`__
+first, then:
 
-========================================
-Compiling on Windows using Visual Studio
-========================================
+.. code-block:: bash
+
+    pip install psutil
 
-In order to compile psutil on Windows you'll need Visual Studio (Mingw32 is
-no longer supported). You must have the same version of Visual Studio used to compile
-your installation of Python, that is::
+Windows
+=======
+
+The easiest way to install psutil on Windows is to just use the pre-compiled
+exe/wheel installers hosted on
+`PYPI <https://pypi.python.org/pypi/psutil/#downloads>`__ via pip:
+
+.. code-block:: bat
 
-* Python 2.6:  VS 2008
-* Python 2.7:  VS 2008
-* Python 3.3, 3.4: VS 2010 (you can download it from `MS website <http://www.visualstudio.com/downloads/download-visual-studio-vs#d-2010-express>`_)
-* Python 3.5: `VS 2015 UP <http://www.visualstudio.com/en-au/news/vs2015-preview-vs>`_
+    C:\Python27\python.exe -m pip install psutil
 
-...then run::
-
-    setup.py build
+If you want to compile psutil from sources you'll need **Visual Studio**
+(Mingw32 is no longer supported):
 
-...or::
-
-    make.bat build
+* Python 2.6, 2.7: `VS-2008 <http://www.microsoft.com/en-us/download/details.aspx?id=44266>`__
+* Python 3.3, 3.4: `VS-2010 <http://www.visualstudio.com/downloads/download-visual-studio-vs#d-2010-express>`__
+* Python 3.5+: `VS-2015 <http://www.visualstudio.com/en-au/news/vs2015-preview-vs>`__
 
 Compiling 64 bit versions of Python 2.6 and 2.7 with VS 2008 requires
-Windows SDK and .NET Framework 3.5 SP1 to be installed first.
-Once you have those run vcvars64.bat, then compile:
-http://stackoverflow.com/questions/11072521/
+`Windows SDK and .NET Framework 3.5 SP1 <https://www.microsoft.com/en-us/download/details.aspx?id=3138>`__.
+Once installed run vcvars64.bat, then you can finally compile (see
+`here <http://stackoverflow.com/questions/11072521/>`__).
+To compile / install psutil from sources on Windows run:
+
+.. code-block:: bat
+
+    make.bat build
+    make.bat install
+
+FreeBSD
+=======
+
+.. code-block:: bash
 
-===================
-Installing on Linux
-===================
+    pkg install python gcc
+    python -m pip install psutil
+
+OpenBSD
+=======
+
+.. code-block:: bash
+
+    export PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/`uname -r`/packages/`arch -s`/"
+    pkg_add -v python gcc
+    python -m pip install psutil
+
+NetBSD
+======
 
-gcc is required and so the python headers. They can easily be installed by
-using the distro package manager. For example, on Debian and Ubuntu::
+.. code-block:: bash
+
+    export PKG_PATH="ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All"
+    pkg_add -v pkgin
+    pkgin install python gcc
+    python -m pip install psutil
 
-    $ sudo apt-get install gcc python-dev
+Solaris
+=======
+
+If ``cc`` compiler is not installed create a symlink to ``gcc``:
+
+.. code-block:: bash
+
+    sudo ln -s /usr/bin/gcc /usr/local/bin/cc
 
-...on Redhat and CentOS::
+Install:
 
-    $ sudo yum install gcc python-devel
+.. code-block:: bash
+
+    pkg install gcc
+    python -m pip install psutil
 
-Once done, you can build/install psutil with::
+Install from sources
+====================
 
-    $ python setup.py install
+.. code-block:: bash
+
+    git clone https://github.com/giampaolo/psutil.git
+    cd psutil
+    python setup.py install
 
 
-==================
-Installing on OS X
-==================
-
-OS X installation from source will require gcc which you can obtain as part of
-the 'XcodeTools' installer from Apple. Then you can run the standard distutils
-commands.
-To build only::
-
-    $ python setup.py build
-
-To install and build::
-
-    $ python setup.py install
-
-
-=====================
-Installing on FreeBSD
-=====================
+Dev Guide
+=========
 
-The same compiler used to install Python must be present on the system in order
-to build modules using distutils. Assuming it is installed, you can build using
-the standard distutils commands.
-
-Build only::
-
-    $ python setup.py build
-
-Install and build::
-
-    $ python setup.py install
-
-
-========
-Makefile
-========
-
-A makefile is available for both UNIX and Windows (make.bat).  It provides
-some automations for the tasks described above and might be preferred over
-using setup.py. With it you can::
-
-    $ make install    # just install (in --user mode)
-    $ make uninstall  # uninstall (needs pip)
-    $ make test       # run tests
-    $ make clean      # remove installation files
+If you plan on hacking on psutil you may want to take a look at the
+`dev guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`__.
--- a/third_party/python/psutil/MANIFEST.in
+++ b/third_party/python/psutil/MANIFEST.in
@@ -1,22 +1,131 @@
 include .coveragerc
 include .git-pre-commit
-include .git-pre-commit
 include .gitignore
-include .travis.yml
 include CREDITS
+include DEVGUIDE.rst
 include HISTORY.rst
+include IDEAS
 include INSTALL.rst
 include LICENSE
-include make.bat
+include MANIFEST.in
 include Makefile
-include MANIFEST.in
 include README.rst
+include docs/Makefile
+include docs/README
+include docs/_static/copybutton.js
+include docs/_static/css/custom.css
+include docs/_static/favicon.ico
+include docs/_static/sidebar.js
+include docs/conf.py
+include docs/index.rst
+include docs/make.bat
+include make.bat
+include psutil/DEVNOTES
+include psutil/__init__.py
+include psutil/_common.py
+include psutil/_compat.py
+include psutil/_exceptions.py
+include psutil/_psaix.py
+include psutil/_psbsd.py
+include psutil/_pslinux.py
+include psutil/_psosx.py
+include psutil/_psposix.py
+include psutil/_pssunos.py
+include psutil/_psutil_aix.c
+include psutil/_psutil_bsd.c
+include psutil/_psutil_common.c
+include psutil/_psutil_common.h
+include psutil/_psutil_linux.c
+include psutil/_psutil_osx.c
+include psutil/_psutil_posix.c
+include psutil/_psutil_posix.h
+include psutil/_psutil_sunos.c
+include psutil/_psutil_windows.c
+include psutil/_pswindows.py
+include psutil/arch/aix/common.c
+include psutil/arch/aix/common.h
+include psutil/arch/aix/ifaddrs.c
+include psutil/arch/aix/ifaddrs.h
+include psutil/arch/aix/net_connections.c
+include psutil/arch/aix/net_connections.h
+include psutil/arch/aix/net_kernel_structs.h
+include psutil/arch/freebsd/proc_socks.c
+include psutil/arch/freebsd/proc_socks.h
+include psutil/arch/freebsd/specific.c
+include psutil/arch/freebsd/specific.h
+include psutil/arch/freebsd/sys_socks.c
+include psutil/arch/freebsd/sys_socks.h
+include psutil/arch/netbsd/socks.c
+include psutil/arch/netbsd/socks.h
+include psutil/arch/netbsd/specific.c
+include psutil/arch/netbsd/specific.h
+include psutil/arch/openbsd/specific.c
+include psutil/arch/openbsd/specific.h
+include psutil/arch/osx/process_info.c
+include psutil/arch/osx/process_info.h
+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/glpi.h
+include psutil/arch/windows/inet_ntop.c
+include psutil/arch/windows/inet_ntop.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/security.c
+include psutil/arch/windows/security.h
+include psutil/arch/windows/services.c
+include psutil/arch/windows/services.h
+include psutil/tests/README.rst
+include psutil/tests/__init__.py
+include psutil/tests/__main__.py
+include psutil/tests/test_aix.py
+include psutil/tests/test_bsd.py
+include psutil/tests/test_connections.py
+include psutil/tests/test_contracts.py
+include psutil/tests/test_linux.py
+include psutil/tests/test_memory_leaks.py
+include psutil/tests/test_misc.py
+include psutil/tests/test_osx.py
+include psutil/tests/test_posix.py
+include psutil/tests/test_process.py
+include psutil/tests/test_sunos.py
+include psutil/tests/test_system.py
+include psutil/tests/test_unicode.py
+include psutil/tests/test_windows.py
+include scripts/battery.py
+include scripts/cpu_distribution.py
+include scripts/disk_usage.py
+include scripts/fans.py
+include scripts/free.py
+include scripts/ifconfig.py
+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/generate_manifest.py
+include scripts/internal/print_announce.py
+include scripts/internal/print_timeline.py
+include scripts/internal/winmake.py
+include scripts/iotop.py
+include scripts/killall.py
+include scripts/meminfo.py
+include scripts/netstat.py
+include scripts/nettop.py
+include scripts/pidof.py
+include scripts/pmap.py
+include scripts/procinfo.py
+include scripts/procsmem.py
+include scripts/ps.py
+include scripts/pstree.py
+include scripts/sensors.py
+include scripts/temperatures.py
+include scripts/top.py
+include scripts/who.py
+include scripts/winservices.py
 include setup.py
-include TODO
 include tox.ini
-recursive-exclude docs/_build *
-recursive-include .appveyor/*
-recursive-include docs *
-recursive-include examples *.py
-recursive-include psutil *.py *.c *.h
-recursive-include test *.py README*
--- a/third_party/python/psutil/Makefile
+++ b/third_party/python/psutil/Makefile
@@ -1,122 +1,269 @@
 # Shortcuts for various tasks (UNIX only).
-# To use a specific Python version run:
-# $ make install PYTHON=python3.3
+# To use a specific Python version run: "make install PYTHON=python3.3"
+# You can set the variables below from the command line.
 
-# You can set these variables from the command line.
-PYTHON    = python
-TSCRIPT   = test/test_psutil.py
+PYTHON = python
+TSCRIPT = psutil/tests/__main__.py
+ARGS =
+# List of nice-to-have dev libs.
+DEPS = \
+	argparse \
+	check-manifest \
+	coverage \
+	flake8 \
+	futures \
+	ipaddress \
+	mock==1.0.1 \
+	pep8 \
+	perf \
+	pyflakes \
+	requests \
+	setuptools \
+	sphinx \
+	twine \
+	unittest2 \
+	wheel
+
+# 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')"`
+TEST_PREFIX = PYTHONWARNINGS=all PSUTIL_TESTING=1 PSUTIL_DEBUG=1
 
 all: test
 
-clean:
-	rm -f `find . -type f -name \*.py[co]`
-	rm -f `find . -type f -name \*.so`
-	rm -f `find . -type f -name .\*~`
-	rm -f `find . -type f -name \*.orig`
-	rm -f `find . -type f -name \*.bak`
-	rm -f `find . -type f -name \*.rej`
-	rm -rf `find . -type d -name __pycache__`
-	rm -rf *.core
-	rm -rf *.egg-info
-	rm -rf *\$testfile*
-	rm -rf .coverage
-	rm -rf .tox
-	rm -rf build
-	rm -rf dist
-	rm -rf docs/_build
-	rm -rf htmlcov
+# ===================================================================
+# Install
+# ===================================================================
 
-build: clean
-	$(PYTHON) setup.py build
-	@# copies *.so files in ./psutil directory in order to allow
+clean:  ## Remove all build files.
+	rm -rf `find . -type d -name __pycache__ \
+		-o -type f -name \*.bak \
+		-o -type f -name \*.orig \
+		-o -type f -name \*.pyc \
+		-o -type f -name \*.pyd \
+		-o -type f -name \*.pyo \
+		-o -type f -name \*.rej \
+		-o -type f -name \*.so \
+		-o -type f -name \*.~ \
+		-o -type f -name \*\$testfn`
+	rm -rf \
+		*.core \
+		*.egg-info \
+		*\$testfn* \
+		.coverage \
+		.tox \
+		build/ \
+		dist/ \
+		docs/_build/ \
+		htmlcov/ \
+		tmp/
+
+_:
+
+build: _  ## Compile without installing.
+	# make sure setuptools is installed (needed for 'develop' / edit mode)
+	$(PYTHON) -c "import setuptools"
+	PYTHONWARNINGS=all $(PYTHON) setup.py build
+	@# copies compiled *.so files in ./psutil directory in order to allow
 	@# "import psutil" when using the interactive interpreter from within
 	@# this directory.
-	$(PYTHON) setup.py build_ext -i
+	PYTHONWARNINGS=all $(PYTHON) setup.py build_ext -i
+	rm -rf tmp
+	$(PYTHON) -c "import psutil"  # make sure it actually worked
 
-# useful deps which are nice to have while developing / testing
-setup-dev-env:
-	python -c "import urllib2; \
-			   r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \
-			   open('/tmp/get-pip.py', 'w').write(r.read());"
-	$(PYTHON) /tmp/get-pip.py --user
-	rm /tmp/get-pip.py
-	$(PYTHON) -m pip install --user --upgrade pip
-	$(PYTHON) -m pip install --user --upgrade \
-		coverage  \
-		flake8 \
-		ipaddress \
-		ipdb \
-		mock==1.0.1 \
-		nose \
-		pep8 \
-		pyflakes \
-		sphinx \
-		sphinx-pypi-upload \
-		unittest2 \
+install:  ## Install this package as current user in "edit" mode.
+	${MAKE} build
+	PYTHONWARNINGS=all $(PYTHON) setup.py develop $(INSTALL_OPTS)
+	rm -rf tmp
 
-install: build
-	$(PYTHON) setup.py install --user
-
-uninstall:
+uninstall:  ## Uninstall this package via pip.
 	cd ..; $(PYTHON) -m pip uninstall -y -v psutil
 
-test: install
-	$(PYTHON) $(TSCRIPT)
-
-test-process: install
-	$(PYTHON) -m unittest -v test.test_psutil.TestProcess
+install-pip:  ## Install pip (no-op if already installed).
+	$(PYTHON) -c \
+		"import sys, ssl, os, pkgutil, tempfile, atexit; \
+		sys.exit(0) if pkgutil.find_loader('pip') else None; \
+		pyexc = 'from urllib.request import urlopen' if sys.version_info[0] == 3 else 'from urllib2 import urlopen'; \
+		exec(pyexc); \
+		ctx = ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None; \
+		kw = dict(context=ctx) if ctx else {}; \
+		req = urlopen('https://bootstrap.pypa.io/get-pip.py', **kw); \
+		data = req.read(); \
+		f = tempfile.NamedTemporaryFile(suffix='.py'); \
+		atexit.register(f.close); \
+		f.write(data); \
+		f.flush(); \
+		print('downloaded %s' % f.name); \
+		code = os.system('%s %s --user' % (sys.executable, f.name)); \
+		f.close(); \
+		sys.exit(code);"
 
-test-system: install
-	$(PYTHON) -m unittest -v test.test_psutil.TestSystemAPIs
+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)
 
-test-memleaks: install
-	$(PYTHON) test/test_memory_leaks.py
+# ===================================================================
+# Tests
+# ===================================================================
+
+test:  ## Run all tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) $(TSCRIPT)
+
+test-process:  ## Run process-related API tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) -m unittest -v psutil.tests.test_process
 
-# Run a specific test by name; e.g. "make test-by-name disk_" will run
-# all test methods containing "disk_" in their name.
-# Requires "pip install nose".
-test-by-name: install
-	@$(PYTHON) -m nose test/test_psutil.py test/_* --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
+test-system:  ## Run system-related API tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) -m unittest -v psutil.tests.test_system
+
+test-misc:  ## Run miscellaneous tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_misc.py
+
+test-unicode:  ## Test APIs dealing with strings.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_unicode.py
+
+test-contracts:  ## APIs sanity tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_contracts.py
 
-# Same as above but for test_memory_leaks.py script.
-test-memleaks-by-name: install
-	@$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
+test-connections:  ## Test net_connections() and Process.connections().
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_connections.py
+
+test-posix:  ## POSIX specific tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_posix.py
 
-coverage: install
+test-platform:  ## Run specific platform tests only.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py
+
+test-memleaks:  ## Memory leak tests.
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) psutil/tests/test_memory_leaks.py
+
+test-by-name:  ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSystemAPIs
+	${MAKE} install
+	@$(TEST_PREFIX) $(PYTHON) -m unittest -v $(ARGS)
+
+test-coverage:  ## Run test coverage.
+	${MAKE} install
 	# Note: coverage options are controlled by .coveragerc file
 	rm -rf .coverage htmlcov
-	$(PYTHON) -m coverage run $(TSCRIPT)
+	$(TEST_PREFIX) $(PYTHON) -m coverage run $(TSCRIPT)
 	$(PYTHON) -m coverage report
 	@echo "writing results to htmlcov/index.html"
 	$(PYTHON) -m coverage html
 	$(PYTHON) -m webbrowser -t htmlcov/index.html
 
-pep8:
+# ===================================================================
+# Linters
+# ===================================================================
+
+pep8:  ## PEP8 linter.
 	@git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8
 
-pyflakes:
+pyflakes:  ## Pyflakes linter.
 	@export PYFLAKES_NODOCTEST=1 && \
 		git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes
 
-flake8:
+flake8:  ## flake8 linter.
 	@git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8
 
-# Upload source tarball on https://pypi.python.org/pypi/psutil.
-upload-src: clean
+# ===================================================================
+# GIT
+# ===================================================================
+
+git-tag-release:  ## Git-tag a new release.
+	git tag -a release-`python -c "import setup; print(setup.get_version())"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD`
+	git push --follow-tags
+
+install-git-hooks:  ## Install GIT pre-commit hook.
+	ln -sf ../../.git-pre-commit .git/hooks/pre-commit
+	chmod +x .git/hooks/pre-commit
+
+# ===================================================================
+# Distribution
+# ===================================================================
+
+# --- create
+
+sdist:  ## Create tar.gz source distribution.
+	${MAKE} generate-manifest
+	$(PYTHON) setup.py sdist
+
+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
+
+# --- upload
+
+upload-src:  ## Upload source tarball on https://pypi.python.org/pypi/psutil.
+	${MAKE} sdist
 	$(PYTHON) setup.py sdist upload
 
-# Build and upload doc on https://pythonhosted.org/psutil/.
-# Requires "pip install sphinx-pypi-upload".
-upload-doc:
-	cd docs; make html
-	$(PYTHON) setup.py upload_sphinx --upload-dir=docs/_build/html
+upload-win-wheels:  ## Upload wheels in dist/* directory on PYPI.
+	$(PYTHON) -m twine upload dist/*.whl
+
+# --- others
+
+pre-release:  ## Check if we're ready to produce a new release.
+	rm -rf dist
+	${MAKE} install
+	${MAKE} generate-manifest
+	git diff MANIFEST.in > /dev/null  # ...otherwise 'git diff-index HEAD' will complain
+	${MAKE} win-download-wheels
+	${MAKE} sdist
+	$(PYTHON) -c \
+		"from psutil import __version__ as ver; \
+		doc = open('docs/index.rst').read(); \
+		history = open('HISTORY.rst').read(); \
+		assert ver in doc, '%r not in docs/index.rst' % ver; \
+		assert ver in history, '%r not in HISTORY.rst' % ver; \
+		assert 'XXXX' not in history, 'XXXX in HISTORY.rst';"
+	$(PYTHON) -c "import subprocess, sys; out = subprocess.check_output('git diff --quiet && git diff --cached --quiet', shell=True).strip(); sys.exit('there are uncommitted changes:\n%s' % out) if out else 0 ;"
+
+release:  ## Create a release (down/uploads tar.gz, wheels, git tag release).
+	${MAKE} pre-release
+	$(PYTHON) -m twine upload dist/*  # upload tar.gz and Windows wheels on PYPI
+	${MAKE} git-tag-release
+
+print-announce:  ## Print announce of new release.
+	@$(TEST_PREFIX) $(PYTHON) scripts/internal/print_announce.py
 
-# git-tag a new release
-git-tag-release:
-	git tag -a release-`python -c "import setup; print(setup.get_version())"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD`
-	echo "done; now run 'git push --follow-tags' to push the new tag on the remote repo"
+print-timeline:  ## Print releases' timeline.
+	@$(TEST_PREFIX) $(PYTHON) scripts/internal/print_timeline.py
+
+check-manifest:  ## Inspect MANIFEST.in file.
+	$(PYTHON) -m check_manifest -v $(ARGS)
+
+generate-manifest:  ## Generates MANIFEST.in file.
+	$(PYTHON) scripts/internal/generate_manifest.py > MANIFEST.in
+
+# ===================================================================
+# Misc
+# ===================================================================
 
-# install GIT pre-commit hook
-install-git-hooks:
-	ln -sf ../../.git-pre-commit .git/hooks/pre-commit
-	chmod +x .git/hooks/pre-commit
+grep-todos:  ## Look for TODOs in the source files.
+	git grep -EIn "TODO|FIXME|XXX"
+
+bench-oneshot:  ## Benchmarks for oneshot() ctx manager (see #799).
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) scripts/internal/bench_oneshot.py
+
+bench-oneshot-2:  ## Same as above but using perf module (supposed to be more precise)
+	${MAKE} install
+	$(TEST_PREFIX) $(PYTHON) scripts/internal/bench_oneshot_2.py
+
+check-broken-links:  ## Look for broken links in source files.
+		git ls-files | xargs $(PYTHON) -Wa scripts/internal/check_broken_links.py
+
+help: ## Display callable targets.
+	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
--- a/third_party/python/psutil/PKG-INFO
+++ b/third_party/python/psutil/PKG-INFO
@@ -1,98 +1,126 @@
 Metadata-Version: 1.1
 Name: psutil
-Version: 3.1.1
-Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python.
+Version: 5.4.3
+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 <at> gmail <dot> com
+Author-email: g.rodola@gmail.com
 License: BSD
-Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg
-            :target: https://pypi.python.org/pypi/psutil#downloads
-            :alt: Downloads this month
-        
-        .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
+Description-Content-Type: UNKNOWN
+Description: .. 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://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
+        .. 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/giampaolo/psutil/badge.svg?branch=master&service=github
+        .. 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://img.shields.io/pypi/v/psutil.svg
+        .. 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/scrutinizer/g/giampaolo/psutil.svg
-            :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
-            :alt: Code quality (scrutinizer-ci.com)
-        
         .. image:: https://img.shields.io/pypi/l/psutil.svg
             :target: https://pypi.python.org/pypi/psutil/
             :alt: License
         
         ===========
         Quick links
         ===========
         
         - `Home page <https://github.com/giampaolo/psutil>`_
-        - `Documentation <http://pythonhosted.org/psutil/>`_
-        - `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+        - `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+        - `Documentation <http://psutil.readthedocs.io>`_
         - `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`_
         - `Forum <http://groups.google.com/group/psutil/topics>`_
         - `Blog <http://grodola.blogspot.com/search/label/psutil>`_
         - `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
         - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
         
         =======
         Summary
         =======
         
-        psutil (python system and process utilities) is a cross-platform library for
+        psutil (process and system utilities) is a cross-platform library for
         retrieving information on **running processes** and **system utilization**
-        (CPU, memory, disks, network) 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 command line
-        tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
-        ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
-        **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and
-        **64-bit** architectures, with Python versions from **2.6 to 3.5** (users of
-        Python 2.4 and 2.5 may use `2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+        (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 <http://pypy.org/>`__ is also known to work.
         
         ====================
         Example applications
         ====================
         
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/top.png
-            :alt: top
+        +------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
+        | .. 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         |
+        +------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
         
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/nettop-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/nettop.png
-            :alt: nettop
+        Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
+        and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
+        
+        =====================
+        Projects using psutil
+        =====================
         
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
-            :alt: iotop
+        At the time of writing psutil has roughly
+        `2.9 milion downloads <https://github.com/giampaolo/psutil/issues/1053#issuecomment-340166262>`__
+        per month and there are over
+        `6000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
+        on github which depend from psutil.
+        Here's some I find particularly interesting:
         
-        See also:
+        - 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/
         
-         * https://github.com/nicolargo/glances
-         * https://github.com/google/grr
-         * https://github.com/Jahaja/psdash
+        ========
+        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
         ===
         
@@ -111,170 +139,212 @@ Description: .. image:: https://img.shie
             >>>
             >>> 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=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
+            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)
+            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()
-            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+            [sconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
-                    snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
-                    snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
-             'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
-                       snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
-                       snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+            {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
+                    snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
+                    snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
+             'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
+                       snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
+                       snic(family=<AddressFamily.AF_LINK: 17>, 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=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
              'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, 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()
-            [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
-             user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+            [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]
+            [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()
+            <psutil.Process(pid=7054, name='bash') at 140008329539408>
+            >>> p.children()
+            [<psutil.Process(pid=8031, name='python') at 14020832451977>,
+             <psutil.Process(pid=8044, name='python') at 19229444921932>]
+            >>>
             >>> 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)
+            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])  # set
-            >>>
-            >>> p.memory_percent()
-            0.63423
+            >>> p.cpu_affinity([0, 1])  # set
+            >>> p.cpu_num()
+            1
             >>>
             >>> p.memory_info()
-            pmem(rss=7471104, vms=68513792)
-            >>> p.memory_info_ex()
-            extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
+            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/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
-             pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
-             pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
-             pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
-             pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
+            [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)
+            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/somefile', fd=3)]
+            [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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-             pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-             pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'),
+             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING'),
+             pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED'),
+             pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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),
@@ -291,16 +361,25 @@ Description: .. image:: https://img.shie
             >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)
             >>> p.ionice()
             pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, 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()
@@ -314,121 +393,129 @@ Description: .. image:: https://img.shie
             root       31763  0.0  0.0       0       0            00:05   00:00  kworker/0:1
             >>>
         
         Further process APIs
         ====================
         
         .. code-block:: python
         
-            >>> for p in psutil.process_iter():
-            ...     print(p)
+            >>> import psutil
+            >>> for proc in psutil.process_iter(attrs=['pid', 'name']):
+            ...     print(proc.info)
             ...
-            psutil.Process(pid=1, name='init')
-            psutil.Process(pid=2, name='kthreadd')
-            psutil.Process(pid=3, name='ksoftirqd/0')
+            {'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, 3, callback=on_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())
+            [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,
+             <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,
+             <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,
+             <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,
+             ...]
+            >>> 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 <http://psutil.readthedocs.io/#recipes>`__.
+        
         ======
-        Donate
+        Author
         ======
         
+        psutil was created and is maintained by
+        `Giampaolo Rodola' <http://grodola.blogspot.com/p/about.html>`__.
         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 Rodola' <http://grodola.blogspot.com/p/about.html>`_) some money.
-        I only ask for a small donation, but of course I appreciate any amount.
+        If you feel psutil is useful to you or your business and want to support its
+        future development please consider donating me
+        (`Giampaolo <http://grodola.blogspot.com/p/about.html>`__) 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 <http://www.linkedin.com/in/grodola>`_.
-        
-        ============
-        Mailing list
-        ============
-        
-        http://groups.google.com/group/psutil/
-        
-        ========
-        Timeline
-        ========
+        Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin <https://www.linkedin.com/in/grodola>`_.
         
-        - 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
-        - 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
-        - 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
-        - 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
-        - 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
-        - 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
-        - 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
-        - 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
-        - 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
-        - 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
-        - 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
-        - 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
-        - 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
-        - 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
-        - 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.2.tar.gz>`_
-        - 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.1.tar.gz>`_
-        - 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.0.tar.gz>`_
-        - 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.1.tar.gz>`_
-        - 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.0.tar.gz>`_
-        - 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.1.tar.gz>`_
-        - 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.0.tar.gz>`_
-        - 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.1.tar.gz>`_
-        - 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.0.tar.gz>`_
-        - 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.1.tar.gz>`_
-        - 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.0.tar.gz>`_
-        - 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.1.tar.gz>`_
-        - 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.0.tar.gz>`_
-        - 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
-        - 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
-        - 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
-        - 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
-        - 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
-        - 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
-        - 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
-        
-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
+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
 Platform: Platform Independent
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Console
 Classifier: Environment :: Win32 (MS Windows)
 Classifier: Intended Audience :: Developers
 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
 Classifier: Operating System :: OS Independent
 Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: BSD :: NetBSD
+Classifier: Operating System :: POSIX :: BSD :: OpenBSD
+Classifier: Operating System :: POSIX :: BSD
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Operating System :: POSIX :: SunOS/Solaris
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: C
 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.0
-Classifier: Programming Language :: Python :: 3.1
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.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
 Classifier: Topic :: System :: Monitoring
 Classifier: Topic :: System :: Networking :: Monitoring
 Classifier: Topic :: System :: Networking
+Classifier: Topic :: System :: Operating System
 Classifier: Topic :: System :: Systems Administration
 Classifier: Topic :: Utilities
--- a/third_party/python/psutil/README.rst
+++ b/third_party/python/psutil/README.rst
@@ -1,90 +1,117 @@
-.. image:: https://img.shields.io/pypi/dm/psutil.svg
-    :target: https://pypi.python.org/pypi/psutil#downloads
-    :alt: Downloads this month
-
-.. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
+.. 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://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
+.. 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/giampaolo/psutil/badge.svg?branch=master&service=github
+.. 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://img.shields.io/pypi/v/psutil.svg
+.. 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/scrutinizer/g/giampaolo/psutil.svg
-    :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
-    :alt: Code quality (scrutinizer-ci.com)
-
 .. image:: https://img.shields.io/pypi/l/psutil.svg
     :target: https://pypi.python.org/pypi/psutil/
     :alt: License
 
 ===========
 Quick links
 ===========
 
 - `Home page <https://github.com/giampaolo/psutil>`_
-- `Documentation <http://pythonhosted.org/psutil/>`_
-- `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+- `Documentation <http://psutil.readthedocs.io>`_
 - `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`_
 - `Forum <http://groups.google.com/group/psutil/topics>`_
 - `Blog <http://grodola.blogspot.com/search/label/psutil>`_
 - `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
 - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
 
 =======
 Summary
 =======
 
-psutil (python system and process utilities) is a cross-platform library for
+psutil (process and system utilities) is a cross-platform library for
 retrieving information on **running processes** and **system utilization**
-(CPU, memory, disks, network) 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 command line
-tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
-ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
-**Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and
-**64-bit** architectures, with Python versions from **2.6 to 3.5** (users of
-Python 2.4 and 2.5 may use `2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+(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 <http://pypy.org/>`__ is also known to work.
 
 ====================
 Example applications
 ====================
 
-.. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
-    :target: http://psutil.googlecode.com/svn/wiki/images/top.png
-    :alt: top
++------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
+| .. 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         |
++------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
 
-.. image:: http://psutil.googlecode.com/svn/wiki/images/nettop-thumb.png
-    :target: http://psutil.googlecode.com/svn/wiki/images/nettop.png
-    :alt: nettop
+Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
+and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
+
+=====================
+Projects using psutil
+=====================
 
-.. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
-    :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
-    :alt: iotop
+At the time of writing psutil has roughly
+`2.9 milion downloads <https://github.com/giampaolo/psutil/issues/1053#issuecomment-340166262>`__
+per month and there are over
+`6000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
+on github which depend from psutil.
+Here's some I find particularly interesting:
 
-See also:
+- 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/
 
- * https://github.com/nicolargo/glances
- * https://github.com/google/grr
- * https://github.com/Jahaja/psdash
+========
+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
 ===
 
@@ -103,170 +130,212 @@ CPU
     >>>
     >>> 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=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
+    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)
+    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()
-    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+    [sconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
-            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
-            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
-     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
-               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
-               snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+    {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
+            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
+            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
+     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
+               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
+               snic(family=<AddressFamily.AF_LINK: 17>, 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=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
      'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, 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()
-    [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
-     user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+    [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]
+    [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()
+    <psutil.Process(pid=7054, name='bash') at 140008329539408>
+    >>> p.children()
+    [<psutil.Process(pid=8031, name='python') at 14020832451977>,
+     <psutil.Process(pid=8044, name='python') at 19229444921932>]
+    >>>
     >>> 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)
+    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])  # set
-    >>>
-    >>> p.memory_percent()
-    0.63423
+    >>> p.cpu_affinity([0, 1])  # set
+    >>> p.cpu_num()
+    1
     >>>
     >>> p.memory_info()
-    pmem(rss=7471104, vms=68513792)
-    >>> p.memory_info_ex()
-    extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
+    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/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
-     pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
-     pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
-     pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
-     pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
+    [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)
+    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/somefile', fd=3)]
+    [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=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-     pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-     pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'),
+     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING'),
+     pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED'),
+     pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, 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),
@@ -283,16 +352,25 @@ Process management
     >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)
     >>> p.ionice()
     pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, 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()
@@ -306,81 +384,87 @@ Process management
     root       31763  0.0  0.0       0       0            00:05   00:00  kworker/0:1
     >>>
 
 Further process APIs
 ====================
 
 .. code-block:: python
 
-    >>> for p in psutil.process_iter():
-    ...     print(p)
+    >>> import psutil
+    >>> for proc in psutil.process_iter(attrs=['pid', 'name']):
+    ...     print(proc.info)
     ...
-    psutil.Process(pid=1, name='init')
-    psutil.Process(pid=2, name='kthreadd')
-    psutil.Process(pid=3, name='ksoftirqd/0')
+    {'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, 3, callback=on_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())
+    [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,
+     <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,
+     <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,
+     <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,
+     ...]
+    >>> 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 <http://psutil.readthedocs.io/#recipes>`__.
+
 ======
-Donate
+Author
 ======
 
+psutil was created and is maintained by
+`Giampaolo Rodola' <http://grodola.blogspot.com/p/about.html>`__.
 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 Rodola' <http://grodola.blogspot.com/p/about.html>`_) some money.
-I only ask for a small donation, but of course I appreciate any amount.
+If you feel psutil is useful to you or your business and want to support its
+future development please consider donating me
+(`Giampaolo <http://grodola.blogspot.com/p/about.html>`__) 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 <http://www.linkedin.com/in/grodola>`_.
-
-============
-Mailing list
-============
-
-http://groups.google.com/group/psutil/
-
-========
-Timeline
-========
-
-- 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
-- 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
-- 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
-- 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
-- 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
-- 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
-- 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
-- 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
-- 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
-- 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
-- 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
-- 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
-- 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
-- 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
-- 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.2.tar.gz>`_
-- 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.1.tar.gz>`_
-- 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.0.tar.gz>`_
-- 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.1.tar.gz>`_
-- 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.0.tar.gz>`_
-- 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.1.tar.gz>`_
-- 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.0.tar.gz>`_
-- 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.1.tar.gz>`_
-- 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.0.tar.gz>`_
-- 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.1.tar.gz>`_
-- 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.0.tar.gz>`_
-- 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.1.tar.gz>`_
-- 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.0.tar.gz>`_
-- 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
-- 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
-- 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
-- 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
-- 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
-- 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
-- 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
+Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin <https://www.linkedin.com/in/grodola>`_.
deleted file mode 100644
--- a/third_party/python/psutil/TODO
+++ /dev/null
@@ -1,167 +0,0 @@
-TODO
-====
-
-A collection of ideas and notes about stuff to implement in future versions.
-"#NNN" occurrences refer to bug tracker issues at:
-https://github.com/giampaolo/psutil/issues
-
-
-HIGHER PRIORITY
-===============
-
- * OpenBSD support.
-
- * #371: CPU temperature (apparently OSX and Linux only; on Linux it requires
-   lm-sensors lib).
-
- * #269: expose network ifaces RX/TW queues. This should probably go into
-   net_if_stats(). Figure out on what platforms this is supported:
-   Linux: yes
-   Others: ?
-
- * Process.threads(): thread names; patch for OSX available at:
-   https://code.google.com/p/plcrashreporter/issues/detail?id=65
-
- * Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964)
-
- * (Windows) fall back on using WMIC for Process methods returning AccessDenied
-
- * #613: thread names.
-
- * #604: emulate os.getloadavg() on Windows
-
- * #269: NIC rx/tx queue.
-
-
-LOWER PRIORITY
-==============
-
- * #355: Android support.
-
- * #276: GNU/Hurd support.
-
- * #429: NetBSD support.
-
- * DragonFlyBSD support?
-
- * AIX support?
-
- * examples/taskmgr-gui.py (using tk).
-
- * system-wide number of open file descriptors:
-    * https://jira.hyperic.com/browse/SIGAR-30
-    * http://www.netadmintools.com/part295.html
-
- * Number of system threads.
-    * Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684824(v=vs.85).aspx
-
- * #357: what CPU a process is on.
-
-  * Doc / wiki which compares similarities between UNIX cli tools and psutil.
-   Example:
-   df -a  ->  psutil.disk_partitions
-   lsof   ->  psutil.Process.open_files() and psutil.Process.open_connections()
-   killall->  (actual script)
-   tty    ->  psutil.Process.terminal()
-   who    ->  psutil.users()
-
-
-DEBATABLE
-=========
-
- * psutil.proc_tree() something which obtains a {pid:ppid, ...} dict for
-   all running processes in one shot. This can be factored out from
-   Process.children() and exposed as a first class function.
-   PROS: on Windows we can take advantage of _psutil_windows.ppid_map()
-   which is faster than iterating over all pids and calling ppid().
-   CONS: examples/pstree.py shows this can be easily done in the user code
-   so maybe it's not worth the addition.
-
- * advanced cmdline interface exposing the whole API and providing different
-   kind of outputs (e.g. pprinted, colorized, json).
-
- * [Linux]: process cgroups (http://en.wikipedia.org/wiki/Cgroups). They look
-   similar to prlimit() in terms of functionality but uglier (they should allow
-   limiting per-process network IO resources though, which is great). Needs
-   further reading.
-
- * Should we expose OS constants (psutil.WINDOWS, psutil.OSX etc.)?
-
- * Python 3.3. exposed different sched.h functions:
-   http://docs.python.org/dev/whatsnew/3.3.html#os
-   http://bugs.python.org/issue12655
-   http://docs.python.org/dev/library/os.html#interface-to-the-scheduler
-   It might be worth to take a look and figure out whether we can include some
-   of those in psutil.
-   Also, we can probably reimplement wait_pid() on POSIX which is currently
-   implemented as a busy-loop.
-
- * Certain systems provide CPU times about process children. On those systems
-   Process.cpu_times() might return a (user, system, user_children,
-   system_children) ntuple.
-   * Linux: /proc/{PID}/stat
-   * Solaris: pr_cutime and pr_cstime
-   * FreeBSD: none
-   * OSX: none
-   * Windows: none
-
- * ...also, os.times() provides 'elapsed' times as well.
-
- * ...also Linux provides guest_time and cguest_time.
-
- * Enrich exception classes hierarchy on Python >= 3.3 / post PEP-3151 so that:
-   - NoSuchProcess inherits from ProcessLookupError
-   - AccessDenied inherits from PermissionError
-   - TimeoutExpired inherits from TimeoutError (debatable)
-   See: http://docs.python.org/3/library/exceptions.html#os-exceptions
-
- * Process.threads() might grow an extra "id" parameter so that it can be
-   used as such:
-
-    >>> p = psutil.Process(os.getpid())
-    >>> p.threads(id=psutil.current_thread_id())
-    thread(id=2539, user_time=0.03, system_time=0.02)
-    >>>
-
-   Note: this leads to questions such as "should we have a custom NoSuchThread
-   exception? Also see issue #418.
-
-   Note #2: this would work with os.getpid() only.
-   psutil.current_thread_id() might be desirable as per issue #418 though.
-
-  * should psutil.TimeoutExpired exception have a 'msg' kwarg similar to
-    NoSuchProcess and AccessDenied? Not that we need it, but currently we
-    cannot raise a TimeoutExpired exception with a specific error string.
-
-  * process_iter() might grow an "attrs" parameter similar to Process.as_dict()
-    invoke the necessary methods and include the results into a "cache"
-    attribute attached to the returned Process instances so that one can avoid
-    catching NSP and AccessDenied:
-        for p in process_iter(attrs=['cpu_percent']):
-            print(p.cache['cpu_percent'])
-    This also leads questions as whether we should introduce a sorting order.
-
-  * round Process.memory_percent() result?
-
-  * #550: number of threads per core.
-
-  * Have psutil.Process().cpu_affinity([]) be an alias for "all CPUs"?
-
-
-COMPATIBILITY BREAKAGE
-======================
-
-Removals (will likely happen in 2.2):
-
- * (S) psutil.Process.nice  (deprecated in 0.5.0)
- * (S) get_process_list  (deprecated in 0.5.0)
- * (S) psutil.*mem* functions  (deprecated in 0.3.0 and 0.6.0)
- * (M) psutil.network_io_counters  (deprecated in 1.0.0)
- * (M) local_address and remote_address Process.connection() namedtuple fields
-       (deprecated in 1.0.0)
-
-
-REJECTED IDEAS
-==============
-
-STUB
--- a/third_party/python/psutil/docs/Makefile
+++ b/third_party/python/psutil/docs/Makefile
@@ -1,177 +1,226 @@
 # Makefile for Sphinx documentation
 #
 
 # You can set these variables from the command line.
+PYTHON        = python
 SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
+SPHINXBUILD   = $(PYTHON) -m sphinx
 PAPER         =
 BUILDDIR      = _build
 
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
 # Internal variables.
 PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
 ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 # the i18n builder cannot share the environment and doctrees with the others
 I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
+.PHONY: help
 help:
 	@echo "Please use \`make <target>' where <target> is one of"
 	@echo "  html       to make standalone HTML files"
 	@echo "  dirhtml    to make HTML files named index.html in directories"
 	@echo "  singlehtml to make a single large HTML file"
 	@echo "  pickle     to make pickle files"
 	@echo "  json       to make JSON files"
 	@echo "  htmlhelp   to make HTML files and a HTML help project"
 	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  applehelp  to make an Apple Help Book"
 	@echo "  devhelp    to make HTML files and a Devhelp project"
 	@echo "  epub       to make an epub"
+	@echo "  epub3      to make an epub3"
 	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
 	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
 	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
 	@echo "  text       to make text files"
 	@echo "  man        to make manual pages"
 	@echo "  texinfo    to make Texinfo files"
 	@echo "  info       to make Texinfo files and run them through makeinfo"
 	@echo "  gettext    to make PO message catalogs"
 	@echo "  changes    to make an overview of all changed/added/deprecated items"
 	@echo "  xml        to make Docutils-native XML files"
 	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
 	@echo "  linkcheck  to check all external links for integrity"
 	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+	@echo "  coverage   to run coverage check of the documentation (if enabled)"
+	@echo "  dummy      to check syntax errors of document sources"
 
+.PHONY: clean
 clean:
 	rm -rf $(BUILDDIR)
 
+.PHONY: html
 html:
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 
+.PHONY: dirhtml
 dirhtml:
 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
 
+.PHONY: singlehtml
 singlehtml:
 	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
 	@echo
 	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
 
+.PHONY: pickle
 pickle:
 	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
 	@echo
 	@echo "Build finished; now you can process the pickle files."
 
+.PHONY: json
 json:
 	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
 	@echo
 	@echo "Build finished; now you can process the JSON files."
 
+.PHONY: htmlhelp
 htmlhelp:
 	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
 	@echo
 	@echo "Build finished; now you can run HTML Help Workshop with the" \
 	      ".hhp project file in $(BUILDDIR)/htmlhelp."
 
+.PHONY: qthelp
 qthelp:
 	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
 	@echo
 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
 	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
 	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/psutil.qhcp"
 	@echo "To view the help file:"
 	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/psutil.qhc"
 
+.PHONY: applehelp
+applehelp:
+	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+	@echo
+	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+	@echo "N.B. You won't be able to view it unless you put it in" \
+	      "~/Library/Documentation/Help or install it in your application" \
+	      "bundle."
+
+.PHONY: devhelp
 devhelp:
 	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
 	@echo
 	@echo "Build finished."
 	@echo "To view the help file:"
 	@echo "# mkdir -p $$HOME/.local/share/devhelp/psutil"
 	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/psutil"
 	@echo "# devhelp"
 
+.PHONY: epub
 epub:
 	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
 	@echo
 	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
 
+.PHONY: epub3
+epub3:
+	$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+	@echo
+	@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
 latex:
 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 	@echo
 	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
 	@echo "Run \`make' in that directory to run these through (pdf)latex" \
 	      "(use \`make latexpdf' here to do that automatically)."
 
+.PHONY: latexpdf
 latexpdf:
 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 	@echo "Running LaTeX files through pdflatex..."
 	$(MAKE) -C $(BUILDDIR)/latex all-pdf
 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
 
+.PHONY: latexpdfja
 latexpdfja:
 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 	@echo "Running LaTeX files through platex and dvipdfmx..."
 	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
 	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
 
+.PHONY: text
 text:
 	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
 	@echo
 	@echo "Build finished. The text files are in $(BUILDDIR)/text."
 
+.PHONY: man
 man:
 	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
 	@echo
 	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
 
+.PHONY: texinfo
 texinfo:
 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
 	@echo
 	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
 	@echo "Run \`make' in that directory to run these through makeinfo" \
 	      "(use \`make info' here to do that automatically)."
 
+.PHONY: info
 info:
 	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
 	@echo "Running Texinfo files through makeinfo..."
 	make -C $(BUILDDIR)/texinfo info
 	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
 
+.PHONY: gettext
 gettext:
 	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
 	@echo
 	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
 
+.PHONY: changes
 changes:
 	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
 	@echo
 	@echo "The overview file is in $(BUILDDIR)/changes."
 
+.PHONY: linkcheck
 linkcheck:
 	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
 	@echo
 	@echo "Link check complete; look for any errors in the above output " \
 	      "or in $(BUILDDIR)/linkcheck/output.txt."
 
+.PHONY: doctest
 doctest:
 	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 	@echo "Testing of doctests in the sources finished, look at the " \
 	      "results in $(BUILDDIR)/doctest/output.txt."
 
+.PHONY: coverage
+coverage:
+	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+	@echo "Testing of coverage in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
 xml:
 	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
 	@echo
 	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
 
+.PHONY: pseudoxml
 pseudoxml:
 	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
 	@echo
 	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+	$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+	@echo
+	@echo "Build finished. Dummy builder generates no files."
--- a/third_party/python/psutil/docs/README
+++ b/third_party/python/psutil/docs/README
@@ -1,15 +1,15 @@
 About
 =====
 
 This directory contains the reStructuredText (reST) sources to the psutil
 documentation.  You don't need to build them yourself, prebuilt versions are
-available at https://pythonhosted.org/psutil/.
+available at http://psutil.readthedocs.io.
 In case you want, you need to install sphinx first:
 
     $ pip install sphinx
 
 Then run:
 
     $ make html
 
-You'll then have an HTML version of the doc at _build/html/index.html.
\ No newline at end of file
+You'll then have an HTML version of the doc at _build/html/index.html.
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/docs/_static/css/custom.css
@@ -0,0 +1,518 @@
+.wy-nav-content {
+    max-width: 100% !important;
+    padding: 15px !important;
+}
+
+.rst-content dl:not(.docutils) {
+    margin: 0px 0px 0px 0px;
+}
+
+.data dd {
+    margin-bottom: 0px !important;
+}
+
+.data .descname {
+    border-right:10px !important;
+}
+
+.local-toc li ul li{
+    padding-left: 20px !important;
+}
+
+.function .descclassname {
+    font-weight: normal !important;
+}
+
+.class .descclassname {
+    font-weight: normal !important;
+}
+
+.admonition.warning {
+    padding-top: 2px !important;
+    padding-bottom: 2px !important;
+}
+
+.admonition.note {
+    padding-top: 2px !important;
+    padding-bottom: 2px !important;
+}
+
+.rst-content dl:not(.docutils) dt {
+    color: #555;
+}
+
+.sig-paren {
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+h1, h2, h3 {
+    background: #eee;
+    padding: 5px;
+    border-bottom: 1px solid #ccc;
+}
+
+h1 {
+    font-size: 35px;
+}
+
+.admonition.warning {
+    padding-top: 5px !important;
+    padding-bottom: 5px !important;
+}
+
+.admonition.warning p {
+    margin-bottom: 5px !important;
+}
+
+.admonition.note {
+    padding-top: 5px !important;
+    padding-bottom: 5px !important;
+}
+
+.admonition.note p {
+    margin-bottom: 5px !important;
+    backround-color: rgb(238, 255, 204) !important;
+}
+
+.codeblock div[class^='highlight'], pre.literal-block div[class^='highlight'], .rst-content .literal-block div[class^='highlight'], div[class^='highlight'] div[class^='highlight'] {
+    background-color: #eeffcc !important;
+}
+
+.highlight .hll {
+    background-color: #ffffcc
+}
+
+.highlight {
+    background: #eeffcc;
+}
+
+.highlight-default, .highlight-python {
+    border-radius: 3px !important;
+    border: 1px solid #ac9 !important;
+}
+
+.highlight .c {
+    color: #408090;
+    font-style: italic
+}
+
+.wy-side-nav-search {
+    background-color: grey !important
+}
+
+.highlight {
+  border-radius: 3px !important;
+
+}
+
+div.highlight-default {
+    margin-bottom: 10px !important;
+}
+
+pre {
+    padding: 5px !important;
+}
+
+/* ================================================================== */
+/* Warnings and info boxes like python doc */
+/* ================================================================== */
+
+div.admonition {
+    margin-top: 10px !important;
+    margin-bottom: 10px !important;
+}
+
+div.warning {
+    background-color: #ffe4e4 !important;
+    border: 1px solid #f66 !important;
+    border-radius: 3px !important;
+}
+
+div.note {
+    background-color: #eee !important;
+    border: 1px solid #ccc !important;
+    border-radius: 3px !important;
+}
+
+div.admonition p.admonition-title + p {
+    display: inline !important;
+}
+
+p.admonition-title {
+    display: inline !important;
+    background: none !important;
+    color: black !important;
+}
+
+p.admonition-title:after {
+    content: ":" !important;
+}
+
+div.body div.admonition, div.body div.impl-detail {
+}
+
+.fa-exclamation-circle:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .rst-content .admonition-title:before {
+    display: none !important;
+}
+
+.note code {
+    background: #d6d6d6 !important;
+}
+
+/* ================================================================== */
+/* Syntax highlight like Python doc.
+/* ================================================================== */
+
+/* Comment */
+.highlight .err {
+    border: 1px solid #FF0000
+}
+
+/* Error */
+.highlight .k {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Keyword */
+.highlight .o {
+    color: #666666
+}
+
+/* Operator */
+.highlight .ch {
+    color: #408090;
+    font-style: italic
+}
+
+/* Comment.Hashbang */
+.highlight .cm {
+    color: #408090;
+    font-style: italic
+}
+
+/* Comment.Multiline */
+.highlight .cp {
+    color: #007020
+}
+
+/* Comment.Preproc */
+.highlight .cpf {
+    color: #408090;
+    font-style: italic
+}
+
+/* Comment.PreprocFile */
+.highlight .c1 {
+    color: #408090;
+    font-style: italic
+}
+
+/* Comment.Single */
+.highlight .cs {
+    color: #408090;
+    background-color: #fff0f0
+}
+
+/* Comment.Special */
+.highlight .gd {
+    color: #A00000
+}
+
+/* Generic.Deleted */
+.highlight .ge {
+    font-style: italic
+}
+
+/* Generic.Emph */
+.highlight .gr {
+    color: #FF0000
+}
+
+/* Generic.Error */
+.highlight .gh {
+    color: #000080;
+    font-weight: bold
+}
+
+/* Generic.Heading */
+.highlight .gi {
+    color: #00A000
+}
+
+/* Generic.Inserted */
+.highlight .go {
+    color: #333333
+}
+
+/* Generic.Output */
+.highlight .gp {
+    color: #c65d09;
+    font-weight: bold
+}
+
+/* Generic.Prompt */
+.highlight .gs {
+    font-weight: bold
+}
+
+/* Generic.Strong */
+.highlight .gu {
+    color: #800080;
+    font-weight: bold
+}
+
+/* Generic.Subheading */
+.highlight .gt {
+    color: #0044DD
+}
+
+/* Generic.Traceback */
+.highlight .kc {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Keyword.Constant */
+.highlight .kd {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Keyword.Declaration */
+.highlight .kn {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Keyword.Namespace */
+.highlight .kp {
+    color: #007020
+}
+
+/* Keyword.Pseudo */
+.highlight .kr {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Keyword.Reserved */
+.highlight .kt {
+    color: #902000
+}
+
+/* Keyword.Type */
+.highlight .m {
+    color: #208050
+}
+
+/* Literal.Number */
+.highlight .s {
+    color: #4070a0
+}
+
+/* Literal.String */
+.highlight .na {
+    color: #4070a0
+}
+
+/* Name.Attribute */
+.highlight .nb {
+    color: #007020
+}
+
+/* Name.Builtin */
+.highlight .nc {
+    color: #0e84b5;
+    font-weight: bold
+}
+
+/* Name.Class */
+.highlight .no {
+    color: #60add5
+}
+
+/* Name.Constant */
+.highlight .nd {
+    color: #555555;
+    font-weight: bold
+}
+
+/* Name.Decorator */
+.highlight .ni {
+    color: #d55537;
+    font-weight: bold
+}
+
+/* Name.Entity */
+.highlight .ne {
+    color: #007020
+}
+
+/* Name.Exception */
+.highlight .nf {
+    color: #06287e
+}
+
+/* Name.Function */
+.highlight .nl {
+    color: #002070;
+    font-weight: bold
+}
+
+/* Name.Label */
+.highlight .nn {
+    color: #0e84b5;
+    font-weight: bold
+}
+
+/* Name.Namespace */
+.highlight .nt {
+    color: #062873;
+    font-weight: bold
+}
+
+/* Name.Tag */
+.highlight .nv {
+    color: #bb60d5
+}
+
+/* Name.Variable */
+.highlight .ow {
+    color: #007020;
+    font-weight: bold
+}
+
+/* Operator.Word */
+.highlight .w {
+    color: #bbbbbb
+}
+
+/* Text.Whitespace */
+.highlight .mb {
+    color: #208050
+}
+
+/* Literal.Number.Bin */
+.highlight .mf {
+    color: #208050
+}
+
+/* Literal.Number.Float */
+.highlight .mh {
+    color: #208050
+}
+
+/* Literal.Number.Hex */
+.highlight .mi {
+    color: #208050
+}
+
+/* Literal.Number.Integer */
+.highlight .mo {
+    color: #208050
+}
+
+/* Literal.Number.Oct */
+.highlight .sa {
+    color: #4070a0
+}
+
+/* Literal.String.Affix */
+.highlight .sb {
+    color: #4070a0
+}
+
+/* Literal.String.Backtick */
+.highlight .sc {
+    color: #4070a0
+}
+
+/* Literal.String.Char */
+.highlight .dl {
+    color: #4070a0
+}
+
+/* Literal.String.Delimiter */
+.highlight .sd {
+    color: #4070a0;
+    font-style: italic
+}
+
+/* Literal.String.Doc */
+.highlight .s2 {
+    color: #4070a0
+}
+
+/* Literal.String.Double */
+.highlight .se {
+    color: #4070a0;
+    font-weight: bold
+}
+
+/* Literal.String.Escape */
+.highlight .sh {
+    color: #4070a0
+}
+
+/* Literal.String.Heredoc */
+.highlight .si {
+    color: #70a0d0;
+    font-style: italic
+}
+
+/* Literal.String.Interpol */
+.highlight .sx {
+    color: #c65d09
+}
+
+/* Literal.String.Other */
+.highlight .sr {
+    color: #235388
+}
+
+/* Literal.String.Regex */
+.highlight .s1 {
+    color: #4070a0
+}
+
+/* Literal.String.Single */
+.highlight .ss {
+    color: #517918
+}
+
+/* Literal.String.Symbol */
+.highlight .bp {
+    color: #007020
+}
+
+/* Name.Builtin.Pseudo */
+.highlight .fm {
+    color: #06287e
+}
+
+/* Name.Function.Magic */
+.highlight .vc {
+    color: #bb60d5
+}
+
+/* Name.Variable.Class */
+.highlight .vg {
+    color: #bb60d5
+}
+
+/* Name.Variable.Global */
+.highlight .vi {
+    color: #bb60d5
+}
+
+/* Name.Variable.Instance */
+.highlight .vm {
+    color: #bb60d5
+}
+
+/* Name.Variable.Magic */
+.highlight .il {
+    color: #208050
+}
deleted file mode 100644
index 7d975ec9d2415c3edea7cf9d73db1d9078358daa..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/third_party/python/psutil/docs/_template/globaltoc.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{#
-    basic/globaltoc.html
-    ~~~~~~~~~~~~~~~~~~~~
-
-    Sphinx sidebar template: global table of contents.
-
-    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-#}
-<h3>{{ _('Manual') }}</h3>
-{{ toctree() }}
-<a href="{{ pathto(master_doc) }}">Back to Welcome</a>
deleted file mode 100644
--- a/third_party/python/psutil/docs/_template/indexcontent.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% extends "defindex.html" %}
-{% block tables %}
-
-{% endblock %}
deleted file mode 100644
--- a/third_party/python/psutil/docs/_template/indexsidebar.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<h3>Useful links</h3>
-<ul>
-  <li><a href="https://github.com/giampaolo/psutil">Github project</a></li>
-  <li><a href="http://grodola.blogspot.com/search/label/psutil">Blog</a></li>
-  <li><a href="https://pypi.python.org/pypi?:action=display&name=psutil#downloads">Download</a></li>
-  <li><a href="https://github.com/giampaolo/psutil/issues">Issues</a></li>
-  <li><a href="http://groups.google.com/group/psutil/topics">Forum</a></li>
-</ul>
deleted file mode 100644
--- a/third_party/python/psutil/docs/_template/page.html
+++ /dev/null
@@ -1,66 +0,0 @@
-{% extends "!page.html" %}
-{% block extrahead %}
-{{ super() }}
-{% if not embedded %}<script type="text/javascript" src="{{ pathto('_static/copybutton.js', 1) }}"></script>{% endif %}
-<script type="text/javascript">
-
-  // Store editor pop-up help state in localStorage
-  // so it does not re-pop-up itself between page loads.
-  // Do not even to pretend to support IE gracefully.
-  (function($) {
-
-    $(document).ready(function() {
-        var box = $("#editor-trap");
-        var klass = "toggled";
-        var storageKey = "toggled";
-
-        function toggle() {
-            box.toggleClass(klass);
-            // Store the toggle status in local storage as "has value string" or null
-            window.localStorage.setItem(storageKey, box.hasClass(klass) ? "toggled" : "not-toggled");
-        }
-
-        box.click(toggle);
-
-        // Check the persistent state of the editor pop-up
-        // Note that localStorage does not necessarily support boolean values (ugh!)
-        // http://stackoverflow.com/questions/3263161/cannot-set-boolean-values-in-localstorage
-        var v = window.localStorage.getItem(storageKey);
-        if(v == "toggled" || !v) {
-          box.addClass(klass);
-        }
-
-    });
-
-  })(jQuery);
-</script>
-<script type="text/javascript">
-
-  var _gaq = _gaq || [];
-  _gaq.push(['_setAccount', 'UA-2097050-4']);
-  _gaq.push(['_trackPageview']);
-
-  (function() {
-    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-  })();
-
-</script>
-{% endblock %}
-
-{% block rootrellink %}
-    <li><a href="https://github.com/giampaolo/psutil/"><img src="{{ pathto('_static/logo.png', 1) }}" style="height: 30px; vertical-align: middle; padding-right: 1em;" /> Project Homepage</a>{{ reldelim1 }}</li>
-	<li><a href="{{ pathto('index') }}">{{ shorttitle }}</a>{{ reldelim1 }}</li>
-{% endblock %}
-
-
-{% block footer %}
-<div class="footer">
-    &copy; Copyright {{ copyright|e }}.
-    <br />
-    Last updated on {{ last_updated|e }}.
-    <br />
-    Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version|e }}.
-</div>
-{% endblock %}
\ No newline at end of file
deleted file mode 100644
--- a/third_party/python/psutil/docs/_themes/pydoctheme/static/pydoctheme.css
+++ /dev/null
@@ -1,187 +0,0 @@
-@import url("default.css");
-
-body {
-    background-color: white;
-    margin-left: 1em;
-    margin-right: 1em;
-}
-
-div.related {
-    margin-bottom: 1.2em;
-    padding: 0.5em 0;
-    border-top: 1px solid #ccc;
-    margin-top: 0.5em;
-}
-
-div.related a:hover {
-    color: #0095C4;
-}
-
-div.related:first-child {
-    border-top: 0;
-    padding-top: 0;
-    border-bottom: 1px solid #ccc;
-}
-
-div.sphinxsidebar {
-    background-color: #eeeeee;
-    border-radius: 5px;
-    line-height: 130%;
-    font-size: smaller;
-}
-
-div.sphinxsidebar h3, div.sphinxsidebar h4 {
-    margin-top: 1.5em;
-}
-
-div.sphinxsidebarwrapper > h3:first-child {
-    margin-top: 0.2em;
-}
-
-div.sphinxsidebarwrapper > ul > li > ul > li {
-    margin-bottom: 0.4em;
-}
-
-div.sphinxsidebar a:hover {
-    color: #0095C4;
-}
-
-div.sphinxsidebar input {
-    font-family: 'Lucida Grande','Lucida Sans','DejaVu Sans',Arial,sans-serif;
-    border: 1px solid #999999;
-    font-size: smaller;
-    border-radius: 3px;
-}
-
-div.sphinxsidebar input[type=text] {
-    max-width: 150px;
-}
-
-div.body {
-    padding: 0 0 0 1.2em;
-}
-
-div.body p {
-    line-height: 140%;
-}
-
-div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 {
-    margin: 0;
-    border: 0;
-    padding: 0.3em 0;
-}
-
-div.body hr {
-    border: 0;
-    background-color: #ccc;
-    height: 1px;
-}
-
-div.body pre {
-    border-radius: 3px;
-    border: 1px solid #ac9;
-}
-
-div.body div.admonition, div.body div.impl-detail {
-    border-radius: 3px;
-}
-
-div.body div.impl-detail > p {
-    margin: 0;
-}
-
-div.body div.seealso {
-    border: 1px solid #dddd66;
-}
-
-div.body a {
-    color: #00608f;
-}
-
-div.body a:visited {
-    color: #30306f;
-}
-
-div.body a:hover {
-    color: #00B0E4;
-}
-
-tt, pre {
-    font-family: monospace, sans-serif;
-    font-size: 96.5%;
-}
-
-div.body tt {
-    border-radius: 3px;
-}
-
-div.body tt.descname {
-    font-size: 120%;
-}
-
-div.body tt.xref, div.body a tt {
-    font-weight: normal;
-}
-
-p.deprecated {
-    border-radius: 3px;
-}
-
-table.docutils {
-    border: 1px solid #ddd;
-    min-width: 20%;
-    border-radius: 3px;
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
-
-table.docutils td, table.docutils th {
-    border: 1px solid #ddd !important;
-    border-radius: 3px;
-}
-
-table p, table li {
-    text-align: left !important;
-}
-
-table.docutils th {
-    background-color: #eee;
-    padding: 0.3em 0.5em;
-}
-
-table.docutils td {
-    background-color: white;
-    padding: 0.3em 0.5em;
-}
-
-table.footnote, table.footnote td {
-    border: 0 !important;
-}
-
-div.footer {
-    line-height: 150%;
-    margin-top: -2em;
-    text-align: right;
-    width: auto;
-    margin-right: 10px;
-}
-
-div.footer a:hover {
-    color: #0095C4;
-}
-
-div.body h1,
-div.body h2,
-div.body h3 {
-    background-color: #EAEAEA;
-    border-bottom: 1px solid #CCC;
-    padding-top: 2px;
-    padding-bottom: 2px;
-    padding-left: 5px;
-    margin-top: 5px;
-    margin-bottom: 5px;
-}
-
-div.body h2 {
-    padding-left:10px;
-}
deleted file mode 100644
--- a/third_party/python/psutil/docs/_themes/pydoctheme/theme.conf
+++ /dev/null
@@ -1,23 +0,0 @@
-[theme]
-inherit = default
-stylesheet = pydoctheme.css
-pygments_style = sphinx
-
-[options]
-bodyfont = 'Lucida Grande', 'Lucida Sans', 'DejaVu Sans', Arial, sans-serif
-headfont = 'Lucida Grande', 'Lucida Sans', 'DejaVu Sans', Arial, sans-serif
-footerbgcolor = white
-footertextcolor = #555555
-relbarbgcolor = white
-relbartextcolor = #666666
-relbarlinkcolor = #444444
-sidebarbgcolor = white
-sidebartextcolor = #444444
-sidebarlinkcolor = #444444
-bgcolor = white
-textcolor = #222222
-linkcolor = #0090c0
-visitedlinkcolor = #00608f
-headtextcolor = #1a1a1a
-headbgcolor = white
-headlinkcolor = #aaaaaa
--- a/third_party/python/psutil/docs/conf.py
+++ b/third_party/python/psutil/docs/conf.py
@@ -1,28 +1,38 @@
 # -*- coding: utf-8 -*-
 #
 # psutil documentation build configuration file, created by
-# sphinx-quickstart.
+# sphinx-quickstart on Wed Oct 19 21:54:30 2016.
 #
 # This file is execfile()d with the current directory set to its
 # containing dir.
 #
 # Note that not all possible configuration values are present in this
 # autogenerated file.
 #
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
 import datetime
 import os
 
 
 PROJECT_NAME = "psutil"
-AUTHOR = "Giampaolo Rodola'"
+AUTHOR = u"Giampaolo Rodola"
 THIS_YEAR = str(datetime.datetime.now().year)
 HERE = os.path.abspath(os.path.dirname(__file__))
 
 
 def get_version():
     INIT = os.path.abspath(os.path.join(HERE, '../psutil/__init__.py'))
     with open(INIT, 'r') as f:
         for line in f:
@@ -30,219 +40,337 @@ def get_version():
                 ret = eval(line.strip().split(' = ')[1])
                 assert ret.count('.') == 2, ret
                 for num in ret.split('.'):
                     assert num.isdigit(), ret
                 return ret
         else:
             raise ValueError("couldn't find version string")
 
+
 VERSION = get_version()
 
 # If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.0'
+#
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = ['sphinx.ext.autodoc',
               'sphinx.ext.coverage',
-              'sphinx.ext.pngmath',
+              'sphinx.ext.imgmath',
               'sphinx.ext.viewcode',
               'sphinx.ext.intersphinx']
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_template']
+templates_path = ['_templates']
 
-# The suffix of source filenames.
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
 source_suffix = '.rst'
 
 # The encoding of source files.
+#
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
 project = PROJECT_NAME
 copyright = '2009-%s, %s' % (THIS_YEAR, AUTHOR)
+author = AUTHOR
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
 version = VERSION
+# The full version, including alpha/beta/rc tags.
+release = VERSION
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-# language = None
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
+#
 # today = ''
+#
 # Else, today_fmt is used as the format for a strftime call.
+#
 # today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
+#
 # default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-add_function_parentheses = True
+#
+# add_function_parentheses = True
+
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
+#
 # add_module_names = True
 
-autodoc_docstring_signature = True
-
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
+#
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
 
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
 
-# -- Options for HTML output -------------------------------------------------
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-html_theme = 'pydoctheme'
-html_theme_options = {'collapsiblesidebar': True}
+#
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ["_themes"]
+# html_theme_path = []
 
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-html_title = "{project} {version} documentation".format(**locals())
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = u'psutil v1.0'
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
+#
 # html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-# html_logo = 'logo.png'
+#
+# html_logo = None
 
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-html_favicon = '_static/favicon.ico'
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or
+# 32x32 pixels large.
+
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ['_static']
 
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-html_use_smartypants = True
+#
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-html_sidebars = {
-    'index': 'indexsidebar.html',
-    '**': ['globaltoc.html',
-           'relations.html',
-           'sourcelink.html',
-           'searchbox.html']
-}
+#
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-# html_additional_pages = {
-#    'index': 'indexcontent.html',
-# }
+#
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-html_domain_indices = False
+#
+# html_domain_indices = True
 
 # If false, no index is generated.
-html_use_index = True
+#
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
+#
 # html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
+#
 # html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
 # html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
 # html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
+#
 # html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
 # html_file_suffix = None
 
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
 # Output file base name for HTML help builder.
 htmlhelp_basename = '%s-doc' % PROJECT_NAME
 
-# -- Options for LaTeX output ------------------------------------------------
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+     # The paper size ('letterpaper' or 'a4paper').
+     #
+     # 'papersize': 'letterpaper',
 
-# The paper size ('letter' or 'a4').
-# latex_paper_size = 'letter'
+     # The font size ('10pt', '11pt' or '12pt').
+     #
+     # 'pointsize': '10pt',
 
-# The font size ('10pt', '11pt' or '12pt').
-# latex_font_size = '10pt'
+     # Additional stuff for the LaTeX preamble.
+     #
+     # 'preamble': '',
+
+     # Latex figure (float) alignment
+     #
+     # 'figure_align': 'htbp',
+}
 
 # Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass
-# [howto/manual]).
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', '%s.tex' % PROJECT_NAME,
-     '%s documentation' % PROJECT_NAME, AUTHOR),
+    (master_doc, 'psutil.tex', u'psutil Documentation',
+     AUTHOR, 'manual'),
 ]
 
-# The name of an image file (relative to this directory) to place at
-# the top of the title page.
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
 # latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
+#
 # latex_use_parts = False
 
 # If true, show page references after internal links.
+#
 # latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
+#
 # latex_show_urls = False
 
-# Additional stuff for the LaTeX preamble.
-# latex_preamble = ''
-
 # Documents to append as an appendix to all manuals.
+#
 # latex_appendices = []
 
+# It false, will not define \strong, \code, 	itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
 # If false, no module index is generated.
+#
 # latex_domain_indices = True
 
 
-# -- Options for manual page output ------------------------------------------
+# -- Options for manual page output ---------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', PROJECT_NAME, '%s documentation' % PROJECT_NAME, [AUTHOR], 1)
+    (master_doc, 'psutil', u'psutil Documentation',
+     [author], 1)
 ]
 
 # If true, show URL addresses after external links.
+#
 # man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'psutil', u'psutil Documentation',
+     author, 'psutil', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+
+html_context = {
+    'css_files': [
+        'https://media.readthedocs.org/css/sphinx_rtd_theme.css',
+        'https://media.readthedocs.org/css/readthedocs-doc-embed.css',
+        '_static/css/custom.css',
+    ],
+}
--- a/third_party/python/psutil/docs/index.rst
+++ b/third_party/python/psutil/docs/index.rst
@@ -1,100 +1,129 @@
 .. module:: psutil
    :synopsis: psutil module
 .. moduleauthor:: Giampaolo Rodola' <grodola@gmail.com>
 
-.. warning::
-
-   This documentation refers to new 2.X version of psutil.
-   Instructions on how to port existing 1.2.1 code are
-   `here <http://grodola.blogspot.com/2014/01/psutil-20-porting.html>`__.
-   Old 1.2.1 documentation is still available
-   `here <https://code.google.com/p/psutil/wiki/Documentation>`__.
-
 psutil documentation
 ====================
 
 Quick links
 -----------
 
-* `Home page <https://github.com/giampaolo/psutil>`__
-* `Blog <http://grodola.blogspot.com/search/label/psutil>`__
-* `Forum <http://groups.google.com/group/psutil/topics>`__
-* `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`__
-* `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
-* `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
-* `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`__
+- `Home page <https://github.com/giampaolo/psutil>`__
+- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+- `Blog <http://grodola.blogspot.com/search/label/psutil>`__
+- `Forum <http://groups.google.com/group/psutil/topics>`__
+- `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`__
+- `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
+- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`__
 
 About
 -----
 
-From project's home page:
-
-  psutil (python system and process utilities) is a cross-platform library for
-  retrieving information on running
-  **processes** and **system utilization** (CPU, memory, disks, network) 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 command line tools
-  such as: *ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
-  ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap*.
-  It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**,
-  both **32-bit** and **64-bit** architectures, with Python versions from
-  **2.6 to 3.4** (users of Python 2.4 and 2.5 may use `2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
-  `PyPy <http://pypy.org/>`__ is also known to work.
+psutil (python system and process 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**, **limiting
+process resources** and the **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** (users of Python 2.4 and 2.5 may use
+`2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+`PyPy <http://pypy.org/>`__ is also known to work.
 
 The psutil documentation you're reading is distributed as a single HTML page.
 
+Install
+-------
+
+The easiest way to install psutil is via ``pip``::
+
+    pip install psutil
+
+On UNIX this requires a C compiler (e.g. gcc) installed. On Windows pip will
+automatically retrieve a pre-compiled wheel version from
+`PYPI repository <https://pypi.python.org/pypi/psutil>`__.
+Alternatively, see more detailed
+`install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+instructions.
+
+
 System related functions
 ========================
 
 CPU
 ---
 
 .. function:: cpu_times(percpu=False)
 
-  Return system CPU times as a namedtuple.
+  Return system CPU times as a named tuple.
   Every attribute represents the seconds the CPU has spent in the given mode.
   The attributes availability varies depending on the platform:
 
-  - **user**
-  - **system**
-  - **idle**
-  - **nice** *(UNIX)*
-  - **iowait** *(Linux)*
-  - **irq** *(Linux, FreeBSD)*
-  - **softirq** *(Linux)*
-  - **steal** *(Linux 2.6.11+)*
-  - **guest** *(Linux 2.6.24+)*
-  - **guest_nice** *(Linux 3.2.0+)*
-
-  When *percpu* is ``True`` return a list of namedtuples for each logical CPU
+  - **user**: time spent by normal processes executing in user mode; on Linux
+    this also includes **guest** time
+  - **system**: time spent by processes executing in kernel mode
+  - **idle**: time spent doing nothing
+
+  Platform-specific fields:
+
+  - **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
+  - **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
+    in a virtualized environment
+  - **guest** *(Linux 2.6.24+)*: time spent running a virtual CPU for guest
+    operating systems under the control of the Linux kernel
+  - **guest_nice** *(Linux 3.2.0+)*: time spent running a niced guest
+    (virtual CPU for guest operating systems under the control of the Linux
+    kernel)
+  - **interrupt** *(Windows)*: time spent for servicing hardware interrupts (
+    similar to "irq" on UNIX)
+  - **dpc** *(Windows)*: time spent servicing deferred procedure calls (DPCs);
+    DPCs are interrupts that run at a lower priority than standard interrupts.
+
+  When *percpu* is ``True`` return a list of named tuples for each logical CPU
   on the system.
   First element of the list refers to first CPU, second element to second CPU
   and so on.
   The order of the list is consistent across calls.
   Example output on Linux:
 
     >>> import psutil
     >>> psutil.cpu_times()
     scputimes(user=17411.7, nice=77.99, system=3797.02, idle=51266.57, iowait=732.58, irq=0.01, softirq=142.43, steal=0.0, guest=0.0, guest_nice=0.0)
 
+  .. versionchanged:: 4.1.0 added *interrupt* and *dpc* fields on Windows.
+
 .. function:: cpu_percent(interval=None, percpu=False)
 
   Return a float representing the current system-wide CPU utilization as a
   percentage. When *interval* is > ``0.0`` compares system CPU times elapsed
   before and after the interval (blocking).
   When *interval* is ``0.0`` or ``None`` compares system CPU times elapsed
   since last call or module import, returning immediately.
   That means the first time this is called it will return a meaningless ``0.0``
   value which you are supposed to ignore.
-  In this case is recommended for accuracy that this function be called with at
-  least ``0.1`` seconds between calls.
+  In this case it is recommended for accuracy that this function be called with
+  at least ``0.1`` seconds between calls.
   When *percpu* is ``True`` returns a list of floats representing the
   utilization as a percentage for each CPU.
   First element of the list refers to first CPU, second element to second CPU
   and so on. The order of the list is consistent across calls.
 
     >>> import psutil
     >>> # blocking
     >>> psutil.cpu_percent(interval=1)
@@ -103,1231 +132,2092 @@ CPU
     >>> psutil.cpu_percent(interval=None)
     2.9
     >>> # blocking, per-cpu
     >>> psutil.cpu_percent(interval=1, percpu=True)
     [2.0, 1.0]
     >>>
 
   .. warning::
-
     the first time this function is called with *interval* = ``0.0`` or ``None``
     it will return a meaningless ``0.0`` value which you are supposed to
     ignore.
 
 .. function:: cpu_times_percent(interval=None, percpu=False)
 
   Same as :func:`cpu_percent()` but provides utilization percentages for each
   specific CPU time as is returned by
   :func:`psutil.cpu_times(percpu=True)<cpu_times()>`.
   *interval* and
   *percpu* arguments have the same meaning as in :func:`cpu_percent()`.
+  On Linux "guest" and "guest_nice" percentages are not accounted in "user"
+  and "user_nice" percentages.
 
   .. warning::
-
     the first time this function is called with *interval* = ``0.0`` or
     ``None`` it will return a meaningless ``0.0`` value which you are supposed
     to ignore.
 
+  .. versionchanged::
+    4.1.0 two new *interrupt* and *dpc* fields are returned on Windows.
+
 .. function:: cpu_count(logical=True)
 
-    Return the number of logical CPUs in the system (same as
-    `os.cpu_count() <http://docs.python.org/3/library/os.html#os.cpu_count>`__
-    in Python 3.4).
-    If *logical* is ``False`` return the number of physical cores only (hyper
-    thread CPUs are excluded). Return ``None`` if undetermined.
-
-      >>> import psutil
-      >>> psutil.cpu_count()
-      4
-      >>> psutil.cpu_count(logical=False)
-      2
-      >>>
+  Return the number of logical CPUs in the system (same as
+  `os.cpu_count() <http://docs.python.org/3/library/os.html#os.cpu_count>`__
+  in Python 3.4) or ``None`` if undetermined.
+  This number may not be equivalent to the number of CPUs the current process
+  can actually use in case process CPU affinity has been changed or Linux
+  cgroups are being used.
+  The number of usable CPUs can be obtained with
+  ``len(psutil.Process().cpu_affinity())``.
+  If *logical* is ``False`` return the number of physical cores only (hyper
+  thread CPUs are excluded).
+  On OpenBSD and NetBSD ``psutil.cpu_count(logical=False)`` always return
+  ``None``. Example on a system having 2 physical hyper-thread CPU cores:
+
+    >>> import psutil
+    >>> psutil.cpu_count()
+    4
+    >>> psutil.cpu_count(logical=False)
+    2
+
+  Example returning the number of CPUs usable by the current process:
+
+    >>> len(psutil.Process().cpu_affinity())
+    1
+
+.. function:: cpu_stats()
+
+  Return various CPU statistics as a named tuple:
+
+  - **ctx_switches**:
+    number of context switches (voluntary + involuntary) since boot.
+  - **interrupts**:
+    number of interrupts since boot.
+  - **soft_interrupts**:
+    number of software interrupts since boot. Always set to ``0`` on Windows
+    and SunOS.
+  - **syscalls**: number of system calls since boot. Always set to ``0`` on
+    Linux.
+
+  Example (Linux):
+
+  .. code-block:: python
+
+     >>> import psutil
+     >>> psutil.cpu_stats()
+     scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
+
+  .. versionadded:: 4.1.0
+
+
+.. function:: cpu_freq(percpu=False)
+
+    Return CPU frequency as a nameduple including *current*, *min* and *max*
+    frequencies expressed in Mhz.
+    On Linux *current* frequency reports the real-time value, on all other
+    platforms it represents the nominal "fixed" value.
+    If *percpu* is ``True`` and the system supports per-cpu frequency
+    retrieval (Linux only) a list of frequencies is returned for each CPU,
+    if not, a list with a single element is returned.
+    If *min* and *max* cannot be determined they are set to ``0``.
+
+    Example (Linux):
+
+    .. code-block:: python
+
+       >>> import psutil
+       >>> psutil.cpu_freq()
+       scpufreq(current=931.42925, min=800.0, max=3500.0)
+       >>> psutil.cpu_freq(percpu=True)
+       [scpufreq(current=2394.945, min=800.0, max=3500.0),
+        scpufreq(current=2236.812, min=800.0, max=3500.0),
+        scpufreq(current=1703.609, min=800.0, max=3500.0),
+        scpufreq(current=1754.289, min=800.0, max=3500.0)]
+
+    Availability: Linux, OSX, Windows
+
+    .. versionadded:: 5.1.0
+
 
 Memory
 ------
 
 .. function:: virtual_memory()
 
-  Return statistics about system memory usage as a namedtuple including the
-  following fields, expressed in bytes:
-
-  - **total**: total physical memory available.
-  - **available**: the actual amount of available memory that can be given
-    instantly to processes that request more memory in bytes; this is
-    calculated by summing different memory values depending on the platform
-    (e.g. free + buffers + cached on Linux) and it is supposed to be used to
-    monitor actual memory usage in a cross platform fashion.
-  - **percent**: the percentage usage calculated as
-    ``(total - available) / total * 100``.
+  Return statistics about system memory usage as a named tuple including the
+  following fields, expressed in bytes. Main metrics:
+
+  - **total**: total physical memory.
+  - **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
+    platform and it is supposed to be used to monitor actual memory usage in a
+    cross platform fashion.
+
+  Other metrics:
+
   - **used**: memory used, calculated differently depending on the platform and
-    designed for informational purposes only.
+    designed for informational purposes only. **total - free** does not
+    necessarily match **used**.
   - **free**: memory not being used at all (zeroed) that is readily available;
-    note that this doesn't reflect the actual memory available (use 'available'
-    instead).
-
-  Platform-specific fields:
-
-  - **active**: (UNIX): memory currently in use or very recently used, and so
+    note that this doesn't reflect the actual memory available (use
+    **available** instead). **total - used** does not necessarily match
+    **free**.
+  - **active** *(UNIX)*: memory currently in use or very recently used, and so
     it is in RAM.
-  - **inactive**: (UNIX): memory that is marked as not used.
-  - **buffers**: (Linux, BSD): cache for things like file system metadata.
-  - **cached**: (Linux, BSD): cache for various things.
-  - **wired**: (BSD, OSX): memory that is marked to always stay in RAM. It is
+  - **inactive** *(UNIX)*: memory that is marked as not used.
+  - **buffers** *(Linux, BSD)*: cache for things like file system metadata.
+  - **cached** *(Linux, BSD)*: cache for various things.
+  - **shared** *(Linux, BSD)*: memory that may be simultaneously accessed by
+    multiple processes.
+  - **wired** *(BSD, OSX)*: memory that is marked to always stay in RAM. It is
     never moved to disk.
-  - **shared**: (BSD): memory that may be simultaneously accessed by multiple
-    processes.
 
   The sum of **used** and **available** does not necessarily equal **total**.
   On Windows **available** and **free** are the same.
-  See `examples/meminfo.py <https://github.com/giampaolo/psutil/blob/master/examples/meminfo.py>`__
+  See `meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
   script providing an example on how to convert bytes in a human readable form.
 
-    >>> import psutil
-    >>> mem = psutil.virtual_memory()
-    >>> mem
-    svmem(total=8374149120L, available=1247768576L, percent=85.1, used=8246628352L, free=127520768L, active=3208777728, inactive=1133408256, buffers=342413312L, cached=777834496)
-    >>>
-    >>> THRESHOLD = 100 * 1024 * 1024  # 100MB
-    >>> if mem.available <= THRESHOLD:
-    ...     print("warning")
-    ...
-    >>>
+  .. note:: if you just want to know how much physical memory is left in a
+    cross platform fashion simply rely on the **available** field.
+
+  >>> import psutil
+  >>> mem = psutil.virtual_memory()
+  >>> mem
+  svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
+  >>>
+  >>> THRESHOLD = 100 * 1024 * 1024  # 100MB
+  >>> if mem.available <= THRESHOLD:
+  ...     print("warning")
+  ...
+  >>>
+
+  .. versionchanged:: 4.2.0 added *shared* metrics on Linux.
+
+  .. versionchanged:: 4.4.0 *available* and *used* values on Linux are more
+    precise and match "free" cmdline utility.
 
 
 .. function:: swap_memory()
 
-  Return system swap memory statistics as a namedtuple including the following
+  Return system swap memory statistics as a named tuple including the following
   fields:
 
   * **total**: total swap memory in bytes
   * **used**: used swap memory in bytes
   * **free**: free swap memory in bytes
   * **percent**: the percentage usage calculated as ``(total - available) / total * 100``
   * **sin**: the number of bytes the system has swapped in from disk
     (cumulative)
   * **sout**: the number of bytes the system has swapped out from disk
     (cumulative)
 
-  **sin** and **sout** on Windows are meaningless and are always set to ``0``.
-  See `examples/meminfo.py <https://github.com/giampaolo/psutil/blob/master/examples/meminfo.py>`__
+  **sin** and **sout** on Windows are always set to ``0``.
+  See `meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
   script providing an example on how to convert bytes in a human readable form.
 
     >>> import psutil
     >>> psutil.swap_memory()
     sswap(total=2097147904L, used=886620160L, free=1210527744L, percent=42.3, sin=1050411008, sout=1906720768)
 
+  .. versionchanged:: 5.2.3 on Linux this function relies on /proc fs instead
+     of sysinfo() syscall so that it can be used in conjunction with
+     :const:`psutil.PROCFS_PATH` in order to retrieve memory info about
+     Linux containers such as Docker and Heroku.
+
 Disks
 -----
 
 .. function:: disk_partitions(all=False)
 
-  Return all mounted disk partitions as a list of namedtuples including device,
+  Return all mounted disk partitions as a list of named tuples including device,
   mount point and filesystem type, similarly to "df" command on UNIX. If *all*
-  parameter is ``False`` return physical devices only (e.g. hard disks, cd-rom
-  drives, USB keys) and ignore all others (e.g. memory partitions such as
+  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 <http://www.cyberciti.biz/tips/what-is-devshm-and-its-practical-usage.html>`__).
-  Namedtuple's **fstype** field is a string which varies depending on the
+  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
   platform.
   On Linux it can be one of the values found in /proc/filesystems (e.g.
   ``'ext3'`` for an ext3 hard drive o ``'iso9660'`` for the CD-ROM drive).
   On Windows it is determined via
   `GetDriveType <http://msdn.microsoft.com/en-us/library/aa364939(v=vs.85).aspx>`__
   and can be either ``"removable"``, ``"fixed"``, ``"remote"``, ``"cdrom"``,
-  ``"unmounted"`` or ``"ramdisk"``. On OSX and FreeBSD it is retrieved via
+  ``"unmounted"`` or ``"ramdisk"``. On OSX and BSD it is retrieved via
   `getfsstat(2) <http://www.manpagez.com/man/2/getfsstat/>`__. See
-  `disk_usage.py <https://github.com/giampaolo/psutil/blob/master/examples/disk_usage.py>`__
+  `disk_usage.py <https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py>`__
   script providing an example usage.
 
     >>> import psutil
     >>> psutil.disk_partitions()
     [sdiskpart(device='/dev/sda3', mountpoint='/', fstype='ext4', opts='rw,errors=remount-ro'),
      sdiskpart(device='/dev/sda7', mountpoint='/home', fstype='ext4', opts='rw')]
 
 .. function:: disk_usage(path)
 
-  Return disk usage statistics about the given *path* as a namedtuple including
-  **total**, **used** and **free** space expressed in bytes, plus the
-  **percentage** usage.
+  Return disk usage statistics about the partition which contains the given
+  *path* as a named tuple including **total**, **used** and **free** space
+  expressed in bytes, plus the **percentage** usage.
   `OSError <http://docs.python.org/3/library/exceptions.html#OSError>`__ is
-  raised if *path* does not exist. See
-  `examples/disk_usage.py <https://github.com/giampaolo/psutil/blob/master/examples/disk_usage.py>`__
-  script providing an example usage. Starting from
-  `Python 3.3 <http://bugs.python.org/issue12442>`__  this is also
-  available as
+  raised if *path* does not exist.
+  Starting from `Python 3.3 <http://bugs.python.org/issue12442>`__  this is
+  also available as
   `shutil.disk_usage() <http://docs.python.org/3/library/shutil.html#shutil.disk_usage>`__.
-  See
-  `disk_usage.py <https://github.com/giampaolo/psutil/blob/master/examples/disk_usage.py>`__
-  script providing an example usage.
+  See `disk_usage.py <https://github.com/giampaolo/psutil/blob/master/scripts/disk_usage.py>`__ script providing an example usage.
 
     >>> import psutil
     >>> psutil.disk_usage('/')
     sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
 
-.. function:: disk_io_counters(perdisk=False)
-
-  Return system-wide disk I/O statistics as a namedtuple including the
+  .. note::
+    UNIX usually reserves 5% of the total disk space for the root user.
+    *total* and *used* fields on UNIX refer to the overall total and used
+    space, whereas *free* represents the space available for the **user** and
+    *percent* represents the **user** utilization (see
+    `source code <https://github.com/giampaolo/psutil/blob/3dea30d583b8c1275057edb1b3b720813b4d0f60/psutil/_psposix.py#L123>`__).
+    That is why *percent* value may look 5% bigger than what you would expect
+    it to be.
+    Also note that both 4 values match "df" cmdline utility.
+
+  .. versionchanged::
+    4.3.0 *percent* value takes root reserved space into account.
+
+.. function:: disk_io_counters(perdisk=False, nowrap=True)
+
+  Return system-wide disk I/O statistics as a named tuple including the
   following fields:
 
   - **read_count**: number of reads
   - **write_count**: number of writes
   - **read_bytes**: number of bytes read
   - **write_bytes**: number of bytes written
-  - **read_time**: time spent reading from disk (in milliseconds)
-  - **write_time**: time spent writing to disk (in milliseconds)
+
+  Platform-specific fields:
+
+  - **read_time**: (all except *NetBSD* and *OpenBSD*) time spent reading from
+    disk (in milliseconds)
+  - **write_time**: (all except *NetBSD* and *OpenBSD*) time spent writing to disk
+    (in milliseconds)
+  - **busy_time**: (*Linux*, *FreeBSD*) time spent doing actual I/Os (in
+    milliseconds)
+  - **read_merged_count** (*Linux*): number of merged reads
+    (see `iostat doc <https://www.kernel.org/doc/Documentation/iostats.txt>`__)
+  - **write_merged_count** (*Linux*): number of merged writes
+    (see `iostats doc <https://www.kernel.org/doc/Documentation/iostats.txt>`__)
 
   If *perdisk* is ``True`` return the same information for every physical disk
   installed on the system as a dictionary with partition names as the keys and
-  the namedtuple described above as the values.
-  See `examples/iotop.py <https://github.com/giampaolo/psutil/blob/master/examples/iotop.py>`__
+  the named tuple described above as the values.
+  See `iotop.py <https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py>`__
   for an example application.
+  On some systems such as Linux, on a very busy or long-lived system, the
+  numbers returned by the kernel may overflow and wrap (restart from zero).
+  If *nowrap* is ``True`` psutil will detect and adjust those numbers across
+  function calls and add "old value" to "new value" so that the returned
+  numbers will always be increasing or remain the same, but never decrease.
+  ``disk_io_counters.cache_clear()`` can be used to invalidate the *nowrap*
+  cache.
 
     >>> import psutil
     >>> psutil.disk_io_counters()
     sdiskio(read_count=8141, write_count=2431, read_bytes=290203, write_bytes=537676, read_time=5868, write_time=94922)
     >>>
     >>> psutil.disk_io_counters(perdisk=True)
     {'sda1': sdiskio(read_count=920, write_count=1, read_bytes=2933248, write_bytes=512, read_time=6016, write_time=4),
      'sda2': sdiskio(read_count=18707, write_count=8830, read_bytes=6060, write_bytes=3443, read_time=24585, write_time=1572),
      'sdb1': sdiskio(read_count=161, write_count=0, read_bytes=786432, write_bytes=0, read_time=44, write_time=0)}
 
+  .. note::
+    on Windows ``"diskperf -y"`` command may need to be executed first
+    otherwise this function won't find any disk.
+
+  .. versionchanged::
+    5.3.0 numbers no longer wrap (restart from zero) across calls thanks to new
+    *nowrap* argument.
+
+  .. versionchanged::
+    4.0.0 added *busy_time* (Linux, FreeBSD), *read_merged_count* and
+    *write_merged_count* (Linux) fields.
+
+  .. versionchanged::
+    4.0.0 NetBSD no longer has *read_time* and *write_time* fields.
+
 Network
 -------
 
 .. function:: net_io_counters(pernic=False)
 
-  Return system-wide network I/O statistics as a namedtuple including the
+  Return system-wide network I/O statistics as a named tuple including the
   following attributes:
 
   - **bytes_sent**: number of bytes sent
   - **bytes_recv**: number of bytes received
   - **packets_sent**: number of packets sent
   - **packets_recv**: number of packets received
   - **errin**: total number of errors while receiving
   - **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)
 
   If *pernic* is ``True`` return the same information for every network
   interface installed on the system as a dictionary with network interface
-  names as the keys and the namedtuple described above as the values.
-  See `examples/nettop.py <https://github.com/giampaolo/psutil/blob/master/examples/nettop.py>`__
-  for an example application.
+  names as the keys and the named tuple described above as the values.
+  On some systems such as Linux, on a very busy or long-lived system, the
+  numbers returned by the kernel may overflow and wrap (restart from zero).
+  If *nowrap* is ``True`` psutil will detect and adjust those numbers across
+  function calls and add "old value" to "new value" so that the returned
+  numbers will always be increasing or remain the same, but never decrease.
+  ``net_io_counters.cache_clear()`` can be used to invalidate the *nowrap*
+  cache.
 
     >>> import psutil
     >>> psutil.net_io_counters()
     snetio(bytes_sent=14508483, bytes_recv=62749361, packets_sent=84311, packets_recv=94888, errin=0, errout=0, dropin=0, dropout=0)
     >>>
     >>> psutil.net_io_counters(pernic=True)
     {'lo': snetio(bytes_sent=547971, bytes_recv=547971, packets_sent=5075, packets_recv=5075, errin=0, errout=0, dropin=0, dropout=0),
     'wlan0': snetio(bytes_sent=13921765, bytes_recv=62162574, packets_sent=79097, packets_recv=89648, errin=0, errout=0, dropin=0, dropout=0)}
 
+  Also see `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+  and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
+  for an example application.
+
+  .. versionchanged::
+    5.3.0 numbers no longer wrap (restart from zero) across calls thanks to new
+    *nowrap* argument.
+
 .. function:: net_connections(kind='inet')
 
-  Return system-wide socket connections as a list of namedtuples.
-  Every namedtuple provides 7 attributes:
-
-  - **fd**: the socket file descriptor, if retrievable, else ``-1``.
-    If the connection refers to the current process this may be passed to
+  Return system-wide socket connections as a list of named tuples.
+  Every named tuple provides 7 attributes:
+
+  - **fd**: the socket file descriptor. If the connection refers to the current
+    process this may be passed to
     `socket.fromfd() <http://docs.python.org/library/socket.html#socket.fromfd>`__
     to obtain a usable socket object.
+    On Windows and SunOS this is always set to ``-1``.
   - **family**: the address family, either `AF_INET
     <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
     `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
     or `AF_UNIX <http://docs.python.org//library/socket.html#socket.AF_UNIX>`__.
   - **type**: the address type, either `SOCK_STREAM
     <http://docs.python.org//library/socket.html#socket.SOCK_STREAM>`__ or
     `SOCK_DGRAM
     <http://docs.python.org//library/socket.html#socket.SOCK_DGRAM>`__.
-  - **laddr**: the local address as a ``(ip, port)`` tuple or a ``path``
-    in case of AF_UNIX sockets.
-  - **raddr**: the remote address as a ``(ip, port)`` tuple or an absolute
-    ``path`` in case of UNIX sockets.
+  - **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
+    absolute ``path`` in case of UNIX sockets.
     When the remote endpoint is not connected you'll get an empty tuple
-    (AF_INET*) or ``None`` (AF_UNIX).
-    On Linux AF_UNIX sockets will always have this set to ``None``.
+    (AF_INET*) or ``""`` (AF_UNIX). For UNIX sockets see notes below.
   - **status**: represents the status of a TCP connection. The return value
     is one of the :data:`psutil.CONN_* <psutil.CONN_ESTABLISHED>` constants
     (a string).
     For UDP and UNIX sockets this is always going to be
     :const:`psutil.CONN_NONE`.
   - **pid**: the PID of the process which opened the socket, if retrievable,
     else ``None``. On some platforms (e.g. Linux) the availability of this
     field changes depending on process privileges (root is needed).
 
-  The *kind* parameter is a string which filters for connections that fit the
+  The *kind* parameter is a string which filters for connections matching the
   following criteria:
 
   .. table::
 
    +----------------+-----------------------------------------------------+
    | **Kind value** | **Connections using**                               |
    +================+=====================================================+
-   | "inet"         | IPv4 and IPv6                                       |
+   | ``"inet"``     | IPv4 and IPv6                                       |
    +----------------+-----------------------------------------------------+
-   | "inet4"        | IPv4                                                |
+   | ``"inet4"``    | IPv4                                                |
    +----------------+-----------------------------------------------------+
-   | "inet6"        | IPv6                                                |
+   | ``"inet6"``    | IPv6                                                |
    +----------------+-----------------------------------------------------+
-   | "tcp"          | TCP                                                 |
+   | ``"tcp"``      | TCP                                                 |
    +----------------+-----------------------------------------------------+
-   | "tcp4"         | TCP over IPv4                                       |
+   | ``"tcp4"``     | TCP over IPv4                                       |
    +----------------+-----------------------------------------------------+
-   | "tcp6"         | TCP over IPv6                                       |
+   | ``"tcp6"``     | TCP over IPv6                                       |
    +----------------+-----------------------------------------------------+
-   | "udp"          | UDP                                                 |
+   | ``"udp"``      | UDP                                                 |
    +----------------+-----------------------------------------------------+
-   | "udp4"         | UDP over IPv4                                       |
+   | ``"udp4"``     | UDP over IPv4                                       |
    +----------------+-----------------------------------------------------+
-   | "udp6"         | UDP over IPv6                                       |
+   | ``"udp6"``     | UDP over IPv6                                       |
    +----------------+-----------------------------------------------------+
-   | "unix"         | UNIX socket (both UDP and TCP protocols)            |
+   | ``"unix"``     | UNIX socket (both UDP and TCP protocols)            |
    +----------------+-----------------------------------------------------+
-   | "all"          | the sum of all the possible families and protocols  |
+   | ``"all"``      | the sum of all the possible families and protocols  |
    +----------------+-----------------------------------------------------+
 
-  On OSX this function requires root privileges.
+  On OSX and AIX this function requires root privileges.
   To get per-process connections use :meth:`Process.connections`.
   Also, see
-  `netstat.py sample script <https://github.com/giampaolo/psutil/blob/master/examples/netstat.py>`__.
+  `netstat.py sample script <https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py>`__.
   Example:
 
     >>> import psutil
     >>> psutil.net_connections()
-    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED', pid=1254),
+     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING', pid=2987),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED', pid=None),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT', pid=None)
      ...]
 
-  .. note:: (OSX) :class:`psutil.AccessDenied` is always raised unless running
-     as root (lsof does the same).
-  .. note:: (Solaris) UNIX sockets are not supported.
+  .. note::
+    (OSX and AIX) :class:`psutil.AccessDenied` is always raised unless running
+    as root. This is a limitation of the OS and ``lsof`` does the same.
+
+  .. note::
+    (Solaris) UNIX sockets are not supported.
+
+  .. note::
+     (Linux, FreeBSD) "raddr" field for UNIX sockets is always set to "".
+     This is a limitation of the OS.
+
+  .. note::
+     (OpenBSD) "laddr" and "raddr" fields for UNIX sockets are always set to
+     "". This is a limitation of the OS.
 
   .. versionadded:: 2.1.0
 
+  .. versionchanged:: 5.3.0 : socket "fd" is now set for real instead of being
+     ``-1``.
+
+  .. versionchanged:: 5.3.0 : "laddr" and "raddr" are named tuples.
+
 .. function:: net_if_addrs()
 
   Return the addresses associated to each NIC (network interface card)
   installed on the system as a dictionary whose keys are the NIC names and
-  value is a list of namedtuples for each address assigned to the NIC.
-  Each namedtuple includes 4 fields:
-
-  - **family**
-  - **address**
-  - **netmask**
-  - **broadcast**
-
-  *family* can be either
-  `AF_INET <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
-  `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
-  or :const:`psutil.AF_LINK`, which refers to a MAC address.
-  *address* is the primary address, *netmask* and *broadcast* may be ``None``.
+  value is a list of named tuples for each address assigned to the NIC.
+  Each named tuple includes 5 fields:
+
+  - **family**: the address family, either
+    `AF_INET <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
+    `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
+    or :const:`psutil.AF_LINK`, which refers to a MAC address.
+  - **address**: the primary NIC address (always set).
+  - **netmask**: the netmask address (may be ``None``).
+  - **broadcast**: the broadcast address (may be ``None``).
+  - **ptp**: stands for "point to point"; it's the destination address on a
+    point to point interface (typically a VPN). *broadcast* and *ptp* are
+    mutually exclusive. May be ``None``.
+
   Example::
 
     >>> import psutil
     >>> psutil.net_if_addrs()
-    {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
-            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
-            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
-     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
-               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
-               snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+    {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
+            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
+            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
+     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
+               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
+               snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
     >>>
 
-  See also `examples/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/examples/ifconfig.py>`__
+  See also `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+  and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
   for an example application.
 
-  .. note:: if you're interested in others families (e.g. AF_BLUETOOTH) you can
-    use the more powerful `netifaces <https://pypi.python.org/pypi/netifaces/>`__
+  .. note::
+    if you're interested in others families (e.g. AF_BLUETOOTH) you can use
+    the more powerful `netifaces <https://pypi.python.org/pypi/netifaces/>`__
     extension.
 
-  .. note:: you can have more than one address of the same family associated
-    with each interface (that's why dict values are lists).
-
-  *New in 3.0.0*
+  .. note::
+    you can have more than one address of the same family associated with each
+    interface (that's why dict values are lists).
+
+  .. note::
+    *broadcast* and *ptp* are not supported on Windows and are always ``None``.
+
+  .. versionadded:: 3.0.0
+
+  .. versionchanged:: 3.2.0 *ptp* field was added.
+
+  .. versionchanged:: 4.4.0 added support for *netmask* field on Windows which
+    is no longer ``None``.
 
 .. function:: net_if_stats()
 
   Return information about each NIC (network interface card) installed on the
-  system as a dictionary whose keys are the NIC names and value is a namedtuple
+  system as a dictionary whose keys are the NIC names and value is a named tuple
   with the following fields:
 
-  - **isup**
-  - **duplex**
-  - **speed**
-  - **mtu**
-
-  *isup* is a boolean indicating whether the NIC is up and running, *duplex*
-  can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or
-  :const:`NIC_DUPLEX_UNKNOWN`, *speed* is the NIC speed expressed in mega bits
-  (MB), if it can't be determined (e.g. 'localhost') it will be set to ``0``,
-  *mtu* is the maximum transmission unit expressed in bytes.
-  See also `examples/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/examples/ifconfig.py>`__
-  for an example application.
+  - **isup**: a bool indicating whether the NIC is up and running.
+  - **duplex**: the duplex communication type;
+    it can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or
+    :const:`NIC_DUPLEX_UNKNOWN`.
+  - **speed**: the NIC speed expressed in mega bits (MB), if it can't be
+    determined (e.g. 'localhost') it will be set to ``0``.
+  - **mtu**: NIC's maximum transmission unit expressed in bytes.
+
   Example:
 
     >>> import psutil
     >>> psutil.net_if_stats()
     {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
      'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
 
-  *New in 3.0.0*
-
-
-Other system info
------------------
-
-.. function:: users()
-
-  Return users currently connected on the system as a list of namedtuples
-  including the following fields:
-
-  - **user**: the name of the user.
-  - **terminal**: the tty or pseudo-tty associated with the user, if any,
-    else ``None``.
-  - **host**: the host name associated with the entry, if any.
-  - **started**: the creation time as a floating point number expressed in
-    seconds since the epoch.
+  Also see `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+  and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
+  for an example application.
+
+  .. versionadded:: 3.0.0
+
+
+Sensors
+-------
+
+.. function:: sensors_temperatures(fahrenheit=False)
+
+  Return hardware temperatures. Each entry is a named tuple representing a
+  certain hardware temperature sensor (it may be a CPU, an hard disk or
+  something else, depending on the OS and its configuration).
+  All temperatures are expressed in celsius unless *fahrenheit* is set to
+  ``True``.
+  If sensors are not supported by the OS an empty dict is returned.
+  Example::
+
+    >>> 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)]}
+
+  See also `temperatures.py <https://github.com/giampaolo/psutil/blob/master/scripts/temperatures.py>`__ and `sensors.py <https://github.com/giampaolo/psutil/blob/master/scripts/sensors.py>`__
+  for an example application.
+
+  Availability: Linux
+
+  .. versionadded:: 5.1.0
+
+  .. warning::
+
+    this API is experimental. Backward incompatible changes may occur if
+    deemed necessary.
+
+.. function:: sensors_fans()
+
+  Return hardware fans speed. Each entry is a named tuple representing a
+  certain hardware sensor fan.
+  Fan speed is expressed in RPM (rounds per minute).
+  If sensors are not supported by the OS an empty dict is returned.
+  Example::
+
+    >>> import psutil
+    >>> psutil.sensors_fans()
+    {'asus': [sfan(label='cpu_fan', current=3200)]}
+
+  See also `fans.py <https://github.com/giampaolo/psutil/blob/master/scripts/fans.py>`__  and `sensors.py <https://github.com/giampaolo/psutil/blob/master/scripts/sensors.py>`__
+  for an example application.
+
+  Availability: Linux
+
+  .. versionadded:: 5.2.0
+
+  .. warning::
+
+    this API is experimental. Backward incompatible changes may occur if
+    deemed necessary.
+
+.. function:: sensors_battery()
+
+  Return battery status information as a named tuple including the following
+  values. If no battery is installed or metrics can't be determined ``None``
+  is returned.
+
+  - **percent**: battery power left as a percentage.
+  - **secsleft**: a rough approximation of how many seconds are left before the
+    battery runs out of power.
+    If the AC power cable is connected this is set to
+    :data:`psutil.POWER_TIME_UNLIMITED <psutil.POWER_TIME_UNLIMITED>`.
+    If it can't be determined it is set to
+    :data:`psutil.POWER_TIME_UNKNOWN <psutil.POWER_TIME_UNKNOWN>`.
+  - **power_plugged**: ``True`` if the AC power cable is connected, ``False``
+    if not or ``None`` if it can't be determined.
 
   Example::
 
     >>> import psutil
-    >>> psutil.users()
-    [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
-     suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+    >>>
+    >>> def secs2hours(secs):
+    ...     mm, ss = divmod(secs, 60)
+    ...     hh, mm = divmod(mm, 60)
+    ...     return "%d:%02d:%02d" % (hh, mm, ss)
+    ...
+    >>> battery = psutil.sensors_battery()
+    >>> battery
+    sbattery(percent=93, secsleft=16628, power_plugged=False)
+    >>> print("charge = %s%%, time left = %s" % (batt.percent, secs2hours(batt.secsleft)))
+    charge = 93%, time left = 4:37:08
+
+  See also `battery.py <https://github.com/giampaolo/psutil/blob/master/scripts/battery.py>`__  and `sensors.py <https://github.com/giampaolo/psutil/blob/master/scripts/sensors.py>`__ for an example application.
+
+  Availability: Linux, Windows, FreeBSD
+
+  .. versionadded:: 5.1.0
+
+  .. versionchanged:: 5.4.2 added OSX support
+
+  .. warning::
+
+    this API is experimental. Backward incompatible changes may occur if
+    deemed necessary.
+
+Other system info
+-----------------
 
 .. function:: boot_time()
 
   Return the system boot time expressed in seconds since the epoch.
   Example:
 
   .. code-block:: python
 
      >>> import psutil, datetime
      >>> psutil.boot_time()
      1389563460.0
      >>> datetime.datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
      '2014-01-12 22:51:00'
 
+  .. note::
+    on Windows this function may return a time which is off by 1 second if it's
+    used across different processes (see
+    `issue #1007 <https://github.com/giampaolo/psutil/issues/1007>`__).
+
+.. function:: users()
+
+  Return users currently connected on the system as a list of named tuples
+  including the following fields:
+
+  - **user**: the name of the user.
+  - **terminal**: the tty or pseudo-tty associated with the user, if any,
+    else ``None``.
+  - **host**: the host name associated with the entry, if any.
+  - **started**: the creation time as a floating point number expressed in
+    seconds since the epoch.
+  - **pid**: the PID of the login process (like sshd, tmux, gdm-session-worker,
+    ...). On Windows and OpenBSD this is always set to ``None``.
+
+  Example::
+
+    >>> 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)]
+
+  .. versionchanged::
+    5.3.0 added "pid" field
+
 Processes
 =========
 
 Functions
 ---------
 
 .. function:: pids()
 
   Return a list of current running PIDs. To iterate over all processes
-  :func:`process_iter()` should be preferred.
-
-.. function:: pid_exists(pid)
-
-  Check whether the given PID exists in the current process list. This is
-  faster than doing ``"pid in psutil.pids()"`` and should be preferred.
-
-.. function:: process_iter()
+  and avoid race conditions :func:`process_iter()` should be preferred.
+
+  >>> import psutil
+  >>> psutil.pids()
+  [1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, ..., 32498]
+
+.. function:: process_iter(attrs=None, ad_value=None)
 
   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 should be preferred over :func:`psutil.pids()` for iterating over
   processes.
   Sorting order in which processes are returned is
-  based on their PID. Example usage::
-
-    import psutil
-
-    for proc in psutil.process_iter():
-        try:
-            pinfo = proc.as_dict(attrs=['pid', 'name'])
-        except psutil.NoSuchProcess:
-            pass
-        else:
-            print(pinfo)
+  based on their PID.
+  *attrs* and *ad_value* have the same meaning as in :meth:`Process.as_dict()`.
+  If *attrs* is specified :meth:`Process.as_dict()` is called interanally and
+  the resulting dict is stored as a ``info`` attribute which is attached to the
+  returned :class:`Process`  instances.
+  If *attrs* is an empty list it will retrieve all process info (slow).
+  Example usage::
+
+    >>> 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']):
+    ...     print(proc.info)
+    ...
+    {'name': 'systemd', 'pid': 1, 'username': 'root'}
+    {'name': 'kthreadd', 'pid': 2, 'username': 'root'}
+    {'name': 'ksoftirqd/0', 'pid': 3, 'username': 'root'}
+    ...
+
+  Example of 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
+    {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.
+
+.. function:: pid_exists(pid)
+
+  Check whether the given PID exists in the current process list. This is
+  faster than doing ``pid in psutil.pids()`` and should be preferred.
 
 .. function:: wait_procs(procs, timeout=None, callback=None)
 
   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 (it may be ``None``).
-  ``callback`` is a function which gets called every time a process terminates
-  (a :class:`Process` instance is passed as callback argument). Function will
-  return as soon as all processes terminate or when timeout occurs. Tipical use
-  case is:
+  *returncode* attribute indicating process exit status (will be ``None`` for
+  processes which are not our children).
+  ``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).
+  This function will return as soon as all processes terminate or when
+  *timeout* (seconds) occurs.
+  Differently from :meth:`Process.wait` it will not raise
+  :class:`TimeoutExpired` if timeout occurs.
+  A typical use case may be:
 
   - send SIGTERM to a list of processes
   - give them some time to terminate
   - send SIGKILL to those ones which are still alive
 
-  Example::
+  Example which terminates and waits all the children of this process::
 
     import psutil
 
     def on_terminate(proc):
         print("process {} terminated with exit code {}".format(proc, proc.returncode))
 
-    procs = [...]  # a list of Process instances
+    procs = psutil.Process().children()
     for p in procs:
         p.terminate()
-    gone, alive = wait_procs(procs, timeout=3, callback=on_terminate)
+    gone, alive = psutil.wait_procs(procs, timeout=3, callback=on_terminate)
     for p in alive:
         p.kill()
 
 Exceptions
 ----------
 
 .. class:: Error()
 
   Base exception class. All other exceptions inherit from this one.
 
 .. class:: NoSuchProcess(pid, name=None, msg=None)
 
-   Raised by :class:`Process` class methods when no process with the given
-   *pid* is found in the current process list or when a process no longer
-   exists. "name" is the name the process had before disappearing
-   and gets set only if :meth:`Process.name()` was previosly called.
+  Raised by :class:`Process` class methods when no process with the given
+  *pid* is found in the current process list or when a process no longer
+  exists. *name* is the name the process had before disappearing
+  and gets set only if :meth:`Process.name()` was previously called.
 
 .. class:: ZombieProcess(pid, name=None, ppid=None, msg=None)
 
-   This may be raised by :class:`Process` class methods when querying a zombie
-   process on UNIX (Windows doesn't have zombie processes). Depending on the
-   method called the OS may be able to succeed in retrieving the process
-   information or not.
-   Note: this is a subclass of :class:`NoSuchProcess` so if you're not
-   interested in retrieving zombies (e.g. when using :func:`process_iter()`)
-   you can ignore this exception and just catch :class:`NoSuchProcess`.
-
-   *New in 3.0.0*
+  This may be raised by :class:`Process` class methods when querying a zombie
+  process on UNIX (Windows doesn't have zombie processes). Depending on the
+  method called the OS may be able to succeed in retrieving the process
+  information or not.
+  Note: this is a subclass of :class:`NoSuchProcess` so if you're not
+  interested in retrieving zombies (e.g. when using :func:`process_iter()`)
+  you can ignore this exception and just catch :class:`NoSuchProcess`.
+
+  .. versionadded:: 3.0.0
 
 .. class:: AccessDenied(pid=None, name=None, msg=None)
 
-    Raised by :class:`Process` class methods when permission to perform an
-    action is denied. "name" is the name of the process (may be ``None``).
+  Raised by :class:`Process` class methods when permission to perform an
+  action is denied. "name" is the name of the process (may be ``None``).
 
 .. class:: TimeoutExpired(seconds, pid=None, name=None, msg=None)
 
-    Raised by :meth:`Process.wait` if timeout expires and process is still
-    alive.
+  Raised by :meth:`Process.wait` if timeout expires and process is still
+  alive.
 
 Process class
 -------------
 
 .. class:: Process(pid=None)
 
-  Represents an OS process with the given *pid*. If *pid* is omitted current
-  process *pid* (`os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__)
-  is used.
+  Represents an OS process with the given *pid*.
+  If *pid* is omitted current process *pid*
+  (`os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__) is used.
   Raise :class:`NoSuchProcess` if *pid* does not exist.
+  On Linux *pid* can also refer to a thread ID (the *id* field returned by
+  :meth:`threads` method).
   When accessing methods of this class always be  prepared to catch
-  :class:`NoSuchProcess` and :class:`AccessDenied` exceptions.
+  :class:`NoSuchProcess`, :class:`ZombieProcess` and :class:`AccessDenied`
+  exceptions.
   `hash() <http://docs.python.org/2/library/functions.html#hash>`__ builtin can
   be used against instances of this class in order to identify a process
   univocally over time (the hash is determined by mixing process PID
   and creation time). As such it can also be used with
   `set()s <http://docs.python.org/2/library/stdtypes.html#types-set>`__.
 
-  .. warning::
+  .. note::
+
+    In order to efficiently fetch more than one information about the process
+    at the same time, make sure to use either :meth:`as_dict` or
+    :meth:`oneshot` context manager.
+
+  .. note::
 
     the way this class is bound to a process is uniquely via its **PID**.
-    That means that if the :class:`Process` instance is old enough and
-    the PID has been reused by another process in the meantime you might end up
-    interacting with another process.
-    The only exceptions for which process identity is pre-emptively checked
-    (via PID + creation time) and guaranteed are for
+    That means that if the process terminates and the OS reuses its PID you may
+    end up interacting with another process.
+    The only exceptions for which process identity is preemptively checked
+    (via PID + creation time) is for the following methods:
     :meth:`nice` (set),
     :meth:`ionice`  (set),
     :meth:`cpu_affinity` (set),
     :meth:`rlimit` (set),
     :meth:`children`,
     :meth:`parent`,
     :meth:`suspend`
     :meth:`resume`,
     :meth:`send_signal`,
-    :meth:`terminate`, and
-    :meth:`kill`
-    methods.
+    :meth:`terminate`
+    :meth:`kill`.
     To prevent this problem for all other methods you can use
-    :meth:`is_running()` before querying the process or use
+    :meth:`is_running()` before querying the process or
     :func:`process_iter()` in case you're iterating over all processes.
+    It must be noted though that unless you deal with very "old" (inactive)
+    :class:`Process` instances this will hardly represent a problem.
+
+  .. method:: oneshot()
+
+    Utility context manager which considerably speeds up the retrieval of
+    multiple process information at the same time.
+    Internally different process info (e.g. :meth:`name`, :meth:`ppid`,
+    :meth:`uids`, :meth:`create_time`, ...) may be fetched by using the same
+    routine, but only one value is returned and the others are discarded.
+    When using this context manager the internal routine is executed once (in
+    the example below on :meth:`name()`) the value of interest is returned and
+    the others are cached.
+    The subsequent calls sharing the same internal routine will return the
+    cached value.
+    The cache is cleared when exiting the context manager block.
+    The advice is to use this every time you retrieve more than one information
+    about the process. If you're lucky, you'll get a hell of a speedup.
+    Example:
+
+    >>> import psutil
+    >>> p = psutil.Process()
+    >>> with p.oneshot():
+    ...     p.name()  # execute internal routine once collecting multiple info
+    ...     p.cpu_times()  # return cached value
+    ...     p.cpu_percent()  # return cached value
+    ...     p.create_time()  # return cached value
+    ...     p.ppid()  # return cached value
+    ...     p.status()  # return cached value
+    ...
+    >>>
+
+    Here's a list of methods which can take advantage of the speedup depending
+    on what platform you're on.
+    In the table below horizontal emtpy rows indicate what process methods can
+    be efficiently grouped together internally.
+    The last column (speedup) shows an approximation of the speedup you can get
+    if you call all the methods together (best case scenario).
+
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | Linux                        | Windows                       | OSX                          | BSD                          | SunOS                    | AIX                      |
+    +==============================+===============================+==============================+==============================+==========================+==========================+
+    | :meth:`cpu_num`              | :meth:`cpu_percent`           | :meth:`cpu_percent`          | :meth:`cpu_num`              | :meth:`name`             | :meth:`name`             |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`cpu_percent`          | :meth:`cpu_times`             | :meth:`cpu_times`            | :meth:`cpu_percent`          | :meth:`cmdline`          | :meth:`cmdline`          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`cpu_times`            | :meth:`io_counters()`         | :meth:`memory_info`          | :meth:`cpu_times`            | :meth:`create_time`      | :meth:`create_time`      |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`create_time`          | :meth:`memory_info`           | :meth:`memory_percent`       | :meth:`create_time`          |                          |                          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`name`                 | :meth:`memory_maps`           | :meth:`num_ctx_switches`     | :meth:`gids`                 | :meth:`memory_info`      | :meth:`memory_info`      |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`ppid`                 | :meth:`num_ctx_switches`      | :meth:`num_threads`          | :meth:`io_counters`          | :meth:`memory_percent`   | :meth:`memory_percent`   |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`status`               | :meth:`num_handles`           |                              | :meth:`name`                 | :meth:`num_threads`      | :meth:`num_threads`      |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`terminal`             | :meth:`num_threads`           | :meth:`create_time`          | :meth:`memory_info`          | :meth:`ppid`             | :meth:`ppid`             |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    |                              | :meth:`username`              | :meth:`gids`                 | :meth:`memory_percent`       | :meth:`status`           | :meth:`status`           |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`gids`                 |                               | :meth:`name`                 | :meth:`num_ctx_switches`     | :meth:`terminal`         | :meth:`terminal`         |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`num_ctx_switches`     |                               | :meth:`ppid`                 | :meth:`ppid`                 |                          |                          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`num_threads`          |                               | :meth:`status`               | :meth:`status`               | :meth:`gids`             | :meth:`gids`             |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`uids`                 |                               | :meth:`terminal`             | :meth:`terminal`             | :meth:`uids`             | :meth:`uids`             |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`username`             |                               | :meth:`uids`                 | :meth:`uids`                 | :meth:`username`         | :meth:`username`         |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    |                              |                               | :meth:`username`             | :meth:`username`             |                          |                          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`memory_full_info`     |                               |                              |                              |                          |                          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | :meth:`memory_maps`          |                               |                              |                              |                          |                          |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+    | *speedup: +2.6x*             | *speedup: +1.8x / +6.5x*      | *speedup: +1.9x*             | *speedup: +2.0x*             | *speedup: +1.3x*         | *speedup: +1.3x*         |
+    +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+
+    .. versionadded:: 5.0.0
 
   .. attribute:: pid
 
-     The process PID.
+     The process PID. This is the only (read-only) attribute of the class.
 
   .. method:: ppid()
 
-     The process parent pid.  On Windows the return value is cached after first
-     call.
+    The process parent PID.  On Windows the return value is cached after first
+    call. Not on POSIX because
+    `ppid may change <https://github.com/giampaolo/psutil/issues/321>`__
+    if process becomes a zombie.
+    See also :meth:`parent` method.
 
   .. method:: name()
 
-     The process name. The return value is cached after first call.
+    The process name.  On Windows the return value is cached after first
+    call. Not on POSIX because the process name
+    `may change <https://github.com/giampaolo/psutil/issues/692>`__.
+    See also how to `find a process by name <#find-process-by-name>`__.
 
   .. method:: exe()
 
-     The process executable as an absolute path.
-     On some systems this may also be an empty string.
-     The return value is cached after first call.
+    The process executable as an absolute path.
+    On some systems this may also be an empty string.
+    The return value is cached after first call.
+
+    >>> import psutil
+    >>> psutil.Process().exe()
+    '/usr/bin/python2.7'
 
   .. method:: cmdline()
 
-     The command line this process has been called with.
+    The command line this process has been called with as a list of strings.
+    The return value is not cached because the cmdline of a process may change.
+
+    >>> import psutil
+    >>> psutil.Process().cmdline()
+    ['python', 'manage.py', 'runserver']
+
+  .. method:: environ()
+
+    The environment variables of the process as a dict.  Note: this might not
+    reflect changes made after the process started.
+
+    >>> import psutil
+    >>> psutil.Process().environ()
+    {'LC_NUMERIC': 'it_IT.UTF-8', 'QT_QPA_PLATFORMTHEME': 'appmenu-qt5', 'IM_CONFIG_PHASE': '1', 'XDG_GREETER_DATA_DIR': '/var/lib/lightdm-data/giampaolo', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'XDG_CURRENT_DESKTOP': 'Unity', 'UPSTART_EVENTS': 'started starting', 'GNOME_KEYRING_PID': '', 'XDG_VTNR': '7', 'QT_IM_MODULE': 'ibus', 'LOGNAME': 'giampaolo', 'USER': 'giampaolo', 'PATH': '/home/giampaolo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/giampaolo/svn/sysconf/bin', 'LC_PAPER': 'it_IT.UTF-8', 'GNOME_KEYRING_CONTROL': '', 'GTK_IM_MODULE': 'ibus', 'DISPLAY': ':0', 'LANG': 'en_US.UTF-8', 'LESS_TERMCAP_se': '\x1b[0m', 'TERM': 'xterm-256color', 'SHELL': '/bin/bash', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XAUTHORITY': '/home/giampaolo/.Xauthority', 'LANGUAGE': 'en_US', 'COMPIZ_CONFIG_PROFILE': 'ubuntu', 'LC_MONETARY': 'it_IT.UTF-8', 'QT_LINUX_ACCESSIBILITY_ALWAYS_ON': '1', 'LESS_TERMCAP_me': '\x1b[0m', 'LESS_TERMCAP_md': '\x1b[01;38;5;74m', 'LESS_TERMCAP_mb': '\x1b[01;31m', 'HISTSIZE': '100000', 'UPSTART_INSTANCE': '', 'CLUTTER_IM_MODULE': 'xim', 'WINDOWID': '58786407', 'EDITOR': 'vim', 'SESSIONTYPE': 'gnome-session', 'XMODIFIERS': '@im=ibus', 'GPG_AGENT_INFO': '/home/giampaolo/.gnupg/S.gpg-agent:0:1', 'HOME': '/home/giampaolo', 'HISTFILESIZE': '100000', 'QT4_IM_MODULE': 'xim', 'GTK2_MODULES': 'overlay-scrollbar', 'XDG_SESSION_DESKTOP': 'ubuntu', 'SHLVL': '1', 'XDG_RUNTIME_DIR': '/run/user/1000', 'INSTANCE': 'Unity', 'LC_ADDRESS': 'it_IT.UTF-8', 'SSH_AUTH_SOCK': '/run/user/1000/keyring/ssh', 'VTE_VERSION': '4205', 'GDMSESSION': 'ubuntu', 'MANDATORY_PATH': '/usr/share/gconf/ubuntu.mandatory.path', 'VISUAL': 'vim', 'DESKTOP_SESSION': 'ubuntu', 'QT_ACCESSIBILITY': '1', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'XDG_SESSION_ID': 'c2', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-9GAJpvnt8r', '_': '/usr/bin/python', 'DEFAULTS_PATH': '/usr/share/gconf/ubuntu.default.path', 'LC_IDENTIFICATION': 'it_IT.UTF-8', 'LESS_TERMCAP_ue': '\x1b[0m', 'UPSTART_SESSION': 'unix:abstract=/com/ubuntu/upstart-session/1000/1294', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg', 'GTK_MODULES': 'gail:atk-bridge:unity-gtk-module', 'XDG_SESSION_TYPE': 'x11', 'PYTHONSTARTUP': '/home/giampaolo/.pythonstart', 'LC_NAME': 'it_IT.UTF-8', 'OLDPWD': '/home/giampaolo/svn/curio_giampaolo/tests', 'GDM_LANG': 'en_US', 'LC_TELEPHONE': 'it_IT.UTF-8', 'HISTCONTROL': 'ignoredups:erasedups', 'LC_MEASUREMENT': 'it_IT.UTF-8', 'PWD': '/home/giampaolo/svn/curio_giampaolo', 'JOB': 'gnome-session', 'LESS_TERMCAP_us': '\x1b[04;38;5;146m', 'UPSTART_JOB': 'unity-settings-daemon', 'LC_TIME': 'it_IT.UTF-8', 'LESS_TERMCAP_so': '\x1b[38;5;246m', 'PAGER': 'less', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'XDG_SEAT': 'seat0'}
+
+    Availability: Linux, OSX, Windows, SunOS
+
+    .. versionadded:: 4.0.0
+    .. versionchanged:: 5.3.0 added SunOS support
 
   .. method:: create_time()
 
-     The process creation time as a floating point number expressed in seconds
-     since the epoch, in
-     `UTC <http://en.wikipedia.org/wiki/Coordinated_universal_time>`__.
-     The return value is cached after first call.
-
-        >>> import psutil, datetime
-        >>> p = psutil.Process()
-        >>> p.create_time()
-        1307289803.47
-        >>> datetime.datetime.fromtimestamp(p.create_time()).strftime("%Y-%m-%d %H:%M:%S")
-        '2011-03-05 18:03:52'
+    The process creation time as a floating point number expressed in seconds
+    since the epoch, in
+    `UTC <http://en.wikipedia.org/wiki/Coordinated_universal_time>`__.
+    The return value is cached after first call.
+
+      >>> import psutil, datetime
+      >>> p = psutil.Process()
+      >>> p.create_time()
+      1307289803.47
+      >>> datetime.datetime.fromtimestamp(p.create_time()).strftime("%Y-%m-%d %H:%M:%S")
+      '2011-03-05 18:03:52'
 
   .. method:: as_dict(attrs=None, ad_value=None)
 
-     Utility method returning process information as a hashable dictionary.
-     If *attrs* is specified it must be a list of strings reflecting available
-     :class:`Process` class's attribute names (e.g. ``['cpu_times', 'name']``)
-     else all public (read only) attributes are assumed. *ad_value* is the
-     value which gets assigned to a dict key in case :class:`AccessDenied`
-     or :class:`ZombieProcess` exception is raised when retrieving that
-     particular process information.
-
-        >>> import psutil
-        >>> p = psutil.Process()
-        >>> p.as_dict(attrs=['pid', 'name', 'username'])
-        {'username': 'giampaolo', 'pid': 12366, 'name': 'python'}
-
-     .. versionchanged:: 3.0.0 *ad_value* is used also when incurring into
-        :class:`ZombieProcess` exception, not only :class:`AccessDenied`
+    Utility method retrieving multiple process information as a dictionary.
+    If *attrs* is specified it must be a list of strings reflecting available
+    :class:`Process` class's attribute names (e.g. ``['cpu_times', 'name']``),
+    else all public (read only) attributes are assumed. *ad_value* is the
+    value which gets assigned to a dict key in case :class:`AccessDenied`
+    or :class:`ZombieProcess` exception is raised when retrieving that
+    particular process information.
+    Internally, :meth:`as_dict` uses :meth:`oneshot` context manager so
+    there's no need you use it also.
+
+      >>> import psutil
+      >>> p = psutil.Process()
+      >>> p.as_dict(attrs=['pid', 'name', 'username'])
+      {'username': 'giampaolo', 'pid': 12366, 'name': 'python'}
+
+    .. versionchanged::
+      3.0.0 *ad_value* is used also when incurring into
+      :class:`ZombieProcess` exception, not only :class:`AccessDenied`
+
+     .. versionchanged:: 4.5.0 :meth:`as_dict` is considerably faster thanks
+        to :meth:`oneshot` context manager.
 
   .. method:: parent()
 
-     Utility method which returns the parent process as a :class:`Process`
-     object pre-emptively checking whether PID has been reused. If no parent
-     PID is known return ``None``.
+    Utility method which returns the parent process as a :class:`Process`
+    object preemptively checking whether PID has been reused. If no parent
+    PID is known return ``None``.
+    See also :meth:`ppid` method.
 
   .. method:: status()
 
-     The current process status as a string. The returned string is one of the
-     :data:`psutil.STATUS_*<psutil.STATUS_RUNNING>` constants.
+    The current process status as a string. The returned string is one of the
+    :data:`psutil.STATUS_*<psutil.STATUS_RUNNING>` constants.
 
   .. method:: cwd()
 
-     The process current working directory as an absolute path.
+    The process current working directory as an absolute path.
 
   .. method:: username()
 
-     The name of the user that owns the process. On UNIX this is calculated by
-     using real process uid.
+    The name of the user that owns the process. On UNIX this is calculated by
+    using real process uid.
 
   .. method:: uids()
 
-     The **real**, **effective** and **saved** user ids of this process as a
-     namedtuple. This is the same as
-     `os.getresuid() <http://docs.python.org//library/os.html#os.getresuid>`__
-     but can be used for every process PID.
-
-     Availability: UNIX
+    The real, effective and saved user ids of this process as a
+    named tuple. This is the same as
+    `os.getresuid() <http://docs.python.org//library/os.html#os.getresuid>`__
+    but can be used for any process PID.
+
+    Availability: UNIX
 
   .. method:: gids()
 
-     The **real**, **effective** and **saved** group ids of this process as a
-     namedtuple. This is the same as
-     `os.getresgid() <http://docs.python.org//library/os.html#os.getresgid>`__
-     but can be used for every process PID.
-
-     Availability: UNIX
+    The real, effective and saved group ids of this process as a
+    named tuple. This is the same as
+    `os.getresgid() <http://docs.python.org//library/os.html#os.getresgid>`__
+    but can be used for any process PID.
+
+    Availability: UNIX
 
   .. method:: terminal()
 
-     The terminal associated with this process, if any, else ``None``. This is
-     similar to "tty" command but can be used for every process PID.
-
-     Availability: UNIX
+    The terminal associated with this process, if any, else ``None``. This is
+    similar to "tty" command but can be used for any process PID.
+
+    Availability: UNIX
 
   .. method:: nice(value=None)
 
-     Get or set process
-     `niceness <blogs.techrepublic.com.com/opensource/?p=140>`__ (priority).
-     On UNIX this is a number which usually goes from ``-20`` to ``20``.
-     The higher the nice value, the lower the priority of the process.
-
-        >>> import psutil
-        >>> p = psutil.Process()
-        >>> p.nice(10)  # set
-        >>> p.nice()  # get
-        10
-        >>>
-
-     Starting from `Python 3.3 <http://bugs.python.org/issue10784>`__ this
-     functionality is also available as
-     `os.getpriority() <http://docs.python.org/3/library/os.html#os.getpriority>`__
-     and
-     `os.setpriority() <http://docs.python.org/3/library/os.html#os.setpriority>`__
-     (UNIX only).
-
-     On Windows this is available as well by using
-     `GetPriorityClass <http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx>`__
-     and `SetPriorityClass <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__
-     and *value* is one of the
-     :data:`psutil.*_PRIORITY_CLASS <psutil.ABOVE_NORMAL_PRIORITY_CLASS>`
-     constants.
-     Example which increases process priority on Windows:
-
-        >>> p.nice(psutil.HIGH_PRIORITY_CLASS)
+    Get or set process
+    `niceness <blogs.techrepublic.com.com/opensource/?p=140>`__ (priority).
+    On UNIX this is a number which usually goes from ``-20`` to ``20``.
+    The higher the nice value, the lower the priority of the process.
+
+      >>> import psutil
+      >>> p = psutil.Process()
+      >>> p.nice(10)  # set
+      >>> p.nice()  # get
+      10
+      >>>
+
+    Starting from `Python 3.3 <http://bugs.python.org/issue10784>`__ this
+    functionality is also available as
+    `os.getpriority() <http://docs.python.org/3/library/os.html#os.getpriority>`__
+    and
+    `os.setpriority() <http://docs.python.org/3/library/os.html#os.setpriority>`__
+    (UNIX only).
+    On Windows this is implemented via
+    `GetPriorityClass <http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx>`__
+    and `SetPriorityClass <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__
+    Windows APIs and *value* is one of the
+    :data:`psutil.*_PRIORITY_CLASS <psutil.ABOVE_NORMAL_PRIORITY_CLASS>`
+    constants reflecting the MSDN documentation.
+    Example which increases process priority on Windows:
+
+      >>> p.nice(psutil.HIGH_PRIORITY_CLASS)
 
   .. method:: ionice(ioclass=None, value=None)
 
-     Get or set
-     `process I/O niceness <http://friedcpu.wordpress.com/2007/07/17/why-arent-you-using-ionice-yet/>`__ (priority).
-     On Linux *ioclass* is one of the
-     :data:`psutil.IOPRIO_CLASS_*<psutil.IOPRIO_CLASS_NONE>` constants.
-     *value* is a number which goes from  ``0`` to ``7``. The higher the value,
-     the lower the I/O priority of the process. On Windows only *ioclass* is
-     used and it can be set to ``2`` (normal), ``1`` (low) or ``0`` (very low).
-     The example below sets IDLE priority class for the current process,
-     meaning it will only get I/O time when no other process needs the disk:
+    Get or set
+    `process I/O niceness <http://friedcpu.wordpress.com/2007/07/17/why-arent-you-using-ionice-yet/>`__ (priority).
+    On Linux *ioclass* is one of the
+    :data:`psutil.IOPRIO_CLASS_*<psutil.IOPRIO_CLASS_NONE>` constants.
+    *value* is a number which goes from  ``0`` to ``7``. The higher the value,
+    the lower the I/O priority of the process. On Windows only *ioclass* is
+    used and it can be set to ``2`` (normal), ``1`` (low) or ``0`` (very low).
+    The example below sets IDLE priority class for the current process,
+    meaning it will only get I/O time when no other process needs the disk:
 
       >>> import psutil
       >>> p = psutil.Process()
       >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # set
       >>> p.ionice()  # get
       pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
       >>>
 
-     On Windows only *ioclass* is used and it can be set to ``2`` (normal),
-     ``1`` (low) or ``0`` (very low).
-
-     Availability: Linux and Windows > Vista
-
-     .. versionchanged:: 3.0.0 on >= Python 3.4 the returned ``ioclass``
-        constant is an `enum <https://docs.python.org/3/library/enum.html#module-enum>`__
-        instead of a plain integer.
+    On Windows only *ioclass* is used and it can be set to ``2`` (normal),
+    ``1`` (low) or ``0`` (very low). Also it returns an integer instead of a
+    named tuple.
+
+    Availability: Linux and Windows > Vista
+
+    .. versionchanged::
+      3.0.0 on Python >= 3.4 the returned ``ioclass`` constant is an
+      `enum <https://docs.python.org/3/library/enum.html#module-enum>`__
+      instead of a plain integer.
 
   .. method:: rlimit(resource, limits=None)
 
-     Get or set process resource limits (see
-     `man prlimit <http://linux.die.net/man/2/prlimit>`__). *resource* is one of
-     the :data:`psutil.RLIMIT_* <psutil.RLIMIT_INFINITY>` constants.
-     *limits* is a ``(soft, hard)`` tuple.
-     This is the same as `resource.getrlimit() <http://docs.python.org/library/resource.html#resource.getrlimit>`__
-     and `resource.setrlimit() <http://docs.python.org/library/resource.html#resource.setrlimit>`__
-     but can be used for every process PID and only on Linux.
-     Example:
+    Get or set process resource limits (see
+    `man prlimit <http://linux.die.net/man/2/prlimit>`__). *resource* is one
+    of the :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>` constants.
+    *limits* is a ``(soft, hard)`` tuple.
+    This is the same as `resource.getrlimit() <http://docs.python.org/library/resource.html#resource.getrlimit>`__
+    and `resource.setrlimit() <http://docs.python.org/library/resource.html#resource.setrlimit>`__
+    but can be used for any process PID, not only
+    `os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__.
+    For get, return value is a ``(soft, hard)`` tuple. Each value may be either
+    and integer or :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>`.
+    Example:
 
       >>> import psutil
       >>> p = psutil.Process()
       >>> # process may open no more than 128 file descriptors
       >>> p.rlimit(psutil.RLIMIT_NOFILE, (128, 128))
       >>> # process may create files no bigger than 1024 bytes
       >>> p.rlimit(psutil.RLIMIT_FSIZE, (1024, 1024))
       >>> # get
       >>> p.rlimit(psutil.RLIMIT_FSIZE)
       (1024, 1024)
       >>>
 
-     Availability: Linux
+    Availability: Linux
 
   .. method:: io_counters()
 
-     Return process I/O statistics as a namedtuple including the number of read
-     and write operations performed by the process and the amount of bytes read
-     and written. For Linux refer to
-     `/proc filesysem documentation <https://www.kernel.org/doc/Documentation/filesystems/proc.txt>`__.
-     On BSD there's apparently no way to retrieve bytes counters, hence ``-1``
-     is returned for **read_bytes** and **write_bytes** fields. OSX is not
-     supported.
+    Return process I/O statistics as a named tuple.
+    For Linux you can refer to
+    `/proc filesysem documentation <http://stackoverflow.com/a/3634088>`__.
+
+    - **read_count**: the number of read operations performed (cumulative).
+      This is supposed to count the number of read-related syscalls such as
+      ``read()`` and ``pread()`` on UNIX.
+    - **write_count**: the number of write operations performed (cumulative).
+      This is supposed to count the number of write-related syscalls such as
+      ``write()`` and ``pwrite()`` on UNIX.
+    - **read_bytes**: the number of bytes read (cumulative).
+      Always ``-1`` on  BSD.
+    - **write_bytes**: the number of bytes written (cumulative).
+      Always ``-1`` on  BSD.
+
+    Linux specific:
+
+    - **read_chars** *(Linux)*: the amount of bytes which this process passed
+      to ``read()`` and ``pread()`` syscalls (cumulative).
+      Differently from *read_bytes* it doesn't care whether or not actual
+      physical disk I/O occurred.
+    - **write_chars** *(Linux)*: the amount of bytes which this process passed
+      to ``write()`` and ``pwrite()`` syscalls (cumulative).
+      Differently from *write_bytes* it doesn't care whether or not actual
+      physical disk I/O occurred.
+
+    Windows specific:
+
+    - **other_count** *(Windows)*: the number of I/O operations performed
+      other than read and write operations.
+    - **other_bytes** *(Windows)*: the number of bytes transferred during
+      operations other than read and write operations.
+
+    >>> import psutil
+    >>> p = psutil.Process()
+    >>> p.io_counters()
+    pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0, read_chars=769931, write_chars=203)
+
+    Availability: Linux, BSD, Windows, AIX
+
+    .. versionchanged:: 5.2.0 added *read_chars* and *write_chars* on Linux;
+      added *other_count* and *other_bytes* on Windows.
+
+  .. method:: num_ctx_switches()
+
+    The number voluntary and involuntary context switches performed by
+    this process (cumulative).
+
+    .. versionchanged:: 5.4.1 added AIX support
+
+  .. method:: num_fds()
+
+    The number of file descriptors currently opened by this process
+    (non cumulative).
+
+    Availability: UNIX
+
+  .. method:: num_handles()
+
+    The number of handles currently used by this process (non cumulative).
+
+    Availability: Windows
+
+  .. method:: num_threads()
+
+    The number of threads currently used by this process (non cumulative).
+
+  .. method:: threads()
+
+    Return threads opened by process as a list of named tuples including thread
+    id and thread CPU times (user/system). On OpenBSD this method requires
+    root privileges.
+
+  .. method:: cpu_times()
+
+    Return a `(user, system, children_user, children_system)` named tuple
+    representing the accumulated process time, in seconds (see
+    `explanation <http://stackoverflow.com/questions/556405/>`__).
+    On Windows and OSX only *user* and *system* are filled, the others are
+    set to ``0``.
+    This is similar to
+    `os.times() <http://docs.python.org//library/os.html#os.times>`__
+    but can be used for any process PID.
+
+    .. versionchanged::
+      4.1.0 return two extra fields: *children_user* and *children_system*.
+
+  .. method:: cpu_percent(interval=None)
+
+    Return a float representing the process CPU utilization as a percentage
+    which can also be ``> 100.0`` in case of a process running multiple threads
+    on different CPUs.
+    When *interval* is > ``0.0`` compares process times to system CPU times
+    elapsed before and after the interval (blocking). When interval is ``0.0``
+    or ``None`` compares process times to system CPU times elapsed since last
+    call, returning immediately. That means the first time this is called it
+    will return a meaningless ``0.0`` value which you are supposed to ignore.
+    In this case is recommended for accuracy that this function be called a
+    second time with at least ``0.1`` seconds between calls.
+    Example:
 
       >>> import psutil
       >>> p = psutil.Process()
-      >>> p.io_counters()
-      pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0)
-
-     Availability: all platforms except OSX and Solaris
-
-  .. method:: num_ctx_switches()
-
-     The number voluntary and involuntary context switches performed by
-     this process.
-
-  .. method:: num_fds()
-
-     The number of file descriptors used by this process.
-
-     Availability: UNIX
-
-  .. method:: num_handles()
-
-     The number of handles used by this process.
-
-     Availability: Windows
-
-  .. method:: num_threads()
-
-     The number of threads currently used by this process.
-
-  .. method:: threads()
-
-     Return threads opened by process as a list of namedtuples including thread
-     id and thread CPU times (user/system).
-
-  .. method:: cpu_times()
-
-     Return a tuple whose values are process CPU **user** and **system**
-     times which means the amount of time expressed in seconds that a process
-     has spent in
-     `user / system mode <http://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1>`__.
-     This is similar to
-     `os.times() <http://docs.python.org//library/os.html#os.times>`__
-     but can be used for every process PID.
-
-  .. method:: cpu_percent(interval=None)
-
-     Return a float representing the process CPU utilization as a percentage.
-     When *interval* is > ``0.0`` compares process times to system CPU times
-     elapsed before and after the interval (blocking). When interval is ``0.0``
-     or ``None`` compares process times to system CPU times elapsed since last
-     call, returning immediately. That means the first time this is called it
-     will return a meaningless ``0.0`` value which you are supposed to ignore.
-     In this case is recommended for accuracy that this function be called a
-     second time with at least ``0.1`` seconds between calls. Example:
-
-      >>> import psutil
-      >>> p = psutil.Process()
-      >>>
       >>> # blocking
       >>> p.cpu_percent(interval=1)
       2.0
       >>> # non-blocking (percentage since last call)
       >>> p.cpu_percent(interval=None)
       2.9
-      >>>
-
-     .. note::
-        a percentage > 100 is legitimate as it can result from a process with
-        multiple threads running on different CPU cores.
-
-     .. warning::
-        the first time this method is called with interval = ``0.0`` or
-        ``None`` it will return a meaningless ``0.0`` value which you are
-        supposed to ignore.
+
+    .. note::
+      the returned value can be > 100.0 in case of a process running multiple
+      threads on different CPU cores.
+
+    .. note::
+      the returned value is explicitly *not* split evenly between all available
+      CPUs (differently from :func:`psutil.cpu_percent()`).
+      This means that a busy loop process running on a system with 2 logical
+      CPUs will be reported as having 100% CPU utilization instead of 50%.
+      This was done in order to be consistent with ``top`` UNIX utility
+      and also to make it easier to identify processes hogging CPU resources
+      independently from the number of CPUs.
+      It must be noted that ``taskmgr.exe`` on Windows does not behave like
+      this (it would report 50% usage instead).
+      To emulate Windows ``taskmgr.exe`` behavior you can do:
+      ``p.cpu_percent() / psutil.cpu_count()``.
+
+    .. warning::
+      the first time this method is called with interval = ``0.0`` or
+      ``None`` it will return a meaningless ``0.0`` value which you are
+      supposed to ignore.
 
   .. method:: cpu_affinity(cpus=None)
 
-     Get or set process current
-     `CPU affinity <http://www.linuxjournal.com/article/6799?page=0,0>`__.
-     CPU affinity consists in telling the OS to run a certain process on a
-     limited set of CPUs only. The number of eligible CPUs can be obtained with
-     ``list(range(psutil.cpu_count()))``. On set raises ``ValueError`` in case
-     an invalid CPU number is specified.
+    Get or set process current
+    `CPU affinity <http://www.linuxjournal.com/article/6799?page=0,0>`__.
+    CPU affinity consists in telling the OS to run a process on a limited set
+    of CPUs only (on Linux cmdline, ``taskset`` command is typically used).
+    If no argument is passed it returns the current CPU affinity as a list
+    of integers.
+    If passed it must be a list of integers specifying the new CPUs affinity.
+    If an empty list is passed all eligible CPUs are assumed (and set).
+    On some systems such as Linux this may not necessarily mean all available
+    logical CPUs as in ``list(range(psutil.cpu_count()))``).
 
       >>> import psutil
       >>> psutil.cpu_count()
       4
       >>> p = psutil.Process()
-      >>> p.cpu_affinity()  # get
+      >>> # get
+      >>> p.cpu_affinity()
       [0, 1, 2, 3]
-      >>> p.cpu_affinity([0])  # set; from now on, process will run on CPU #0 only
+      >>> # set; from now on, process will run on CPU #0 and #1 only
+      >>> p.cpu_affinity([0, 1])
       >>> p.cpu_affinity()
-      [0]
-      >>>
-      >>> # reset affinity against all CPUs
-      >>> all_cpus = list(range(psutil.cpu_count()))
-      >>> p.cpu_affinity(all_cpus)
-      >>>
-
-     Availability: Linux, Windows, BSD
-
-     .. versionchanged:: 2.2.0 added support for FreeBSD
+      [0, 1]
+      >>> # reset affinity against all eligible CPUs
+      >>> p.cpu_affinity([])
+
+    Availability: Linux, Windows, FreeBSD
+
+    .. versionchanged:: 2.2.0 added support for FreeBSD
+    .. versionchanged:: 5.1.0 an empty list can be passed to set affinity
+      against all eligible CPUs.
+
+  .. method:: cpu_num()
+
+    Return what CPU this process is currently running on.
+    The returned number should be ``<=`` :func:`psutil.cpu_count()`.
+    On FreeBSD certain kernel process may return ``-1``.
+    It may be used in conjunction with ``psutil.cpu_percent(percpu=True)`` to
+    observe the system workload distributed across multiple CPUs as shown by
+    `cpu_distribution.py <https://github.com/giampaolo/psutil/blob/master/scripts/cpu_distribution.py>`__ example script.
+
+    Availability: Linux, FreeBSD, SunOS
+
+    .. versionadded:: 5.1.0
 
   .. method:: memory_info()
 
-     Return a tuple representing RSS (Resident Set Size) and VMS (Virtual
-     Memory Size) in bytes. On UNIX *rss* and *vms* are the same values shown
-     by ps. On Windows *rss* and *vms* refer to "Mem Usage" and "VM Size"
-     columns of taskmgr.exe. For more detailed memory stats use
-     :meth:`memory_info_ex`.
+    Return a named tuple with variable fields depending on the platform
+    representing memory information about the process.
+    The "portable" fields available on all plaforms are `rss` and `vms`.
+    All numbers are expressed in bytes.
+
+    +---------+---------+-------+---------+-----+------------------------------+
+    | Linux   | OSX     | BSD   | Solaris | AIX | Windows                      |
+    +=========+=========+=======+=========+=====+==============================+
+    | rss     | rss     | rss   | rss     | rss | rss (alias for ``wset``)     |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | vms     | vms     | vms   | vms     | vms | vms (alias for ``pagefile``) |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | shared  | pfaults | text  |         |     | num_page_faults              |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | text    | pageins | data  |         |     | peak_wset                    |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | lib     |         | stack |         |     | wset                         |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | data    |         |       |         |     | peak_paged_pool              |
+    +---------+---------+-------+---------+-----+------------------------------+
+    | dirty   |         |       |         |     | paged_pool                   |
+    +---------+---------+-------+---------+-----+------------------------------+
+    |         |         |       |         |     | peak_nonpaged_pool           |
+    +---------+---------+-------+---------+-----+------------------------------+
+    |         |         |       |         |     | nonpaged_pool                |
+    +---------+---------+-------+---------+-----+------------------------------+
+    |         |         |       |         |     | pagefile                     |
+    +---------+---------+-------+---------+-----+------------------------------+
+    |         |         |       |         |     | peak_pagefile                |
+    +---------+---------+-------+---------+-----+------------------------------+
+    |         |         |       |         |     | private                      |
+    +---------+---------+-------+---------+-----+------------------------------+
+
+    - **rss**: aka "Resident Set Size", this is the non-swapped physical
+      memory a process has used.
+      On UNIX it matches "top"'s RES column
+      (see `doc <http://linux.die.net/man/1/top>`__).
+      On Windows this is an alias for `wset` field and it matches "Mem Usage"
+      column of taskmgr.exe.
+
+    - **vms**: aka "Virtual Memory Size", this is the total amount of virtual
+      memory used by the process.
+      On UNIX it matches "top"'s VIRT column
+      (see `doc <http://linux.die.net/man/1/top>`__).
+      On Windows this is an alias for `pagefile` field and it matches
+      "Mem Usage" "VM Size" column of taskmgr.exe.
+
+    - **shared**: *(Linux)*
+      memory that could be potentially shared with other processes.
+      This matches "top"'s SHR column
+      (see `doc <http://linux.die.net/man/1/top>`__).
+
+    - **text** *(Linux, BSD)*:
+      aka TRS (text resident set) the amount of memory devoted to
+      executable code. This matches "top"'s CODE column
+      (see `doc <http://linux.die.net/man/1/top>`__).
+
+    - **data** *(Linux, BSD)*:
+      aka DRS (data resident set) the amount of physical memory devoted to
+      other than executable code. It matches "top"'s DATA column
+      (see `doc <http://linux.die.net/man/1/top>`__).
+
+    - **lib** *(Linux)*: the memory used by shared libraries.
+
+    - **dirty** *(Linux)*: the number of dirty pages.
+
+    - **pfaults** *(OSX)*: number of page faults.
+
+    - **pageins** *(OSX)*: number of actual pageins.
+
+    For on explanation of Windows fields rely on
+    `PROCESS_MEMORY_COUNTERS_EX <http://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx>`__ structure doc.
+    Example on Linux:
+
+      >>> import psutil
+      >>> p = psutil.Process()
+      >>> p.memory_info()
+      pmem(rss=15491072, vms=84025344, shared=5206016, text=2555904, lib=0, data=9891840, dirty=0)
+
+    .. versionchanged::
+      4.0.0 multiple fields are returned, not only `rss` and `vms`.
 
   .. method:: memory_info_ex()
 
-     Return a namedtuple with variable fields depending on the platform
-     representing extended memory information about the process.
-     All numbers are expressed in bytes.
-
-     +--------+---------+-------+-------+--------------------+
-     | Linux  | OSX     | BSD   | SunOS | Windows            |
-     +========+=========+=======+=======+====================+
-     | rss    | rss     | rss   | rss   | num_page_faults    |
-     +--------+---------+-------+-------+--------------------+
-     | vms    | vms     | vms   | vms   | peak_wset          |
-     +--------+---------+-------+-------+--------------------+
-     | shared | pfaults | text  |       | wset               |
-     +--------+---------+-------+-------+--------------------+
-     | text   | pageins | data  |       | peak_paged_pool    |
-     +--------+---------+-------+-------+--------------------+
-     | lib    |         | stack |       | paged_pool         |
-     +--------+---------+-------+-------+--------------------+
-     | data   |         |       |       | peak_nonpaged_pool |
-     +--------+---------+-------+-------+--------------------+
-     | dirty  |         |       |       | nonpaged_pool      |
-     +--------+---------+-------+-------+--------------------+
-     |        |         |       |       | pagefile           |
-     +--------+---------+-------+-------+--------------------+
-     |        |         |       |       | peak_pagefile      |
-     +--------+---------+-------+-------+--------------------+
-     |        |         |       |       | private            |
-     +--------+---------+-------+-------+--------------------+
-
-     Windows metrics are extracted from
-     `PROCESS_MEMORY_COUNTERS_EX <http://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx>`__ structure.
-     Example on Linux:
-
-     >>> import psutil
-     >>> p = psutil.Process()
-     >>> p.memory_info_ex()
-     pextmem(rss=15491072, vms=84025344, shared=5206016, text=2555904, lib=0, data=9891840, dirty=0)
-
-  .. method:: memory_percent()
-
-     Compare physical system memory to process resident memory (RSS) and
-     calculate process memory utilization as a percentage.
+    Same as :meth:`memory_info` (deprecated).
+
+    .. warning::
+      deprecated in version 4.0.0; use :meth:`memory_info` instead.
+
+  .. method:: memory_full_info()
+
+    This method returns the same information as :meth:`memory_info`, plus, on
+    some platform (Linux, OSX, Windows), also provides additional metrics
+    (USS, PSS and swap).
+    The additional metrics provide a better representation of "effective"
+    process memory consumption (in case of USS) as explained in detail in this
+    `blog post <http://grodola.blogspot.com/2016/02/psutil-4-real-process-memory-and-environ.html>`__.
+    It does so by passing through the whole process address.
+    As such it usually requires higher user privileges than
+    :meth:`memory_info` and is considerably slower.
+    On platforms where extra fields are not implemented this simply returns the
+    same metrics as :meth:`memory_info`.
+
+    - **uss** *(Linux, OSX, Windows)*:
+      aka "Unique Set Size", this is the memory which is unique to a process
+      and which would be freed if the process was terminated right now.
+
+    - **pss** *(Linux)*: aka "Proportional Set Size", is the amount of memory
+      shared with other processes, accounted in a way that the amount is
+      divided evenly between the processes that share it.
+      I.e. if a process has 10 MBs all to itself and 10 MBs shared with
+      another process its PSS will be 15 MBs.
+
+    - **swap** *(Linux)*: amount of memory that has been swapped out to disk.
+
+    .. note::
+      `uss` is probably the most representative metric for determining how
+      much memory is actually being used by a process.
+      It represents the amount of memory that would be freed if the process
+      was terminated right now.
+
+    Example on Linux:
+
+      >>> import psutil
+      >>> p = psutil.Process()
+      >>> p.memory_full_info()
+      pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
+      >>>
+
+    See also `procsmem.py <https://github.com/giampaolo/psutil/blob/master/scripts/procsmem.py>`__
+    for an example application.
+
+    .. versionadded:: 4.0.0
+
+  .. method:: memory_percent(memtype="rss")
+
+    Compare process memory to total physical system memory and calculate
+    process memory utilization as a percentage.
+    *memtype* argument is a string that dictates what type of process memory
+    you want to compare against. You can choose between the named tuple field
+    names returned by :meth:`memory_info` and :meth:`memory_full_info`
+    (defaults to ``"rss"``).
+
+    .. versionchanged:: 4.0.0 added `memtype` parameter.
 
   .. method:: memory_maps(grouped=True)
 
-     Return process's mapped memory regions as a list of namedtuples whose
-     fields are variable depending on the platform. As such, portable
-     applications should rely on namedtuple's `path` and `rss` fields only.
-     This method is useful to obtain a detailed representation of process
-     memory usage as explained
-     `here <http://bmaurer.blogspot.it/2006/03/memory-usage-with-smaps.html>`__.
-     If *grouped* is ``True`` the mapped regions with the same *path* are
-     grouped together and the different memory fields are summed.  If *grouped*
-     is ``False`` every mapped region is shown as a single entity and the
-     namedtuple will also include the mapped region's address space (*addr*)
-     and permission set (*perms*).
-     See `examples/pmap.py <https://github.com/giampaolo/psutil/blob/master/examples/pmap.py>`__
-     for an example application.
+    Return process's mapped memory regions as a list of named tuples whose
+    fields are variable depending on the platform.
+    This method is useful to obtain a detailed representation of process
+    memory usage as explained
+    `here <http://bmaurer.blogspot.it/2006/03/memory-usage-with-smaps.html>`__
+    (the most important value is "private" memory).
+    If *grouped* is ``True`` the mapped regions with the same *path* are
+    grouped together and the different memory fields are summed.  If *grouped*
+    is ``False`` each mapped region is shown as a single entity and the
+    named tuple will also include the mapped region's address space (*addr*)
+    and permission set (*perms*).
+    See `pmap.py <https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py>`__
+    for an example application.
+
+    +---------------+--------------+---------+-----------+--------------+
+    | Linux         |  OSX         | Windows | Solaris   | FreeBSD      |
+    +===============+==============+=========+===========+==============+
+    | rss           | rss          | rss     | rss       | rss          |
+    +---------------+--------------+---------+-----------+--------------+
+    | size          | private      |         | anonymous | private      |
+    +---------------+--------------+---------+-----------+--------------+
+    | pss           | swapped      |         | locked    | ref_count    |
+    +---------------+--------------+---------+-----------+--------------+
+    | shared_clean  | dirtied      |         |           | shadow_count |
+    +---------------+--------------+---------+-----------+--------------+
+    | shared_dirty  | ref_count    |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
+    | private_clean | shadow_depth |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
+    | private_dirty |              |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
+    | referenced    |              |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
+    | anonymous     |              |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
+    | swap          |              |         |           |              |
+    +---------------+--------------+---------+-----------+--------------+
 
       >>> import psutil
       >>> p = psutil.Process()
       >>> p.memory_maps()
-      [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
-       pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
-       pmmap_grouped(path='/lib/x8664-linux-gnu/libcrypto.so.0.1', rss=34124, anonymous=1245, swap=0),
-       pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
-       pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
+      [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.memory_maps(grouped=False)
+      [pmmap_ext(addr='00400000-006ea000', perms='r-xp', path='/usr/bin/python2.7', rss=2293760, size=3055616, pss=1157120, shared_clean=2273280, shared_dirty=0, private_clean=20480, private_dirty=0, referenced=2293760, anonymous=0, swap=0),
+       pmmap_ext(addr='008e9000-008eb000', perms='r--p', path='/usr/bin/python2.7', rss=8192, size=8192, pss=6144, shared_clean=4096, shared_dirty=0, private_clean=0, private_dirty=4096, referenced=8192, anonymous=4096, swap=0),
+       pmmap_ext(addr='008eb000-00962000', perms='rw-p', path='/usr/bin/python2.7', rss=417792, size=487424, pss=317440, shared_clean=200704, shared_dirty=0, private_clean=16384, private_dirty=200704, referenced=417792, anonymous=200704, swap=0),
+       pmmap_ext(addr='00962000-00985000', perms='rw-p', path='[anon]', rss=139264, size=143360, pss=139264, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=139264, referenced=139264, anonymous=139264, swap=0),
+       pmmap_ext(addr='02829000-02ccf000', perms='rw-p', path='[heap]', rss=4743168, size=4874240, pss=4743168, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=4743168, referenced=4718592, anonymous=4743168, swap=0),
+       ...]
+
+    Availability: All platforms except OpenBSD, NetBSD and AIX.
 
   .. method:: children(recursive=False)
 
-     Return the children of this process as a list of :Class:`Process` objects,
-     pre-emptively checking whether PID has been reused. If recursive is `True`
-     return all the parent descendants.
-     Example assuming *A == this process*:
-     ::
-
-          A ─┐
-             │
-             ├─ B (child) ─┐
-             │             └─ X (grandchild) ─┐
-             │                                └─ Y (great grandchild)
-             ├─ C (child)
-             └─ D (child)
-
-          >>> p.children()
-          B, C, D
-          >>> p.children(recursive=True)
-          B, X, Y, C, D
-
-     Note that in the example above if process X disappears process Y won't be
-     returned either as the reference to process A is lost.
+    Return the children of this process as a list of :class:`Process`
+    instances.
+    If recursive is `True` return all the parent descendants.
+    Pseudo code example assuming *A == this process*:
+    ::
+
+      A ─┐
+         │
+         ├─ B (child) ─┐
+         │             └─ X (grandchild) ─┐
+         │                                └─ Y (great grandchild)
+         ├─ C (child)
+         └─ D (child)
+
+      >>> p.children()
+      B, C, D
+      >>> p.children(recursive=True)
+      B, X, Y, C, D
+
+    Note that in the example above if process X disappears process Y won't be
+    returned either as the reference to process A is lost.
+    This concept is well summaried by this
+    `unit test <https://github.com/giampaolo/psutil/blob/65a52341b55faaab41f68ebc4ed31f18f0929754/psutil/tests/test_process.py#L1064-L1075>`__.
+    See also how to `kill a process tree <#kill-process-tree>`__ and
+    `terminate my children <#terminate-my-children>`__.
 
   .. method:: open_files()
 
-     Return regular files opened by process as a list of namedtuples including
-     the absolute file name and the file descriptor number (on Windows this is
-     always ``-1``). Example:
-
-     >>> import psutil
-     >>> f = open('file.ext', 'w')
-     >>> p = psutil.Process()
-     >>> p.open_files()
-     [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)]
-
-     .. warning::
-       on Windows this is not fully reliable as due to some limitations of the
-       Windows API the underlying implementation may hang when retrieving
-       certain file handles.
-       In order to work around that psutil on Windows Vista (and higher) spawns
-       a thread and kills it if it's not responding after 100ms.
-       That implies that on Windows this method is not guaranteed to enumerate
-       all regular file handles (see full discusion
-       `here <https://github.com/giampaolo/psutil/pull/597>`_).
-
-     .. warning::
-       on FreeBSD this method can return files with a 'null' path (see
-       `issue 595 <https://github.com/giampaolo/psutil/pull/595>`_).
-
-     .. versionchanged:: 3.1.0 no longer hangs on Windows.
+    Return regular files opened by process as a list of named tuples including
+    the following fields:
+
+    - **path**: the absolute file name.
+    - **fd**: the file descriptor number; on Windows this is always ``-1``.
+
+    Linux only:
+
+    - **position** (*Linux*): the file (offset) position.
+    - **mode** (*Linux*): a string indicating how the file was opened, similarly
+      `open <https://docs.python.org/3/library/functions.html#open>`__'s
+      ``mode`` argument. Possible values are ``'r'``, ``'w'``, ``'a'``,
+      ``'r+'`` and ``'a+'``. There's no distinction between files opened in
+      bynary or text mode (``"b"`` or ``"t"``).
+    - **flags** (*Linux*): the flags which were passed to the underlying
+      `os.open <https://docs.python.org/2/library/os.html#os.open>`__ C call
+      when the file was opened (e.g.
+      `os.O_RDONLY <https://docs.python.org/3/library/os.html#os.O_RDONLY>`__,
+      `os.O_TRUNC <https://docs.python.org/3/library/os.html#os.O_TRUNC>`__,
+      etc).
+
+    >>> import psutil
+    >>> f = open('file.ext', 'w')
+    >>> p = psutil.Process()
+    >>> p.open_files()
+    [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3, position=0, mode='w', flags=32769)]
+
+    .. warning::
+      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.
+      That implies that this method on Windows is not guaranteed to enumerate
+      all regular file handles (see
+      `issue 597 <https://github.com/giampaolo/psutil/pull/597>`_).
+      Also, it will only list files living in the C:\\ drive (see
+      `issue 1020 <https://github.com/giampaolo/psutil/pull/1020>`_).
+
+    .. warning::
+      on BSD this method can return files with a null path ("") due to a
+      kernel bug, hence it's not reliable
+      (see `issue 595 <https://github.com/giampaolo/psutil/pull/595>`_).
+
+    .. versionchanged::
+      3.1.0 no longer hangs on Windows.
+
+    .. versionchanged::
+      4.1.0 new *position*, *mode* and *flags* fields on Linux.
 
   .. method:: connections(kind="inet")
 
-    Return socket connections opened by process as a list of namedtuples.
+    Return socket connections opened by process as a list of named tuples.
     To get system-wide connections use :func:`psutil.net_connections()`.
-    Every namedtuple provides 6 attributes:
+    Every named tuple provides 6 attributes:
 
     - **fd**: the socket file descriptor. This can be passed to
       `socket.fromfd() <http://docs.python.org/library/socket.html#socket.fromfd>`__
       to obtain a usable socket object.
-      This is only available on UNIX; on Windows ``-1`` is always returned.
+      On Windows, FreeBSD and SunOS this is always set to ``-1``.
     - **family**: the address family, either `AF_INET
       <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
       `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
       or `AF_UNIX <http://docs.python.org//library/socket.html#socket.AF_UNIX>`__.
-    - **type**: the address type, either `SOCK_STREAM
-      <http://docs.python.org//library/socket.html#socket.SOCK_STREAM>`__ or
-      `SOCK_DGRAM
-      <http://docs.python.org//library/socket.html#socket.SOCK_DGRAM>`__.
-    - **laddr**: the local address as a ``(ip, port)`` tuple or a ``path``
-      in case of AF_UNIX sockets.
-    - **raddr**: the remote address as a ``(ip, port)`` tuple or an absolute
-      ``path`` in case of UNIX sockets.
+    - **type**: the address type, either
+      `SOCK_STREAM <http://docs.python.org//library/socket.html#socket.SOCK_STREAM>`__ or
+      `SOCK_DGRAM <http://docs.python.org//library/socket.html#socket.SOCK_DGRAM>`__.
+    - **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
+      absolute ``path`` in case of UNIX sockets.
       When the remote endpoint is not connected you'll get an empty tuple
-      (AF_INET) or ``None`` (AF_UNIX).
-      On Linux AF_UNIX sockets will always have this set to ``None``.
+      (AF_INET*) or ``""`` (AF_UNIX). For UNIX sockets see notes below.
     - **status**: represents the status of a TCP connection. The return value
       is one of the :data:`psutil.CONN_* <psutil.CONN_ESTABLISHED>` constants.
       For UDP and UNIX sockets this is always going to be
       :const:`psutil.CONN_NONE`.
 
     The *kind* parameter is a string which filters for connections that fit the
     following criteria:
 
-    .. table::
-
-     +----------------+-----------------------------------------------------+
-     | **Kind value** | **Connections using**                               |
-     +================+=====================================================+
-     | "inet"         | IPv4 and IPv6                                       |
-     +----------------+-----------------------------------------------------+
-     | "inet4"        | IPv4                                                |
-     +----------------+-----------------------------------------------------+
-     | "inet6"        | IPv6                                                |
-     +----------------+-----------------------------------------------------+
-     | "tcp"          | TCP                                                 |
-     +----------------+-----------------------------------------------------+
-     | "tcp4"         | TCP over IPv4                                       |
-     +----------------+-----------------------------------------------------+
-     | "tcp6"         | TCP over IPv6                                       |
-     +----------------+-----------------------------------------------------+
-     | "udp"          | UDP                                                 |
-     +----------------+-----------------------------------------------------+
-     | "udp4"         | UDP over IPv4                                       |
-     +----------------+-----------------------------------------------------+
-     | "udp6"         | UDP over IPv6                                       |
-     +----------------+-----------------------------------------------------+
-     | "unix"         | UNIX socket (both UDP and TCP protocols)            |
-     +----------------+-----------------------------------------------------+
-     | "all"          | the sum of all the possible families and protocols  |
-     +----------------+-----------------------------------------------------+
+    +----------------+-----------------------------------------------------+
+    | **Kind value** | **Connections using**                               |
+    +================+=====================================================+
+    | ``"inet"``     | IPv4 and IPv6                                       |
+    +----------------+-----------------------------------------------------+
+    | ``"inet4"``    | IPv4                                                |
+    +----------------+-----------------------------------------------------+
+    | ``"inet6"``    | IPv6                                                |
+    +----------------+-----------------------------------------------------+
+    | ``"tcp"``      | TCP                                                 |
+    +----------------+-----------------------------------------------------+
+    | ``"tcp4"``     | TCP over IPv4                                       |
+    +----------------+-----------------------------------------------------+
+    | ``"tcp6"``     | TCP over IPv6                                       |
+    +----------------+-----------------------------------------------------+
+    | ``"udp"``      | UDP                                                 |
+    +----------------+-----------------------------------------------------+
+    | ``"udp4"``     | UDP over IPv4                                       |
+    +----------------+-----------------------------------------------------+
+    | ``"udp6"``     | UDP over IPv6                                       |
+    +----------------+-----------------------------------------------------+
+    | ``"unix"``     | UNIX socket (both UDP and TCP protocols)            |
+    +----------------+-----------------------------------------------------+
+    | ``"all"``      | the sum of all the possible families and protocols  |
+    +----------------+-----------------------------------------------------+
 
     Example:
 
       >>> import psutil
       >>> p = psutil.Process(1694)
       >>> p.name()
       'firefox'
       >>> p.connections()
-      [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-       pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-       pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-       pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+      [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'),
+       pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING'),
+       pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=60759), raddr=addr(ip='72.14.234.104', port=80), status='ESTABLISHED'),
+       pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=51314), raddr=addr(ip='72.14.234.83', port=443), status='SYN_SENT')]
+
+    .. note::
+      (Solaris) UNIX sockets are not supported.
+
+    .. note::
+       (Linux, FreeBSD) "raddr" field for UNIX sockets is always set to "".
+       This is a limitation of the OS.
+
+    .. note::
+       (OpenBSD) "laddr" and "raddr" fields for UNIX sockets are always set to
+       "". This is a limitation of the OS.
+
+    .. versionchanged:: 5.3.0 : "laddr" and "raddr" are named tuples.
+
+  .. note::
+    (AIX) :class:`psutil.AccessDenied` is always raised unless running
+    as root (lsof does the same).
 
   .. method:: is_running()
 
-     Return whether the current process is running in the current process list.
-     This is reliable also in case the process is gone and its PID reused by
-     another process, therefore it must be preferred over doing
-     ``psutil.pid_exists(p.pid)``.
-
-     .. note::
+    Return whether the current process is running in the current process list.
+    This is reliable also in case the process is gone and its PID reused by
+    another process, therefore it must be preferred over doing
+    ``psutil.pid_exists(p.pid)``.
+
+    .. note::
       this will return ``True`` also if the process is a zombie
       (``p.status() == psutil.STATUS_ZOMBIE``).
 
   .. method:: send_signal(signal)
 
-     Send a signal to process (see
-     `signal module <http://docs.python.org//library/signal.html>`__
-     constants) pre-emptively checking whether PID has been reused.
-     This is the same as ``os.kill(pid, sig)``.
-     On Windows only **SIGTERM** is valid and is treated as an alias for
-     :meth:`kill()`.
+    Send a signal to process (see
+    `signal module <http://docs.python.org//library/signal.html>`__
+    constants) preemptively checking whether PID has been reused.
+    On UNIX this is the same as ``os.kill(pid, sig)``.
+    On Windows only *SIGTERM*, *CTRL_C_EVENT* and *CTRL_BREAK_EVENT* signals
+    are supported and *SIGTERM* is treated as an alias for :meth:`kill()`.
+    See also how to `kill a process tree <#kill-process-tree>`__ and
+    `terminate my children <#terminate-my-children>`__.
+
+    .. versionchanged::
+      3.2.0 support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals on Windows
+      was added.
 
   .. method:: suspend()
 
-     Suspend process execution with **SIGSTOP** signal pre-emptively checking
-     whether PID has been reused.
-     On UNIX this is the same as ``os.kill(pid, signal.SIGSTOP)``.
-     On Windows this is done by suspending all process threads execution.
+    Suspend process execution with *SIGSTOP* signal preemptively checking
+    whether PID has been reused.
+    On UNIX this is the same as ``os.kill(pid, signal.SIGSTOP)``.
+    On Windows this is done by suspending all process threads execution.
 
   .. method:: resume()
 
-     Resume process execution with **SIGCONT** signal pre-emptively checking
-     whether PID has been reused.
-     On UNIX this is the same as ``os.kill(pid, signal.SIGCONT)``.
-     On Windows this is done by resuming all process threads execution.
+    Resume process execution with *SIGCONT* signal preemptively checking
+    whether PID has been reused.
+    On UNIX this is the same as ``os.kill(pid, signal.SIGCONT)``.
+    On Windows this is done by resuming all process threads execution.
 
   .. method:: terminate()
 
-     Terminate the process with **SIGTERM** signal pre-emptively checking
-     whether PID has been reused.
-     On UNIX this is the same as ``os.kill(pid, signal.SIGTERM)``.
-     On Windows this is an alias for :meth:`kill`.
+    Terminate the process with *SIGTERM* signal preemptively checking
+    whether PID has been reused.
+    On UNIX this is the same as ``os.kill(pid, signal.SIGTERM)``.
+    On Windows this is an alias for :meth:`kill`.
+    See also how to `kill a process tree <#kill-process-tree>`__ and
+    `terminate my children <#terminate-my-children>`__.
 
   .. method:: kill()
 
-     Kill the current process by using **SIGKILL** signal pre-emptively
-     checking whether PID has been reused.
-     On UNIX this is the same as ``os.kill(pid, signal.SIGKILL)``.
-     On Windows this is done by using
-     `TerminateProcess <http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714(v=vs.85).aspx>`__.
+    Kill the current process by using *SIGKILL* signal preemptively
+    checking whether PID has been reused.
+    On UNIX this is the same as ``os.kill(pid, signal.SIGKILL)``.
+    On Windows this is done by using
+    `TerminateProcess <http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714(v=vs.85).aspx>`__.
+    See also how to `kill a process tree <#kill-process-tree>`__ and
+    `terminate my children <#terminate-my-children>`__.
 
   .. method:: wait(timeout=None)
 
-     Wait for process termination and if the process is a children of the
-     current one also return the 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
-     :class:`NoSuchProcess`. If *timeout* is specified and process is still
-     alive raise :class:`TimeoutExpired` exception. It can also be used in a
-     non-blocking fashion by specifying ``timeout=0`` in which case it will
-     either return immediately or raise :class:`TimeoutExpired`.
-     To wait for multiple processes use :func:`psutil.wait_procs()`.
-
+    Wait for process termination and if the process is a child of the current
+    one also return the 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
+    :class:`NoSuchProcess`.
+    *timeout* is expressed in seconds. If specified and the process is still
+    alive raise :class:`TimeoutExpired` exception.
+    ``timeout=0`` can be used in non-blocking apps: it will either return
+    immediately or raise :class:`TimeoutExpired`.
+    To wait for multiple processes use :func:`psutil.wait_procs()`.
+
+    >>> import psutil
+    >>> p = psutil.Process(9891)
+    >>> p.terminate()
+    >>> p.wait()
 
 Popen class
 -----------
 
 .. class:: Popen(*args, **kwargs)
 
   A more convenient interface to stdlib
   `subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__.
-  It starts a sub process and deals with it exactly as when using
+  It starts a sub process and you deal with it exactly as when using
   `subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__
-  but in addition it also provides all the methods of
-  :class:`psutil.Process` class in a single interface.
+  but in addition it also provides all the methods of :class:`psutil.Process`
+  class.
   For method names common to both classes such as
   :meth:`send_signal() <psutil.Process.send_signal()>`,
   :meth:`terminate() <psutil.Process.terminate()>` and
   :meth:`kill() <psutil.Process.kill()>`
   :class:`psutil.Process` implementation takes precedence.
   For a complete documentation refer to
   `subprocess module documentation <http://docs.python.org/library/subprocess.html>`__.
 
   .. note::
 
-     Unlike `subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__
-     this class pre-emptively checks wheter PID has been reused on
-     :meth:`send_signal() <psutil.Process.send_signal()>`,
-     :meth:`terminate() <psutil.Process.terminate()>` and
-     :meth:`kill() <psutil.Process.kill()>`
-     so that you can't accidentally terminate another process, fixing
-     http://bugs.python.org/issue6973.
+    Unlike `subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__
+    this class preemptively checks whether PID has been reused on
+    :meth:`send_signal() <psutil.Process.send_signal()>`,
+    :meth:`terminate() <psutil.Process.terminate()>` and
+    :meth:`kill() <psutil.Process.kill()>`
+    so that you can't accidentally terminate another process, fixing
+    http://bugs.python.org/issue6973.
 
   >>> 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
   >>>
 
+  :class:`psutil.Popen` objects are supported as context managers via the with
+  statement: on exit, standard file descriptors are closed, and the process
+  is waited for. This is supported on all Python versions.
+
+  >>> import psutil, subprocess
+  >>> with psutil.Popen(["ifconfig"], stdout=subprocess.PIPE) as proc:
+  >>>     log.write(proc.stdout.read())
+
+
+  .. versionchanged:: 4.4.0 added context manager support
+
+Windows services
+================
+
+.. function:: win_service_iter()
+
+  Return an iterator yielding a :class:`WindowsService` class instance for all
+  Windows services installed.
+
+  .. versionadded:: 4.2.0
+
+  Availability: Windows
+
+.. function:: win_service_get(name)
+
+  Get a Windows service by name, returning a :class:`WindowsService` instance.
+  Raise :class:`psutil.NoSuchProcess` if no service with such name exists.
+
+  .. versionadded:: 4.2.0
+
+  Availability: Windows
+
+.. class:: WindowsService
+
+  Represents a Windows service with the given *name*. This class is returned
+  by :func:`win_service_iter` and :func:`win_service_get` functions and it is
+  not supposed to be instantiated directly.
+
+  .. method:: name()
+
+    The service name. This string is how a service is referenced and can be
+    passed to :func:`win_service_get` to get a new :class:`WindowsService`
+    instance.
+
+  .. method:: display_name()
+
+    The service display name. The value is cached when this class is
+    instantiated.
+
+  .. method:: binpath()
+
+    The fully qualified path to the service binary/exe file as a string,
+    including command line arguments.
+
+  .. method:: username()
+
+    The name of the user that owns this service.
+
+  .. method:: start_type()
+
+    A string which can either be `"automatic"`, `"manual"` or `"disabled"`.
+
+  .. method:: pid()
+
+    The process PID, if any, else `None`. This can be passed to
+    :class:`Process` class to control the service's process.
+
+  .. method:: status()
+
+    Service status as a string, which may be either `"running"`, `"paused"`,
+    `"start_pending"`, `"pause_pending"`, `"continue_pending"`,
+    `"stop_pending"` or `"stopped"`.
+
+  .. method:: description()
+
+    Service long description.
+
+  .. method:: as_dict()
+
+    Utility method retrieving all the information above as a dictionary.
+
+  .. versionadded:: 4.2.0
+
+  Availability: Windows
+
+Example code:
+
+  >>> import psutil
+  >>> list(psutil.win_service_iter())
+  [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,
+   <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,
+   <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,
+   <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,
+   ...]
+  >>> 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'}
+
 Constants
 =========
 
+.. _const-oses:
+.. data:: POSIX
+.. data:: WINDOWS
+.. data:: LINUX
+.. data:: OSX
+.. data:: FREEBSD
+.. data:: NETBSD
+.. data:: OPENBSD
+.. data:: BSD
+.. data:: SUNOS
+.. data:: AIX
+
+  ``bool`` constants which define what platform you're on. E.g. if on Windows,
+  :const:`WINDOWS` constant will be ``True``, all others will be ``False``.
+
+  .. versionadded:: 4.0.0
+  .. versionchanged:: 5.4.0 added AIX
+
+.. _const-procfs_path:
+.. data:: PROCFS_PATH
+
+  The path of the /proc filesystem on Linux, Solaris and AIX (defaults to
+  ``"/proc"``).
+  You may want to re-set this constant right after importing psutil in case
+  your /proc filesystem is mounted elsewhere or if you want to retrieve
+  information about Linux containers such as
+  `Docker <https://www.docker.io/>`__,
+  `Heroku <https://www.heroku.com/>`__ or
+  `LXC <https://linuxcontainers.org/>`__ (see
+  `here <https://fabiokung.com/2014/03/13/memory-inside-linux-containers/>`__
+  for more info).
+  It must be noted that this trick works only for APIs which rely on /proc
+  filesystem (e.g. `memory`_ APIs and most :class:`Process` class methods).
+
+  Availability: Linux, Solaris, AIX
+
+  .. versionadded:: 3.2.3
+  .. versionchanged:: 3.4.2 also available on Solaris.
+  .. versionchanged:: 5.4.0 also available on AIX.
+
 .. _const-pstatus:
 .. data:: STATUS_RUNNING
-          STATUS_SLEEPING
-          STATUS_DISK_SLEEP
-          STATUS_STOPPED
-          STATUS_TRACING_STOP
-          STATUS_ZOMBIE
-          STATUS_DEAD
-          STATUS_WAKE_KILL
-          STATUS_WAKING
-          STATUS_IDLE
-          STATUS_LOCKED
-          STATUS_WAITING
+.. data:: STATUS_SLEEPING
+.. data:: STATUS_DISK_SLEEP
+.. data:: STATUS_STOPPED
+.. data:: STATUS_TRACING_STOP
+.. data:: STATUS_ZOMBIE
+.. data:: STATUS_DEAD
+.. data:: STATUS_WAKE_KILL
+.. data:: STATUS_WAKING
+.. data:: STATUS_IDLE (OSX, FreeBSD)
+.. data:: STATUS_LOCKED (FreeBSD)
+.. data:: STATUS_WAITING (FreeBSD)
+.. data:: STATUS_SUSPENDED (NetBSD)
 
   A set of strings representing the status of a process.
   Returned by :meth:`psutil.Process.status()`.
 
+  .. versionadded:: 3.4.1 STATUS_SUSPENDED (NetBSD)
+
 .. _const-conn:
 .. data:: CONN_ESTABLISHED
-          CONN_SYN_SENT
-          CONN_SYN_RECV
-          CONN_FIN_WAIT1
-          CONN_FIN_WAIT2
-          CONN_TIME_WAIT
-          CONN_CLOSE
-          CONN_CLOSE_WAIT
-          CONN_LAST_ACK
-          CONN_LISTEN
-          CONN_CLOSING
-          CONN_NONE
-          CONN_DELETE_TCB (Windows)
-          CONN_IDLE (Solaris)
-          CONN_BOUND (Solaris)
+.. data:: CONN_SYN_SENT
+.. data:: CONN_SYN_RECV
+.. data:: CONN_FIN_WAIT1
+.. data:: CONN_FIN_WAIT2
+.. data:: CONN_TIME_WAIT
+.. data:: CONN_CLOSE
+.. data:: CONN_CLOSE_WAIT
+.. data:: CONN_LAST_ACK
+.. data:: CONN_LISTEN
+.. data:: CONN_CLOSING
+.. data:: CONN_NONE
+.. data:: CONN_DELETE_TCB (Windows)
+.. data:: CONN_IDLE (Solaris)
+.. data:: CONN_BOUND (Solaris)
 
   A set of strings representing the status of a TCP connection.
   Returned by :meth:`psutil.Process.connections()` (`status` field).
 
 .. _const-prio:
 .. data:: ABOVE_NORMAL_PRIORITY_CLASS
-          BELOW_NORMAL_PRIORITY_CLASS
-          HIGH_PRIORITY_CLASS
-          IDLE_PRIORITY_CLASS
-          NORMAL_PRIORITY_CLASS
-          REALTIME_PRIORITY_CLASS
+.. data:: BELOW_NORMAL_PRIORITY_CLASS
+.. data:: HIGH_PRIORITY_CLASS
+.. data:: IDLE_PRIORITY_CLASS
+.. data:: NORMAL_PRIORITY_CLASS
+.. data:: REALTIME_PRIORITY_CLASS
 
   A set of integers representing the priority of a process on Windows (see
   `MSDN documentation <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__).
   They can be used in conjunction with
   :meth:`psutil.Process.nice()` to get or set process priority.
 
   Availability: Windows
 
-  .. versionchanged:: 3.0.0 on Python >= 3.4 these constants are
+  .. versionchanged::
+    3.0.0 on Python >= 3.4 these constants are
     `enums <https://docs.python.org/3/library/enum.html#module-enum>`__
     instead of a plain integer.
 
 .. _const-ioprio:
 .. data:: IOPRIO_CLASS_NONE
-          IOPRIO_CLASS_RT
-          IOPRIO_CLASS_BE
-          IOPRIO_CLASS_IDLE
+.. data:: IOPRIO_CLASS_RT
+.. data:: IOPRIO_CLASS_BE
+.. data:: IOPRIO_CLASS_IDLE
 
   A set of integers representing the I/O priority of a process on Linux. They
   can be used in conjunction with :meth:`psutil.Process.ionice()` to get or set
   process I/O priority.
   *IOPRIO_CLASS_NONE* and *IOPRIO_CLASS_BE* (best effort) is the default for
   any process that hasn't set a specific I/O priority.
   *IOPRIO_CLASS_RT* (real time) means the process is given first access to the
   disk, regardless of what else is going on in the system.
@@ -1336,65 +2226,615 @@ Constants
   For further information refer to manuals of
   `ionice <http://linux.die.net/man/1/ionice>`__
   command line utility or
   `ioprio_get <http://linux.die.net/man/2/ioprio_get>`__
   system call.
 
   Availability: Linux
 
-  .. versionchanged:: 3.0.0 on Python >= 3.4 thse constants are
+  .. versionchanged::
+    3.0.0 on Python >= 3.4 these constants are
     `enums <https://docs.python.org/3/library/enum.html#module-enum>`__
     instead of a plain integer.
 
 .. _const-rlimit:
-.. data:: RLIMIT_INFINITY
-          RLIMIT_AS
-          RLIMIT_CORE
-          RLIMIT_CPU
-          RLIMIT_DATA
-          RLIMIT_FSIZE
-          RLIMIT_LOCKS
-          RLIMIT_MEMLOCK
-          RLIMIT_MSGQUEUE
-          RLIMIT_NICE
-          RLIMIT_NOFILE
-          RLIMIT_NPROC
-          RLIMIT_RSS
-          RLIMIT_RTPRIO
-          RLIMIT_RTTIME
-          RLIMIT_RTPRIO
-          RLIMIT_SIGPENDING
-          RLIMIT_STACK
+.. data:: RLIM_INFINITY
+.. data:: RLIMIT_AS
+.. data:: RLIMIT_CORE
+.. data:: RLIMIT_CPU
+.. data:: RLIMIT_DATA
+.. data:: RLIMIT_FSIZE
+.. data:: RLIMIT_LOCKS
+.. data:: RLIMIT_MEMLOCK
+.. data:: RLIMIT_MSGQUEUE
+.. data:: RLIMIT_NICE
+.. data:: RLIMIT_NOFILE
+.. data:: RLIMIT_NPROC
+.. data:: RLIMIT_RSS
+.. data:: RLIMIT_RTPRIO
+.. data:: RLIMIT_RTTIME
+.. data:: RLIMIT_SIGPENDING
+.. data:: RLIMIT_STACK
 
   Constants used for getting and setting process resource limits to be used in
   conjunction with :meth:`psutil.Process.rlimit()`. See
-  `man prlimit <http://linux.die.net/man/2/prlimit>`__ for futher information.
+  `man prlimit <http://linux.die.net/man/2/prlimit>`__ for further information.
 
   Availability: Linux
 
 .. _const-aflink:
 .. data:: AF_LINK
 
   Constant which identifies a MAC address associated with a network interface.
   To be used in conjunction with :func:`psutil.net_if_addrs()`.
 
-  *New in 3.0.0*
+  .. versionadded:: 3.0.0
 
 .. _const-duplex:
 .. data:: NIC_DUPLEX_FULL
-          NIC_DUPLEX_HALF
-          NIC_DUPLEX_UNKNOWN
+.. data:: NIC_DUPLEX_HALF
+.. data:: NIC_DUPLEX_UNKNOWN
 
   Constants which identifies whether a NIC (network interface card) has full or
   half mode speed.  NIC_DUPLEX_FULL means the NIC is able to send and receive
   data (files) simultaneously, NIC_DUPLEX_FULL means the NIC can either send or
   receive data at a time.
   To be used in conjunction with :func:`psutil.net_if_stats()`.
 
-  *New in 3.0.0*
+  .. versionadded:: 3.0.0
+
+.. _const-power:
+.. data:: POWER_TIME_UNKNOWN
+.. data:: POWER_TIME_UNLIMITED
+
+  Whether the remaining time of the battery cannot be determined or is
+  unlimited.
+  May be assigned to :func:`psutil.sensors_battery()`'s *secsleft* field.
+
+  .. versionadded:: 5.1.0
+
+.. _const-version-info:
+.. data:: version_info
+
+  A tuple to check psutil installed version. Example:
+
+      >>> import psutil
+      >>> if psutil.version_info >= (4, 5):
+      ...    pass
+
+----
+
+Unicode
+=======
+
+Starting from version 5.3.0 psutil fully supports unicode, see
+`issue #1040 <https://github.com/giampaolo/psutil/issues/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 OSX, "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
+=======
+
+Follows a collection of utilities and examples which are common but not generic
+enough to be part of the public API.
+
+Find process by name
+--------------------
+
+Check string against :meth:`Process.name()`:
+
+::
+
+  import psutil
+
+  def find_procs_by_name(name):
+      "Return a list of processes matching 'name'."
+      ls = []
+      for p in psutil.process_iter(attrs=['name']):
+          if p.info['name'] == name:
+              ls.append(p)
+      return ls
+
+A bit more advanced, check string against :meth:`Process.name()`,
+:meth:`Process.exe()` and :meth:`Process.cmdline()`:
+
+::
+
+  import os
+  import psutil
+
+  def find_procs_by_name(name):
+      "Return a list of processes matching 'name'."
+      ls = []
+      for p in psutil.process_iter(attrs=["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:
+              ls.append(p)
+      return ls
+
+Kill process tree
+-----------------
+
+::
+
+  import os
+  import signal
+  import psutil
+
+  def kill_proc_tree(pid, sig=signal.SIGTERM, include_parent=True,
+                     timeout=None, on_terminate=None):
+      """Kill a process tree (including grandchildren) with signal
+      "sig" and return a (gone, still_alive) tuple.
+      "on_terminate", if specified, is a callabck function which is
+      called as soon as a child terminates.
+      """
+      if pid == os.getpid():
+          raise RuntimeError("I refuse to kill myself")
+      parent = psutil.Process(pid)
+      children = parent.children(recursive=True)
+      if include_parent:
+          children.append(parent)
+      for p in children:
+          p.send_signal(sig)
+      gone, alive = psutil.wait_procs(children, timeout=timeout,
+                                      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:
+          p.terminate()
+      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)
+              p.kill()
+          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::
+
+  >>> 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()])
+  (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])
+  [(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 file in p.info['open_files'] or []:
+  ...          if os.path.splitext(file.path)[1] == '.log':
+  ...               print("%-5s %-10s %s" % (p.pid, p.info['name'][:10], file.path))
+  ...
+  1510  upstart    /home/giampaolo/.cache/upstart/unity-settings-daemon.log
+  2174  nautilus   /home/giampaolo/.local/share/gvfs-metadata/home-ce08efac.log
+  2650  chrome     /home/giampaolo/.config/google-chrome/Default/data_reduction_proxy_leveldb/000003.log
+
+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])
+  [(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:])
+  [(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})]
+
+FAQs
+====
+
+* Q: What Windows versions are supported?
+* A: From Windows **Vista** onwards, both 32 and 64 bit versions.
+  Latest binary (wheel / exe) release which supports Windows **2000**, **XP**
+  and **2003 server** is
+  `psutil 3.4.2 <https://pypi.python.org/pypi?name=psutil&version=3.4.2&:action=files>`__.
+  On such old systems psutil is no longer tested or maintained, but it can
+  still be compiled from sources (you'll need `Visual Studio <(https://github.com/giampaolo/psutil/blob/master/INSTALL.rst#windows>`__)
+  and it should "work" (more or less).
+
+----
+
+* Q: What Python versions are supported?
+* A: From 2.6 to 3.6, both 32 and 64 bit versions. Last version supporting
+  Python 2.4 and 2.5 is `psutil 2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__.
+  PyPy is also known to work.
+
+----
+
+* Q: What SunOS versions are supported?
+* A: From Solaris 10 onwards.
+
+----
+
+* Q: Why do I get :class:`AccessDenied` for certain processes?
+* A: This may happen when you query processess owned by another user,
+  especially on `OSX <https://github.com/giampaolo/psutil/issues/883>`__ and
+  Windows.
+  Unfortunately there's not much you can do about this except running the
+  Python process with higher privileges.
+  On Unix you may run the the Python process as root or use the SUID bit
+  (this is the trick used by tools such as ``ps`` and ``netstat``).
+  On Windows you may run the Python process as NT AUTHORITY\\SYSTEM or install
+  the Python script as a Windows service (this is the trick used by tools
+  such as ProcessHacker).
+
+----
+
+* Q: What about load average?
+* A: psutil does not expose any load average function as it's already available
+  in python as
+  `os.getloadavg <https://docs.python.org/2/library/os.html#os.getloadavg>`__.
+
+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
 
 Development guide
 =================
 
 If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug)
 take a look at the
 `development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_.
+
+Timeline
+========
+
+- 2018-01-01:
+  `5.4.3 <https://pypi.python.org/pypi?name=psutil&version=5.4.3&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#542>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.2...release-5.4.3#files_bucket>`__
+- 2017-12-07:
+  `5.4.2 <https://pypi.python.org/pypi?name=psutil&version=5.4.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#542>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.1...release-5.4.2#files_bucket>`__
+- 2017-11-08:
+  `5.4.1 <https://pypi.python.org/pypi?name=psutil&version=5.4.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#541>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.4.0...release-5.4.1#files_bucket>`__
+- 2017-10-12:
+  `5.4.0 <https://pypi.python.org/pypi?name=psutil&version=5.4.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#540>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.3.1...release-5.4.0#files_bucket>`__
+- 2017-09-10:
+  `5.3.1 <https://pypi.python.org/pypi?name=psutil&version=5.3.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#530>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.3.0...release-5.3.1#files_bucket>`__
+- 2017-09-01:
+  `5.3.0 <https://pypi.python.org/pypi?name=psutil&version=5.3.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#530>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.2...release-5.3.0#files_bucket>`__
+- 2017-04-10:
+  `5.2.2 <https://pypi.python.org/pypi?name=psutil&version=5.2.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#522>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.1...release-5.2.2#files_bucket>`__
+- 2017-03-24:
+  `5.2.1 <https://pypi.python.org/pypi?name=psutil&version=5.2.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#521>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.2.0...release-5.2.1#files_bucket>`__
+- 2017-03-05:
+  `5.2.0 <https://pypi.python.org/pypi?name=psutil&version=5.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#520>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.3...release-5.2.0#files_bucket>`__
+- 2017-02-07:
+  `5.1.3 <https://pypi.python.org/pypi?name=psutil&version=5.1.3&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#513>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.2...release-5.1.3#files_bucket>`__
+- 2017-02-03:
+  `5.1.2 <https://pypi.python.org/pypi?name=psutil&version=5.1.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#512>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.1...release-5.1.2#files_bucket>`__
+- 2017-02-03:
+  `5.1.1 <https://pypi.python.org/pypi?name=psutil&version=5.1.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#511>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.1.0...release-5.1.1#files_bucket>`__
+- 2017-02-01:
+  `5.1.0 <https://pypi.python.org/pypi?name=psutil&version=5.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#510>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.0.1...release-5.1.0#files_bucket>`__
+- 2016-12-21:
+  `5.0.1 <https://pypi.python.org/pypi?name=psutil&version=5.0.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#501>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.0.0...release-5.0.1#files_bucket>`__
+- 2016-11-06:
+  `5.0.0 <https://pypi.python.org/pypi?name=psutil&version=5.0.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#500>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.2...release-5.0.0#files_bucket>`__
+- 2016-10-05:
+  `4.4.2 <https://pypi.python.org/pypi?name=psutil&version=4.4.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#442>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.1...release-4.4.2#files_bucket>`__
+- 2016-10-25:
+  `4.4.1 <https://pypi.python.org/pypi?name=psutil&version=4.4.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#441>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.4.0...release-4.4.1#files_bucket>`__
+- 2016-10-23:
+  `4.4.0 <https://pypi.python.org/pypi?name=psutil&version=4.4.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#440>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.3.1...release-4.4.0#files_bucket>`__
+- 2016-09-01:
+  `4.3.1 <https://pypi.python.org/pypi?name=psutil&version=4.3.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#431>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.3.0...release-4.3.1#files_bucket>`__
+- 2016-06-18:
+  `4.3.0 <https://pypi.python.org/pypi?name=psutil&version=4.3.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#430>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.2.0...release-4.3.0#files_bucket>`__
+- 2016-05-14:
+  `4.2.0 <https://pypi.python.org/pypi?name=psutil&version=4.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#420>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.1.0...release-4.2.0#files_bucket>`__
+- 2016-03-12:
+  `4.1.0 <https://pypi.python.org/pypi?name=psutil&version=4.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#410>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-4.0.0...release-4.1.0#files_bucket>`__
+- 2016-02-17:
+  `4.0.0 <https://pypi.python.org/pypi?name=psutil&version=4.0.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#400>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.4.2...release-4.0.0#files_bucket>`__
+- 2016-01-20:
+  `3.4.2 <https://pypi.python.org/pypi?name=psutil&version=3.4.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#342>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.4.1...release-3.4.2#files_bucket>`__
+- 2016-01-15:
+  `3.4.1 <https://pypi.python.org/pypi?name=psutil&version=3.4.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#341>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.3.0...release-3.4.1#files_bucket>`__
+- 2015-11-25:
+  `3.3.0 <https://pypi.python.org/pypi?name=psutil&version=3.3.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#330>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.2...release-3.3.0#files_bucket>`__
+- 2015-10-04:
+  `3.2.2 <https://pypi.python.org/pypi?name=psutil&version=3.2.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#322>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.1...release-3.2.2#files_bucket>`__
+- 2015-09-03:
+  `3.2.1 <https://pypi.python.org/pypi?name=psutil&version=3.2.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#321>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.2.0...release-3.2.1#files_bucket>`__
+- 2015-09-02:
+  `3.2.0 <https://pypi.python.org/pypi?name=psutil&version=3.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#320>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.1.1...release-3.2.0#files_bucket>`__
+- 2015-07-15:
+  `3.1.1 <https://pypi.python.org/pypi?name=psutil&version=3.1.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#311>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.1.0...release-3.1.1#files_bucket>`__
+- 2015-07-15:
+  `3.1.0 <https://pypi.python.org/pypi?name=psutil&version=3.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#310>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.0.1...release-3.1.0#files_bucket>`__
+- 2015-06-18:
+  `3.0.1 <https://pypi.python.org/pypi?name=psutil&version=3.0.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#301>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-3.0.0...release-3.0.1#files_bucket>`__
+- 2015-06-13:
+  `3.0.0 <https://pypi.python.org/pypi?name=psutil&version=3.0.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#300>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.2.1...release-3.0.0#files_bucket>`__
+- 2015-02-02:
+  `2.2.1 <https://pypi.python.org/pypi?name=psutil&version=2.2.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#221>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.2.0...release-2.2.1#files_bucket>`__
+- 2015-01-06:
+  `2.2.0 <https://pypi.python.org/pypi?name=psutil&version=2.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#220>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.3...release-2.2.0#files_bucket>`__
+- 2014-09-26:
+  `2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#213>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.2...release-2.1.3#files_bucket>`__
+- 2014-09-21:
+  `2.1.2 <https://pypi.python.org/pypi?name=psutil&version=2.1.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#212>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.1...release-2.1.2#files_bucket>`__
+- 2014-04-30:
+  `2.1.1 <https://pypi.python.org/pypi?name=psutil&version=2.1.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#211>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.1.0...release-2.1.1#files_bucket>`__
+- 2014-04-08:
+  `2.1.0 <https://pypi.python.org/pypi?name=psutil&version=2.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#210>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-2.0.0...release-2.1.0#files_bucket>`__
+- 2014-03-10:
+  `2.0.0 <https://pypi.python.org/pypi?name=psutil&version=2.0.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#200>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.2.1...release-2.0.0#files_bucket>`__
+- 2013-11-25:
+  `1.2.1 <https://pypi.python.org/pypi?name=psutil&version=1.2.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#121>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.2.0...release-1.2.1#files_bucket>`__
+- 2013-11-20:
+  `1.2.0 <https://pypi.python.org/pypi?name=psutil&version=1.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#120>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.2...release-1.2.0#files_bucket>`__
+- 2013-10-22:
+  `1.1.2 <https://pypi.python.org/pypi?name=psutil&version=1.1.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#112>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.1...release-1.1.2#files_bucket>`__
+- 2013-10-08:
+  `1.1.1 <https://pypi.python.org/pypi?name=psutil&version=1.1.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#111>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.1.0...release-1.1.1#files_bucket>`__
+- 2013-09-28:
+  `1.1.0 <https://pypi.python.org/pypi?name=psutil&version=1.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#110>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.0.1...release-1.1.0#files_bucket>`__
+- 2013-07-12:
+  `1.0.1 <https://pypi.python.org/pypi?name=psutil&version=1.0.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#101>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-1.0.0...release-1.0.1#files_bucket>`__
+- 2013-07-10:
+  `1.0.0 <https://pypi.python.org/pypi?name=psutil&version=1.0.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#100>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.7.1...release-1.0.0#files_bucket>`__
+- 2013-05-03:
+  `0.7.1 <https://pypi.python.org/pypi?name=psutil&version=0.7.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#071>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.7.0...release-0.7.1#files_bucket>`__
+- 2013-04-12:
+  `0.7.0 <https://pypi.python.org/pypi?name=psutil&version=0.7.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#070>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.6.1...release-0.7.0#files_bucket>`__
+- 2012-08-16:
+  `0.6.1 <https://pypi.python.org/pypi?name=psutil&version=0.6.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#061>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.6.0...release-0.6.1#files_bucket>`__
+- 2012-08-13:
+  `0.6.0 <https://pypi.python.org/pypi?name=psutil&version=0.6.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#060>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.5.1...release-0.6.0#files_bucket>`__
+- 2012-06-29:
+  `0.5.1 <https://pypi.python.org/pypi?name=psutil&version=0.5.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#051>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.5.0...release-0.5.1#files_bucket>`__
+- 2012-06-27:
+  `0.5.0 <https://pypi.python.org/pypi?name=psutil&version=0.5.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#050>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.4.1...release-0.5.0#files_bucket>`__
+- 2011-12-14:
+  `0.4.1 <https://pypi.python.org/pypi?name=psutil&version=0.4.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#041>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.4.0...release-0.4.1#files_bucket>`__
+- 2011-10-29:
+  `0.4.0 <https://pypi.python.org/pypi?name=psutil&version=0.4.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#040>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.3.0...release-0.4.0#files_bucket>`__
+- 2011-07-08:
+  `0.3.0 <https://pypi.python.org/pypi?name=psutil&version=0.3.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#030>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.2.1...release-0.3.0#files_bucket>`__
+- 2011-03-20:
+  `0.2.1 <https://pypi.python.org/pypi?name=psutil&version=0.2.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#021>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.2.0...release-0.2.1#files_bucket>`__
+- 2010-11-13:
+  `0.2.0 <https://pypi.python.org/pypi?name=psutil&version=0.2.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#020>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.3...release-0.2.0#files_bucket>`__
+- 2010-03-02:
+  `0.1.3 <https://pypi.python.org/pypi?name=psutil&version=0.1.3&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#013>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.2...release-0.1.3#files_bucket>`__
+- 2009-05-06:
+  `0.1.2 <https://pypi.python.org/pypi?name=psutil&version=0.1.2&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#012>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.1...release-0.1.2#files_bucket>`__
+- 2009-03-06:
+  `0.1.1 <https://pypi.python.org/pypi?name=psutil&version=0.1.1&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#011>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-0.1.0...release-0.1.1#files_bucket>`__
+- 2009-01-27:
+  `0.1.0 <https://pypi.python.org/pypi?name=psutil&version=0.1.0&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#010>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/d84cc9a783d977368a64016cdb3568d2c9bceacc...release-0.1.0#files_bucket>`__
--- a/third_party/python/psutil/docs/make.bat
+++ b/third_party/python/psutil/docs/make.bat
@@ -1,242 +1,242 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
-	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
-	:help
-	echo.Please use `make ^<target^>` where ^<target^> is one of
-	echo.  html       to make standalone HTML files
-	echo.  dirhtml    to make HTML files named index.html in directories
-	echo.  singlehtml to make a single large HTML file
-	echo.  pickle     to make pickle files
-	echo.  json       to make JSON files
-	echo.  htmlhelp   to make HTML files and a HTML help project
-	echo.  qthelp     to make HTML files and a qthelp project
-	echo.  devhelp    to make HTML files and a Devhelp project
-	echo.  epub       to make an epub
-	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
-	echo.  text       to make text files
-	echo.  man        to make manual pages
-	echo.  texinfo    to make Texinfo files
-	echo.  gettext    to make PO message catalogs
-	echo.  changes    to make an overview over all changed/added/deprecated items
-	echo.  xml        to make Docutils-native XML files
-	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
-	echo.  linkcheck  to check all external links for integrity
-	echo.  doctest    to run all doctests embedded in the documentation if enabled
-	goto end
-)
-
-if "%1" == "clean" (
-	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
-	del /q /s %BUILDDIR%\*
-	goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
-	exit /b 1
-)
-
-if "%1" == "html" (
-	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
-	goto end
-)
-
-if "%1" == "dirhtml" (
-	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
-	goto end
-)
-
-if "%1" == "singlehtml" (
-	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
-	goto end
-)
-
-if "%1" == "pickle" (
-	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the pickle files.
-	goto end
-)
-
-if "%1" == "json" (
-	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the JSON files.
-	goto end
-)
-
-if "%1" == "htmlhelp" (
-	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
-	goto end
-)
-
-if "%1" == "qthelp" (
-	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
-	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\psutil.qhcp
-	echo.To view the help file:
-	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\psutil.ghc
-	goto end
-)
-
-if "%1" == "devhelp" (
-	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished.
-	goto end
-)
-
-if "%1" == "epub" (
-	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The epub file is in %BUILDDIR%/epub.
-	goto end
-)
-
-if "%1" == "latex" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdf" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf
-	cd %BUILDDIR%/..
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdfja" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf-ja
-	cd %BUILDDIR%/..
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "text" (
-	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The text files are in %BUILDDIR%/text.
-	goto end
-)
-
-if "%1" == "man" (
-	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The manual pages are in %BUILDDIR%/man.
-	goto end
-)
-
-if "%1" == "texinfo" (
-	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
-	goto end
-)
-
-if "%1" == "gettext" (
-	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
-	goto end
-)
-
-if "%1" == "changes" (
-	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.The overview file is in %BUILDDIR%/changes.
-	goto end
-)
-
-if "%1" == "linkcheck" (
-	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
-	goto end
-)
-
-if "%1" == "doctest" (
-	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
-	goto end
-)
-
-if "%1" == "xml" (
-	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The XML files are in %BUILDDIR%/xml.
-	goto end
-)
-
-if "%1" == "pseudoxml" (
-	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
-	goto end
-)
-
-:end
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  xml        to make Docutils-native XML files
+	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyftpdlib.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyftpdlib.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "latexpdf" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	cd %BUILDDIR%/latex
+	make all-pdf
+	cd %BUILDDIR%/..
+	echo.
+	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "latexpdfja" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	cd %BUILDDIR%/latex
+	make all-pdf-ja
+	cd %BUILDDIR%/..
+	echo.
+	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+if "%1" == "xml" (
+	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The XML files are in %BUILDDIR%/xml.
+	goto end
+)
+
+if "%1" == "pseudoxml" (
+	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+	goto end
+)
+
+:end
deleted file mode 100644
--- a/third_party/python/psutil/docs/xxx
+++ /dev/null
@@ -1,11 +0,0 @@
-cpu  1974613 1749 485728 6305758 80280 15 5924 0 0 0
-
-cpu0 519156 374 132999 5977865 72925 10 1458 0 0 0
-
-cpu1 524667 401 125931 108960 2110 4 2214 0 0 0
-
-cpu2 462286 520 117046 109514 2666 0 828 0 0 0
-
-cpu3 468502 453 109750 109418 2578 0 1424 0 0 0
-
-
deleted file mode 100755
--- a/third_party/python/psutil/examples/disk_usage.py
+++ /dev/null
@@ -1,62 +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.
-
-"""
-List all mounted disk partitions a-la "df -h" command.
-
-$ python examples/disk_usage.py
-Device               Total     Used     Free  Use %      Type  Mount
-/dev/sdb3            18.9G    14.7G     3.3G    77%      ext4  /
-/dev/sda6           345.9G    83.8G   244.5G    24%      ext4  /home
-/dev/sda1           296.0M    43.1M   252.9M    14%      vfat  /boot/efi
-/dev/sda2           600.0M   312.4M   287.6M    52%   fuseblk  /media/Recovery
-"""
-
-import sys
-import os
-import psutil
-
-
-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 '%.1f%s' % (value, s)
-    return "%sB" % n
-
-
-def main():
-    templ = "%-17s %8s %8s %8s %5s%% %9s  %s"
-    print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type",
-                   "Mount"))
-    for part in psutil.disk_partitions(all=False):
-        if os.name == 'nt':
-            if 'cdrom' in part.opts or part.fstype == '':
-                # skip cd-rom drives with no disk in it; they may raise
-                # ENOENT, pop-up a Windows GUI error for a non-ready
-                # partition or just hang.
-                continue
-        usage = psutil.disk_usage(part.mountpoint)
-        print(templ % (
-            part.device,
-            bytes2human(usage.total),
-            bytes2human(usage.used),
-            bytes2human(usage.free),
-            int(usage.percent),
-            part.fstype,
-            part.mountpoint))
-
-if __name__ == '__main__':
-    sys.exit(main())
deleted file mode 100755
--- a/third_party/python/psutil/examples/free.py
+++ /dev/null
@@ -1,41 +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.
-
-"""
-A clone of 'free' cmdline utility.
-
-$ python examples/free.py
-             total       used       free     shared    buffers      cache
-Mem:      10125520    8625996    1499524          0     349500    3307836
-Swap:            0          0          0
-"""
-
-import psutil
-
-
-def main():
-    virt = psutil.virtual_memory()
-    swap = psutil.swap_memory()
-    templ = "%-7s %10s %10s %10s %10s %10s %10s"
-    print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache'))
-    print(templ % (
-        'Mem:',
-        int(virt.total / 1024),
-        int(virt.used / 1024),
-        int(virt.free / 1024),
-        int(getattr(virt, 'shared', 0) / 1024),
-        int(getattr(virt, 'buffers', 0) / 1024),
-        int(getattr(virt, 'cached', 0) / 1024)))
-    print(templ % (
-        'Swap:', int(swap.total / 1024),
-        int(swap.used / 1024),
-        int(swap.free / 1024),
-        '',
-        '',
-        ''))
-
-if __name__ == '__main__':
-    main()
deleted file mode 100644
--- a/third_party/python/psutil/examples/ifconfig.py
+++ /dev/null
@@ -1,78 +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.
-
-"""
-A clone of 'ifconfig' on UNIX.
-
-$ python examples/ifconfig.py
-lo (speed=0MB, duplex=?, mtu=65536, up=yes):
-    IPv4     address   : 127.0.0.1
-             broadcast : 127.0.0.1
-             netmask   : 255.0.0.0
-    IPv6     address   : ::1
-             netmask   : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-    MAC      address   : 00:00:00:00:00:00
-             broadcast : 00:00:00:00:00:00
-
-wlan0 (speed=0MB, duplex=?, mtu=1500, up=yes):
-    IPv4     address   : 10.0.3.1
-             broadcast : 10.0.3.255
-             netmask   : 255.255.255.0
-    IPv6     address   : fe80::3005:adff:fe31:8698
-             netmask   : ffff:ffff:ffff:ffff::
-    MAC      address   : 32:05:ad:31:86:98
-             broadcast : ff:ff:ff:ff:ff:ff
-
-eth0 (speed=100MB, duplex=full, mtu=1500, up=yes):
-    IPv4     address   : 192.168.1.2
-             broadcast : 192.168.1.255
-             netmask   : 255.255.255.0
-    IPv6     address   : fe80::c685:8ff:fe45:641
-             netmask   : ffff:ffff:ffff:ffff::
-    MAC      address   : c4:85:08:45:06:41
-             broadcast : ff:ff:ff:ff:ff:ff
-"""
-
-from __future__ import print_function
-import socket
-
-import psutil
-
-
-af_map = {
-    socket.AF_INET: 'IPv4',
-    socket.AF_INET6: 'IPv6',
-    psutil.AF_LINK: 'MAC',
-}
-
-duplex_map = {
-    psutil.NIC_DUPLEX_FULL: "full",
-    psutil.NIC_DUPLEX_HALF: "half",
-    psutil.NIC_DUPLEX_UNKNOWN: "?",
-}
-
-
-def main():
-    stats = psutil.net_if_stats()
-    for nic, addrs in psutil.net_if_addrs().items():
-        if nic in stats:
-            print("%s (speed=%sMB, duplex=%s, mtu=%s, up=%s):" % (
-                nic, stats[nic].speed, duplex_map[stats[nic].duplex],
-                stats[nic].mtu, "yes" if stats[nic].isup else "no"))
-        else:
-            print("%s:" % (nic))
-        for addr in addrs:
-            print("    %-8s" % af_map.get(addr.family, addr.family), end="")
-            print(" address   : %s" % addr.address)
-            if addr.broadcast:
-                print("             broadcast : %s" % addr.broadcast)
-            if addr.netmask:
-                print("             netmask   : %s" % addr.netmask)
-        print("")
-
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/iotop.py
+++ /dev/null
@@ -1,179 +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.
-
-"""
-A clone of iotop (http://guichaz.free.fr/iotop/) showing real time
-disk I/O statistics.
-
-It works on Linux only (FreeBSD and OSX are missing support for IO
-counters).
-It doesn't work on Windows as curses module is required.
-
-Example output:
-
-$ python examples/iotop.py
-Total DISK READ: 0.00 B/s | Total DISK WRITE: 472.00 K/s
-PID   USER      DISK READ  DISK WRITE  COMMAND
-13155 giampao    0.00 B/s  428.00 K/s  /usr/bin/google-chrome-beta
-3260  giampao    0.00 B/s    0.00 B/s  bash
-3779  giampao    0.00 B/s    0.00 B/s  gnome-session --session=ubuntu
-3830  giampao    0.00 B/s    0.00 B/s  /usr/bin/dbus-launch
-3831  giampao    0.00 B/s    0.00 B/s  //bin/dbus-daemon --fork --print-pid 5
-3841  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi-bus-launcher
-3845  giampao    0.00 B/s    0.00 B/s  /bin/dbus-daemon
-3848  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi2-core/at-spi2-registryd
-3862  giampao    0.00 B/s    0.00 B/s  /usr/lib/gnome-settings-daemon
-
-Author: Giampaolo Rodola' <g.rodola@gmail.com>
-"""
-
-import atexit
-import time
-import sys
-try:
-    import curses
-except ImportError:
-    sys.exit('platform not supported')
-
-import psutil
-
-
-# --- curses stuff
-def tear_down():
-    win.keypad(0)
-    curses.nocbreak()
-    curses.echo()
-    curses.endwin()
-
-win = curses.initscr()
-atexit.register(tear_down)
-curses.endwin()
-lineno = 0
-
-
-def print_line(line, highlight=False):
-    """A thin wrapper around curses's addstr()."""
-    global lineno
-    try:
-        if highlight:
-            line += " " * (win.getmaxyx()[1] - len(line))
-            win.addstr(lineno, 0, line, curses.A_REVERSE)
-        else:
-            win.addstr(lineno, 0, line, 0)
-    except curses.error:
-        lineno = 0
-        win.refresh()
-        raise
-    else:
-        lineno += 1
-# --- /curses stuff
-
-
-def bytes2human(n):
-    """
-    >>> bytes2human(10000)
-    '9.8 K/s'
-    >>> bytes2human(100001221)
-    '95.4 M/s'
-    """
-    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/s' % (value, s)
-    return '%.2f B/s' % (n)
-
-
-def poll(interval):
-    """Calculate IO usage by comparing IO statics before and
-    after the interval.
-    Return a tuple including all currently running processes
-    sorted by IO activity and total disks I/O activity.
-    """
-    # first get a list of all processes and disk io counters
-    procs = [p for p in psutil.process_iter()]
-    for p in procs[:]:
-        try:
-            p._before = p.io_counters()
-        except psutil.Error:
-            procs.remove(p)
-            continue
-    disks_before = psutil.disk_io_counters()
-
-    # sleep some time
-    time.sleep(interval)
-
-    # then retrieve the same info again
-    for p in procs[:]:
-        try:
-            p._after = p.io_counters()
-            p._cmdline = ' '.join(p.cmdline())
-            if not p._cmdline:
-                p._cmdline = p.name()
-            p._username = p.username()
-        except (psutil.NoSuchProcess, psutil.ZombieProcess):
-            procs.remove(p)
-    disks_after = psutil.disk_io_counters()
-
-    # finally calculate results by comparing data before and
-    # after the interval
-    for p in procs:
-        p._read_per_sec = p._after.read_bytes - p._before.read_bytes
-        p._write_per_sec = p._after.write_bytes - p._before.write_bytes
-        p._total = p._read_per_sec + p._write_per_sec
-
-    disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes
-    disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes
-
-    # sort processes by total disk IO so that the more intensive
-    # ones get listed first
-    processes = sorted(procs, key=lambda p: p._total, reverse=True)
-
-    return (processes, disks_read_per_sec, disks_write_per_sec)
-
-
-def refresh_window(procs, disks_read, disks_write):
-    """Print results on screen by using curses."""
-    curses.endwin()
-    templ = "%-5s %-7s %11s %11s  %s"
-    win.erase()
-
-    disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \
-                % (bytes2human(disks_read), bytes2human(disks_write))
-    print_line(disks_tot)
-
-    header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND")
-    print_line(header, highlight=True)
-
-    for p in procs:
-        line = templ % (
-            p.pid,
-            p._username[:7],
-            bytes2human(p._read_per_sec),
-            bytes2human(p._write_per_sec),
-            p._cmdline)
-        try:
-            print_line(line)
-        except curses.error:
-            break
-    win.refresh()
-
-
-def main():
-    try:
-        interval = 0
-        while True:
-            args = poll(interval)
-            refresh_window(*args)
-            interval = 1
-    except (KeyboardInterrupt, SystemExit):
-        pass
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/killall.py
+++ /dev/null
@@ -1,32 +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.
-
-"""
-Kill a process by name.
-"""
-
-import os
-import sys
-import psutil
-
-
-def main():
-    if len(sys.argv) != 2:
-        sys.exit('usage: %s name' % __file__)
-    else:
-        NAME = sys.argv[1]
-
-    killed = []
-    for proc in psutil.process_iter():
-        if proc.name() == NAME and proc.pid != os.getpid():
-            proc.kill()
-            killed.append(proc.pid)
-    if not killed:
-        sys.exit('%s: no process found' % NAME)
-    else:
-        sys.exit(0)
-
-sys.exit(main())
deleted file mode 100755
--- a/third_party/python/psutil/examples/meminfo.py
+++ /dev/null
@@ -1,68 +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.
-
-"""
-Print system memory information.
-
-$ python examples/meminfo.py
-MEMORY
-------
-Total      :    9.7G
-Available  :    4.9G
-Percent    :    49.0
-Used       :    8.2G
-Free       :    1.4G
-Active     :    5.6G
-Inactive   :    2.1G
-Buffers    :  341.2M
-Cached     :    3.2G
-
-SWAP
-----
-Total      :      0B
-Used       :      0B
-Free       :      0B
-Percent    :     0.0
-Sin        :      0B
-Sout       :      0B
-"""
-
-import psutil
-
-
-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 '%.1f%s' % (value, s)
-    return "%sB" % n
-
-
-def pprint_ntuple(nt):
-    for name in nt._fields:
-        value = getattr(nt, name)
-        if name != 'percent':
-            value = bytes2human(value)
-        print('%-10s : %7s' % (name.capitalize(), value))
-
-
-def main():
-    print('MEMORY\n------')
-    pprint_ntuple(psutil.virtual_memory())
-    print('\nSWAP\n----')
-    pprint_ntuple(psutil.swap_memory())
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/netstat.py
+++ /dev/null
@@ -1,64 +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.
-
-"""
-A clone of 'netstat -antp' on Linux.
-
-$ python examples/netstat.py
-Proto Local address      Remote address   Status        PID    Program name
-tcp   127.0.0.1:48256    127.0.0.1:45884  ESTABLISHED   13646  chrome
-tcp   127.0.0.1:47073    127.0.0.1:45884  ESTABLISHED   13646  chrome
-tcp   127.0.0.1:47072    127.0.0.1:45884  ESTABLISHED   13646  chrome
-tcp   127.0.0.1:45884    -                LISTEN        13651  GoogleTalkPlugi
-tcp   127.0.0.1:60948    -                LISTEN        13651  GoogleTalkPlugi
-tcp   172.17.42.1:49102  127.0.0.1:19305  CLOSE_WAIT    13651  GoogleTalkPlugi
-tcp   172.17.42.1:55797  127.0.0.1:443    CLOSE_WAIT    13651  GoogleTalkPlugi
-...
-"""
-
-import socket
-from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
-
-import psutil
-
-
-AD = "-"
-AF_INET6 = getattr(socket, 'AF_INET6', object())
-proto_map = {
-    (AF_INET, SOCK_STREAM): 'tcp',
-    (AF_INET6, SOCK_STREAM): 'tcp6',
-    (AF_INET, SOCK_DGRAM): 'udp',
-    (AF_INET6, SOCK_DGRAM): 'udp6',
-}
-
-
-def main():
-    templ = "%-5s %-30s %-30s %-13s %-6s %s"
-    print(templ % (
-        "Proto", "Local address", "Remote address", "Status", "PID",
-        "Program name"))
-    proc_names = {}
-    for p in psutil.process_iter():
-        try:
-            proc_names[p.pid] = p.name()
-        except psutil.Error:
-            pass
-    for c in psutil.net_connections(kind='inet'):
-        laddr = "%s:%s" % (c.laddr)
-        raddr = ""
-        if c.raddr:
-            raddr = "%s:%s" % (c.raddr)
-        print(templ % (
-            proto_map[(c.family, c.type)],
-            laddr,
-            raddr or AD,
-            c.status,
-            c.pid or AD,
-            proc_names.get(c.pid, '?')[:15],
-        ))
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/nettop.py
+++ /dev/null
@@ -1,165 +0,0 @@
-#!/usr/bin/env python
-#
-# $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $
-#
-# 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.
-
-"""
-Shows real-time network statistics.
-
-Author: Giampaolo Rodola' <g.rodola@gmail.com>
-
-$ python examples/nettop.py
------------------------------------------------------------
-total bytes:           sent: 1.49 G       received: 4.82 G
-total packets:         sent: 7338724      received: 8082712
-
-wlan0                     TOTAL         PER-SEC
------------------------------------------------------------
-bytes-sent               1.29 G        0.00 B/s
-bytes-recv               3.48 G        0.00 B/s
-pkts-sent               7221782               0
-pkts-recv               6753724               0
-
-eth1                      TOTAL         PER-SEC
------------------------------------------------------------
-bytes-sent             131.77 M        0.00 B/s
-bytes-recv               1.28 G        0.00 B/s
-pkts-sent                     0               0
-pkts-recv               1214470               0
-"""
-
-import atexit
-import time
-import sys
-try:
-    import curses
-except ImportError:
-    sys.exit('platform not supported')
-
-import psutil
-
-
-# --- curses stuff
-def tear_down():
-    win.keypad(0)
-    curses.nocbreak()
-    curses.echo()
-    curses.endwin()
-
-win = curses.initscr()
-atexit.register(tear_down)
-curses.endwin()
-lineno = 0
-
-
-def print_line(line, highlight=False):
-    """A thin wrapper around curses's addstr()."""
-    global lineno
-    try:
-        if highlight:
-            line += " " * (win.getmaxyx()[1] - len(line))
-            win.addstr(lineno, 0, line, curses.A_REVERSE)
-        else:
-            win.addstr(lineno, 0, line, 0)
-    except curses.error:
-        lineno = 0
-        win.refresh()
-        raise
-    else:
-        lineno += 1
-# --- curses stuff
-
-
-def bytes2human(n):
-    """
-    >>> bytes2human(10000)
-    '9.8 K'
-    >>> bytes2human(100001221)
-    '95.4 M'
-    """
-    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 '%.2f B' % (n)
-
-
-def poll(interval):
-    """Retrieve raw stats within an interval window."""
-    tot_before = psutil.net_io_counters()
-    pnic_before = psutil.net_io_counters(pernic=True)
-    # sleep some time
-    time.sleep(interval)
-    tot_after = psutil.net_io_counters()
-    pnic_after = psutil.net_io_counters(pernic=True)
-    return (tot_before, tot_after, pnic_before, pnic_after)
-
-
-def refresh_window(tot_before, tot_after, pnic_before, pnic_after):
-    """Print stats on screen."""
-    global lineno
-
-    # totals
-    print_line("total bytes:           sent: %-10s   received: %s" % (
-        bytes2human(tot_after.bytes_sent),
-        bytes2human(tot_after.bytes_recv))
-    )
-    print_line("total packets:         sent: %-10s   received: %s" % (
-        tot_after.packets_sent, tot_after.packets_recv))
-
-    # per-network interface details: let's sort network interfaces so
-    # that the ones which generated more traffic are shown first
-    print_line("")
-    nic_names = list(pnic_after.keys())
-    nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True)
-    for name in nic_names:
-        stats_before = pnic_before[name]
-        stats_after = pnic_after[name]
-        templ = "%-15s %15s %15s"
-        print_line(templ % (name, "TOTAL", "PER-SEC"), highlight=True)
-        print_line(templ % (
-            "bytes-sent",
-            bytes2human(stats_after.bytes_sent),
-            bytes2human(
-                stats_after.bytes_sent - stats_before.bytes_sent) + '/s',
-        ))
-        print_line(templ % (
-            "bytes-recv",
-            bytes2human(stats_after.bytes_recv),
-            bytes2human(
-                stats_after.bytes_recv - stats_before.bytes_recv) + '/s',
-        ))
-        print_line(templ % (
-            "pkts-sent",
-            stats_after.packets_sent,
-            stats_after.packets_sent - stats_before.packets_sent,
-        ))
-        print_line(templ % (
-            "pkts-recv",
-            stats_after.packets_recv,
-            stats_after.packets_recv - stats_before.packets_recv,
-        ))
-        print_line("")
-    win.refresh()
-    lineno = 0
-
-
-def main():
-    try:
-        interval = 0
-        while True:
-            args = poll(interval)
-            refresh_window(*args)
-            interval = 1
-    except (KeyboardInterrupt, SystemExit):
-        pass
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/pidof.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""
-A clone of 'pidof' cmdline utility.
-$ pidof python
-1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119
-"""
-
-from __future__ import print_function
-import psutil
-import sys
-
-
-def pidof(pgname):
-    pids = []
-    for proc in psutil.process_iter():
-        # search for matches in the process name and cmdline
-        try:
-            name = proc.name()
-        except psutil.Error:
-            pass
-        else:
-            if name == pgname:
-                pids.append(str(proc.pid))
-                continue
-
-        try:
-            cmdline = proc.cmdline()
-        except psutil.Error:
-            pass
-        else:
-            if cmdline and cmdline[0] == pgname:
-                pids.append(str(proc.pid))
-
-    return pids
-
-
-def main():
-    if len(sys.argv) != 2:
-        sys.exit('usage: %s pgname' % __file__)
-    else:
-        pgname = sys.argv[1]
-    pids = pidof(pgname)
-    if pids:
-        print(" ".join(pids))
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/pmap.py
+++ /dev/null
@@ -1,57 +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.
-
-"""
-A clone of 'pmap' utility on Linux, 'vmmap' on OSX and 'procstat -v' on BSD.
-Report memory map of a process.
-
-$ python examples/pmap.py 32402
-pid=32402, name=hg
-Address                 RSS  Mode    Mapping
-0000000000400000      1200K  r-xp    /usr/bin/python2.7
-0000000000838000         4K  r--p    /usr/bin/python2.7
-0000000000839000       304K  rw-p    /usr/bin/python2.7
-00000000008ae000        68K  rw-p    [anon]
-000000000275e000      5396K  rw-p    [heap]
-00002b29bb1e0000       124K  r-xp    /lib/x86_64-linux-gnu/ld-2.17.so
-00002b29bb203000         8K  rw-p    [anon]
-00002b29bb220000       528K  rw-p    [anon]
-00002b29bb2d8000       768K  rw-p    [anon]
-00002b29bb402000         4K  r--p    /lib/x86_64-linux-gnu/ld-2.17.so
-00002b29bb403000         8K  rw-p    /lib/x86_64-linux-gnu/ld-2.17.so
-00002b29bb405000        60K  r-xp    /lib/x86_64-linux-gnu/libpthread-2.17.so
-00002b29bb41d000         0K  ---p    /lib/x86_64-linux-gnu/libpthread-2.17.so
-00007fff94be6000        48K  rw-p    [stack]
-00007fff94dd1000         4K  r-xp    [vdso]
-ffffffffff600000         0K  r-xp    [vsyscall]
-...
-"""
-
-import sys
-
-import psutil
-
-
-def main():
-    if len(sys.argv) != 2:
-        sys.exit('usage: pmap <pid>')
-    p = psutil.Process(int(sys.argv[1]))
-    print("pid=%s, name=%s" % (p.pid, p.name()))
-    templ = "%-16s %10s  %-7s %s"
-    print(templ % ("Address", "RSS", "Mode", "Mapping"))
-    total_rss = 0
-    for m in p.memory_maps(grouped=False):
-        total_rss += m.rss
-        print(templ % (
-            m.addr.split('-')[0].zfill(16),
-            str(m.rss / 1024) + 'K',
-            m.perms,
-            m.path))
-    print("-" * 33)
-    print(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/process_detail.py
+++ /dev/null
@@ -1,167 +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.
-
-"""
-Print detailed information about a process.
-Author: Giampaolo Rodola' <g.rodola@gmail.com>
-
-$ python examples/process_detail.py
-pid               820
-name              python
-exe               /usr/bin/python2.7
-parent            29613 (bash)
-cmdline           python examples/process_detail.py
-started           2014-41-27 03:41
-user              giampaolo
-uids              real=1000, effective=1000, saved=1000
-gids              real=1000, effective=1000, saved=1000
-terminal          /dev/pts/17
-cwd               /ssd/svn/psutil
-memory            0.1% (resident=10.6M, virtual=58.5M)
-cpu               0.0% (user=0.09, system=0.0)
-status            running
-niceness          0
-num threads       1
-I/O               bytes-read=0B, bytes-written=0B
-open files
-running threads   id=820, user-time=0.09, sys-time=0.0
-"""
-
-import datetime
-import os
-import socket
-import sys
-
-import psutil
-
-
-POSIX = os.name == 'posix'
-
-
-def convert_bytes(n):
-    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 '%.1f%s' % (value, s)
-    return "%sB" % n
-
-
-def print_(a, b):
-    if sys.stdout.isatty() and POSIX:
-        fmt = '\x1b[1;32m%-17s\x1b[0m %s' % (a, b)
-    else:
-        fmt = '%-15s %s' % (a, b)
-    # python 2/3 compatibility layer
-    sys.stdout.write(fmt + '\n')
-    sys.stdout.flush()
-
-
-def run(pid):
-    ACCESS_DENIED = ''
-    try:
-        p = psutil.Process(pid)
-        pinfo = p.as_dict(ad_value=ACCESS_DENIED)
-    except psutil.NoSuchProcess as err:
-        sys.exit(str(err))
-
-    try:
-        parent = p.parent()
-        if parent:
-            parent = '(%s)' % parent.name()
-        else:
-            parent = ''
-    except psutil.Error:
-        parent = ''
-    if pinfo['create_time'] != ACCESS_DENIED:
-        started = datetime.datetime.fromtimestamp(
-            pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
-    else:
-        started = ACCESS_DENIED
-    io = pinfo.get('io_counters', ACCESS_DENIED)
-    if pinfo['memory_info'] != ACCESS_DENIED:
-        mem = '%s%% (resident=%s, virtual=%s) ' % (
-            round(pinfo['memory_percent'], 1),
-            convert_bytes(pinfo['memory_info'].rss),
-            convert_bytes(pinfo['memory_info'].vms))
-    else:
-        mem = ACCESS_DENIED
-    children = p.children()
-
-    print_('pid', pinfo['pid'])
-    print_('name', pinfo['name'])
-    print_('exe', pinfo['exe'])
-    print_('parent', '%s %s' % (pinfo['ppid'], parent))
-    print_('cmdline', ' '.join(pinfo['cmdline']))
-    print_('started', started)
-    print_('user', pinfo['username'])
-    if POSIX and pinfo['uids'] and pinfo['gids']:
-        print_('uids', 'real=%s, effective=%s, saved=%s' % pinfo['uids'])
-    if POSIX and pinfo['gids']:
-        print_('gids', 'real=%s, effective=%s, saved=%s' % pinfo['gids'])
-    if POSIX:
-        print_('terminal', pinfo['terminal'] or '')
-    print_('cwd', pinfo['cwd'])
-    print_('memory', mem)
-    print_('cpu', '%s%% (user=%s, system=%s)' % (
-        pinfo['cpu_percent'],
-        getattr(pinfo['cpu_times'], 'user', '?'),
-        getattr(pinfo['cpu_times'], 'system', '?')))
-    print_('status', pinfo['status'])
-    print_('niceness', pinfo['nice'])
-    print_('num threads', pinfo['num_threads'])
-    if io != ACCESS_DENIED:
-        print_('I/O', 'bytes-read=%s, bytes-written=%s' % (
-            convert_bytes(io.read_bytes),
-            convert_bytes(io.write_bytes)))
-    if children:
-        print_('children', '')
-        for child in children:
-            print_('', 'pid=%s name=%s' % (child.pid, child.name()))
-
-    if pinfo['open_files'] != ACCESS_DENIED:
-        print_('open files', '')
-        for file in pinfo['open_files']:
-            print_('', 'fd=%s %s ' % (file.fd, file.path))
-
-    if pinfo['threads']:
-        print_('running threads', '')
-        for thread in pinfo['threads']:
-            print_('', 'id=%s, user-time=%s, sys-time=%s' % (
-                thread.id, thread.user_time, thread.system_time))
-    if pinfo['connections'] not in (ACCESS_DENIED, []):
-        print_('open connections', '')
-        for conn in pinfo['connections']:
-            if conn.type == socket.SOCK_STREAM:
-                type = 'TCP'
-            elif conn.type == socket.SOCK_DGRAM:
-                type = 'UDP'
-            else:
-                type = 'UNIX'
-            lip, lport = conn.laddr
-            if not conn.raddr:
-                rip, rport = '*', '*'
-            else:
-                rip, rport = conn.raddr
-            print_('', '%s:%s -> %s:%s type=%s status=%s' % (
-                lip, lport, rip, rport, type, conn.status))
-
-
-def main(argv=None):
-    if argv is None:
-        argv = sys.argv
-    if len(argv) == 1:
-        sys.exit(run(os.getpid()))
-    elif len(argv) == 2:
-        sys.exit(run(int(argv[1])))
-    else:
-        sys.exit('usage: %s [pid]' % __file__)
-
-if __name__ == '__main__':
-    sys.exit(main())
deleted file mode 100644
--- a/third_party/python/psutil/examples/ps.py
+++ /dev/null
@@ -1,81 +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.
-
-"""
-A clone of 'ps -aux' on UNIX.
-
-$ python examples/ps.py
-...
-"""
-
-import datetime
-import os
-import time
-
-import psutil
-
-
-def main():
-    today_day = datetime.date.today()
-    templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s  %s"
-    attrs = ['pid', 'cpu_percent', 'memory_percent', 'name', 'cpu_times',
-             'create_time', 'memory_info']
-    if os.name == 'posix':
-        attrs.append('uids')
-        attrs.append('terminal')
-    print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
-                   "START", "TIME", "COMMAND"))
-    for p in psutil.process_iter():
-        try:
-            pinfo = p.as_dict(attrs, ad_value='')
-        except psutil.NoSuchProcess:
-            pass
-        else:
-            if pinfo['create_time']:
-                ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
-                if ctime.date() == today_day:
-                    ctime = ctime.strftime("%H:%M")
-                else:
-                    ctime = ctime.strftime("%b%d")
-            else:
-                ctime = ''
-            cputime = time.strftime("%M:%S",
-                                    time.localtime(sum(pinfo['cpu_times'])))
-            try:
-                user = p.username()
-            except KeyError:
-                if os.name == 'posix':
-                    if pinfo['uids']:
-                        user = str(pinfo['uids'].real)
-                    else:
-                        user = ''
-                else:
-                    raise
-            except psutil.Error:
-                user = ''
-            if os.name == 'nt' and '\\' in user:
-                user = user.split('\\')[1]
-            vms = pinfo['memory_info'] and \
-                int(pinfo['memory_info'].vms / 1024) or '?'
-            rss = pinfo['memory_info'] and \
-                int(pinfo['memory_info'].rss / 1024) or '?'
-            memp = pinfo['memory_percent'] and \
-                round(pinfo['memory_percent'], 1) or '?'
-            print(templ % (
-                user[:10],
-                pinfo['pid'],
-                pinfo['cpu_percent'],
-                memp,
-                vms,
-                rss,
-                pinfo.get('terminal', '') or '?',
-                ctime,
-                cputime,
-                pinfo['name'].strip() or '?'))
-
-
-if __name__ == '__main__':
-    main()
deleted file mode 100644
--- a/third_party/python/psutil/examples/pstree.py
+++ /dev/null
@@ -1,71 +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.
-
-"""
-Similar to 'ps aux --forest' on Linux, prints the process list
-as a tree structure.
-
-$ python examples/pstree.py
-0 ?
-|- 1 init
-| |- 289 cgmanager
-| |- 616 upstart-socket-bridge
-| |- 628 rpcbind
-| |- 892 upstart-file-bridge
-| |- 907 dbus-daemon
-| |- 978 avahi-daemon
-| | `_ 979 avahi-daemon
-| |- 987 NetworkManager
-| | |- 2242 dnsmasq
-| | `_ 10699 dhclient
-| |- 993 polkitd
-| |- 1061 getty
-| |- 1066 su
-| | `_ 1190 salt-minion...
-...
-"""
-
-from __future__ import print_function
-import collections
-import sys
-
-import psutil
-
-
-def print_tree(parent, tree, indent=''):
-    try:
-        name = psutil.Process(parent).name()
-    except psutil.Error:
-        name = "?"
-    print(parent, name)
-    if parent not in tree:
-        return
-    children = tree[parent][:-1]
-    for child in children:
-        sys.stdout.write(indent + "|- ")
-        print_tree(child, tree, indent + "| ")
-    child = tree[parent][-1]
-    sys.stdout.write(indent + "`_ ")
-    print_tree(child, tree, indent + "  ")
-
-
-def main():
-    # construct a dict where 'values' are all the processes
-    # having 'key' as their parent
-    tree = collections.defaultdict(list)
-    for p in psutil.process_iter():
-        try:
-            tree[p.ppid()].append(p.pid)
-        except (psutil.NoSuchProcess, psutil.ZombieProcess):
-            pass
-    # on systems supporting PID 0, PID 0's parent is usually 0
-    if 0 in tree and 0 in tree[0]:
-        tree[0].remove(0)
-    print_tree(min(tree), tree)
-
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/top.py
+++ /dev/null
@@ -1,233 +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.
-
-"""
-A clone of top / htop.
-
-Author: Giampaolo Rodola' <g.rodola@gmail.com>
-
-$ python examples/top.py
- CPU0  [|                                       ]   4.9%
- CPU1  [|||                                     ]   7.8%
- CPU2  [                                        ]   2.0%
- CPU3  [|||||                                   ]  13.9%
- Mem   [|||||||||||||||||||                     ]  49.8%  4920M/9888M
- Swap  [                                        ]   0.0%     0M/0M
- Processes: 287 (running=1 sleeping=286)
- Load average: 0.34 0.54 0.46  Uptime: 3 days, 10:16:37
-
-PID    USER       NI  VIRT   RES   CPU% MEM%     TIME+  NAME
-------------------------------------------------------------
-989    giampaol    0   66M   12M    7.4  0.1   0:00.61  python
-2083   root        0  506M  159M    6.5  1.6   0:29.26  Xorg
-4503   giampaol    0  599M   25M    6.5  0.3   3:32.60  gnome-terminal
-3868   giampaol    0  358M    8M    2.8  0.1  23:12.60  pulseaudio
-3936   giampaol    0    1G  111M    2.8  1.1  33:41.67  compiz
-4401   giampaol    0  536M  141M    2.8  1.4  35:42.73  skype
-4047   giampaol    0  743M   76M    1.8  0.8  42:03.33  unity-panel-service
-13155  giampaol    0    1G  280M    1.8  2.8  41:57.34  chrome
-10     root        0    0B    0B    0.9  0.0   4:01.81  rcu_sched
-339    giampaol    0    1G  113M    0.9  1.1   8:15.73  chrome
-...
-"""
-
-from datetime import datetime, timedelta
-import atexit
-import os
-import time
-import sys
-try:
-    import curses
-except ImportError:
-    sys.exit('platform not supported')
-
-import psutil
-
-
-# --- curses stuff
-def tear_down():
-    win.keypad(0)
-    curses.nocbreak()
-    curses.echo()
-    curses.endwin()
-
-win = curses.initscr()
-atexit.register(tear_down)
-curses.endwin()
-lineno = 0
-
-
-def print_line(line, highlight=False):
-    """A thin wrapper around curses's addstr()."""
-    global lineno
-    try:
-        if highlight:
-            line += " " * (win.getmaxyx()[1] - len(line))
-            win.addstr(lineno, 0, line, curses.A_REVERSE)
-        else:
-            win.addstr(lineno, 0, line, 0)
-    except curses.error:
-        lineno = 0
-        win.refresh()
-        raise
-    else:
-        lineno += 1
-# --- /curses stuff
-
-
-def bytes2human(n):
-    """
-    >>> bytes2human(10000)
-    '9K'
-    >>> bytes2human(100001221)
-    '95M'
-    """
-    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 = int(float(n) / prefix[s])
-            return '%s%s' % (value, s)
-    return "%sB" % n
-
-
-def poll(interval):
-    # sleep some time
-    time.sleep(interval)
-    procs = []
-    procs_status = {}
-    for p in psutil.process_iter():
-        try:
-            p.dict = p.as_dict(['username', 'nice', 'memory_info',
-                                'memory_percent', 'cpu_percent',
-                                'cpu_times', 'name', 'status'])
-            try:
-                procs_status[p.dict['status']] += 1
-            except KeyError:
-                procs_status[p.dict['status']] = 1
-        except psutil.NoSuchProcess:
-            pass
-        else:
-            procs.append(p)
-
-    # return processes sorted by CPU percent usage
-    processes = sorted(procs, key=lambda p: p.dict['cpu_percent'],
-                       reverse=True)
-    return (processes, procs_status)
-
-
-def print_header(procs_status, num_procs):
-    """Print system-related info, above the process list."""
-
-    def get_dashes(perc):
-        dashes = "|" * int((float(perc) / 10 * 4))
-        empty_dashes = " " * (40 - len(dashes))
-        return dashes, empty_dashes
-
-    # cpu usage
-    percs = psutil.cpu_percent(interval=0, percpu=True)
-    for cpu_num, perc in enumerate(percs):
-        dashes, empty_dashes = get_dashes(perc)
-        print_line(" CPU%-2s [%s%s] %5s%%" % (cpu_num, dashes, empty_dashes,
-                                              perc))
-    mem = psutil.virtual_memory()
-    dashes, empty_dashes = get_dashes(mem.percent)
-    used = mem.total - mem.available
-    line = " Mem   [%s%s] %5s%% %6s/%s" % (
-        dashes, empty_dashes,
-        mem.percent,
-        str(int(used / 1024 / 1024)) + "M",
-        str(int(mem.total / 1024 / 1024)) + "M"
-    )
-    print_line(line)
-
-    # swap usage
-    swap = psutil.swap_memory()
-    dashes, empty_dashes = get_dashes(swap.percent)
-    line = " Swap  [%s%s] %5s%% %6s/%s" % (
-        dashes, empty_dashes,
-        swap.percent,
-        str(int(swap.used / 1024 / 1024)) + "M",
-        str(int(swap.total / 1024 / 1024)) + "M"
-    )
-    print_line(line)
-
-    # processes number and status
-    st = []
-    for x, y in procs_status.items():
-        if y:
-            st.append("%s=%s" % (x, y))
-    st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1)
-    print_line(" Processes: %s (%s)" % (num_procs, ' '.join(st)))
-    # load average, uptime
-    uptime = datetime.now() - datetime.fromtimestamp(psutil.boot_time())
-    av1, av2, av3 = os.getloadavg()
-    line = " Load average: %.2f %.2f %.2f  Uptime: %s" \
-        % (av1, av2, av3, str(uptime).split('.')[0])
-    print_line(line)
-
-
-def refresh_window(procs, procs_status):
-    """Print results on screen by using curses."""
-    curses.endwin()
-    templ = "%-6s %-8s %4s %5s %5s %6s %4s %9s  %2s"
-    win.erase()
-    header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%",
-                      "TIME+", "NAME")
-    print_header(procs_status, len(procs))
-    print_line("")
-    print_line(header, highlight=True)
-    for p in procs:
-        # TIME+ column shows process CPU cumulative time and it
-        # is expressed as: "mm:ss.ms"
-        if p.dict['cpu_times'] is not None:
-            ctime = timedelta(seconds=sum(p.dict['cpu_times']))
-            ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60,
-                                  str((ctime.seconds % 60)).zfill(2),
-                                  str(ctime.microseconds)[:2])
-        else:
-            ctime = ''
-        if p.dict['memory_percent'] is not None:
-            p.dict['memory_percent'] = round(p.dict['memory_percent'], 1)
-        else:
-            p.dict['memory_percent'] = ''
-        if p.dict['cpu_percent'] is None:
-            p.dict['cpu_percent'] = ''
-        if p.dict['username']:
-            username = p.dict['username'][:8]
-        else:
-            username = ""
-        line = templ % (p.pid,
-                        username,
-                        p.dict['nice'],
-                        bytes2human(getattr(p.dict['memory_info'], 'vms', 0)),
-                        bytes2human(getattr(p.dict['memory_info'], 'rss', 0)),
-                        p.dict['cpu_percent'],
-                        p.dict['memory_percent'],
-                        ctime,
-                        p.dict['name'] or '',
-                        )
-        try:
-            print_line(line)
-        except curses.error:
-            break
-        win.refresh()
-
-
-def main():
-    try:
-        interval = 0
-        while True:
-            args = poll(interval)
-            refresh_window(*args)
-            interval = 1
-    except (KeyboardInterrupt, SystemExit):
-        pass
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/third_party/python/psutil/examples/who.py
+++ /dev/null
@@ -1,33 +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.
-
-"""
-A clone of 'who' command; print information about users who are
-currently logged in.
-
-$ python examples/who.py
-giampaolo       tty7            2014-02-23 17:25  (:0)
-giampaolo       pts/7           2014-02-24 18:25  (:192.168.1.56)
-giampaolo       pts/8           2014-02-24 18:25  (:0)
-giampaolo       pts/9           2014-02-27 01:32  (:0)
-"""
-
-from datetime import datetime
-
-import psutil
-
-
-def main():
-    users = psutil.users()
-    for user in users:
-        print("%-15s %-15s %s  (%s)" % (
-            user.name,
-            user.terminal or '-',
-            datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"),
-            user.host))
-
-if __name__ == '__main__':
-    main()
--- a/third_party/python/psutil/make.bat
+++ b/third_party/python/psutil/make.bat
@@ -18,184 +18,15 @@ rem
 rem To use a different test script:
 rem      set PYTHON=C:\Python34\python.exe & set TSCRIPT=foo.py & make.bat test
 rem ==========================================================================
 
 if "%PYTHON%" == "" (
     set PYTHON=C:\Python27\python.exe
 )
 if "%TSCRIPT%" == "" (
-    set TSCRIPT=test\test_psutil.py
+    set TSCRIPT=psutil\tests\__main__.py
 )
 
-set PYTHON26=C:\Python26\python.exe
-set PYTHON27=C:\Python27\python.exe
-set PYTHON33=C:\Python33\python.exe
-set PYTHON34=C:\Python34\python.exe
-set PYTHON26-64=C:\Python26-64\python.exe
-set PYTHON27-64=C:\Python27-64\python.exe
-set PYTHON33-64=C:\Python33-64\python.exe
-set PYTHON34-64=C:\Python34-64\python.exe
-
-set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64%
-
 rem Needed to locate the .pypirc file and upload exes on PYPI.
 set HOME=%USERPROFILE%
 
-rem ==========================================================================
-
-if "%1" == "help" (
-    :help
-    echo Run `make ^<target^>` where ^<target^> is one of:
-    echo   build         compile without installing
-    echo   build-all     build exes + wheels
-    echo   clean         clean build files
-    echo   flake8        run flake8
-    echo   install       compile and install
-    echo   setup-dev-env install pip, pywin32, wheels, etc. for all python versions
-    echo   test          run tests
-    echo   test-memleaks run memory leak tests
-    echo   test-process  run process related tests
-    echo   test-system   run system APIs related tests
-    echo   uninstall     uninstall
-    echo   upload-all    upload exes + wheels
-    goto :eof
-)
-
-if "%1" == "clean" (
-    for /r %%R in (__pycache__) do if exist %%R (rmdir /S /Q %%R)
-    for /r %%R in (*.pyc) do if exist %%R (del /s %%R)
-    for /r %%R in (*.pyd) do if exist %%R (del /s %%R)
-    for /r %%R in (*.orig) do if exist %%R (del /s %%R)
-    for /r %%R in (*.bak) do if exist %%R (del /s %%R)
-    for /r %%R in (*.rej) do if exist %%R (del /s %%R)
-    if exist psutil.egg-info (rmdir /S /Q psutil.egg-info)
-    if exist build (rmdir /S /Q build)
-    if exist dist (rmdir /S /Q dist)
-    goto :eof
-)
-
-if "%1" == "build" (
-    :build
-    "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    %PYTHON% setup.py build
-    if %errorlevel% neq 0 goto :error
-	rem copies *.pyd files in ./psutil directory in order to allow
-	rem "import psutil" when using the interactive interpreter from
-    rem within this directory.
-    %PYTHON% setup.py build_ext -i
-    if %errorlevel% neq 0 goto :error
-    goto :eof
-)
-
-if "%1" == "install" (
-    :install
-    call :build
-    %PYTHON% setup.py install
-    goto :eof
-)
-
-if "%1" == "uninstall" (
-    for %%A in ("%PYTHON%") do (
-        set folder=%%~dpA
-    )
-    for /F "delims=" %%i in ('dir /b %folder%\Lib\site-packages\*psutil*') do (
-        rmdir /S /Q %folder%\Lib\site-packages\%%i
-    )
-    goto :eof
-)
-
-if "%1" == "test" (
-    call :install
-    %PYTHON% %TSCRIPT%
-    goto :eof
-)
-
-if "%1" == "test-process" (
-    call :install
-    %PYTHON% -m unittest -v test.test_psutil.TestProcess
-    goto :eof
-)
-
-if "%1" == "test-system" (
-    call :install
-    %PYTHON% -m unittest -v test.test_psutil.TestSystem
-    goto :eof
-)
-
-if "%1" == "test-memleaks" (
-    call :install
-    %PYTHON% test\test_memory_leaks.py
-    goto :eof
-)
-
-if "%1" == "build-all" (
-    :build-all
-    "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    for %%P in (%ALL_PYTHONS%) do (
-        @echo ------------------------------------------------
-        @echo building exe for %%P
-        @echo ------------------------------------------------
-        %%P setup.py build bdist_wininst || goto :error
-        @echo ------------------------------------------------
-        @echo building wheel for %%P
-        @echo ------------------------------------------------
-        %%P setup.py build bdist_wheel || goto :error
-    )
-    echo OK
-    goto :eof
-)
-
-if "%1" == "upload-all" (
-    :upload-exes
-    "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    for %%P in (%ALL_PYTHONS%) do (
-        @echo ------------------------------------------------
-        @echo uploading exe for %%P
-        @echo ------------------------------------------------
-        %%P setup.py build bdist_wininst upload || goto :error
-        @echo ------------------------------------------------
-        @echo uploading wheel for %%P
-        @echo ------------------------------------------------
-        %%P setup.py build bdist_wheel upload || goto :error
-    )
-    echo OK
-    goto :eof
-)
-
-if "%1" == "setup-dev-env" (
-    :setup-env
-    @echo ------------------------------------------------
-    @echo downloading pip installer
-    @echo ------------------------------------------------
-    C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); open('get-pip.py', 'wb').write(r.read())"
-    for %%P in (%ALL_PYTHONS%) do (
-        @echo ------------------------------------------------
-        @echo installing pip for %%P
-        @echo ------------------------------------------------
-        %%P get-pip.py
-    )
-    for %%P in (%ALL_PYTHONS%) do (
-        @echo ------------------------------------------------
-        @echo installing deps for %%P
-        @echo ------------------------------------------------
-        rem mandatory / for unittests
-        %%P -m pip install unittest2 ipaddress mock wmi wheel pypiwin32 --upgrade
-        rem nice to have
-        %%P -m pip install ipdb pep8 pyflakes flake8 --upgrade
-    )
-    goto :eof
-)
-
-if "%1" == "flake8" (
-    :flake8
-    %PYTHON% -c "from flake8.main import main; main()"
-    goto :eof
-)
-
-goto :help
-
-:error
-    @echo ------------------------------------------------
-    @echo last command exited with error code %errorlevel%
-    @echo ------------------------------------------------
-    @exit /b %errorlevel%
-    goto :eof
+%PYTHON% scripts\internal\winmake.py %1 %2 %3 %4 %5 %6
deleted file mode 100644
--- a/third_party/python/psutil/psutil.egg-info/PKG-INFO
+++ /dev/null
@@ -1,434 +0,0 @@
-Metadata-Version: 1.1
-Name: psutil
-Version: 3.1.1
-Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python.
-Home-page: https://github.com/giampaolo/psutil
-Author: Giampaolo Rodola
-Author-email: g.rodola <at> gmail <dot> com
-License: BSD
-Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg
-            :target: https://pypi.python.org/pypi/psutil#downloads
-            :alt: Downloads this month
-        
-        .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
-            :target: https://travis-ci.org/giampaolo/psutil
-            :alt: Linux tests (Travis)
-        
-        .. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
-            :target: https://ci.appveyor.com/project/giampaolo/psutil
-            :alt: Windows tests (Appveyor)
-        
-        .. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github
-            :target: https://coveralls.io/github/giampaolo/psutil?branch=master
-            :alt: Test coverage (coverall.io)
-        
-        .. image:: https://img.shields.io/pypi/v/psutil.svg
-            :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/scrutinizer/g/giampaolo/psutil.svg
-            :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
-            :alt: Code quality (scrutinizer-ci.com)
-        
-        .. image:: https://img.shields.io/pypi/l/psutil.svg
-            :target: https://pypi.python.org/pypi/psutil/
-            :alt: License
-        
-        ===========
-        Quick links
-        ===========
-        
-        - `Home page <https://github.com/giampaolo/psutil>`_
-        - `Documentation <http://pythonhosted.org/psutil/>`_
-        - `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
-        - `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`_
-        - `Forum <http://groups.google.com/group/psutil/topics>`_
-        - `Blog <http://grodola.blogspot.com/search/label/psutil>`_
-        - `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
-        - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
-        
-        =======
-        Summary
-        =======
-        
-        psutil (python system and process utilities) is a cross-platform library for
-        retrieving information on **running processes** and **system utilization**
-        (CPU, memory, disks, network) 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 command line
-        tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
-        ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
-        **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and
-        **64-bit** architectures, with Python versions from **2.6 to 3.5** (users of
-        Python 2.4 and 2.5 may use `2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
-        `PyPy <http://pypy.org/>`__ is also known to work.
-        
-        ====================
-        Example applications
-        ====================
-        
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/top.png
-            :alt: top
-        
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/nettop-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/nettop.png
-            :alt: nettop
-        
-        .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
-            :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
-            :alt: iotop
-        
-        See also:
-        
-         * https://github.com/nicolargo/glances
-         * https://github.com/google/grr
-         * https://github.com/Jahaja/psdash
-        
-        ==============
-        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
-            >>>
-        
-        Memory
-        ======
-        
-        .. code-block:: python
-        
-            >>> psutil.virtual_memory()
-            svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
-            >>> psutil.swap_memory()
-            sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
-            >>>
-        
-        Disks
-        =====
-        
-        .. code-block:: python
-        
-            >>> 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)
-            >>>
-        
-        Network
-        =======
-        
-        .. code-block:: python
-        
-            >>> 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()
-            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
-             ...]
-            >>>
-            >>> psutil.net_if_addrs()
-            {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
-                    snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
-                    snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
-             'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
-                       snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
-                       snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
-            >>>
-            >>> psutil.net_if_stats()
-            {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
-             'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
-        
-        Other system info
-        =================
-        
-        .. code-block:: python
-        
-            >>> psutil.users()
-            [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
-             user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
-            >>>
-            >>> 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.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)
-            >>> p.cpu_percent(interval=1.0)
-            12.1
-            >>> p.cpu_affinity()
-            [0, 1, 2, 3]
-            >>> p.cpu_affinity([0])  # set
-            >>>
-            >>> p.memory_percent()
-            0.63423
-            >>>
-            >>> p.memory_info()
-            pmem(rss=7471104, vms=68513792)
-            >>> p.memory_info_ex()
-            extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
-            >>> p.memory_maps()
-            [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
-             pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
-             pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
-             pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
-             pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
-             ...]
-            >>>
-            >>> p.io_counters()
-            pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
-            >>>
-            >>> p.open_files()
-            [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
-            >>>
-            >>> p.connections()
-            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-             pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-             pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 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=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
-            >>>
-            >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))  # set resource limits (Linux only)
-            >>> p.rlimit(psutil.RLIMIT_NOFILE)
-            (5, 5)
-            >>>
-            >>> 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
-        
-            >>> for p in psutil.process_iter():
-            ...     print(p)
-            ...
-            psutil.Process(pid=1, name='init')
-            psutil.Process(pid=2, name='kthreadd')
-            psutil.Process(pid=3, name='ksoftirqd/0')
-            ...
-            >>>
-            >>> def on_terminate(proc):
-            ...     print("process {} terminated".format(proc))
-            ...
-            >>> # waits for multiple processes to terminate
-            >>> gone, alive = psutil.wait_procs(procs_list, 3, callback=on_terminate)
-            >>>
-        
-        ======
-        Donate
-        ======
-        
-        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 Rodola' <http://grodola.blogspot.com/p/about.html>`_) some money.
-        I only ask for a small donation, but of course I appreciate any amount.
-        
-        .. 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 <http://www.linkedin.com/in/grodola>`_.
-        
-        ============
-        Mailing list
-        ============
-        
-        http://groups.google.com/group/psutil/
-        
-        ========
-        Timeline
-        ========
-        
-        - 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
-        - 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
-        - 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
-        - 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
-        - 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
-        - 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
-        - 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
-        - 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
-        - 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
-        - 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
-        - 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
-        - 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
-        - 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
-        - 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
-        - 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.2.tar.gz>`_
-        - 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.1.tar.gz>`_
-        - 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.0.tar.gz>`_
-        - 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.1.tar.gz>`_
-        - 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.0.tar.gz>`_
-        - 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.1.tar.gz>`_
-        - 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.0.tar.gz>`_
-        - 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.1.tar.gz>`_
-        - 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.0.tar.gz>`_
-        - 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.1.tar.gz>`_
-        - 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.0.tar.gz>`_
-        - 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.1.tar.gz>`_
-        - 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.0.tar.gz>`_
-        - 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
-        - 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
-        - 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
-        - 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
-        - 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
-        - 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
-        - 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
-        
-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
-Platform: Platform Independent
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Console
-Classifier: Environment :: Win32 (MS Windows)
-Classifier: Intended Audience :: Developers
-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
-Classifier: Operating System :: OS Independent
-Classifier: Operating System :: POSIX :: BSD :: FreeBSD
-Classifier: Operating System :: POSIX :: Linux
-Classifier: Operating System :: POSIX :: SunOS/Solaris
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: C
-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.0
-Classifier: Programming Language :: Python :: 3.1
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-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
-Classifier: Topic :: System :: Monitoring
-Classifier: Topic :: System :: Networking :: Monitoring
-Classifier: Topic :: System :: Networking
-Classifier: Topic :: System :: Systems Administration
-Classifier: Topic :: Utilities
deleted file mode 100644
--- a/third_party/python/psutil/psutil.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,96 +0,0 @@
-.coveragerc
-.git-pre-commit
-.gitignore
-.travis.yml
-CREDITS
-HISTORY.rst
-INSTALL.rst
-LICENSE
-MANIFEST.in
-Makefile
-README.rst
-TODO
-make.bat
-setup.py
-tox.ini
-docs/Makefile
-docs/README
-docs/conf.py
-docs/index.rst
-docs/make.bat
-docs/xxx
-docs/_static/copybutton.js
-docs/_static/favicon.ico
-docs/_static/logo.png
-docs/_static/sidebar.js
-docs/_template/globaltoc.html
-docs/_template/indexcontent.html
-docs/_template/indexsidebar.html
-docs/_template/page.html
-docs/_themes/pydoctheme/theme.conf
-docs/_themes/pydoctheme/static/pydoctheme.css
-examples/disk_usage.py
-examples/free.py
-examples/ifconfig.py
-examples/iotop.py
-examples/killall.py
-examples/meminfo.py
-examples/netstat.py
-examples/nettop.py
-examples/pidof.py
-examples/pmap.py
-examples/process_detail.py
-examples/ps.py
-examples/pstree.py
-examples/top.py
-examples/who.py
-psutil/__init__.py
-psutil/_common.py
-psutil/_compat.py
-psutil/_psbsd.py
-psutil/_pslinux.py
-psutil/_psosx.py
-psutil/_psposix.py
-psutil/_pssunos.py
-psutil/_psutil_bsd.c
-psutil/_psutil_bsd.h
-psutil/_psutil_common.c
-psutil/_psutil_common.h
-psutil/_psutil_linux.c
-psutil/_psutil_linux.h
-psutil/_psutil_osx.c
-psutil/_psutil_osx.h
-psutil/_psutil_posix.c
-psutil/_psutil_posix.h
-psutil/_psutil_sunos.c
-psutil/_psutil_sunos.h
-psutil/_psutil_windows.c
-psutil/_psutil_windows.h
-psutil/_pswindows.py
-psutil.egg-info/PKG-INFO
-psutil.egg-info/SOURCES.txt
-psutil.egg-info/dependency_links.txt
-psutil.egg-info/top_level.txt
-psutil/arch/bsd/process_info.c
-psutil/arch/bsd/process_info.h
-psutil/arch/osx/process_info.c
-psutil/arch/osx/process_info.h
-psutil/arch/windows/glpi.h
-psutil/arch/windows/inet_ntop.c
-psutil/arch/windows/inet_ntop.h
-psutil/arch/windows/ntextapi.h
-psutil/arch/windows/process_handles.c
-psutil/arch/windows/process_handles.h
-psutil/arch/windows/process_info.c
-psutil/arch/windows/process_info.h
-psutil/arch/windows/security.c
-psutil/arch/windows/security.h
-test/README.rst
-test/_bsd.py
-test/_linux.py
-test/_osx.py
-test/_posix.py
-test/_sunos.py
-test/_windows.py
-test/test_memory_leaks.py
-test/test_psutil.py
\ No newline at end of file
deleted file mode 100644
--- a/third_party/python/psutil/psutil.egg-info/dependency_links.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-
deleted file mode 100644
--- a/third_party/python/psutil/psutil.egg-info/top_level.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-psutil
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/DEVNOTES
@@ -0,0 +1,5 @@
+API REFERENCES
+==============
+
+- psutil.sensors_battery:
+    https://github.com/Kentzo/Power/
--- a/third_party/python/psutil/psutil/__init__.py
+++ b/third_party/python/psutil/psutil/__init__.py
@@ -1,90 +1,127 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 """psutil is a cross-platform library for retrieving information on
-running processes and system utilization (CPU, memory, disks, network)
-in Python.
+running processes and system utilization (CPU, memory, disks, network,
+sensors) in Python. Supported platforms:
+
+ - Linux
+ - Windows
+ - OSX
+ - FreeBSD
+ - OpenBSD
+ - NetBSD
+ - Sun Solaris
+ - AIX
+
+Works with Python versions from 2.6 to 3.X.
 """
 
 from __future__ import division
 
 import collections
+import contextlib
+import datetime
 import errno
 import functools
 import os
 import signal
 import subprocess
 import sys
 import time
+import traceback
 try:
     import pwd
 except ImportError:
     pwd = None
 
 from . import _common
+from ._common import deprecated_method
 from ._common import memoize
-from ._compat import callable, long
+from ._common import memoize_when_activated
+from ._common import wrap_numbers as _wrap_numbers
+from ._compat import callable
+from ._compat import long
 from ._compat import PY3 as _PY3
 
-from ._common import (STATUS_RUNNING,  # NOQA
-                      STATUS_SLEEPING,
-                      STATUS_DISK_SLEEP,
-                      STATUS_STOPPED,
-                      STATUS_TRACING_STOP,
-                      STATUS_ZOMBIE,
-                      STATUS_DEAD,
-                      STATUS_WAKING,
-                      STATUS_LOCKED,
-                      STATUS_IDLE,  # bsd
-                      STATUS_WAITING)  # bsd
+from ._common import STATUS_DEAD
+from ._common import STATUS_DISK_SLEEP
+from ._common import STATUS_IDLE  # bsd
+from ._common import STATUS_LOCKED
+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_WAKING
+from ._common import STATUS_ZOMBIE
 
-from ._common import (CONN_ESTABLISHED,
-                      CONN_SYN_SENT,
-                      CONN_SYN_RECV,
-                      CONN_FIN_WAIT1,
-                      CONN_FIN_WAIT2,
-                      CONN_TIME_WAIT,
-                      CONN_CLOSE,
-                      CONN_CLOSE_WAIT,
-                      CONN_LAST_ACK,
-                      CONN_LISTEN,
-                      CONN_CLOSING,
-                      CONN_NONE)
+from ._common import CONN_CLOSE
+from ._common import CONN_CLOSE_WAIT
+from ._common import CONN_CLOSING
+from ._common import CONN_ESTABLISHED
+from ._common import CONN_FIN_WAIT1
+from ._common import CONN_FIN_WAIT2
+from ._common import CONN_LAST_ACK
+from ._common import CONN_LISTEN
+from ._common import CONN_NONE
+from ._common import CONN_SYN_RECV
+from ._common import CONN_SYN_SENT
+from ._common import CONN_TIME_WAIT
+from ._common import NIC_DUPLEX_FULL
+from ._common import NIC_DUPLEX_HALF
+from ._common import NIC_DUPLEX_UNKNOWN
 
-from ._common import (NIC_DUPLEX_FULL,  # NOQA
-                      NIC_DUPLEX_HALF,
-                      NIC_DUPLEX_UNKNOWN)
+from ._common import AIX
+from ._common import BSD
+from ._common import FREEBSD  # NOQA
+from ._common import LINUX
+from ._common import NETBSD  # NOQA
+from ._common import OPENBSD  # NOQA
+from ._common import OSX
+from ._common import POSIX  # NOQA
+from ._common import SUNOS
+from ._common import WINDOWS
 
-if sys.platform.startswith("linux"):
+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.
+    PROCFS_PATH = "/proc"
+
     from . import _pslinux as _psplatform
 
-    from ._pslinux import (IOPRIO_CLASS_NONE,  # NOQA
-                           IOPRIO_CLASS_RT,
-                           IOPRIO_CLASS_BE,
-                           IOPRIO_CLASS_IDLE)
+    from ._pslinux import IOPRIO_CLASS_BE  # NOQA
+    from ._pslinux import IOPRIO_CLASS_IDLE  # NOQA
+    from ._pslinux import IOPRIO_CLASS_NONE  # NOQA
+    from ._pslinux import IOPRIO_CLASS_RT  # NOQA
     # Linux >= 2.6.36
     if _psplatform.HAS_PRLIMIT:
-        from ._psutil_linux import (RLIM_INFINITY,  # NOQA
-                                    RLIMIT_AS,
-                                    RLIMIT_CORE,
-                                    RLIMIT_CPU,
-                                    RLIMIT_DATA,
-                                    RLIMIT_FSIZE,
-                                    RLIMIT_LOCKS,
-                                    RLIMIT_MEMLOCK,
-                                    RLIMIT_NOFILE,
-                                    RLIMIT_NPROC,
-                                    RLIMIT_RSS,
-                                    RLIMIT_STACK)
+        from ._psutil_linux import RLIM_INFINITY  # NOQA
+        from ._psutil_linux import RLIMIT_AS  # NOQA
+        from ._psutil_linux import RLIMIT_CORE  # NOQA
+        from ._psutil_linux import RLIMIT_CPU  # NOQA
+        from ._psutil_linux import RLIMIT_DATA  # NOQA
+        from ._psutil_linux import RLIMIT_FSIZE  # NOQA
+        from ._psutil_linux import RLIMIT_LOCKS  # NOQA
+        from ._psutil_linux import RLIMIT_MEMLOCK  # NOQA
+        from ._psutil_linux import RLIMIT_NOFILE  # NOQA
+        from ._psutil_linux import RLIMIT_NPROC  # NOQA
+        from ._psutil_linux import RLIMIT_RSS  # NOQA
+        from ._psutil_linux import RLIMIT_STACK  # NOQA
         # Kinda ugly but considerably faster than using hasattr() and
         # setattr() against the module object (we are at import time:
         # speed matters).
         from . import _psutil_linux
         try:
             RLIMIT_MSGQUEUE = _psutil_linux.RLIMIT_MSGQUEUE
         except AttributeError:
             pass
@@ -99,213 +136,179 @@ if sys.platform.startswith("linux"):
         try:
             RLIMIT_RTTIME = _psutil_linux.RLIMIT_RTTIME
         except AttributeError:
             pass
         try:
             RLIMIT_SIGPENDING = _psutil_linux.RLIMIT_SIGPENDING
         except AttributeError:
             pass
-        del _psutil_linux
 
-elif sys.platform.startswith("win32"):
+elif WINDOWS:
     from . import _pswindows as _psplatform
-    from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,  # NOQA
-                                  BELOW_NORMAL_PRIORITY_CLASS,
-                                  HIGH_PRIORITY_CLASS,
-                                  IDLE_PRIORITY_CLASS,
-                                  NORMAL_PRIORITY_CLASS,
-                                  REALTIME_PRIORITY_CLASS)
+    from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS  # NOQA
+    from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS  # NOQA
+    from ._psutil_windows import HIGH_PRIORITY_CLASS  # NOQA
+    from ._psutil_windows import IDLE_PRIORITY_CLASS  # NOQA
+    from ._psutil_windows import NORMAL_PRIORITY_CLASS  # NOQA
+    from ._psutil_windows import REALTIME_PRIORITY_CLASS  # NOQA
     from ._pswindows import CONN_DELETE_TCB  # NOQA
 
-elif sys.platform.startswith("darwin"):
+elif OSX:
     from . import _psosx as _psplatform
 
-elif sys.platform.startswith("freebsd"):
+elif BSD:
     from . import _psbsd as _psplatform
 
-elif sys.platform.startswith("sunos"):
+elif SUNOS:
     from . import _pssunos as _psplatform
-    from ._pssunos import (CONN_IDLE,  # NOQA
-                           CONN_BOUND)
+    from ._pssunos import CONN_BOUND  # NOQA
+    from ._pssunos import CONN_IDLE  # NOQA
+
+    # This is public writable API which is read from _pslinux.py and
+    # _pssunos.py via sys.modules.
+    PROCFS_PATH = "/proc"
+
+elif AIX:
+    from . import _psaix as _psplatform
+
+    # This is public API and it will be retrieved from _pslinux.py
+    # via sys.modules.
+    PROCFS_PATH = "/proc"
 
 else:  # pragma: no cover
     raise NotImplementedError('platform %s is not supported' % sys.platform)
 
 
 __all__ = [
     # exceptions
     "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
     "TimeoutExpired",
+
     # constants
     "version_info", "__version__",
+
     "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",
+
     "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
     "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
     "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE",
+
     "AF_LINK",
+
     "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN",
+
+    "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
+
+    "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS",
+    "WINDOWS", "AIX",
+
     # classes
     "Process", "Popen",
+
     # functions
     "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",
     "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__ = "3.1.1"
+__version__ = "5.4.3"
 version_info = tuple([int(num) for num in __version__.split('.')])
 AF_LINK = _psplatform.AF_LINK
+POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
+POWER_TIME_UNKNOWN = _common.POWER_TIME_UNKNOWN
 _TOTAL_PHYMEM = None
-_POSIX = os.name == 'posix'
-_WINDOWS = os.name == 'nt'
 _timer = getattr(time, 'monotonic', time.time)
 
 
 # Sanity check in case the user messed up with psutil installation
 # or did something weird with sys.path. In this case we might end
 # up importing a python module using a C extension module which
 # was compiled for a different version of psutil.
 # We want to prevent that by failing sooner rather than later.
 # See: https://github.com/giampaolo/psutil/issues/564
 if (int(__version__.replace('.', '')) !=
         getattr(_psplatform.cext, 'version', None)):
     msg = "version conflict: %r C extension module was built for another " \
-          "version of psutil (different than %s)" % (_psplatform.cext.__file__,
-                                                     __version__)
+          "version of psutil" % getattr(_psplatform.cext, "__file__")
+    if hasattr(_psplatform.cext, 'version'):
+        msg += " (%s instead of %s)" % (
+            '.'.join([x for x in str(_psplatform.cext.version)]), __version__)
+    else:
+        msg += " (different than %s)" % __version__
+    msg += "; you may try to 'pip uninstall psutil', manually remove %s" % (
+        getattr(_psplatform.cext, "__file__",
+                "the existing psutil install directory"))
+    msg += " or clean the virtual env somehow, then reinstall"
     raise ImportError(msg)
 
 
 # =====================================================================
-# --- exceptions
+# --- Utils
 # =====================================================================
 
-class Error(Exception):
-    """Base exception class. All other psutil exceptions inherit
-    from this one.
-    """
-
-    def __init__(self, msg=""):
-        self.msg = msg
-
-    def __repr__(self):
-        ret = "%s.%s %s" % (self.__class__.__module__,
-                            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):
-        Error.__init__(self, msg)
-        self.pid = pid
-        self.ppid = ppid
-        self.name = name
-        self.msg = msg
-        if msg is None:
-            if name and ppid:
-                details = "(pid=%s, name=%s, ppid=%s)" % (
-                    self.pid, repr(self.name), self.ppid)
-            elif name:
-                details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
+if hasattr(_psplatform, 'ppid_map'):
+    # Faster version (Windows and Linux).
+    _ppid_map = _psplatform.ppid_map
+else:
+    def _ppid_map():
+        """Return a {pid: ppid, ...} dict for all running processes in
+        one shot. Used to speed up Process.children().
+        """
+        ret = {}
+        for pid in pids():
+            try:
+                proc = _psplatform.Process(pid)
+                ppid = proc.ppid()
+            except (NoSuchProcess, AccessDenied):
+                # Note: AccessDenied is unlikely to happen.
+                pass
             else:
-                details = "(pid=%s)" % self.pid
-            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
-
-
-# =====================================================================
-# --- Process class
-# =====================================================================
+                ret[pid] = ppid
+        return ret
 
 
 def _assert_pid_not_reused(fun):
     """Decorator which raises NoSuchProcess in case a process is no
     longer running or its PID has been reused.
     """
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         if not self.is_running():
             raise NoSuchProcess(self.pid, self._name)
         return fun(self, *args, **kwargs)
     return wrapper
 
 
+def _pprint_secs(secs):
+    """Format seconds in a human readable form."""
+    now = time.time()
+    secs_ago = int(now - secs)
+    if secs_ago < 60 * 60 * 24:
+        fmt = "%H:%M:%S"
+    else:
+        fmt = "%Y-%m-%d %H:%M:%S"
+    return datetime.datetime.fromtimestamp(secs).strftime(fmt)
+
+
+# =====================================================================
+# --- Process class
+# =====================================================================
+
+
 class Process(object):
     """Represents an OS process with the given PID.
     If PID is omitted current process PID (os.getpid()) is used.
     Raise NoSuchProcess if PID does not exist.
 
     Note that most of the methods of this class do not make sure
     the PID of the process being queried has been reused over time.
     That means you might end up retrieving an information referring
@@ -323,20 +326,20 @@ class Process(object):
      - cpu_affinity (set)
      - suspend()
      - resume()
      - send_signal()
      - terminate()
      - kill()
 
     To prevent this problem for all other methods you can:
-      - use is_running() before querying the process
-      - if you're continuously iterating over a set of Process
-        instances use process_iter() which pre-emptively checks
-        process identity for every yielded instance
+     - use is_running() before querying the process
+     - if you're continuously iterating over a set of Process
+       instances use process_iter() which pre-emptively checks
+     process identity for every yielded instance
     """
 
     def __init__(self, pid=None):
         self._init(pid)
 
     def _init(self, pid, _ignore_nsp=False):
         if pid is None:
             pid = os.getpid()
@@ -347,122 +350,197 @@ class Process(object):
                 raise ValueError('pid must be a positive integer (got %s)'
                                  % pid)
         self._pid = pid
         self._name = None
         self._exe = None
         self._create_time = None
         self._gone = False
         self._hash = None
+        self._oneshot_inctx = False
         # used for caching on Windows only (on POSIX ppid may change)
         self._ppid = None
         # platform-specific modules define an _psplatform.Process
         # implementation class
         self._proc = _psplatform.Process(pid)
         self._last_sys_cpu_times = None
         self._last_proc_cpu_times = None
         # cache creation time for later use in is_running() method
         try:
             self.create_time()
         except AccessDenied:
-            # we should never get here as AFAIK we're able to get
+            # We should never get here as AFAIK we're able to get
             # process creation time on all platforms even as a
-            # limited user
+            # limited user.
             pass
         except ZombieProcess:
-            # Let's consider a zombie process as legitimate as
-            # tehcnically it's still alive (it can be queried,
-            # although not always, and it's returned by pids()).
+            # Zombies can still be queried by this class (although
+            # not always) and pids() return them so just go on.
             pass
         except NoSuchProcess:
             if not _ignore_nsp:
                 msg = 'no process found with pid %s' % pid
                 raise NoSuchProcess(pid, None, msg)
             else:
                 self._gone = True
         # This pair is supposed to indentify a Process instance
         # univocally over time (the PID alone is not enough as
         # it might refer to a process whose PID has been reused).
         # This will be used later in __eq__() and is_running().
         self._ident = (self.pid, self._create_time)
 
     def __str__(self):
         try:
-            pid = self.pid
-            name = repr(self.name())
+            info = collections.OrderedDict()
+        except AttributeError:
+            info = {}  # Python 2.6
+        info["pid"] = self.pid
+        try:
+            info["name"] = self.name()
+            if self._create_time:
+                info['started'] = _pprint_secs(self._create_time)
         except ZombieProcess:
-            details = "(pid=%s (zombie))" % self.pid
+            info["status"] = "zombie"
         except NoSuchProcess:
-            details = "(pid=%s (terminated))" % self.pid
+            info["status"] = "terminated"
         except AccessDenied:
-            details = "(pid=%s)" % (self.pid)
-        else:
-            details = "(pid=%s, name=%s)" % (pid, name)
-        return "%s.%s%s" % (self.__class__.__module__,
-                            self.__class__.__name__, details)
+            pass
+        return "%s.%s(%s)" % (
+            self.__class__.__module__,
+            self.__class__.__name__,
+            ", ".join(["%s=%r" % (k, v) for k, v in info.items()]))
 
-    def __repr__(self):
-        return "<%s at %s>" % (self.__str__(), id(self))
+    __repr__ = __str__
 
     def __eq__(self, other):
         # Test for equality with another Process object based
         # on PID and creation time.
         if not isinstance(other, Process):
             return NotImplemented
         return self._ident == other._ident
 
     def __ne__(self, other):
         return not self == other
 
     def __hash__(self):
         if self._hash is None:
             self._hash = hash(self._ident)
         return self._hash
 
+    @property
+    def pid(self):
+        """The process PID."""
+        return self._pid
+
     # --- utility methods
 
+    @contextlib.contextmanager
+    def oneshot(self):
+        """Utility context manager which considerably speeds up the
+        retrieval of multiple process information at the same time.
+
+        Internally different process info (e.g. name, ppid, uids,
+        gids, ...) may be fetched by using the same routine, but
+        only one information is returned and the others are discarded.
+        When using this context manager the internal routine is
+        executed once (in the example below on name()) and the
+        other info are cached.
+
+        The cache is cleared when exiting the context manager block.
+        The advice is to use this every time you retrieve more than
+        one information about the process. If you're lucky, you'll
+        get a hell of a speedup.
+
+        >>> import psutil
+        >>> p = psutil.Process()
+        >>> with p.oneshot():
+        ...     p.name()  # collect multiple info
+        ...     p.cpu_times()  # return cached value
+        ...     p.cpu_percent()  # return cached value
+        ...     p.create_time()  # return cached value
+        ...
+        >>>
+        """
+        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()
+                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
+
     def as_dict(self, attrs=None, ad_value=None):
         """Utility method returning process information as a
         hashable dictionary.
-
-        If 'attrs' is specified it must be a list of strings
+        If *attrs* is specified it must be a list of strings
         reflecting available Process class' attribute names
         (e.g. ['cpu_times', 'name']) else all public (read
         only) attributes are assumed.
-
-        'ad_value' is the value which gets assigned in case
+        *ad_value* is the value which gets assigned in case
         AccessDenied or ZombieProcess exception is raised when
         retrieving that particular process information.
         """
-        excluded_names = set(
-            ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
-             'is_running', 'as_dict', 'parent', 'children', 'rlimit'])
+        valid_names = _as_dict_attrnames
+        if attrs is not None:
+            if not isinstance(attrs, (list, tuple, set, frozenset)):
+                raise TypeError("invalid attrs type %s" % type(attrs))
+            attrs = set(attrs)
+            invalid_names = attrs - valid_names
+            if invalid_names:
+                raise ValueError("invalid attr name%s %s" % (
+                    "s" if len(invalid_names) > 1 else "",
+                    ", ".join(map(repr, invalid_names))))
+
         retdict = dict()
-        ls = set(attrs or [x for x in dir(self)])
-        for name in ls:
-            if name.startswith('_'):
-                continue
-            if name in excluded_names:
-                continue
-            try:
-                attr = getattr(self, name)
-                if callable(attr):
-                    ret = attr()
-                else:
-                    ret = attr
-            except (AccessDenied, ZombieProcess):
-                ret = ad_value
-            except NotImplementedError:
-                # in case of not implemented functionality (may happen
-                # on old or exotic systems) we want to crash only if
-                # the user explicitly asked for that particular attr
-                if attrs:
-                    raise
-                continue
-            retdict[name] = ret
+        ls = attrs or valid_names
+        with self.oneshot():
+            for name in ls:
+                try:
+                    if name == 'pid':
+                        ret = self.pid
+                    else:
+                        meth = getattr(self, name)
+                        ret = meth()
+                except (AccessDenied, ZombieProcess):
+                    ret = ad_value
+                except NotImplementedError:
+                    # in case of not implemented functionality (may happen
+                    # on old or exotic systems) we want to crash only if
+                    # the user explicitly asked for that particular attr
+                    if attrs:
+                        raise
+                    continue
+                retdict[name] = ret
         return retdict
 
     def parent(self):
         """Return the parent process as a Process object pre-emptively
         checking whether PID has been reused.
         If no parent is known return None.
         """
         ppid = self.ppid()
@@ -481,69 +559,73 @@ class Process(object):
         It also checks if PID has been reused by another process in
         which case return False.
         """
         if self._gone:
             return False
         try:
             # Checking if PID is alive is not enough as the PID might
             # have been reused by another process: we also want to
-            # check process identity.
-            # Process identity / uniqueness over time is greanted by
+            # verify process identity.
+            # Process identity / uniqueness over time is guaranteed by
             # (PID + creation time) and that is verified in __eq__.
             return self == Process(self.pid)
+        except ZombieProcess:
+            # We should never get here as it's already handled in
+            # Process.__init__; here just for extra safety.
+            return True
         except NoSuchProcess:
             self._gone = True
             return False
 
     # --- actual API
 
-    @property
-    def pid(self):
-        """The process PID."""
-        return self._pid
-
+    @memoize_when_activated
     def ppid(self):
         """The process parent PID.
         On Windows the return value is cached after first call.
         """
         # On POSIX we don't want to cache the ppid as it may unexpectedly
         # change to 1 (init) in case this process turns into a zombie:
         # https://github.com/giampaolo/psutil/issues/321
         # http://stackoverflow.com/questions/356722/
 
         # XXX should we check creation time here rather than in
         # Process.parent()?
-        if _POSIX:
+        if POSIX:
             return self._proc.ppid()
-        else:
+        else:  # pragma: no cover
             self._ppid = self._ppid or self._proc.ppid()
             return self._ppid
 
     def name(self):
         """The process name. The return value is cached after first call."""
-        if self._name is None:
-            name = self._proc.name()
-            if _POSIX and len(name) >= 15:
-                # On UNIX the name gets truncated to the first 15 characters.
-                # If it matches the first part of the cmdline we return that
-                # one instead because it's usually more explicative.
-                # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon".
-                try:
-                    cmdline = self.cmdline()
-                except AccessDenied:
-                    pass
-                else:
-                    if cmdline:
-                        extended_name = os.path.basename(cmdline[0])
-                        if extended_name.startswith(name):
-                            name = extended_name
-            self._proc._name = name
-            self._name = name
-        return self._name
+        # Process name is only cached on Windows as on POSIX it may
+        # change, see:
+        # https://github.com/giampaolo/psutil/issues/692
+        if WINDOWS and self._name is not None:
+            return self._name
+        name = self._proc.name()
+        if POSIX and len(name) >= 15:
+            # On UNIX the name gets truncated to the first 15 characters.
+            # If it matches the first part of the cmdline we return that
+            # one instead because it's usually more explicative.
+            # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon".
+            try:
+                cmdline = self.cmdline()
+            except AccessDenied:
+                pass
+            else:
+                if cmdline:
+                    extended_name = os.path.basename(cmdline[0])
+                    if extended_name.startswith(name):
+                        name = extended_name
+        self._name = name
+        self._proc._name = name
+        return name
 
     def exe(self):
         """The process executable as an absolute path.
         May also be an empty string.
         The return value is cached after first call.
         """
         def guess_it(fallback):
             # try to guess exe from cmdline[0] in absence of a native
@@ -589,17 +671,17 @@ class Process(object):
             return self._proc.status()
         except ZombieProcess:
             return STATUS_ZOMBIE
 
     def username(self):
         """The name of the user that owns the process.
         On UNIX this is calculated by using *real* process uid.
         """
-        if _POSIX:
+        if POSIX:
             if pwd is None:
                 # might happen if python was installed from sources
                 raise ImportError(
                     "requires pwd module shipped with standard python")
             real_uid = self.uids().real
             try:
                 return pwd.getpwuid(real_uid).pw_name
             except KeyError:
@@ -625,18 +707,19 @@ class Process(object):
         """Get or set process niceness (priority)."""
         if value is None:
             return self._proc.nice_get()
         else:
             if not self.is_running():
                 raise NoSuchProcess(self.pid, self._name)
             self._proc.nice_set(value)
 
-    if _POSIX:
+    if POSIX:
 
+        @memoize_when_activated
         def uids(self):
             """Return process UIDs as a (real, effective, saved)
             namedtuple.
             """
             return self._proc.uids()
 
         def gids(self):
             """Return process GIDs as a (real, effective, saved)
@@ -651,17 +734,17 @@ class Process(object):
             return self._proc.terminal()
 
         def num_fds(self):
             """Return the number of file descriptors opened by this
             process (POSIX only).
             """
             return self._proc.num_fds()
 
-    # Linux, BSD and Windows only
+    # Linux, BSD, AIX and Windows only
     if hasattr(_psplatform.Process, "io_counters"):
 
         def io_counters(self):
             """Return process I/O statistics as a
             (read_count, write_count, read_bytes, write_bytes)
             namedtuple.
             Those are the number of read/write calls performed and the
             amount of bytes read and written by the process.
@@ -669,21 +752,21 @@ class Process(object):
             return self._proc.io_counters()
 
     # Linux and Windows >= Vista only
     if hasattr(_psplatform.Process, "ionice_get"):
 
         def ionice(self, ioclass=None, value=None):
             """Get or set process I/O niceness (priority).
 
-            On Linux 'ioclass' is one of the IOPRIO_CLASS_* constants.
-            'value' is a number which goes from 0 to 7. The higher the
+            On Linux *ioclass* is one of the IOPRIO_CLASS_* constants.
+            *value* is a number which goes from 0 to 7. The higher the
             value, the lower the I/O priority of the process.
 
-            On Windows only 'ioclass' is used and it can be set to 2
+            On Windows only *ioclass* is used and it can be set to 2
             (normal), 1 (low) or 0 (very low).
 
             Available on Linux and Windows > Vista only.
             """
             if ioclass is None:
                 if value is not None:
                     raise ValueError("'ioclass' argument must be specified")
                 return self._proc.ionice_get()
@@ -692,45 +775,73 @@ class Process(object):
 
     # Linux only
     if hasattr(_psplatform.Process, "rlimit"):
 
         def rlimit(self, resource, limits=None):
             """Get or set process resource limits as a (soft, hard)
             tuple.
 
-            'resource' is one of the RLIMIT_* constants.
-            'limits' is supposed to be a (soft, hard)  tuple.
+            *resource* is one of the RLIMIT_* constants.
+            *limits* is supposed to be a (soft, hard)  tuple.
 
             See "man prlimit" for further info.
             Available on Linux only.
             """
             if limits is None:
                 return self._proc.rlimit(resource)
             else:
                 return self._proc.rlimit(resource, limits)
 
-    # Windows, Linux and BSD only
+    # Windows, Linux and FreeBSD only
     if hasattr(_psplatform.Process, "cpu_affinity_get"):
 
         def cpu_affinity(self, cpus=None):
             """Get or set process CPU affinity.
-            If specified 'cpus' must be a list of CPUs for which you
+            If specified, *cpus* must be a list of CPUs for which you
             want to set the affinity (e.g. [0, 1]).
+            If an empty list is passed, all egible CPUs are assumed
+            (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:
+                if not cpus:
+                    if hasattr(self._proc, "_get_eligible_cpus"):
+                        cpus = self._proc._get_eligible_cpus()
+                    else:
+                        cpus = tuple(range(len(cpu_times(percpu=True))))
                 self._proc.cpu_affinity_set(list(set(cpus)))
 
-    if _WINDOWS:
+    # Linux, FreeBSD, SunOS
+    if hasattr(_psplatform.Process, "cpu_num"):
+
+        def cpu_num(self):
+            """Return what CPU this process is currently running on.
+            The returned number should be <= psutil.cpu_count()
+            and <= len(psutil.cpu_percent(percpu=True)).
+            It may be used in conjunction with
+            psutil.cpu_percent(percpu=True) to observe the system
+            workload distributed across CPUs.
+            """
+            return self._proc.cpu_num()
+
+    # Linux, OSX and Windows only
+    if hasattr(_psplatform.Process, "environ"):
+
+        def environ(self):
+            """The environment variables of the process as a dict.  Note: this
+            might not reflect changes made after the process started.  """
+            return self._proc.environ()
+
+    if WINDOWS:
 
         def num_handles(self):
             """Return the number of handles opened by this process
             (Windows only).
             """
             return self._proc.num_handles()
 
     def num_ctx_switches(self):
@@ -738,28 +849,31 @@ class Process(object):
         switches performed by this process.
         """
         return self._proc.num_ctx_switches()
 
     def num_threads(self):
         """Return the number of threads used by this process."""
         return self._proc.num_threads()
 
-    def threads(self):
-        """Return threads opened by process as a list of
-        (id, user_time, system_time) namedtuples representing
-        thread id and thread CPU times (user/system).
-        """
-        return self._proc.threads()
+    if hasattr(_psplatform.Process, "threads"):
+
+        def threads(self):
+            """Return threads opened by process as a list of
+            (id, user_time, system_time) namedtuples representing
+            thread id and thread CPU times (user/system).
+            On OpenBSD this method requires root access.
+            """
+            return self._proc.threads()
 
     @_assert_pid_not_reused
     def children(self, recursive=False):
         """Return the children of this process as a list of Process
         instances, pre-emptively checking whether PID has been reused.
-        If recursive is True return all the parent descendants.
+        If *recursive* is True return all the parent descendants.
 
         Example (A == this process):
 
          A ─┐

             ├─ B (child) ─┐
             │             └─ X (grandchild) ─┐
             │                                └─ Y (great grandchild)
@@ -772,120 +886,102 @@ class Process(object):
         B, C, D
         >>> p.children(recursive=True)
         B, X, Y, C, D
 
         Note that in the example above if process X disappears
         process Y won't be listed as the reference to process A
         is lost.
         """
-        if hasattr(_psplatform, 'ppid_map'):
-            # Windows only: obtain a {pid:ppid, ...} dict for all running
-            # processes in one shot (faster).
-            ppid_map = _psplatform.ppid_map()
-        else:
-            ppid_map = None
-
+        ppid_map = _ppid_map()
         ret = []
         if not recursive:
-            if ppid_map is None:
-                # 'slow' version, common to all platforms except Windows
-                for p in process_iter():
+            for pid, ppid in ppid_map.items():
+                if ppid == self.pid:
                     try:
-                        if p.ppid() == self.pid:
-                            # if child happens to be older than its parent
-                            # (self) it means child's PID has been reused
-                            if self.create_time() <= p.create_time():
-                                ret.append(p)
+                        child = Process(pid)
+                        # if child happens to be older than its parent
+                        # (self) it means child's PID has been reused
+                        if self.create_time() <= child.create_time():
+                            ret.append(child)
                     except (NoSuchProcess, ZombieProcess):
                         pass
-            else:
-                # Windows only (faster)
-                for pid, ppid in ppid_map.items():
-                    if ppid == self.pid:
-                        try:
-                            child = Process(pid)
-                            # if child happens to be older than its parent
-                            # (self) it means child's PID has been reused
-                            if self.create_time() <= child.create_time():
-                                ret.append(child)
-                        except (NoSuchProcess, ZombieProcess):
-                            pass
         else:
-            # construct a dict where 'values' are all the processes
-            # having 'key' as their parent
-            table = collections.defaultdict(list)
-            if ppid_map is None:
-                for p in process_iter():
+            # Construct a {pid: [child pids]} dict
+            reverse_ppid_map = collections.defaultdict(list)
+            for pid, ppid in ppid_map.items():
+                reverse_ppid_map[ppid].append(pid)
+            # Recursively traverse that dict, starting from self.pid,
+            # such that we only call Process() on actual children
+            seen = set()
+            stack = [self.pid]
+            while stack:
+                pid = stack.pop()
+                if pid in seen:
+                    # Since pids can be reused while the ppid_map is
+                    # constructed, there may be rare instances where
+                    # there's a cycle in the recorded process "tree".
+                    continue
+                seen.add(pid)
+                for child_pid in reverse_ppid_map[pid]:
                     try:
-                        table[p.ppid()].append(p)
-                    except (NoSuchProcess, ZombieProcess):
-                        pass
-            else:
-                for pid, ppid in ppid_map.items():
-                    try:
-                        p = Process(pid)
-                        table[ppid].append(p)
-                    except (NoSuchProcess, ZombieProcess):
-                        pass
-            # At this point we have a mapping table where table[self.pid]
-            # are the current process' children.
-            # Below, we look for all descendants recursively, similarly
-            # to a recursive function call.
-            checkpids = [self.pid]
-            for pid in checkpids:
-                for child in table[pid]:
-                    try:
+                        child = Process(child_pid)
                         # if child happens to be older than its parent
                         # (self) it means child's PID has been reused
                         intime = self.create_time() <= child.create_time()
+                        if intime:
+                            ret.append(child)
+                            stack.append(child_pid)
                     except (NoSuchProcess, ZombieProcess):
                         pass
-                    else:
-                        if intime:
-                            ret.append(child)
-                            if child.pid not in checkpids:
-                                checkpids.append(child.pid)
         return ret
 
     def cpu_percent(self, interval=None):
         """Return a float representing the current process CPU
         utilization as a percentage.
 
-        When interval is 0.0 or None (default) compares process times
+        When *interval* is 0.0 or None (default) compares process times
         to system CPU times elapsed since last call, returning
         immediately (non-blocking). That means that the first time
         this is called it will return a meaningful 0.0 value.
 
-        When interval is > 0.0 compares process times to system CPU
+        When *interval* is > 0.0 compares process times to system CPU
         times elapsed before and after the interval (blocking).
 
         In this case is recommended for accuracy that this function
         be called with at least 0.1 seconds between calls.
 
+        A value > 100.0 can be returned in case of processes running
+        multiple threads on different CPU cores.
+
+        The returned value is explicitly NOT split evenly between
+        all available logical CPUs. This means that a busy loop process
+        running on a system with 2 logical CPUs will be reported as
+        having 100% CPU utilization instead of 50%.
+
         Examples:
 
           >>> import psutil
           >>> p = psutil.Process(os.getpid())
           >>> # blocking
           >>> p.cpu_percent(interval=1)
           2.0
           >>> # non-blocking (percentage since last call)
           >>> p.cpu_percent(interval=None)
           2.9
           >>>
         """
         blocking = interval is not None and interval > 0.0
-        num_cpus = cpu_count()
-        if _POSIX:
-            def timer():
-                return _timer() * num_cpus
-        else:
-            def timer():
-                return sum(cpu_times())
+        if interval is not None and interval < 0:
+            raise ValueError("interval is not positive (got %r)" % interval)
+        num_cpus = cpu_count() or 1
+
+        def timer():
+            return _timer() * num_cpus
+
         if blocking:
             st1 = timer()
             pt1 = self._proc.cpu_times()
             time.sleep(interval)
             st2 = timer()
             pt2 = self._proc.cpu_times()
         else:
             st1 = self._last_sys_cpu_times
@@ -899,221 +995,287 @@ class Process(object):
 
         delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system)
         delta_time = st2 - st1
         # reset values for next call in case of interval == None
         self._last_sys_cpu_times = st2
         self._last_proc_cpu_times = pt2
 
         try:
-            # The utilization split between all CPUs.
-            # Note: a percentage > 100 is legitimate as it can result
-            # from a process with multiple threads running on different
-            # CPU cores, see:
-            # http://stackoverflow.com/questions/1032357
-            # https://github.com/giampaolo/psutil/issues/474
-            overall_percent = ((delta_proc / delta_time) * 100) * num_cpus
+            # This is the utilization split evenly between all CPUs.
+            # E.g. a busy loop process on a 2-CPU-cores system at this
+            # point is reported as 50% instead of 100%.
+            overall_cpus_percent = ((delta_proc / delta_time) * 100)
         except ZeroDivisionError:
             # interval was too low
             return 0.0
         else:
-            return round(overall_percent, 1)
+            # Note 1:
+            # in order to emulate "top" we multiply the value for the num
+            # of CPU cores. This way the busy process will be reported as
+            # having 100% (or more) usage.
+            #
+            # Note 2:
+            # taskmgr.exe on Windows differs in that it will show 50%
+            # instead.
+            #
+            # Note 3:
+            # a percentage > 100 is legitimate as it can result from a
+            # process with multiple threads running on different CPU
+            # cores (top does the same), see:
+            # http://stackoverflow.com/questions/1032357
+            # https://github.com/giampaolo/psutil/issues/474
+            single_cpu_percent = overall_cpus_percent * num_cpus
+            return round(single_cpu_percent, 1)
 
+    @memoize_when_activated
     def cpu_times(self):
-        """Return a (user, system) namedtuple representing  the
-        accumulated process time, in seconds.
-        This is the same as os.times() but per-process.
+        """Return a (user, system, children_user, children_system)
+        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
+        always set to 0.
         """
         return self._proc.cpu_times()
 
+    @memoize_when_activated
     def memory_info(self):
-        """Return a tuple representing RSS (Resident Set Size) and VMS
-        (Virtual Memory Size) in bytes.
+        """Return a namedtuple with variable fields depending on the
+        platform, representing memory information about the process.
 
-        On UNIX RSS and VMS are the same values shown by 'ps'.
+        The "portable" fields available on all plaforms are `rss` and `vms`.
 
-        On Windows RSS and VMS refer to "Mem Usage" and "VM Size"
-        columns of taskmgr.exe.
+        All numbers are expressed in bytes.
         """
         return self._proc.memory_info()
 
+    @deprecated_method(replacement="memory_info")
     def memory_info_ex(self):
-        """Return a namedtuple with variable fields depending on the
-        platform representing extended memory information about
-        this process. All numbers are expressed in bytes.
+        return self.memory_info()
+
+    def memory_full_info(self):
+        """This method returns the same information as memory_info(),
+        plus, on some platform (Linux, OSX, Windows), also provides
+        additional metrics (USS, PSS and swap).
+        The additional metrics provide a better representation of actual
+        process memory usage.
+
+        Namely USS is the memory which is unique to a process and which
+        would be freed if the process was terminated right now.
+
+        It does so by passing through the whole process address.
+        As such it usually requires higher user privileges than
+        memory_info() and is considerably slower.
         """
-        return self._proc.memory_info_ex()
+        return self._proc.memory_full_info()
+
+    def memory_percent(self, memtype="rss"):
+        """Compare process memory to total physical system memory and
+        calculate process memory utilization as a percentage.
+        *memtype* argument is a string that dictates what type of
+        process memory you want to compare against (defaults to "rss").
+        The list of available strings can be obtained like this:
 
-    def memory_percent(self):
-        """Compare physical system memory to process resident memory
-        (RSS) and calculate process memory utilization as a percentage.
+        >>> psutil.Process().memory_info()._fields
+        ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
         """
-        rss = self._proc.memory_info()[0]
+        valid_types = list(_psplatform.pfullmem._fields)
+        if memtype not in valid_types:
+            raise ValueError("invalid memtype %r; valid types are %r" % (
+                memtype, tuple(valid_types)))
+        fun = self.memory_info if memtype in _psplatform.pmem._fields else \
+            self.memory_full_info
+        metrics = fun()
+        value = getattr(metrics, memtype)
+
         # use cached value if available
         total_phymem = _TOTAL_PHYMEM or virtual_memory().total
-        try:
-            return (rss / float(total_phymem)) * 100
-        except ZeroDivisionError:
-            return 0.0
+        if not total_phymem > 0:
+            # we should never get here
+            raise ValueError(
+                "can't calculate process memory percent because "
+                "total physical system memory is not positive (%r)"
+                % total_phymem)
+        return (value / float(total_phymem)) * 100
 
-    def memory_maps(self, grouped=True):
-        """Return process' mapped memory regions as a list of namedtuples
-        whose fields are variable depending on the platform.
+    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.
 
-        If 'grouped' is True the mapped regions with the same 'path'
-        are grouped together and the different memory fields are summed.
+            If *grouped* is True the mapped regions with the same 'path'
+            are grouped together and the different memory fields are summed.
 
-        If 'grouped' is False every mapped region is shown as a single
-        entity and the namedtuple will also include the mapped region's
-        address space ('addr') and permission set ('perms').
-        """
-        it = self._proc.memory_maps()
-        if grouped:
-            d = {}
-            for tupl in it:
-                path = tupl[2]
-                nums = tupl[3:]
-                try:
-                    d[path] = map(lambda x, y: x + y, d[path], nums)
-                except KeyError:
-                    d[path] = nums
-            nt = _psplatform.pmmap_grouped
-            return [nt(path, *d[path]) for path in d]  # NOQA
-        else:
-            nt = _psplatform.pmmap_ext
-            return [nt(*x) for x in it]
+            If *grouped* is False every mapped region is shown as a single
+            entity and the namedtuple will also include the mapped region's
+            address space ('addr') and permission set ('perms').
+            """
+            it = self._proc.memory_maps()
+            if grouped:
+                d = {}
+                for tupl in it:
+                    path = tupl[2]
+                    nums = tupl[3:]
+                    try:
+                        d[path] = map(lambda x, y: x + y, d[path], nums)
+                    except KeyError:
+                        d[path] = nums
+                nt = _psplatform.pmmap_grouped
+                return [nt(path, *d[path]) for path in d]  # NOQA
+            else:
+                nt = _psplatform.pmmap_ext
+                return [nt(*x) for x in it]
 
     def open_files(self):
         """Return files opened by process as a list of
         (path, fd) namedtuples including the absolute file name
         and file descriptor number.
         """
         return self._proc.open_files()
 
     def connections(self, kind='inet'):
-        """Return connections opened by process as a list of
+        """Return socket connections opened by process as a list of
         (fd, family, type, laddr, raddr, status) namedtuples.
-        The 'kind' parameter filters for connections that match the
+        The *kind* parameter filters for connections that match the
         following criteria:
 
-        Kind Value      Connections using
-        inet            IPv4 and IPv6
-        inet4           IPv4
-        inet6           IPv6
-        tcp             TCP
-        tcp4            TCP over IPv4
-        tcp6            TCP over IPv6
-        udp             UDP
-        udp4            UDP over IPv4
-        udp6            UDP over IPv6
-        unix            UNIX socket (both UDP and TCP protocols)
-        all             the sum of all the possible families and protocols
+        +------------+----------------------------------------------------+
+        | Kind Value | Connections using                                  |
+        +------------+----------------------------------------------------+
+        | inet       | IPv4 and IPv6                                      |
+        | inet4      | IPv4                                               |
+        | inet6      | IPv6                                               |
+        | tcp        | TCP                                                |
+        | tcp4       | TCP over IPv4                                      |
+        | tcp6       | TCP over IPv6                                      |
+        | udp        | UDP                                                |
+        | udp4       | UDP over IPv4                                      |
+        | udp6       | UDP over IPv6                                      |
+        | unix       | UNIX socket (both UDP and TCP protocols)           |
+        | all        | the sum of all the possible families and protocols |
+        +------------+----------------------------------------------------+
         """
         return self._proc.connections(kind)
 
-    if _POSIX:
+    # --- signals
+
+    if POSIX:
         def _send_signal(self, sig):
+            assert not self.pid < 0, self.pid
             if self.pid == 0:
                 # see "man 2 kill"
                 raise ValueError(
                     "preventing sending signal to process with PID 0 as it "
                     "would affect every process in the process group of the "
                     "calling process (os.getpid()) instead of PID 0")
             try:
                 os.kill(self.pid, sig)
             except OSError as err:
                 if err.errno == errno.ESRCH:
-                    self._gone = True
-                    raise NoSuchProcess(self.pid, self._name)
-                if err.errno == errno.EPERM:
+                    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
 
     @_assert_pid_not_reused
     def send_signal(self, sig):
-        """Send a signal to process pre-emptively checking whether
-        PID has been reused (see signal module constants) .
+        """Send a signal *sig* to process pre-emptively checking
+        whether PID has been reused (see signal module constants) .
         On Windows only SIGTERM is valid and is treated as an alias
         for kill().
         """
-        if _POSIX:
+        if POSIX:
             self._send_signal(sig)
-        else:
+        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 is supported on Windows")
+                raise ValueError(
+                    "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals "
+                    "are supported on Windows")
 
     @_assert_pid_not_reused
     def suspend(self):
         """Suspend process execution with SIGSTOP pre-emptively checking
         whether PID has been reused.
         On Windows this has the effect ot suspending all process threads.
         """
-        if _POSIX:
+        if POSIX:
             self._send_signal(signal.SIGSTOP)
-        else:
+        else:  # pragma: no cover
             self._proc.suspend()
 
     @_assert_pid_not_reused
     def resume(self):
         """Resume process execution with SIGCONT pre-emptively checking
         whether PID has been reused.
         On Windows this has the effect of resuming all process threads.
         """
-        if _POSIX:
+        if POSIX:
             self._send_signal(signal.SIGCONT)
-        else:
+        else:  # pragma: no cover
             self._proc.resume()
 
     @_assert_pid_not_reused
     def terminate(self):
         """Terminate the process with SIGTERM pre-emptively checking
         whether PID has been reused.
         On Windows this is an alias for kill().
         """
-        if _POSIX:
+        if POSIX:
             self._send_signal(signal.SIGTERM)
-        else:
+        else:  # pragma: no cover
             self._proc.kill()
 
     @_assert_pid_not_reused
     def kill(self):
         """Kill the current process with SIGKILL pre-emptively checking
         whether PID has been reused.
         """
-        if _POSIX:
+        if POSIX:
             self._send_signal(signal.SIGKILL)
-        else:
+        else:  # pragma: no cover
             self._proc.kill()
 
     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.
 
         If the process is already terminated immediately return None
         instead of raising NoSuchProcess.
 
-        If timeout (in seconds) is specified and process is still alive
-        raise TimeoutExpired.
+        If *timeout* (in seconds) is specified and process is still
+        alive raise TimeoutExpired.
 
         To wait for multiple Process(es) use psutil.wait_procs().
         """
         if timeout is not None and not timeout >= 0:
             raise ValueError("timeout must be a positive integer")
         return self._proc.wait(timeout)
 
 
 # =====================================================================
 # --- Popen class
 # =====================================================================
 
 
 class Popen(Process):
-    """A more convenient interface to stdlib subprocess module.
+    """A more convenient interface to stdlib subprocess.Popen class.
     It starts a sub process and deals with it exactly as when using
     subprocess.Popen class but in addition also provides all the
     properties and methods of psutil.Process class as a unified
     interface:
 
       >>> import psutil
       >>> from subprocess import PIPE
       >>> p = psutil.Popen(["python", "-c", "print 'hi'"], stdout=PIPE)
@@ -1128,17 +1290,17 @@ class Popen(Process):
       >>> p.terminate()
       >>> p.wait(timeout=2)
       0
       >>>
 
     For method names common to both classes such as kill(), terminate()
     and wait(), psutil.Process implementation takes precedence.
 
-    Unlike subprocess.Popen this class pre-emptively checks wheter PID
+    Unlike subprocess.Popen this class pre-emptively checks whether PID
     has been reused on send_signal(), terminate() and kill() so that
     you don't accidentally terminate another process, fixing
     http://bugs.python.org/issue6973.
 
     For a complete documentation refer to:
     http://docs.python.org/library/subprocess.html
     """
 
@@ -1147,16 +1309,37 @@ class Popen(Process):
         # spawned by subprocess.Popen terminates too quickly, see:
         # https://github.com/giampaolo/psutil/issues/193
         self.__subproc = subprocess.Popen(*args, **kwargs)
         self._init(self.__subproc.pid, _ignore_nsp=True)
 
     def __dir__(self):
         return sorted(set(dir(Popen) + dir(subprocess.Popen)))
 
+    def __enter__(self):
+        if hasattr(self.__subproc, '__enter__'):
+            self.__subproc.__enter__()
+        return self
+
+    def __exit__(self, *args, **kwargs):
+        if hasattr(self.__subproc, '__exit__'):
+            return self.__subproc.__exit__(*args, **kwargs)
+        else:
+            if self.stdout:
+                self.stdout.close()
+            if self.stderr:
+                self.stderr.close()
+            try:
+                # Flushing a BufferedWriter may raise an error.
+                if self.stdin:
+                    self.stdin.close()
+            finally:
+                # Wait for the process to terminate, to avoid zombies.
+                self.wait()
+
     def __getattribute__(self, name):
         try:
             return object.__getattribute__(self, name)
         except AttributeError:
             try:
                 return object.__getattribute__(self.__subproc, name)
             except AttributeError:
                 raise AttributeError("%s instance has no attribute '%s'"
@@ -1165,16 +1348,24 @@ class Popen(Process):
     def wait(self, timeout=None):
         if self.__subproc.returncode is not None:
             return self.__subproc.returncode
         ret = super(Popen, self).wait(timeout)
         self.__subproc.returncode = ret
         return ret
 
 
+# The valid attr names which can be processed by Process.as_dict().
+_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',
+      'memory_info_ex', 'oneshot']])
+
+
 # =====================================================================
 # --- system processes related functions
 # =====================================================================
 
 
 def pids():
     """Return a list of current running PIDs."""
     return _psplatform.pids()
@@ -1182,46 +1373,55 @@ def pids():
 
 def pid_exists(pid):
     """Return True if given PID exists in the current process list.
     This is faster than doing "pid in psutil.pids()" and
     should be preferred.
     """
     if pid < 0:
         return False
-    elif pid == 0 and _POSIX:
+    elif pid == 0 and POSIX:
         # On POSIX we use os.kill() to determine PID existence.
         # According to "man 2 kill" PID 0 has a special meaning
         # though: it refers to <<every process in the process
         # group of the calling process>> and that is not we want
         # to do here.
         return pid in pids()
     else:
         return _psplatform.pid_exists(pid)
 
 
 _pmap = {}
 
 
-def process_iter():
+def process_iter(attrs=None, ad_value=None):
     """Return a generator yielding a Process instance for all
     running processes.
 
     Every new Process instance is only created once and then cached
     into an internal table which is updated every time this is used.
 
     Cached Process instances are checked for identity so that you're
     safe in case a PID has been reused by another process, in which
     case the cached instance is updated.
 
     The sorting order in which processes are yielded is based on
     their PIDs.
+
+    *attrs* and *ad_value* have the same meaning as in
+    Process.as_dict(). If *attrs* is specified as_dict() is called
+    and the resulting dict is stored as a 'info' attribute attached
+    to returned Process instance.
+    If *attrs* is an empty list it will retrieve all process info
+    (slow).
     """
     def add(pid):
         proc = Process(pid)
+        if attrs is not None:
+            proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value)
         _pmap[proc.pid] = proc
         return proc
 
     def remove(pid):
         _pmap.pop(pid, None)
 
     a = set(pids())
     b = set(_pmap.keys())
@@ -1234,43 +1434,56 @@ def process_iter():
                             list(dict.fromkeys(new_pids).items())):
         try:
             if proc is None:  # new process
                 yield add(pid)
             else:
                 # use is_running() to check whether PID has been reused by
                 # another process in which case yield a new Process instance
                 if proc.is_running():
+                    if attrs is not None:
+                        proc.info = proc.as_dict(
+                            attrs=attrs, ad_value=ad_value)
                     yield proc
                 else:
                     yield add(pid)
         except NoSuchProcess:
             remove(pid)
         except AccessDenied:
             # Process creation time can't be determined hence there's
             # no way to tell whether the pid of the cached process
             # has been reused. Just return the cached version.
-            yield proc
+            if proc is None and pid in _pmap:
+                try:
+                    yield _pmap[pid]
+                except KeyError:
+                    # If we get here it is likely that 2 threads were
+                    # using process_iter().
+                    pass
+            else:
+                raise
 
 
 def wait_procs(procs, timeout=None, callback=None):
     """Convenience function which waits for a list of processes 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
+    The gone ones will have a new *returncode* attribute indicating
     process exit status (may be None).
 
-    'callback' is a function which gets called every time a process
+    *callback* is a function which gets called every time a process
     terminates (a Process instance is passed as callback argument).
 
     Function will return as soon as all processes terminate or when
-    timeout occurs.
+    *timeout* occurs.
+    Differently from Process.wait() it will not raise TimeoutExpired if
+    *timeout* occurs.
 
     Typical use case is:
 
      - send SIGTERM to a list of processes
      - give them some time to terminate
      - send SIGKILL to those ones which are still alive
 
     Example:
@@ -1337,82 +1550,132 @@ def wait_procs(procs, timeout=None, call
     return (list(gone), list(alive))
 
 
 # =====================================================================
 # --- CPU related functions
 # =====================================================================
 
 
-@memoize
 def cpu_count(logical=True):
     """Return the number of logical CPUs in the system (same as
     os.cpu_count() in Python 3.4).
 
-    If logical is False return the number of physical cores only
+    If *logical* is False return the number of physical cores only
     (e.g. hyper thread CPUs are excluded).
 
     Return None if undetermined.
 
     The return value is cached after first call.
     If desired cache can be cleared like this:
 
     >>> psutil.cpu_count.cache_clear()
     """
     if logical:
-        return _psplatform.cpu_count_logical()
+        ret = _psplatform.cpu_count_logical()
     else:
-        return _psplatform.cpu_count_physical()
+        ret = _psplatform.cpu_count_physical()
+    if ret is not None and ret < 1:
+        ret = None
+    return ret
 
 
 def cpu_times(percpu=False):
     """Return system-wide CPU times as a namedtuple.
-    Every CPU time represents the seconds the CPU has spent in the given mode.
-    The namedtuple's fields availability varies depending on the platform:
+    Every CPU time represents the seconds the CPU has spent in the
+    given mode. The namedtuple's fields availability varies depending on the
+    platform:
+
      - user
      - system
      - idle
      - nice (UNIX)
      - iowait (Linux)
      - irq (Linux, FreeBSD)
      - softirq (Linux)
      - steal (Linux >= 2.6.11)
      - guest (Linux >= 2.6.24)
      - guest_nice (Linux >= 3.2.0)
 
-    When percpu is True return a list of namedtuples for each CPU.
+    When *percpu* is True return a list of namedtuples for each CPU.
     First element of the list refers to first CPU, second element
     to second CPU and so on.
     The order of the list is consistent across calls.
     """
     if not percpu:
         return _psplatform.cpu_times()
     else:
         return _psplatform.per_cpu_times()
 
 
-_last_cpu_times = cpu_times()
-_last_per_cpu_times = cpu_times(percpu=True)
+try:
+    _last_cpu_times = cpu_times()
+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):
+    """Given a cpu_time() ntuple calculates the total CPU time
+    (including idle time).
+    """
+    tot = sum(times)
+    if LINUX:
+        # On Linux guest times are already accounted in "user" or
+        # "nice" times, so we subtract them from total.
+        # Htop does the same. References:
+        # https://github.com/giampaolo/psutil/pull/940
+        # http://unix.stackexchange.com/questions/178045
+        # https://github.com/torvalds/linux/blob/
+        #     447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/
+        #     cputime.c#L158
+        tot -= getattr(times, "guest", 0)  # Linux 2.6.24+
+        tot -= getattr(times, "guest_nice", 0)  # Linux 3.2.0+
+    return tot
+
+
+def _cpu_busy_time(times):
+    """Given a cpu_time() ntuple calculates the busy CPU time.
+    We do so by subtracting all idle CPU times.
+    """
+    busy = _cpu_tot_time(times)
+    busy -= times.idle
+    # Linux: "iowait" is time during which the CPU does not do anything
+    # (waits for IO to complete). On Linux IO wait is *not* accounted
+    # in "idle" time so we subtract it. Htop does the same.
+    # References:
+    # https://github.com/torvalds/linux/blob/
+    #     447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/cputime.c#L244
+    busy -= getattr(times, "iowait", 0)
+    return busy
 
 
 def cpu_percent(interval=None, percpu=False):
     """Return a float representing the current system-wide CPU
     utilization as a percentage.
 
-    When interval is > 0.0 compares system CPU times elapsed before
+    When *interval* is > 0.0 compares system CPU times elapsed before
     and after the interval (blocking).
 
-    When interval is 0.0 or None compares system CPU times elapsed
+    When *interval* is 0.0 or None compares system CPU times elapsed
     since last call or module import, returning immediately (non
     blocking). That means the first time this is called it will
     return a meaningless 0.0 value which you should ignore.
     In this case is recommended for accuracy that this function be
     called with at least 0.1 seconds between calls.
 
-    When percpu is True returns a list of floats representing the
+    When *percpu* is True returns a list of floats representing the
     utilization as a percentage for each CPU.
     First element of the list refers to first CPU, second element
     to second CPU and so on.
     The order of the list is consistent across calls.
 
     Examples:
 
       >>> # blocking, system-wide
@@ -1426,50 +1689,66 @@ def cpu_percent(interval=None, percpu=Fa
       >>> # non-blocking (percentage since last call)
       >>> psutil.cpu_percent(interval=None)
       2.9
       >>>
     """
     global _last_cpu_times
     global _last_per_cpu_times
     blocking = interval is not None and interval > 0.0
+    if interval is not None and interval < 0:
+        raise ValueError("interval is not positive (got %r)" % interval)
 
     def calculate(t1, t2):
-        t1_all = sum(t1)
-        t1_busy = t1_all - t1.idle
+        t1_all = _cpu_tot_time(t1)
+        t1_busy = _cpu_busy_time(t1)
 
-        t2_all = sum(t2)
-        t2_busy = t2_all - t2.idle
+        t2_all = _cpu_tot_time(t2)
+        t2_busy = _cpu_busy_time(t2)
 
         # 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
-        busy_perc = (busy_delta / all_delta) * 100
-        return round(busy_perc, 1)
+        try:
+            busy_perc = (busy_delta / all_delta) * 100
+        except ZeroDivisionError:
+            return 0.0
+        else:
+            return round(busy_perc, 1)
 
     # system-wide usage
     if not percpu:
         if blocking:
             t1 = cpu_times()
             time.sleep(interval)
         else:
             t1 = _last_cpu_times
+            if t1 is None:
+                # Something bad happened at import time. We'll
+                # get a meaningful result on the next call. See:
+                # https://github.com/giampaolo/psutil/pull/715
+                t1 = cpu_times()
         _last_cpu_times = cpu_times()
         return calculate(t1, _last_cpu_times)
     # per-cpu usage
     else:
         ret = []
         if blocking:
             tot1 = cpu_times(percpu=True)
             time.sleep(interval)
         else:
             tot1 = _last_per_cpu_times
+            if tot1 is None:
+                # Something bad happened at import time. We'll
+                # get a meaningful result on the next call. See:
+                # https://github.com/giampaolo/psutil/pull/715
+                tot1 = cpu_times(percpu=True)
         _last_per_cpu_times = cpu_times(percpu=True)
         for t1, t2 in zip(tot1, _last_per_cpu_times):
             ret.append(calculate(t1, t2))
         return ret
 
 
 # Use separate global vars for cpu_times_percent() so that it's
 # independent from cpu_percent() and they can both be used within
@@ -1483,26 +1762,28 @@ def cpu_times_percent(interval=None, per
     for each specific CPU time as is returned by cpu_times().
     For instance, on Linux we'll get:
 
       >>> cpu_times_percent()
       cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0,
                  irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
       >>>
 
-    interval and percpu arguments have the same meaning as in
+    *interval* and *percpu* arguments have the same meaning as in
     cpu_percent().
     """
     global _last_cpu_times_2
     global _last_per_cpu_times_2
     blocking = interval is not None and interval > 0.0
+    if interval is not None and interval < 0:
+        raise ValueError("interval is not positive (got %r)" % interval)
 
     def calculate(t1, t2):
         nums = []
-        all_delta = sum(t2) - sum(t1)
+        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
             field_perc = round(field_perc, 1)
             # CPU times are always supposed to increase over time
@@ -1524,58 +1805,105 @@ def cpu_times_percent(interval=None, per
 
     # system-wide usage
     if not percpu:
         if blocking:
             t1 = cpu_times()
             time.sleep(interval)
         else:
             t1 = _last_cpu_times_2
+            if t1 is None:
+                # Something bad happened at import time. We'll
+                # get a meaningful result on the next call. See:
+                # https://github.com/giampaolo/psutil/pull/715
+                t1 = cpu_times()
         _last_cpu_times_2 = cpu_times()
         return calculate(t1, _last_cpu_times_2)
     # per-cpu usage
     else:
         ret = []
         if blocking:
             tot1 = cpu_times(percpu=True)
             time.sleep(interval)
         else:
             tot1 = _last_per_cpu_times_2
+            if tot1 is None:
+                # Something bad happened at import time. We'll
+                # get a meaningful result on the next call. See:
+                # https://github.com/giampaolo/psutil/pull/715
+                tot1 = cpu_times(percpu=True)
         _last_per_cpu_times_2 = cpu_times(percpu=True)
         for t1, t2 in zip(tot1, _last_per_cpu_times_2):
             ret.append(calculate(t1, t2))
         return ret
 
 
+def cpu_stats():
+    """Return CPU statistics."""
+    return _psplatform.cpu_stats()
+
+
+if hasattr(_psplatform, "cpu_freq"):
+
+    def cpu_freq(percpu=False):
+        """Return CPU frequency as a nameduple including current,
+        min and max frequency expressed in Mhz.
+
+        If *percpu* is True and the system supports per-cpu frequency
+        retrieval (Linux only) a list of frequencies is returned for
+        each CPU. If not a list with one element is returned.
+        """
+        ret = _psplatform.cpu_freq()
+        if percpu:
+            return ret
+        else:
+            num_cpus = float(len(ret))
+            if num_cpus == 0:
+                return None
+            elif num_cpus == 1:
+                return ret[0]
+            else:
+                currs, mins, maxs = 0.0, 0.0, 0.0
+                for cpu in ret:
+                    currs += cpu.current
+                    mins += cpu.min
+                    maxs += cpu.max
+                current = currs / num_cpus
+                min_ = mins / num_cpus
+                max_ = maxs / num_cpus
+                return _common.scpufreq(current, min_, max_)
+
+    __all__.append("cpu_freq")
+
+
 # =====================================================================
 # --- system memory related functions
 # =====================================================================
 
 
 def virtual_memory():
     """Return statistics about system memory usage as a namedtuple
     including the following fields, expressed in bytes:
 
      - total:
        total physical memory available.
 
      - available:
-       the actual amount of available memory that can be given
-       instantly to processes that request more memory in bytes; this
-       is calculated by summing different memory values depending on
-       the platform (e.g. free + buffers + cached on Linux) and it is
-       supposed to be used to monitor actual memory usage in a cross
-       platform fashion.
+       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 platform and it is supposed to be used to monitor actual
+       memory usage in a cross platform fashion.
 
      - percent:
        the percentage usage calculated as (total - available) / total * 100
 
      - used:
-       memory used, calculated differently depending on the platform and
-       designed for informational purposes only:
+        memory used, calculated differently depending on the platform and
+        designed for informational purposes only:
         OSX: active + inactive + wired
         BSD: active + wired + cached
         LINUX: total - free
 
      - free:
        memory not being used at all (zeroed) that is readily available;
        note that this doesn't reflect the actual memory available
        (use 'available' instead)
@@ -1627,164 +1955,209 @@ def swap_memory():
 
 
 # =====================================================================
 # --- disks/paritions related functions
 # =====================================================================
 
 
 def disk_usage(path):
-    """Return disk usage statistics about the given path as a namedtuple
-    including total, used and free space expressed in bytes plus the
-    percentage usage.
+    """Return disk usage statistics about the given *path* as a
+    namedtuple including total, used and free space expressed in bytes
+    plus the percentage usage.
     """
     return _psplatform.disk_usage(path)
 
 
 def disk_partitions(all=False):
     """Return mounted partitions as a list of
     (device, mountpoint, fstype, opts) namedtuple.
     'opts' field is a raw string separated by commas indicating mount
     options which may vary depending on the platform.
 
-    If "all" parameter is False return physical devices only and ignore
+    If *all* parameter is False return physical devices only and ignore
     all others.
     """
     return _psplatform.disk_partitions(all)
 
 
-def disk_io_counters(perdisk=False):
+def disk_io_counters(perdisk=False, nowrap=True):
     """Return system disk I/O statistics as a namedtuple including
     the following fields:
 
      - read_count:  number of reads
      - write_count: number of writes
      - read_bytes:  number of bytes read
      - write_bytes: number of bytes written
-     - read_time:   time spent reading from disk (in milliseconds)
-     - write_time:  time spent writing to disk (in milliseconds)
+     - read_time:   time spent reading from disk (in ms)
+     - write_time:  time spent writing to disk (in ms)
+
+    Platform specific:
 
-    If perdisk is True return the same information for every
+     - busy_time: (Linux, FreeBSD) time spent doing actual I/Os (in ms)
+     - read_merged_count (Linux): number of merged reads
+     - write_merged_count (Linux): number of merged writes
+
+    If *perdisk* is True return the same information for every
     physical disk installed on the system as a dictionary
     with partition names as the keys and the namedtuple
     described above as the values.
 
+    If *nowrap* is True it detects and adjust the numbers which overflow
+    and wrap (restart from 0) and add "old value" to "new value" so that
+    the returned numbers will always be increasing or remain the same,
+    but never decrease.
+    "disk_io_counters.cache_clear()" can be used to invalidate the
+    cache.
+
     On recent Windows versions 'diskperf -y' command may need to be
     executed first otherwise this function won't find any disk.
     """
     rawdict = _psplatform.disk_io_counters()
     if not rawdict:
-        raise RuntimeError("couldn't find any physical disk")
+        return {} if perdisk else None
+    if nowrap:
+        rawdict = _wrap_numbers(rawdict, 'psutil.disk_io_counters')
+    nt = getattr(_psplatform, "sdiskio", _common.sdiskio)
     if perdisk:
         for disk, fields in rawdict.items():
-            rawdict[disk] = _common.sdiskio(*fields)
+            rawdict[disk] = nt(*fields)
         return rawdict
     else:
-        return _common.sdiskio(*[sum(x) for x in zip(*rawdict.values())])
+        return nt(*[sum(x) for x in zip(*rawdict.values())])
+
+
+disk_io_counters.cache_clear = functools.partial(
+    _wrap_numbers.cache_clear, 'psutil.disk_io_counters')
+disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
 
 
 # =====================================================================
 # --- network related functions
 # =====================================================================
 
 
-def net_io_counters(pernic=False):
+def net_io_counters(pernic=False, nowrap=True):
     """Return network I/O statistics as a namedtuple including
     the following fields:
 
      - bytes_sent:   number of bytes sent
      - bytes_recv:   number of bytes received
      - packets_sent: number of packets sent
      - packets_recv: number of packets received
      - errin:        total number of errors while receiving
      - 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)
 
-    If pernic is True return the same information for every
+    If *pernic* is True return the same information for every
     network interface installed on the system as a dictionary
     with network interface names as the keys and the namedtuple
     described above as the values.
+
+    If *nowrap* is True it detects and adjust the numbers which overflow
+    and wrap (restart from 0) and add "old value" to "new value" so that
+    the returned numbers will always be increasing or remain the same,
+    but never decrease.
+    "disk_io_counters.cache_clear()" can be used to invalidate the
+    cache.
     """
     rawdict = _psplatform.net_io_counters()
     if not rawdict:
-        raise RuntimeError("couldn't find any network interface")
+        return {} if pernic else None
+    if nowrap:
+        rawdict = _wrap_numbers(rawdict, 'psutil.net_io_counters')
     if pernic:
         for nic, fields in rawdict.items():
             rawdict[nic] = _common.snetio(*fields)
         return rawdict
     else:
         return _common.snetio(*[sum(x) for x in zip(*rawdict.values())])
 
 
+net_io_counters.cache_clear = functools.partial(
+    _wrap_numbers.cache_clear, 'psutil.net_io_counters')
+net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
+
+
 def net_connections(kind='inet'):
-    """Return system-wide connections as a list of
+    """Return system-wide socket connections as a list of
     (fd, family, type, laddr, raddr, status, pid) namedtuples.
     In case of limited privileges 'fd' and 'pid' may be set to -1
     and None respectively.
-    The 'kind' parameter filters for connections that fit the
+    The *kind* parameter filters for connections that fit the
     following criteria:
 
-    Kind Value      Connections using
-    inet            IPv4 and IPv6
-    inet4           IPv4
-    inet6           IPv6
-    tcp             TCP
-    tcp4            TCP over IPv4
-    tcp6            TCP over IPv6
-    udp             UDP
-    udp4            UDP over IPv4
-    udp6            UDP over IPv6
-    unix            UNIX socket (both UDP and TCP protocols)
-    all             the sum of all the possible families and protocols
+    +------------+----------------------------------------------------+
+    | Kind Value | Connections using                                  |
+    +------------+----------------------------------------------------+
+    | inet       | IPv4 and IPv6                                      |
+    | inet4      | IPv4                                               |
+    | inet6      | IPv6                                               |
+    | tcp        | TCP                                                |
+    | tcp4       | TCP over IPv4                                      |
+    | tcp6       | TCP over IPv6                                      |
+    | udp        | UDP                                                |
+    | udp4       | UDP over IPv4                                      |
+    | udp6       | UDP over IPv6                                      |
+    | unix       | UNIX socket (both UDP and TCP protocols)           |
+    | all        | the sum of all the possible families and protocols |
+    +------------+----------------------------------------------------+
 
     On OSX this function requires root privileges.
     """
     return _psplatform.net_connections(kind)
 
 
 def net_if_addrs():
     """Return the addresses associated to each NIC (network interface
     card) installed on the system as a dictionary whose keys are the
     NIC names and value is a list of namedtuples for each address
-    assigned to the NIC. Each namedtuple includes 4 fields:
+    assigned to the NIC. Each namedtuple includes 5 fields:
 
-     - family
-     - address
-     - netmask
-     - broadcast
+     - family: can be either socket.AF_INET, socket.AF_INET6 or
+               psutil.AF_LINK, which refers to a MAC address.
+     - address: is the primary address and it is always set.
+     - netmask: and 'broadcast' and 'ptp' may be None.
+     - ptp: stands for "point to point" and references the
+            destination address on a point to point interface
+            (typically a VPN).
+     - broadcast: and *ptp* are mutually exclusive.
 
-    'family' can be either socket.AF_INET, socket.AF_INET6 or
-    psutil.AF_LINK, which refers to a MAC address.
-    'address' is the primary address, 'netmask' and 'broadcast'
-    may be None.
     Note: you can have more than one address of the same family
     associated with each interface.
     """
     has_enums = sys.version_info >= (3, 4)
     if has_enums:
         import socket
     rawlist = _psplatform.net_if_addrs()
     rawlist.sort(key=lambda x: x[1])  # sort by family
     ret = collections.defaultdict(list)
-    for name, fam, addr, mask, broadcast in rawlist:
+    for name, fam, addr, mask, broadcast, ptp in rawlist:
         if has_enums:
             try:
                 fam = socket.AddressFamily(fam)
             except ValueError:
-                if os.name == 'nt' and fam == -1:
+                if WINDOWS and fam == -1:
                     fam = _psplatform.AF_LINK
                 elif (hasattr(_psplatform, "AF_LINK") and
                         _psplatform.AF_LINK == fam):
                     # Linux defines AF_LINK as an alias for AF_PACKET.
                     # We re-set the family here so that repr(family)
                     # will show AF_LINK rather than AF_PACKET
                     fam = _psplatform.AF_LINK
-        ret[name].append(_common.snic(fam, addr, mask, broadcast))
+        if fam == _psplatform.AF_LINK:
+            # The underlying C function may return an incomplete MAC
+            # address in which case we fill it with null bytes, see:
+            # https://github.com/giampaolo/psutil/issues/786
+            separator = ":" if POSIX else "-"
+            while addr.count(separator) < 5:
+                addr += "%s00" % separator
+        ret[name].append(_common.snic(fam, addr, mask, broadcast, ptp))
     return dict(ret)
 
 
 def net_if_stats():
     """Return information about each NIC (network interface card)
     installed on the system as a dictionary whose keys are the
     NIC names and value is a namedtuple with the following fields:
 
@@ -1794,16 +2167,90 @@ def net_if_stats():
      - speed: the NIC speed expressed in mega bits (MB); if it can't
               be determined (e.g. 'localhost') it will be set to 0.
      - mtu: the maximum transmission unit expressed in bytes.
     """
     return _psplatform.net_if_stats()
 
 
 # =====================================================================
+# --- sensors
+# =====================================================================
+
+
+# Linux
+if hasattr(_psplatform, "sensors_temperatures"):
+
+    def sensors_temperatures(fahrenheit=False):
+        """Return hardware temperatures. Each entry is a namedtuple
+        representing a certain hardware sensor (it may be a CPU, an
+        hard disk or something else, depending on the OS and its
+        configuration).
+        All temperatures are expressed in celsius unless *fahrenheit*
+        is set to True.
+        """
+        def convert(n):
+            if n is not None:
+                return (float(n) * 9 / 5) + 32 if fahrenheit else n
+
+        ret = collections.defaultdict(list)
+        rawdict = _psplatform.sensors_temperatures()
+
+        for name, values in rawdict.items():
+            while values:
+                label, current, high, critical = values.pop(0)
+                current = convert(current)
+                high = convert(high)
+                critical = convert(critical)
+
+                if high and not critical:
+                    critical = high
+                elif critical and not high:
+                    high = critical
+
+                ret[name].append(
+                    _common.shwtemp(label, current, high, critical))
+
+        return dict(ret)
+
+    __all__.append("sensors_temperatures")
+
+
+# Linux
+if hasattr(_psplatform, "sensors_fans"):
+
+    def sensors_fans():
+        """Return fans speed. Each entry is a namedtuple
+        representing a certain hardware sensor.
+        All speed are expressed in RPM (rounds per minute).
+        """
+        return _psplatform.sensors_fans()
+
+    __all__.append("sensors_fans")
+
+
+# Linux, Windows, FreeBSD, OSX
+if hasattr(_psplatform, "sensors_battery"):
+
+    def sensors_battery():
+        """Return battery information. If no battery is installed
+        returns None.
+
+         - percent: battery power left as a percentage.
+         - secsleft: a rough approximation of how many seconds are left
+                     before the battery runs out of power. May be
+                     POWER_TIME_UNLIMITED or POWER_TIME_UNLIMITED.
+         - power_plugged: True if the AC power cable is connected.
+        """
+        return _psplatform.sensors_battery()
+
+    __all__.append("sensors_battery")
+
+
+# =====================================================================
 # --- other system related functions
 # =====================================================================
 
 
 def boot_time():
     """Return the system boot time expressed in seconds since the epoch."""
     # Note: we are not caching this because it is subject to
     # system clock updates.
@@ -1818,70 +2265,85 @@ def users():
      - terminal: the tty or pseudo-tty associated with the user, if any.
      - host: the host name associated with the entry, if any.
      - started: the creation time as a floating point number expressed in
        seconds since the epoch.
     """
     return _psplatform.users()
 
 
-def test():
+# =====================================================================
+# --- Windows services
+# =====================================================================
+
+
+if WINDOWS:
+
+    def win_service_iter():
+        """Return a generator yielding a WindowsService instance for all
+        Windows services installed.
+        """
+        return _psplatform.win_service_iter()
+
+    def win_service_get(name):
+        """Get a Windows service by *name*.
+        Raise NoSuchProcess if no service with such name exists.
+        """
+        return _psplatform.win_service_get(name)
+
+
+# =====================================================================
+
+
+def test():  # pragma: no cover
     """List info of all currently running processes emulating ps aux
     output.
     """
-    import datetime
-
     today_day = datetime.date.today()
-    templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s  %s"
-    attrs = ['pid', 'cpu_percent', 'memory_percent', 'name', 'cpu_times',
-             'create_time', 'memory_info']
-    if _POSIX:
+    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", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
-                   "START", "TIME", "COMMAND"))
-    for p in process_iter():
-        try:
-            pinfo = p.as_dict(attrs, ad_value='')
-        except NoSuchProcess:
-            pass
-        else:
-            if pinfo['create_time']:
-                ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
-                if ctime.date() == today_day:
-                    ctime = ctime.strftime("%H:%M")
-                else:
-                    ctime = ctime.strftime("%b%d")
+    print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "TTY", "START", "TIME",
+                   "COMMAND"))
+    for p in process_iter(attrs=attrs, ad_value=''):
+        if p.info['create_time']:
+            ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
+            if ctime.date() == today_day:
+                ctime = ctime.strftime("%H:%M")
             else:
-                ctime = ''
-            cputime = time.strftime("%M:%S",
-                                    time.localtime(sum(pinfo['cpu_times'])))
-            try:
-                user = p.username()
-            except Error:
-                user = ''
-            if _WINDOWS and '\\' in user:
-                user = user.split('\\')[1]
-            vms = pinfo['memory_info'] and \
-                int(pinfo['memory_info'].vms / 1024) or '?'
-            rss = pinfo['memory_info'] and \
-                int(pinfo['memory_info'].rss / 1024) or '?'
-            memp = pinfo['memory_percent'] and \
-                round(pinfo['memory_percent'], 1) or '?'
-            print(templ % (
-                user[:10],
-                pinfo['pid'],
-                pinfo['cpu_percent'],
-                memp,
-                vms,
-                rss,
-                pinfo.get('terminal', '') or '?',
-                ctime,
-                cputime,
-                pinfo['name'].strip() or '?'))
+                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:
+            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[:10],
+            p.info['pid'],
+            memp,
+            vms,
+            rss,
+            p.info.get('terminal', '') or '?',
+            ctime,
+            cputime,
+            p.info['name'].strip() or '?'))
 
 
-del memoize, division
-if sys.version_info < (3, 0):
-    del num
+del memoize, memoize_when_activated, division, deprecated_method
+if sys.version_info[0] < 3:
+    del num, x
 
 if __name__ == "__main__":
     test()
--- a/third_party/python/psutil/psutil/_common.py
+++ b/third_party/python/psutil/psutil/_common.py
@@ -1,175 +1,240 @@
-# /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.
 
-"""Common objects shared by all _ps* modules."""
+"""Common objects shared by __init__.py and _ps*.py modules."""
+
+# Note: this module is imported by setup.py so it should not import
+# psutil or third-party modules.
 
 from __future__ import division
+
+import contextlib
 import errno
 import functools
 import os
 import socket
 import stat
 import sys
+import threading
+import warnings
+from collections import defaultdict
 from collections import namedtuple
-from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
+from socket import AF_INET
+from socket import SOCK_DGRAM
+from socket import SOCK_STREAM
 try:
-    import threading
+    from socket import AF_INET6
 except ImportError:
-    import dummy_threading as threading
+    AF_INET6 = None
+try:
+    from socket import AF_UNIX
+except ImportError:
+    AF_UNIX = None
 
 if sys.version_info >= (3, 4):
     import enum
 else:
     enum = None
 
-
-# --- constants
+# can't take it from _common.py as this script is imported by setup.py
+PY3 = sys.version_info[0] == 3
 
-AF_INET6 = getattr(socket, 'AF_INET6', None)
-AF_UNIX = getattr(socket, 'AF_UNIX', None)
+__all__ = [
+    # constants
+    'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', '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',
+    'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT',
+    # net constants
+    'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN',
+    # process status constants
+    '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',
+    # named tuples
+    'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile',
+    'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart',
+    'sdiskusage', 'snetio', 'snic', '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",
+]
 
+
+# ===================================================================
+# --- OS constants
+# ===================================================================
+
+
+POSIX = os.name == "posix"
+WINDOWS = os.name == "nt"
+LINUX = sys.platform.startswith("linux")
+OSX = sys.platform.startswith("darwin")
+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")
+AIX = sys.platform.startswith("aix")
+
+
+# ===================================================================
+# --- API constants
+# ===================================================================
+
+
+# Process.status()
 STATUS_RUNNING = "running"
 STATUS_SLEEPING = "sleeping"
 STATUS_DISK_SLEEP = "disk-sleep"
 STATUS_STOPPED = "stopped"
 STATUS_TRACING_STOP = "tracing-stop"
 STATUS_ZOMBIE = "zombie"
 STATUS_DEAD = "dead"
 STATUS_WAKE_KILL = "wake-kill"
 STATUS_WAKING = "waking"
-STATUS_IDLE = "idle"  # BSD
-STATUS_LOCKED = "locked"  # BSD
-STATUS_WAITING = "waiting"  # BSD
+STATUS_IDLE = "idle"  # FreeBSD, OSX
+STATUS_LOCKED = "locked"  # FreeBSD
+STATUS_WAITING = "waiting"  # FreeBSD
+STATUS_SUSPENDED = "suspended"  # NetBSD
 
+# Process.connections() and psutil.net_connections()
 CONN_ESTABLISHED = "ESTABLISHED"
 CONN_SYN_SENT = "SYN_SENT"
 CONN_SYN_RECV = "SYN_RECV"
 CONN_FIN_WAIT1 = "FIN_WAIT1"
 CONN_FIN_WAIT2 = "FIN_WAIT2"
 CONN_TIME_WAIT = "TIME_WAIT"
 CONN_CLOSE = "CLOSE"
 CONN_CLOSE_WAIT = "CLOSE_WAIT"
 CONN_LAST_ACK = "LAST_ACK"
 CONN_LISTEN = "LISTEN"
 CONN_CLOSING = "CLOSING"
 CONN_NONE = "NONE"
 
+# net_if_stats()
 if enum is None:
     NIC_DUPLEX_FULL = 2
     NIC_DUPLEX_HALF = 1
     NIC_DUPLEX_UNKNOWN = 0
 else:
     class NicDuplex(enum.IntEnum):
         NIC_DUPLEX_FULL = 2
         NIC_DUPLEX_HALF = 1
         NIC_DUPLEX_UNKNOWN = 0
 
     globals().update(NicDuplex.__members__)
 
-
-# --- functions
+# sensors_battery()
+if enum is None:
+    POWER_TIME_UNKNOWN = -1
+    POWER_TIME_UNLIMITED = -2
+else:
+    class BatteryTime(enum.IntEnum):
+        POWER_TIME_UNKNOWN = -1
+        POWER_TIME_UNLIMITED = -2
 
-def usage_percent(used, total, _round=None):
-    """Calculate percentage usage of 'used' against 'total'."""
+    globals().update(BatteryTime.__members__)
+
+# --- others
+
+ENCODING = sys.getfilesystemencoding()
+if not PY3:
+    ENCODING_ERRS = "replace"
+else:
     try:
-        ret = (used / total) * 100
-    except ZeroDivisionError:
-        ret = 0
-    if _round is not None:
-        return round(ret, _round)
-    else:
-        return ret
+        ENCODING_ERRS = sys.getfilesystemencodeerrors()  # py 3.6
+    except AttributeError:
+        ENCODING_ERRS = "surrogateescape" if POSIX else "replace"
 
 
-def memoize(fun):
-    """A simple memoize decorator for functions supporting (hashable)
-    positional arguments.
-    It also provides a cache_clear() function for clearing the cache:
+# ===================================================================
+# --- namedtuples
+# ===================================================================
+
+# --- for system functions
 
-    >>> @memoize
-    ... def foo()
-    ...     return 1
-    ...
-    >>> foo()
-    1
-    >>> foo.cache_clear()
-    >>>
-    """
-    @functools.wraps(fun)
-    def wrapper(*args, **kwargs):
-        key = (args, frozenset(sorted(kwargs.items())))
-        lock.acquire()
-        try:
-            try:
-                return cache[key]
-            except KeyError:
-                ret = cache[key] = fun(*args, **kwargs)
-        finally:
-            lock.release()
-        return ret
+# psutil.swap_memory()
+sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
+                             'sout'])
+# psutil.disk_usage()
+sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent'])
+# psutil.disk_io_counters()
+sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
+                                 'read_bytes', 'write_bytes',
+                                 'read_time', 'write_time'])
+# psutil.disk_partitions()
+sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
+# psutil.net_io_counters()
+snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
+                               'packets_sent', 'packets_recv',
+                               'errin', 'errout',
+                               'dropin', 'dropout'])
+# psutil.users()
+suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid'])
+# psutil.net_connections()
+sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
+                             'status', 'pid'])
+# psutil.net_if_addrs()
+snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp'])
+# psutil.net_if_stats()
+snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
+# psutil.cpu_stats()
+scpustats = namedtuple(
+    'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'])
+# psutil.cpu_freq()
+scpufreq = namedtuple('scpufreq', ['current', 'min', 'max'])
+# psutil.sensors_temperatures()
+shwtemp = namedtuple(
+    'shwtemp', ['label', 'current', 'high', 'critical'])
+# psutil.sensors_battery()
+sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])
+# psutil.sensors_battery()
+sfan = namedtuple('sfan', ['label', 'current'])
 
-    def cache_clear():
-        """Clear cache."""
-        lock.acquire()
-        try:
-            cache.clear()
-        finally:
-            lock.release()
+# --- for Process methods
 
-    lock = threading.RLock()
-    cache = {}
-    wrapper.cache_clear = cache_clear
-    return wrapper
+# psutil.Process.cpu_times()
+pcputimes = namedtuple('pcputimes',
+                       ['user', 'system', 'children_user', 'children_system'])
+# psutil.Process.open_files()
+popenfile = namedtuple('popenfile', ['path', 'fd'])
+# psutil.Process.threads()
+pthread = namedtuple('pthread', ['id', 'user_time', 'system_time'])
+# psutil.Process.uids()
+puids = namedtuple('puids', ['real', 'effective', 'saved'])
+# psutil.Process.gids()
+pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
+# psutil.Process.io_counters()
+pio = namedtuple('pio', ['read_count', 'write_count',
+                         'read_bytes', 'write_bytes'])
+# psutil.Process.ionice()
+pionice = namedtuple('pionice', ['ioclass', 'value'])
+# psutil.Process.ctx_switches()
+pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
+# psutil.Process.connections()
+pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
+                             'status'])
+
+# psutil.connections() and psutil.Process.connections()
+addr = namedtuple('addr', ['ip', 'port'])
 
 
-def isfile_strict(path):
-    """Same as os.path.isfile() but does not swallow EACCES / EPERM
-    exceptions, see:
-    http://mail.python.org/pipermail/python-dev/2012-June/120787.html
-    """
-    try:
-        st = os.stat(path)
-    except OSError as err:
-        if err.errno in (errno.EPERM, errno.EACCES):
-            raise
-        return False
-    else:
-        return stat.S_ISREG(st.st_mode)
-
+# ===================================================================
+# --- Process.connections() 'kind' parameter mapping
+# ===================================================================
 
-def sockfam_to_enum(num):
-    """Convert a numeric socket family value to an IntEnum member.
-    If it's not a known member, return the numeric value itself.
-    """
-    if enum is None:
-        return num
-    try:
-        return socket.AddressFamily(num)
-    except (ValueError, AttributeError):
-        return num
-
-
-def socktype_to_enum(num):
-    """Convert a numeric socket type value to an IntEnum member.
-    If it's not a known member, return the numeric value itself.
-    """
-    if enum is None:
-        return num
-    try:
-        return socket.AddressType(num)
-    except (ValueError, AttributeError):
-        return num
-
-
-# --- Process.connections() 'kind' parameter mapping
 
 conn_tmap = {
     "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
     "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]),
     "tcp4": ([AF_INET], [SOCK_STREAM]),
     "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]),
     "udp4": ([AF_INET], [SOCK_DGRAM]),
     "inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
@@ -183,64 +248,328 @@ if AF_INET6 is not None:
         "udp6": ([AF_INET6], [SOCK_DGRAM]),
     })
 
 if AF_UNIX is not None:
     conn_tmap.update({
         "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
     })
 
-del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
+del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
+
+
+# ===================================================================
+# --- utils
+# ===================================================================
+
+
+def usage_percent(used, total, _round=None):
+    """Calculate percentage usage of 'used' against 'total'."""
+    try:
+        ret = (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)
+    else:
+        return ret
+
+
+def memoize(fun):
+    """A simple memoize decorator for functions supporting (hashable)
+    positional arguments.
+    It also provides a cache_clear() function for clearing the cache:
+
+    >>> @memoize
+    ... def foo()
+    ...     return 1
+        ...
+    >>> foo()
+    1
+    >>> foo.cache_clear()
+    >>>
+    """
+    @functools.wraps(fun)
+    def wrapper(*args, **kwargs):
+        key = (args, frozenset(sorted(kwargs.items())))
+        try:
+            return cache[key]
+        except KeyError:
+            ret = cache[key] = fun(*args, **kwargs)
+            return ret
+
+    def cache_clear():
+        """Clear cache."""
+        cache.clear()
+
+    cache = {}
+    wrapper.cache_clear = cache_clear
+    return wrapper
+
+
+def memoize_when_activated(fun):
+    """A memoize decorator which is disabled by default. It can be
+    activated and deactivated on request.
+    For efficiency reasons it can be used only against class methods
+    accepting no arguments.
+
+    >>> class Foo:
+    ...     @memoize
+    ...     def foo()
+    ...         print(1)
+    ...
+    >>> f = Foo()
+    >>> # deactivated (default)
+    >>> foo()
+    1
+    >>> foo()
+    1
+    >>>
+    >>> # activated
+    >>> foo.cache_activate()
+    >>> foo()
+    1
+    >>> foo()
+    >>> foo()
+    >>>
+    """
+    @functools.wraps(fun)
+    def wrapper(self):
+        if not wrapper.cache_activated:
+            return fun(self)
+        else:
+            try:
+                ret = cache[fun]
+            except KeyError:
+                ret = cache[fun] = fun(self)
+            return ret
+
+    def cache_activate():
+        """Activate cache."""
+        wrapper.cache_activated = True
+
+    def cache_deactivate():
+        """Deactivate and clear cache."""
+        wrapper.cache_activated = False
+        cache.clear()
+
+    cache = {}
+    wrapper.cache_activated = False
+    wrapper.cache_activate = cache_activate
+    wrapper.cache_deactivate = cache_deactivate
+    return wrapper
+
+
+def isfile_strict(path):
+    """Same as os.path.isfile() but does not swallow EACCES / EPERM
+    exceptions, see:
+    http://mail.python.org/pipermail/python-dev/2012-June/120787.html
+    """
+    try:
+        st = os.stat(path)
+    except OSError as err:
+        if err.errno in (errno.EPERM, errno.EACCES):
+            raise
+        return False
+    else:
+        return stat.S_ISREG(st.st_mode)
+
+
+def path_exists_strict(path):
+    """Same as os.path.exists() but does not swallow EACCES / EPERM
+    exceptions, see:
+    http://mail.python.org/pipermail/python-dev/2012-June/120787.html
+    """
+    try:
+        os.stat(path)
+    except OSError as err:
+        if err.errno in (errno.EPERM, errno.EACCES):
+            raise
+        return False
+    else:
+        return True
+
+
+@memoize
+def supports_ipv6():
+    """Return True if IPv6 is supported on this platform."""
+    if not socket.has_ipv6 or AF_INET6 is None:
+        return False
+    try:
+        sock = socket.socket(AF_INET6, socket.SOCK_STREAM)
+        with contextlib.closing(sock):
+            sock.bind(("::1", 0))
+        return True
+    except socket.error:
+        return False
 
 
-# --- namedtuples for psutil.* system-related functions
+def parse_environ_block(data):
+    """Parse a C environ block of environment variables into a dictionary."""
+    # The block is usually raw data from the target process.  It might contain
+    # trailing garbage and lines that do not look like assignments.
+    ret = {}
+    pos = 0
+
+    # localize global variable to speed up access.
+    WINDOWS_ = WINDOWS
+    while True:
+        next_pos = data.find("\0", pos)
+        # nul byte at the beginning or double nul byte means finish
+        if next_pos <= pos:
+            break
+        # there might not be an equals sign
+        equal_pos = data.find("=", pos, next_pos)
+        if equal_pos > pos:
+            key = data[pos:equal_pos]
+            value = data[equal_pos + 1:next_pos]
+            # Windows expects environment variables to be uppercase only
+            if WINDOWS_:
+                key = key.upper()
+            ret[key] = value
+        pos = next_pos + 1
+
+    return ret
+
 
-# psutil.swap_memory()
-sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
-                             'sout'])
-# psutil.disk_usage()
-sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent'])
-# psutil.disk_io_counters()
-sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
-                                 'read_bytes', 'write_bytes',
-                                 'read_time', 'write_time'])
-# psutil.disk_partitions()
-sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
-# psutil.net_io_counters()
-snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
-                               'packets_sent', 'packets_recv',
-                               'errin', 'errout',
-                               'dropin', 'dropout'])
-# psutil.users()
-suser = namedtuple('suser', ['name', 'terminal', 'host', 'started'])
-# psutil.net_connections()
-sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
-                             'status', 'pid'])
-# psutil.net_if_addrs()
-snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast'])
-# psutil.net_if_stats()
-snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
+def sockfam_to_enum(num):
+    """Convert a numeric socket family value to an IntEnum member.
+    If it's not a known member, return the numeric value itself.
+    """
+    if enum is None:
+        return num
+    else:  # pragma: no cover
+        try:
+            return socket.AddressFamily(num)
+        except (ValueError, AttributeError):
+            return num
+
+
+def socktype_to_enum(num):
+    """Convert a numeric socket type value to an IntEnum member.
+    If it's not a known member, return the numeric value itself.
+    """
+    if enum is None:
+        return num
+    else:  # pragma: no cover
+        try:
+            return socket.AddressType(num)
+        except (ValueError, AttributeError):
+            return num
+
+
+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.
+    """
+    def outer(fun):
+        msg = "%s() is deprecated and will be removed; use %s() instead" % (
+            fun.__name__, replacement)
+        if fun.__doc__ is None:
+            fun.__doc__ = msg
+
+        @functools.wraps(fun)
+        def inner(self, *args, **kwargs):
+            warnings.warn(msg, category=FutureWarning, stacklevel=2)
+            return getattr(self, replacement)(*args, **kwargs)
+        return inner
+    return outer
 
 
-# --- namedtuples for psutil.Process methods
+class _WrapNumbers:
+    """Watches numbers so that they don't overflow and wrap
+    (reset to zero).
+    """
+
+    def __init__(self):
+        self.lock = threading.Lock()
+        self.cache = {}
+        self.reminders = {}
+        self.reminder_keys = {}
+
+    def _add_dict(self, input_dict, name):
+        assert name not in self.cache
+        assert name not in self.reminders
+        assert name not in self.reminder_keys
+        self.cache[name] = input_dict
+        self.reminders[name] = defaultdict(int)
+        self.reminder_keys[name] = defaultdict(set)
+
+    def _remove_dead_reminders(self, input_dict, name):
+        """In case the number of keys changed between calls (e.g. a
+        disk disappears) this removes the entry from self.reminders.
+        """
+        old_dict = self.cache[name]
+        gone_keys = set(old_dict.keys()) - set(input_dict.keys())
+        for gone_key in gone_keys:
+            for remkey in self.reminder_keys[name][gone_key]:
+                del self.reminders[name][remkey]
+            del self.reminder_keys[name][gone_key]
+
+    def run(self, input_dict, name):
+        """Cache dict and sum numbers which overflow and wrap.
+        Return an updated copy of `input_dict`
+        """
+        if name not in self.cache:
+            # This was the first call.
+            self._add_dict(input_dict, name)
+            return input_dict
+
+        self._remove_dead_reminders(input_dict, name)
 
-# psutil.Process.memory_info()
-pmem = namedtuple('pmem', ['rss', 'vms'])
-# psutil.Process.cpu_times()
-pcputimes = namedtuple('pcputimes', ['user', 'system'])
-# psutil.Process.open_files()
-popenfile = namedtuple('popenfile', ['path', 'fd'])
-# psutil.Process.threads()
-pthread = namedtuple('pthread', ['id', 'user_time', 'system_time'])
-# psutil.Process.uids()
-puids = namedtuple('puids', ['real', 'effective', 'saved'])
-# psutil.Process.gids()
-pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
-# psutil.Process.io_counters()
-pio = namedtuple('pio', ['read_count', 'write_count',
-                         'read_bytes', 'write_bytes'])
-# psutil.Process.ionice()
-pionice = namedtuple('pionice', ['ioclass', 'value'])
-# psutil.Process.ctx_switches()
-pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
-# psutil.Process.connections()
-pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
-                             'status'])
+        old_dict = self.cache[name]
+        new_dict = {}
+        for key in input_dict.keys():
+            input_tuple = input_dict[key]
+            try:
+                old_tuple = old_dict[key]
+            except KeyError:
+                # The input dict has a new key (e.g. a new disk or NIC)
+                # which didn't exist in the previous call.
+                new_dict[key] = input_tuple
+                continue
+
+            bits = []
+            for i in range(len(input_tuple)):
+                input_value = input_tuple[i]
+                old_value = old_tuple[i]
+                remkey = (key, i)
+                if input_value < old_value:
+                    # it wrapped!
+                    self.reminders[name][remkey] += old_value
+                    self.reminder_keys[name][key].add(remkey)
+                bits.append(input_value + self.reminders[name][remkey])
+
+            new_dict[key] = tuple(bits)
+
+        self.cache[name] = input_dict
+        return new_dict
+
+    def cache_clear(self, name=None):
+        """Clear the internal cache, optionally only for function 'name'."""
+        with self.lock:
+            if name is None:
+                self.cache.clear()
+                self.reminders.clear()
+                self.reminder_keys.clear()
+            else:
+                self.cache.pop(name, None)
+                self.reminders.pop(name, None)
+                self.reminder_keys.pop(name, None)
+
+    def cache_info(self):
+        """Return internal cache dicts as a tuple of 3 elements."""
+        with self.lock:
+            return (self.cache, self.reminders, self.reminder_keys)
+
+
+def wrap_numbers(input_dict, name):
+    """Given an `input_dict` and a function `name`, adjust the numbers
+    which "wrap" (restart from zero) across different calls by adding
+    "old value" to "new value" and return an updated dict.
+    """
+    with _wn.lock:
+        return _wn.run(input_dict, name)
+
+
+_wn = _WrapNumbers()
+wrap_numbers.cache_clear = _wn.cache_clear
+wrap_numbers.cache_info = _wn.cache_info
--- a/third_party/python/psutil/psutil/_compat.py
+++ b/third_party/python/psutil/psutil/_compat.py
@@ -1,39 +1,47 @@
-#!/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.
 
 """Module which provides compatibility with older Python versions."""
 
 import collections
 import functools
+import os
 import sys
 
-__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"]
+__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
+           "callable", "lru_cache", "which"]
 
 PY3 = sys.version_info[0] == 3
 
 if PY3:
     long = int
     xrange = range
     unicode = str
+    basestring = str
 
     def u(s):
         return s
+
+    def b(s):
+        return s.encode("latin-1")
 else:
     long = long
     xrange = xrange
     unicode = unicode
+    basestring = basestring
 
     def u(s):
         return unicode(s, "unicode_escape")
 
+    def b(s):
+        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__)
 
@@ -182,8 +190,60 @@ except ImportError:
                     lock.release()
 
             wrapper.__wrapped__ = user_function
             wrapper.cache_info = cache_info
             wrapper.cache_clear = cache_clear
             return functools.update_wrapper(wrapper, user_function)
 
         return decorating_function
+
+
+# python 3.3
+try:
+    from shutil import which
+except ImportError:
+    def which(cmd, mode=os.F_OK | os.X_OK, path=None):
+        """Given a command, mode, and a PATH string, return the path which
+        conforms to the given mode on the PATH, or None if there is no such
+        file.
+
+        `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
+        of os.environ.get("PATH"), or can be overridden with a custom search
+        path.
+        """
+        def _access_check(fn, mode):
+            return (os.path.exists(fn) and os.access(fn, mode) and
+                    not os.path.isdir(fn))
+
+        if os.path.dirname(cmd):
+            if _access_check(cmd, mode):
+                return cmd
+            return None
+
+        if path is None:
+            path = os.environ.get("PATH", os.defpath)
+        if not path:
+            return None
+        path = path.split(os.pathsep)
+
+        if sys.platform == "win32":
+            if os.curdir not in path:
+                path.insert(0, os.curdir)
+
+            pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+            if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
+                files = [cmd]
+            else:
+                files = [cmd + ext for ext in pathext]
+        else:
+            files = [cmd]
+
+        seen = set()
+        for dir in path:
+            normdir = os.path.normcase(dir)
+            if normdir not in seen:
+                seen.add(normdir)
+                for thefile in files:
+                    name = os.path.join(dir, thefile)
+                    if _access_check(name, mode):
+                        return name
+        return None
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/_exceptions.py
@@ -0,0 +1,94 @@
+# 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
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/_psaix.py
@@ -0,0 +1,573 @@
+# Copyright (c) 2009, Giampaolo Rodola'
+# Copyright (c) 2017, Arnon Yaari
+# All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""AIX platform implementation."""
+
+import errno
+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 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 usage_percent
+from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
+
+
+__extra__all__ = ["PROCFS_PATH"]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+HAS_THREADS = hasattr(cext, "proc_threads")
+
+PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = cext_posix.AF_LINK
+
+PROC_STATUSES = {
+    cext.SIDL: _common.STATUS_IDLE,
+    cext.SZOMB: _common.STATUS_ZOMBIE,
+    cext.SACTIVE: _common.STATUS_RUNNING,
+    cext.SSWAP: _common.STATUS_RUNNING,      # TODO what status is this?
+    cext.SSTOP: _common.STATUS_STOPPED,
+}
+
+TCP_STATUSES = {
+    cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
+    cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
+    cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
+    cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
+    cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
+    cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
+    cext.TCPS_CLOSED: _common.CONN_CLOSE,
+    cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
+    cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
+    cext.TCPS_LISTEN: _common.CONN_LISTEN,
+    cext.TCPS_CLOSING: _common.CONN_CLOSING,
+    cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
+}
+
+proc_info_map = dict(
+    ppid=0,
+    rss=1,
+    vms=2,
+    create_time=3,
+    nice=4,
+    num_threads=5,
+    status=6,
+    ttynr=7)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms'])
+# psutil.Process.memory_full_info()
+pfullmem = pmem
+# psutil.Process.cpu_times()
+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
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+    total, avail, free, pinned, inuse = cext.virtual_mem()
+    percent = usage_percent((total - avail), total, _round=1)
+    return svmem(total, avail, percent, inuse, free)
+
+
+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)
+    return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+    """Return system-wide CPU times as a named tuple"""
+    ret = cext.per_cpu_times()
+    return scputimes(*[sum(x) for x in zip(*ret)])
+
+
+def per_cpu_times():
+    """Return system per-CPU times as a list of named tuples"""
+    ret = cext.per_cpu_times()
+    return [scputimes(*x) for x in ret]
+
+
+def cpu_count_logical():
+    """Return the number of logical CPUs in the system."""
+    try:
+        return os.sysconf("SC_NPROCESSORS_ONLN")
+    except ValueError:
+        # mimic os.cpu_count() behavior
+        return None
+
+
+def cpu_count_physical():
+    cmd = "lsdev -Cc processor"
+    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    stdout, stderr = p.communicate()
+    if PY3:
+        stdout, stderr = [x.decode(sys.stdout.encoding)
+                          for x in (stdout, stderr)]
+    if p.returncode != 0:
+        raise RuntimeError("%r command error\n%s" % (cmd, stderr))
+    processors = stdout.strip().splitlines()
+    return len(processors) or None
+
+
+def cpu_stats():
+    """Return various CPU stats as a named tuple."""
+    ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()
+    return _common.scpustats(
+        ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+disk_usage = _psposix.disk_usage
+
+
+def disk_partitions(all=False):
+    """Return system disk partitions."""
+    # TODO - the filtering logic should be better checked so that
+    # it tries to reflect 'df' as much as possible
+    retlist = []
+    partitions = cext.disk_partitions()
+    for partition in partitions:
+        device, mountpoint, fstype, opts = partition
+        if device == 'none':
+            device = ''
+        if not all:
+            # 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:
+                continue
+        ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
+        retlist.append(ntuple)
+    return retlist
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_if_addrs = cext_posix.net_if_addrs
+net_io_counters = cext.net_io_counters
+
+
+def net_connections(kind, _pid=-1):
+    """Return socket connections.  If pid == -1 return system-wide
+    connections (as opposed to connections opened by one process only).
+    """
+    cmap = _common.conn_tmap
+    if kind not in cmap:
+        raise ValueError("invalid %r kind argument; choose between %s"
+                         % (kind, ', '.join([repr(x) for x in cmap])))
+    families, types = _common.conn_tmap[kind]
+    rawlist = cext.net_connections(_pid)
+    ret = set()
+    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)
+
+
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    duplex_map = {"Full": NIC_DUPLEX_FULL,
+                  "Half": NIC_DUPLEX_HALF}
+    names = set([x[0] for x in net_if_addrs()])
+    ret = {}
+    for name in names:
+        isup, mtu = cext.net_if_stats(name)
+
+        # try to get speed and duplex
+        # TODO: rewrite this in C (entstat forks, so use truss -f to follow.
+        # looks like it is using an undocumented ioctl?)
+        duplex = ""
+        speed = 0
+        p = subprocess.Popen(["/usr/bin/entstat", "-d", name],
+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        if PY3:
+            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)
+            if re_result is not None:
+                speed = int(re_result.group(1))
+                duplex = re_result.group(2)
+
+        duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN)
+        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+    return ret
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+    """The system boot time expressed in seconds since the epoch."""
+    return cext.boot_time()
+
+
+def users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = cext.users()
+    localhost = (':0.0', ':0')
+    for item in rawlist:
+        user, tty, hostname, tstamp, user_process, pid = item
+        # note: the underlying C function includes entries about
+        # system boot, run level and others.  We might want
+        # to use them in the future.
+        if not user_process:
+            continue
+        if hostname in localhost:
+            hostname = 'localhost'
+        nt = _common.suser(user, tty, hostname, tstamp, pid)
+        retlist.append(nt)
+    return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+    """Returns a list of PIDs currently running on the system."""
+    return [int(x) for x in os.listdir(get_procfs_path()) if x.isdigit()]
+
+
+def pid_exists(pid):
+    """Check for the existence of a unix pid."""
+    return os.path.exists(os.path.join(get_procfs_path(), str(pid), "psinfo"))
+
+
+def wrap_exceptions(fun):
+    """Call callable into a try/except clause and translate ENOENT,
+    EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
+    """
+
+    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
+            # 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
+
+
+class Process(object):
+    """Wrapper class around underlying C implementation."""
+
+    __slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
+
+    def __init__(self, pid):
+        self.pid = pid
+        self._name = None
+        self._ppid = None
+        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()
+
+    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)
+
+    @memoize_when_activated
+    def _proc_basic_info(self):
+        return cext.proc_basic_info(self.pid, self._procfs_path)
+
+    @memoize_when_activated
+    def _proc_cred(self):
+        return cext.proc_cred(self.pid, self._procfs_path)
+
+    @wrap_exceptions
+    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")
+
+    @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]
+        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)):
+                return exe
+            # not found, move to search in PATH using basename only
+            exe = os.path.basename(exe)
+        # search for exe name PATH
+        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)):
+                return possible_exe
+        return ''
+
+    @wrap_exceptions
+    def cmdline(self):
+        return self._proc_name_and_args()[1].split(' ')
+
+    @wrap_exceptions
+    def create_time(self):
+        return self._proc_basic_info()[proc_info_map['create_time']]
+
+    @wrap_exceptions
+    def num_threads(self):
+        return self._proc_basic_info()[proc_info_map['num_threads']]
+
+    if HAS_THREADS:
+        @wrap_exceptions
+        def threads(self):
+            rawlist = cext.proc_threads(self.pid)
+            retlist = []
+            for thread_id, utime, stime in rawlist:
+                ntuple = _common.pthread(thread_id, utime, stime)
+                retlist.append(ntuple)
+            # The underlying C implementation retrieves all OS threads
+            # and filters them by PID.  At this point we can't tell whether
+            # an empty list means there were no connections for process or
+            # process is no longer active so we force NSP in case the PID
+            # is no longer there.
+            if not retlist:
+                # will raise NSP if process is gone
+                os.stat('%s/%s' % (self._procfs_path, self.pid))
+            return retlist
+
+    @wrap_exceptions
+    def connections(self, kind='inet'):
+        ret = net_connections(kind, _pid=self.pid)
+        # The underlying C implementation retrieves all OS connections
+        # and filters them by PID.  At this point we can't tell whether
+        # an empty list means there were no connections for process or
+        # process is no longer active so we force NSP in case the PID
+        # is no longer there.
+        if not ret:
+            # will raise NSP if process is gone
+            os.stat('%s/%s' % (self._procfs_path, self.pid))
+        return ret
+
+    @wrap_exceptions
+    def nice_get(self):
+        return cext_posix.getpriority(self.pid)
+
+    @wrap_exceptions
+    def nice_set(self, value):
+        return cext_posix.setpriority(self.pid, value)
+
+    @wrap_exceptions
+    def ppid(self):
+        self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
+        return self._ppid
+
+    @wrap_exceptions
+    def uids(self):
+        real, effective, saved, _, _, _ = self._proc_cred()
+        return _common.puids(real, effective, saved)
+
+    @wrap_exceptions
+    def gids(self):
+        _, _, _, real, effective, saved = self._proc_cred()
+        return _common.puids(real, effective, saved)
+
+    @wrap_exceptions
+    def cpu_times(self):
+        cpu_times = cext.proc_cpu_times(self.pid, self._procfs_path)
+        return _common.pcputimes(*cpu_times)
+
+    @wrap_exceptions
+    def terminal(self):
+        ttydev = self._proc_basic_info()[proc_info_map['ttynr']]
+        # convert from 64-bit dev_t to 32-bit dev_t and then map the device
+        ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
+        # try to match rdev of /dev/pts/* files ttydev
+        for dev in glob.glob("/dev/**/*"):
+            if os.stat(dev).st_rdev == ttydev:
+                return dev
+        return None
+
+    @wrap_exceptions
+    def cwd(self):
+        procfs_path = self._procfs_path
+        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
+
+    @wrap_exceptions
+    def memory_info(self):
+        ret = self._proc_basic_info()
+        rss = ret[proc_info_map['rss']] * 1024
+        vms = ret[proc_info_map['vms']] * 1024
+        return pmem(rss, vms)
+
+    memory_full_info = memory_info
+
+    @wrap_exceptions
+    def status(self):
+        code = self._proc_basic_info()[proc_info_map['status']]
+        # XXX is '?' legit? (we're not supposed to return it anyway)
+        return PROC_STATUSES.get(code, '?')
+
+    def open_files(self):
+        # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then
+        # find matching name of the inode)
+        p = subprocess.Popen(["/usr/bin/procfiles", "-n", str(self.pid)],
+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        if PY3:
+            stdout, stderr = [x.decode(sys.stdout.encoding)
+                              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)
+        retlist = []
+        for fd, path in procfiles:
+            path = path.strip()
+            if path.startswith("//"):
+                path = path[1:]
+            if path.lower() == "cannot be retrieved":
+                continue
+            retlist.append(_common.popenfile(path, int(fd)))
+        return retlist
+
+    @wrap_exceptions
+    def num_fds(self):
+        if self.pid == 0:       # no /proc/0/fd
+            return 0
+        return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
+
+    @wrap_exceptions
+    def num_ctx_switches(self):
+        return _common.pctxsw(
+            *cext.proc_num_ctx_switches(self.pid))
+
+    @wrap_exceptions
+    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)
--- a/third_party/python/psutil/psutil/_psbsd.py
+++ b/third_party/python/psutil/psutil/_psbsd.py
@@ -1,99 +1,215 @@
-#!/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.
 
-"""FreeBSD platform implementation."""
+"""FreeBSD, OpenBSD and NetBSD platforms implementation."""
 
+import contextlib
 import errno
 import functools
 import os
 import xml.etree.ElementTree as ET
 from collections import namedtuple
+from socket import AF_INET
 
 from . import _common
 from . import _psposix
 from . import _psutil_bsd as cext
 from . import _psutil_posix as cext_posix
-from ._common import conn_tmap, usage_percent, sockfam_to_enum
+from ._common import AF_INET6
+from ._common import conn_tmap
+from ._common import FREEBSD
+from ._common import memoize
+from ._common import memoize_when_activated
+from ._common import NETBSD
+from ._common import OPENBSD
+from ._common import sockfam_to_enum
 from ._common import socktype_to_enum
-
+from ._common import usage_percent
+from ._compat import which
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
 
 __extra__all__ = []
 
-# --- constants
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
 
-PROC_STATUSES = {
-    cext.SSTOP: _common.STATUS_STOPPED,
-    cext.SSLEEP: _common.STATUS_SLEEPING,
-    cext.SRUN: _common.STATUS_RUNNING,
-    cext.SIDL: _common.STATUS_IDLE,
-    cext.SWAIT: _common.STATUS_WAITING,
-    cext.SLOCK: _common.STATUS_LOCKED,
-    cext.SZOMB: _common.STATUS_ZOMBIE,
-}
+if FREEBSD:
+    PROC_STATUSES = {
+        cext.SIDL: _common.STATUS_IDLE,
+        cext.SRUN: _common.STATUS_RUNNING,
+        cext.SSLEEP: _common.STATUS_SLEEPING,
+        cext.SSTOP: _common.STATUS_STOPPED,
+        cext.SZOMB: _common.STATUS_ZOMBIE,
+        cext.SWAIT: _common.STATUS_WAITING,
+        cext.SLOCK: _common.STATUS_LOCKED,
+    }
+elif OPENBSD or NETBSD:
+    PROC_STATUSES = {
+        cext.SIDL: _common.STATUS_IDLE,
+        cext.SSLEEP: _common.STATUS_SLEEPING,
+        cext.SSTOP: _common.STATUS_STOPPED,
+        # According to /usr/include/sys/proc.h SZOMB is unused.
+        # test_zombie_process() shows that SDEAD is the right
+        # equivalent. Also it appears there's no equivalent of
+        # psutil.STATUS_DEAD. SDEAD really means STATUS_ZOMBIE.
+        # cext.SZOMB: _common.STATUS_ZOMBIE,
+        cext.SDEAD: _common.STATUS_ZOMBIE,
+        cext.SZOMB: _common.STATUS_ZOMBIE,
+        # From http://www.eecs.harvard.edu/~margo/cs161/videos/proc.h.txt
+        # OpenBSD has SRUN and SONPROC: SRUN indicates that a process
+        # is runnable but *not* yet running, i.e. is on a run queue.
+        # SONPROC indicates that the process is actually executing on
+        # a CPU, i.e. it is no longer on a run queue.
+        # As such we'll map SRUN to STATUS_WAKING and SONPROC to
+        # STATUS_RUNNING
+        cext.SRUN: _common.STATUS_WAKING,
+        cext.SONPROC: _common.STATUS_RUNNING,
+    }
+elif NETBSD:
+    PROC_STATUSES = {
+        cext.SIDL: _common.STATUS_IDLE,
+        cext.SACTIVE: _common.STATUS_RUNNING,
+        cext.SDYING: _common.STATUS_ZOMBIE,
+        cext.SSTOP: _common.STATUS_STOPPED,
+        cext.SZOMB: _common.STATUS_ZOMBIE,
+        cext.SDEAD: _common.STATUS_DEAD,
+        cext.SSUSPENDED: _common.STATUS_SUSPENDED,  # unique to NetBSD
+    }
 
 TCP_STATUSES = {
     cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
     cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
     cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
     cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
     cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
     cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
     cext.TCPS_CLOSED: _common.CONN_CLOSE,
     cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
     cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
     cext.TCPS_LISTEN: _common.CONN_LISTEN,
     cext.TCPS_CLOSING: _common.CONN_CLOSING,
     cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
 }
 
-PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+if NETBSD:
+    PAGESIZE = os.sysconf("SC_PAGESIZE")
+else:
+    PAGESIZE = os.sysconf("SC_PAGE_SIZE")
 AF_LINK = cext_posix.AF_LINK
 
-# extend base mem ntuple with BSD-specific memory metrics
+kinfo_proc_map = dict(
+    ppid=0,
+    status=1,
+    real_uid=2,
+    effective_uid=3,
+    saved_uid=4,
+    real_gid=5,
+    effective_gid=6,
+    saved_gid=7,
+    ttynr=8,
+    create_time=9,
+    ctx_switches_vol=10,
+    ctx_switches_unvol=11,
+    read_io_count=12,
+    write_io_count=13,
+    user_time=14,
+    sys_time=15,
+    ch_user_time=16,
+    ch_sys_time=17,
+    rss=18,
+    vms=19,
+    memtext=20,
+    memdata=21,
+    memstack=22,
+    cpunum=23,
+    name=24,
+)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.virtual_memory()
 svmem = namedtuple(
     'svmem', ['total', 'available', 'percent', 'used', 'free',
               'active', 'inactive', 'buffers', 'cached', 'shared', 'wired'])
+# psutil.cpu_times()
 scputimes = namedtuple(
     'scputimes', ['user', 'nice', 'system', 'idle', 'irq'])
-pextmem = namedtuple('pextmem', ['rss', 'vms', 'text', 'data', 'stack'])
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms', 'text', 'data', 'stack'])
+# psutil.Process.memory_full_info()
+pfullmem = pmem
+# psutil.Process.cpu_times()
+pcputimes = namedtuple('pcputimes',
+                       ['user', 'system', 'children_user', 'children_system'])
+# psutil.Process.memory_maps(grouped=True)
 pmmap_grouped = namedtuple(
     'pmmap_grouped', 'path rss, private, ref_count, shadow_count')
+# psutil.Process.memory_maps(grouped=False)
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count')
+# psutil.disk_io_counters()
+if FREEBSD:
+    sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
+                                     'read_bytes', 'write_bytes',
+                                     'read_time', 'write_time',
+                                     'busy_time'])
+else:
+    sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
+                                     'read_bytes', 'write_bytes'])
 
-# set later from __init__.py
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
+
+# =====================================================================
+# --- memory
+# =====================================================================
 
 
 def virtual_memory():
     """System virtual memory as a namedtuple."""
     mem = cext.virtual_mem()
     total, free, active, inactive, wired, cached, buffers, shared = mem
+    if NETBSD:
+        # On NetBSD buffers and shared mem is determined via /proc.
+        # The C ext set them to 0.
+        with open('/proc/meminfo', 'rb') as f:
+            for line in f:
+                if line.startswith(b'Buffers:'):
+                    buffers = int(line.split()[1]) * 1024
+                elif line.startswith(b'MemShared:'):
+                    shared = int(line.split()[1]) * 1024
     avail = inactive + cached + free
     used = active + wired + cached
     percent = usage_percent((total - avail), total, _round=1)
     return svmem(total, avail, percent, used, free,
                  active, inactive, buffers, cached, shared, wired)
 
 
 def swap_memory():
     """System swap memory as (total, used, free, sin, sout) namedtuple."""
-    total, used, free, sin, sout = [x * PAGESIZE for x in cext.swap_mem()]
+    total, used, free, sin, sout = cext.swap_mem()
     percent = usage_percent(used, total, _round=1)
     return _common.sswap(total, used, free, percent, sin, sout)
 
 
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
 def cpu_times():
     """Return system per-CPU times as a namedtuple"""
     user, nice, system, idle, irq = cext.cpu_times()
     return scputimes(user, nice, system, idle, irq)
 
 
 if hasattr(cext, "per_cpu_times"):
     def per_cpu_times():
@@ -108,348 +224,650 @@ else:
     # XXX
     # Ok, this is very dirty.
     # On FreeBSD < 8 we cannot gather per-cpu information, see:
     # https://github.com/giampaolo/psutil/issues/226
     # If num cpus > 1, on first call we return single cpu times to avoid a
     # crash at psutil import time.
     # Next calls will fail with NotImplementedError
     def per_cpu_times():
+        """Return system CPU times as a namedtuple"""
         if cpu_count_logical() == 1:
             return [cpu_times()]
         if per_cpu_times.__called__:
             raise NotImplementedError("supported only starting from FreeBSD 8")
         per_cpu_times.__called__ = True
         return [cpu_times()]
 
     per_cpu_times.__called__ = False
 
 
 def cpu_count_logical():
     """Return the number of logical CPUs in the system."""
     return cext.cpu_count_logical()
 
 
-def cpu_count_physical():
-    """Return the number of physical CPUs in the system."""
-    # From the C module we'll get an XML string similar to this:
-    # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
-    # We may get None in case "sysctl kern.sched.topology_spec"
-    # is not supported on this BSD version, in which case we'll mimic
-    # os.cpu_count() and return None.
-    ret = None
-    s = cext.cpu_count_phys()
-    if s is not None:
-        # get rid of padding chars appended at the end of the string
-        index = s.rfind("</groups>")
-        if index != -1:
-            s = s[:index + 9]
-            root = ET.fromstring(s)
-            try:
-                ret = len(root.findall('group/children/group/cpu')) or None
-            finally:
-                # needed otherwise it will memleak
-                root.clear()
-    if not ret:
-        # If logical CPUs are 1 it's obvious we'll have only 1
-        # physical CPU.
-        if cpu_count_logical() == 1:
-            return 1
-    return ret
+if OPENBSD or NETBSD:
+    def cpu_count_physical():
+        # OpenBSD and NetBSD do not implement this.
+        return 1 if cpu_count_logical() == 1 else None
+else:
+    def cpu_count_physical():
+        """Return the number of physical CPUs in the system."""
+        # From the C module we'll get an XML string similar to this:
+        # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
+        # We may get None in case "sysctl kern.sched.topology_spec"
+        # is not supported on this BSD version, in which case we'll mimic
+        # os.cpu_count() and return None.
+        ret = None
+        s = cext.cpu_count_phys()
+        if s is not None:
+            # get rid of padding chars appended at the end of the string
+            index = s.rfind("</groups>")
+            if index != -1:
+                s = s[:index + 9]
+                root = ET.fromstring(s)
+                try:
+                    ret = len(root.findall('group/children/group/cpu')) or None
+                finally:
+                    # needed otherwise it will memleak
+                    root.clear()
+        if not ret:
+            # If logical CPUs are 1 it's obvious we'll have only 1
+            # physical CPU.
+            if cpu_count_logical() == 1:
+                return 1
+        return ret
 
 
-def boot_time():
-    """The system boot time expressed in seconds since the epoch."""
-    return cext.boot_time()
+def cpu_stats():
+    """Return various CPU stats as a named tuple."""
+    if FREEBSD:
+        # Note: the C ext is returning some metrics we are not exposing:
+        # traps.
+        ctxsw, intrs, soft_intrs, syscalls, traps = cext.cpu_stats()
+    elif NETBSD:
+        # XXX
+        # Note about intrs: the C extension returns 0. intrs
+        # can be determined via /proc/stat; it has the same value as
+        # soft_intrs thought so the kernel is faking it (?).
+        #
+        # Note about syscalls: the C extension always sets it to 0 (?).
+        #
+        # Note: the C ext is returning some metrics we are not exposing:
+        # traps, faults and forks.
+        ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
+            cext.cpu_stats()
+        with open('/proc/stat', 'rb') as f:
+            for line in f:
+                if line.startswith(b'intr'):
+                    intrs = int(line.split()[1])
+    elif OPENBSD:
+        # Note: the C ext is returning some metrics we are not exposing:
+        # traps, faults and forks.
+        ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
+            cext.cpu_stats()
+    return _common.scpustats(ctxsw, intrs, soft_intrs, syscalls)
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
 
 
 def disk_partitions(all=False):
+    """Return mounted disk partitions as a list of namedtuples.
+    'all' argument is ignored, see:
+    https://github.com/giampaolo/psutil/issues/906
+    """
     retlist = []
     partitions = cext.disk_partitions()
     for partition in partitions:
         device, mountpoint, fstype, opts = partition
-        if device == 'none':
-            device = ''
-        if not all:
-            if not os.path.isabs(device) or not os.path.exists(device):
-                continue
         ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
         retlist.append(ntuple)
     return retlist
 
 
-def users():
-    retlist = []
-    rawlist = cext.users()
-    for item in rawlist:
-        user, tty, hostname, tstamp = item
-        if tty == '~':
-            continue  # reboot or shutdown
-        nt = _common.suser(user, tty or None, hostname, tstamp)
-        retlist.append(nt)
-    return retlist
+disk_usage = _psposix.disk_usage
+disk_io_counters = cext.disk_io_counters
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_io_counters = cext.net_io_counters
+net_if_addrs = cext_posix.net_if_addrs
+
+
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    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)
+    return ret
 
 
 def net_connections(kind):
+    """System-wide network connections."""
+    if OPENBSD:
+        ret = []
+        for pid in pids():
+            try:
+                cons = Process(pid).connections(kind)
+            except (NoSuchProcess, ZombieProcess):
+                continue
+            else:
+                for conn in cons:
+                    conn = list(conn)
+                    conn.append(pid)
+                    ret.append(_common.sconn(*conn))
+        return ret
+
     if kind not in _common.conn_tmap:
         raise ValueError("invalid %r kind argument; choose between %s"
                          % (kind, ', '.join([repr(x) for x in conn_tmap])))
     families, types = conn_tmap[kind]
     ret = set()
-    rawlist = cext.net_connections()
+    if NETBSD:
+        rawlist = cext.net_connections(-1)
+    else:
+        rawlist = cext.net_connections()
     for item in rawlist:
         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)
             ret.add(nt)
     return list(ret)
 
 
-def net_if_stats():
-    """Get NIC stats (isup, duplex, speed, mtu)."""
-    names = net_io_counters().keys()
-    ret = {}
-    for name in names:
-        isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
-        if hasattr(_common, 'NicDuplex'):
-            duplex = _common.NicDuplex(duplex)
-        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+# =====================================================================
+#  --- sensors
+# =====================================================================
+
+
+if FREEBSD:
+
+    def sensors_battery():
+        """Return battery info."""
+        try:
+            percent, minsleft, power_plugged = cext.sensors_battery()
+        except NotImplementedError:
+            # See: https://github.com/giampaolo/psutil/issues/1074
+            return None
+        power_plugged = power_plugged == 1
+        if power_plugged:
+            secsleft = _common.POWER_TIME_UNLIMITED
+        elif minsleft == -1:
+            secsleft = _common.POWER_TIME_UNKNOWN
+        else:
+            secsleft = minsleft * 60
+        return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+#  --- other system functions
+# =====================================================================
+
+
+def boot_time():
+    """The system boot time expressed in seconds since the epoch."""
+    return cext.boot_time()
+
+
+def users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = cext.users()
+    for item in rawlist:
+        user, tty, hostname, tstamp, pid = item
+        if pid == -1:
+            assert OPENBSD
+            pid = None
+        if tty == '~':
+            continue  # reboot or shutdown
+        nt = _common.suser(user, tty or None, hostname, tstamp, pid)
+        retlist.append(nt)
+    return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+@memoize
+def _pid_0_exists():
+    try:
+        Process(0).name()
+    except NoSuchProcess:
+        return False
+    except AccessDenied:
+        return True
+    else:
+        return True
+
+
+def pids():
+    """Returns a list of PIDs currently running on the system."""
+    ret = cext.pids()
+    if OPENBSD and (0 not in ret) and _pid_0_exists():
+        # On OpenBSD the kernel does not return PID 0 (neither does
+        # ps) but it's actually querable (Process(0) will succeed).
+        ret.insert(0, 0)
     return ret
 
 
-pids = cext.pids
-pid_exists = _psposix.pid_exists
-disk_usage = _psposix.disk_usage
-net_io_counters = cext.net_io_counters
-disk_io_counters = cext.disk_io_counters
-net_if_addrs = cext_posix.net_if_addrs
+if OPENBSD or NETBSD:
+    def pid_exists(pid):
+        """Return True if pid exists."""
+        exists = _psposix.pid_exists(pid)
+        if not exists:
+            # We do this because _psposix.pid_exists() lies in case of
+            # zombie processes.
+            return pid in pids()
+        else:
+            return True
+else:
+    pid_exists = _psposix.pid_exists
 
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError exceptions into
     NoSuchProcess and AccessDenied.
     """
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
         except OSError as err:
-            # support for private module import
-            if (NoSuchProcess is None or AccessDenied is None or
-                    ZombieProcess is None):
-                raise
+            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
 
 
+@contextlib.contextmanager
+def wrap_exceptions_procfs(inst):
+    """Same as above, for routines relying on reading /proc fs."""
+    try:
+        yield
+    except EnvironmentError as err:
+        # 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
+
+
 class Process(object):
     """Wrapper class around underlying C implementation."""
 
     __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
         self._ppid = None
 
+    @memoize_when_activated
+    def oneshot(self):
+        """Retrieves multiple process info in one shot as a raw tuple."""
+        ret = cext.proc_oneshot_info(self.pid)
+        assert len(ret) == len(kinfo_proc_map)
+        return ret
+
+    def oneshot_enter(self):
+        self.oneshot.cache_activate()
+
+    def oneshot_exit(self):
+        self.oneshot.cache_deactivate()
+
     @wrap_exceptions
     def name(self):
-        return cext.proc_name(self.pid)
+        name = self.oneshot()[kinfo_proc_map['name']]
+        return name if name is not None else cext.proc_name(self.pid)
 
     @wrap_exceptions
     def exe(self):
-        return cext.proc_exe(self.pid)
+        if FREEBSD:
+            return cext.proc_exe(self.pid)
+        elif NETBSD:
+            if self.pid == 0:
+                # /proc/0 dir exists but /proc/0/exe doesn't
+                return ""
+            with wrap_exceptions_procfs(self):
+                return os.readlink("/proc/%s/exe" % self.pid)
+        else:
+            # OpenBSD: exe cannot be determined; references:
+            # https://chromium.googlesource.com/chromium/src/base/+/
+            #     master/base_paths_posix.cc
+            # We try our best guess by using which against the first
+            # cmdline arg (may return None).
+            cmdline = self.cmdline()
+            if cmdline:
+                return which(cmdline[0])
+            else:
+                return ""
 
     @wrap_exceptions
     def cmdline(self):
-        return cext.proc_cmdline(self.pid)
+        if OPENBSD and self.pid == 0:
+            return []  # ...else it crashes
+        elif NETBSD:
+            # XXX - most of the times the underlying sysctl() call on Net
+            # and Open BSD returns a truncated string.
+            # Also /proc/pid/cmdline behaves the same so it looks
+            # like this is a kernel bug.
+            try:
+                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:
+                        raise ZombieProcess(self.pid, self._name, self._ppid)
+                else:
+                    raise
+        else:
+            return cext.proc_cmdline(self.pid)
 
     @wrap_exceptions
     def terminal(self):
-        tty_nr = cext.proc_tty_nr(self.pid)
-        tmap = _psposix._get_terminal_map()
+        tty_nr = self.oneshot()[kinfo_proc_map['ttynr']]
+        tmap = _psposix.get_terminal_map()
         try:
             return tmap[tty_nr]
         except KeyError:
             return None
 
     @wrap_exceptions
     def ppid(self):
-        return cext.proc_ppid(self.pid)
+        self._ppid = self.oneshot()[kinfo_proc_map['ppid']]
+        return self._ppid
 
     @wrap_exceptions
     def uids(self):
-        real, effective, saved = cext.proc_uids(self.pid)
-        return _common.puids(real, effective, saved)
+        rawtuple = self.oneshot()
+        return _common.puids(
+            rawtuple[kinfo_proc_map['real_uid']],
+            rawtuple[kinfo_proc_map['effective_uid']],
+            rawtuple[kinfo_proc_map['saved_uid']])
 
     @wrap_exceptions
     def gids(self):
-        real, effective, saved = cext.proc_gids(self.pid)
-        return _common.pgids(real, effective, saved)
+        rawtuple = self.oneshot()
+        return _common.pgids(
+            rawtuple[kinfo_proc_map['real_gid']],
+            rawtuple[kinfo_proc_map['effective_gid']],
+            rawtuple[kinfo_proc_map['saved_gid']])
 
     @wrap_exceptions
     def cpu_times(self):
-        user, system = cext.proc_cpu_times(self.pid)
-        return _common.pcputimes(user, system)
+        rawtuple = self.oneshot()
+        return _common.pcputimes(
+            rawtuple[kinfo_proc_map['user_time']],
+            rawtuple[kinfo_proc_map['sys_time']],
+            rawtuple[kinfo_proc_map['ch_user_time']],
+            rawtuple[kinfo_proc_map['ch_sys_time']])
+
+    if FREEBSD:
+        @wrap_exceptions
+        def cpu_num(self):
+            return self.oneshot()[kinfo_proc_map['cpunum']]
 
     @wrap_exceptions
     def memory_info(self):
-        rss, vms = cext.proc_memory_info(self.pid)[:2]
-        return _common.pmem(rss, vms)
+        rawtuple = self.oneshot()
+        return pmem(
+            rawtuple[kinfo_proc_map['rss']],
+            rawtuple[kinfo_proc_map['vms']],
+            rawtuple[kinfo_proc_map['memtext']],
+            rawtuple[kinfo_proc_map['memdata']],
+            rawtuple[kinfo_proc_map['memstack']])
 
-    @wrap_exceptions
-    def memory_info_ex(self):
-        return pextmem(*cext.proc_memory_info(self.pid))
+    memory_full_info = memory_info
 
     @wrap_exceptions
     def create_time(self):
-        return cext.proc_create_time(self.pid)
+        return self.oneshot()[kinfo_proc_map['create_time']]
 
     @wrap_exceptions
     def num_threads(self):
-        return cext.proc_num_threads(self.pid)
+        if hasattr(cext, "proc_num_threads"):
+            # FreeBSD
+            return cext.proc_num_threads(self.pid)
+        else:
+            return len(self.threads())
 
     @wrap_exceptions
     def num_ctx_switches(self):
-        return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
+        rawtuple = self.oneshot()
+        return _common.pctxsw(
+            rawtuple[kinfo_proc_map['ctx_switches_vol']],
+            rawtuple[kinfo_proc_map['ctx_switches_unvol']])
 
     @wrap_exceptions
     def threads(self):
+        # Note: on OpenSBD this (/dev/mem) requires root access.
         rawlist = cext.proc_threads(self.pid)
         retlist = []
         for thread_id, utime, stime in rawlist:
             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
         return retlist
 
     @wrap_exceptions
     def connections(self, kind='inet'):
         if kind not in conn_tmap:
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in conn_tmap])))
+
+        if NETBSD:
+            families, types = conn_tmap[kind]
+            ret = set()
+            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
+            return list(ret)
+
         families, types = conn_tmap[kind]
         rawlist = cext.proc_connections(self.pid, families, types)
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
+            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)
             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
         return ret
 
     @wrap_exceptions
     def wait(self, timeout=None):
-        try:
-            return _psposix.wait_pid(self.pid, timeout)
-        except _psposix.TimeoutExpired:
-            # support for private module import
-            if TimeoutExpired is None:
-                raise
-            raise TimeoutExpired(timeout, self.pid, self._name)
+        return _psposix.wait_pid(self.pid, timeout, self._name)
 
     @wrap_exceptions
     def nice_get(self):
         return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     def nice_set(self, value):
         return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def status(self):
-        code = cext.proc_status(self.pid)
-        if code in PROC_STATUSES:
-            return PROC_STATUSES[code]
-        # XXX is this legit? will we even ever get here?
-        return "?"
+        code = self.oneshot()[kinfo_proc_map['status']]
+        # XXX is '?' legit? (we're not supposed to return it anyway)
+        return PROC_STATUSES.get(code, '?')
 
     @wrap_exceptions
     def io_counters(self):
-        rc, wc, rb, wb = cext.proc_io_counters(self.pid)
-        return _common.pio(rc, wc, rb, wb)
+        rawtuple = self.oneshot()
+        return _common.pio(
+            rawtuple[kinfo_proc_map['read_io_count']],
+            rawtuple[kinfo_proc_map['write_io_count']],
+            -1,
+            -1)
+
+    @wrap_exceptions
+    def cwd(self):
+        """Return process current working directory."""
+        # sometimes we get an empty string, in which case we turn
+        # 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'):
+            # FreeBSD < 8 does not support functions based on
+            # kinfo_getfile() and kinfo_getvmmap()
+            return cext.proc_cwd(self.pid) or None
+        else:
+            raise NotImplementedError(
+                "supported only starting from FreeBSD 8" if
+                FREEBSD else "")
 
     nt_mmap_grouped = namedtuple(
         'mmap', 'path rss, private, ref_count, shadow_count')
     nt_mmap_ext = namedtuple(
         'mmap', 'addr, perms path rss, private, ref_count, shadow_count')
 
+    def _not_implemented(self):
+        raise NotImplementedError
+
     # FreeBSD < 8 does not support functions based on kinfo_getfile()
     # and kinfo_getvmmap()
     if hasattr(cext, 'proc_open_files'):
-
         @wrap_exceptions
         def open_files(self):
             """Return files opened by process as a list of namedtuples."""
             rawlist = cext.proc_open_files(self.pid)
             return [_common.popenfile(path, fd) for path, fd in rawlist]
+    else:
+        open_files = _not_implemented
+
+    # FreeBSD < 8 does not support functions based on kinfo_getfile()
+    # and kinfo_getvmmap()
+    if hasattr(cext, '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
+            return ret
+    else:
+        num_fds = _not_implemented
+
+    # --- FreeBSD only APIs
+
+    if FREEBSD:
 
         @wrap_exceptions
-        def cwd(self):
-            """Return process current working directory."""
-            # sometimes we get an empty string, in which case we turn
-            # it into None
-            return cext.proc_cwd(self.pid) or None
+        def cpu_affinity_get(self):
+            return cext.proc_cpu_affinity_get(self.pid)
+
+        @wrap_exceptions
+        def cpu_affinity_set(self, cpus):
+            # Pre-emptively check if CPUs are valid because the C
+            # function has a weird behavior in case of invalid CPUs,
+            # see: https://github.com/giampaolo/psutil/issues/586
+            allcpus = tuple(range(len(per_cpu_times())))
+            for cpu in cpus:
+                if cpu not in allcpus:
+                    raise ValueError("invalid CPU #%i (choose between %s)"
+                                     % (cpu, allcpus))
+            try:
+                cext.proc_cpu_affinity_set(self.pid, cpus)
+            except OSError as err:
+                # 'man cpuset_setaffinity' about EDEADLK:
+                # <<the call would leave a thread without a valid CPU to run
+                # on because the set does not overlap with the thread's
+                # anonymous mask>>
+                if err.errno in (errno.EINVAL, errno.EDEADLK):
+                    for cpu in cpus:
+                        if cpu not in allcpus:
+                            raise ValueError(
+                                "invalid CPU #%i (choose between %s)" % (
+                                    cpu, allcpus))
+                raise
 
         @wrap_exceptions
         def memory_maps(self):
             return cext.proc_memory_maps(self.pid)
-
-        @wrap_exceptions
-        def num_fds(self):
-            """Return the number of file descriptors opened by this process."""
-            return cext.proc_num_fds(self.pid)
-
-    else:
-        def _not_implemented(self):
-            raise NotImplementedError("supported only starting from FreeBSD 8")
-
-        open_files = _not_implemented
-        proc_cwd = _not_implemented
-        memory_maps = _not_implemented
-        num_fds = _not_implemented
-
-    @wrap_exceptions
-    def cpu_affinity_get(self):
-        return cext.proc_cpu_affinity_get(self.pid)
-
-    @wrap_exceptions
-    def cpu_affinity_set(self, cpus):
-        # Pre-emptively check if CPUs are valid because the C
-        # function has a weird behavior in case of invalid CPUs,
-        # see: https://github.com/giampaolo/psutil/issues/586
-        allcpus = tuple(range(len(per_cpu_times())))
-        for cpu in cpus:
-            if cpu not in allcpus:
-                raise ValueError("invalid CPU #%i (choose between %s)"
-                                 % (cpu, allcpus))
-        try:
-            cext.proc_cpu_affinity_set(self.pid, cpus)
-        except OSError as err:
-            # 'man cpuset_setaffinity' about EDEADLK:
-            # <<the call would leave a thread without a valid CPU to run
-            # on because the set does not overlap with the thread's
-            # anonymous mask>>
-            if err.errno in (errno.EINVAL, errno.EDEADLK):
-                for cpu in cpus:
-                    if cpu not in allcpus:
-                        raise ValueError("invalid CPU #%i (choose between %s)"
-                                         % (cpu, allcpus))
-            raise
--- a/third_party/python/psutil/psutil/_pslinux.py
+++ b/third_party/python/psutil/psutil/_pslinux.py
@@ -1,72 +1,104 @@
-#!/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.
 
 """Linux platform implementation."""
 
 from __future__ import division
 
 import base64
+import collections
 import errno
 import functools
+import glob
 import os
 import re
 import socket
 import struct
 import sys
+import traceback
 import warnings
-from collections import namedtuple, defaultdict
+from collections import defaultdict
+from collections import namedtuple
 
 from . import _common
 from . import _psposix
 from . import _psutil_linux as cext
 from . import _psutil_posix as cext_posix
-from ._common import isfile_strict, usage_percent
-from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN
-from ._compat import PY3, long
+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 NIC_DUPLEX_FULL
+from ._common import NIC_DUPLEX_HALF
+from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import parse_environ_block
+from ._common import path_exists_strict
+from ._common import supports_ipv6
+from ._common import usage_percent
+from ._compat import b
+from ._compat import basestring
+from ._compat import long
+from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
 
 if sys.version_info >= (3, 4):
     import enum
 else:
     enum = None
 
 
 __extra__all__ = [
+    #
+    'PROCFS_PATH',
     # io prio constants
     "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE",
     "IOPRIO_CLASS_IDLE",
     # connection status constants
     "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
     "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
     "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ]
 
-# --- constants
+
+# =====================================================================
+# --- globals
+# =====================================================================
 
+
+POWER_SUPPLY_PATH = "/sys/class/power_supply"
+HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
 HAS_PRLIMIT = hasattr(cext, "linux_prlimit")
+_DEFAULT = object()
 
 # RLIMIT_* constants, not guaranteed to be present on all kernels
 if HAS_PRLIMIT:
     for name in dir(cext):
         if name.startswith('RLIM'):
             __extra__all__.append(name)
 
 # Number of clock ticks per second
 CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
 BOOT_TIME = None  # set later
-DEFAULT_ENCODING = sys.getdefaultencoding()
+# Used when reading "big" files, namely /proc/{pid}/smaps and /proc/net/*.
+# On Python 2, using a buffer with open() for such files may result in a
+# 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
 if enum is None:
     AF_LINK = socket.AF_PACKET
 else:
     AddressFamily = enum.IntEnum('AddressFamily',
-                                 {'AF_LINK': socket.AF_PACKET})
+                                 {'AF_LINK': int(socket.AF_PACKET)})
     AF_LINK = AddressFamily.AF_LINK
 
 # ioprio_* constants http://linux.die.net/man/2/ioprio_get
 if enum is None:
     IOPRIO_CLASS_NONE = 0
     IOPRIO_CLASS_RT = 1
     IOPRIO_CLASS_BE = 2
     IOPRIO_CLASS_IDLE = 3
@@ -88,157 +120,457 @@ PROC_STATUSES = {
     "t": _common.STATUS_TRACING_STOP,
     "Z": _common.STATUS_ZOMBIE,
     "X": _common.STATUS_DEAD,
     "x": _common.STATUS_DEAD,
     "K": _common.STATUS_WAKE_KILL,
     "W": _common.STATUS_WAKING
 }
 
-# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
+# https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h
 TCP_STATUSES = {
     "01": _common.CONN_ESTABLISHED,
     "02": _common.CONN_SYN_SENT,
     "03": _common.CONN_SYN_RECV,
     "04": _common.CONN_FIN_WAIT1,
     "05": _common.CONN_FIN_WAIT2,
     "06": _common.CONN_TIME_WAIT,
     "07": _common.CONN_CLOSE,
     "08": _common.CONN_CLOSE_WAIT,
     "09": _common.CONN_LAST_ACK,
     "0A": _common.CONN_LISTEN,
     "0B": _common.CONN_CLOSING
 }
 
-# set later from __init__.py
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.virtual_memory()
+svmem = namedtuple(
+    'svmem', ['total', 'available', 'percent', 'used', 'free',
+              'active', 'inactive', 'buffers', 'cached', 'shared'])
+# psutil.disk_io_counters()
+sdiskio = namedtuple(
+    'sdiskio', ['read_count', 'write_count',
+                'read_bytes', 'write_bytes',
+                'read_time', 'write_time',
+                'read_merged_count', 'write_merged_count',
+                'busy_time'])
+# psutil.Process().open_files()
+popenfile = namedtuple(
+    'popenfile', ['path', 'fd', 'position', 'mode', 'flags'])
+# psutil.Process().memory_info()
+pmem = namedtuple('pmem', 'rss vms shared text lib data dirty')
+# psutil.Process().memory_full_info()
+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', 'pss', 'swap'))
+# psutil.Process().memory_maps(grouped=True)
+pmmap_grouped = namedtuple(
+    'pmmap_grouped',
+    ['path', 'rss', 'size', 'pss', 'shared_clean', 'shared_dirty',
+     'private_clean', 'private_dirty', 'referenced', 'anonymous', 'swap'])
+# psutil.Process().memory_maps(grouped=False)
+pmmap_ext = namedtuple(
+    'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+# psutil.Process.io_counters()
+pio = namedtuple('pio', ['read_count', 'write_count',
+                         'read_bytes', 'write_bytes',
+                         'read_chars', 'write_chars'])
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+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)
 
 
-# --- named tuples
+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
+    path = os.readlink(path)
+    # readlink() might return paths containing null bytes ('\x00')
+    # resulting in "TypeError: must be encoded string without NULL
+    # bytes, not str" errors when the string is passed to other
+    # fs-related functions (os.*, open(), ...).
+    # Apparently everything after '\x00' is garbage (we can have
+    # ' (deleted)', 'new' and possibly others), see:
+    # https://github.com/giampaolo/psutil/issues/717
+    path = path.split('\x00')[0]
+    # Certain paths have ' (deleted)' appended. Usually this is
+    # bogus as the file actually exists. Even if it doesn't we
+    # don't care.
+    if path.endswith(' (deleted)') and not path_exists_strict(path):
+        path = path[:-10]
+    return path
 
-def _get_cputimes_fields():
-    """Return a namedtuple of variable fields depending on the
-    CPU times available on this Linux kernel version which may be:
+
+def file_flags_to_mode(flags):
+    """Convert file's open() flags into a readable string.
+    Used by Process.open_files().
+    """
+    modes_map = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
+    mode = modes_map[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
+    if flags & os.O_APPEND:
+        mode = mode.replace('w', 'a', 1)
+    mode = mode.replace('w+', 'r+')
+    # possible values: r, w, a, r+, a+
+    return mode
+
+
+def get_sector_size(partition):
+    """Return the sector size of a partition.
+    Used by disk_io_counters().
+    """
+    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
+
+
+@memoize
+def set_scputimes_ntuple(procfs_path):
+    """Set a namedtuple of variable fields depending on the CPU times
+    available on this Linux kernel version which may be:
     (user, nice, system, idle, iowait, irq, softirq, [steal, [guest,
      [guest_nice]]])
+    Used by cpu_times() function.
     """
-    with open('/proc/stat', 'rb') as f:
+    global scputimes
+    with open_binary('%s/stat' % procfs_path) as f:
         values = f.readline().split()[1:]
     fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq']
     vlen = len(values)
     if vlen >= 8:
         # Linux >= 2.6.11
         fields.append('steal')
     if vlen >= 9:
         # Linux >= 2.6.24
         fields.append('guest')
     if vlen >= 10:
         # Linux >= 3.2.0
         fields.append('guest_nice')
-    return fields
+    scputimes = namedtuple('scputimes', fields)
+
+
+def cat(fname, fallback=_DEFAULT, binary=True):
+    """Return file content.
+    fallback: the value returned in case the file does not exist or
+              cannot be read
+    binary: whether to open the file in binary or text mode.
+    """
+    try:
+        with open_binary(fname) if binary else open_text(fname) as f:
+            return f.read().strip()
+    except IOError:
+        if fallback is not _DEFAULT:
+            return fallback
+        else:
+            raise
+
+
+try:
+    set_scputimes_ntuple("/proc")
+except Exception:
+    # Don't want to crash at import time.
+    traceback.print_exc()
+    scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0)
+
+
+# =====================================================================
+# --- system memory
+# =====================================================================
 
 
-scputimes = namedtuple('scputimes', _get_cputimes_fields())
-
-svmem = namedtuple(
-    'svmem', ['total', 'available', 'percent', 'used', 'free',
-              'active', 'inactive', 'buffers', 'cached'])
-
-pextmem = namedtuple('pextmem', 'rss vms shared text lib data dirty')
+def calculate_avail_vmem(mems):
+    """Fallback for kernels < 3.14 where /proc/meminfo does not provide
+    "MemAvailable:" column, see:
+    https://blog.famzah.net/2014/09/24/
+    This code reimplements the algorithm outlined here:
+    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+        commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
 
-pmmap_grouped = namedtuple(
-    'pmmap_grouped', ['path', 'rss', 'size', 'pss', 'shared_clean',
-                      'shared_dirty', 'private_clean', 'private_dirty',
-                      'referenced', 'anonymous', 'swap'])
+    XXX: on recent kernels this calculation differs by ~1.5% than
+    "MemAvailable:" as it's calculated slightly differently, see:
+    https://gitlab.com/procps-ng/procps/issues/42
+    https://github.com/famzah/linux-memavailable-procfs/issues/2
+    It is still way more realistic than doing (free + cached) though.
+    """
+    # Fallback for very old distros. According to
+    # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+    #     commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+    # ...long ago "avail" was calculated as (free + cached).
+    # We might fallback in such cases:
+    # "Active(file)" not available: 2.6.28 / Dec 2008
+    # "Inactive(file)" not available: 2.6.28 / Dec 2008
+    # "SReclaimable:" not available: 2.6.19 / Nov 2006
+    # /proc/zoneinfo not available: 2.6.13 / Aug 2005
+    free = mems[b'MemFree:']
+    fallback = free + mems.get(b"Cached:", 0)
+    try:
+        lru_active_file = mems[b'Active(file):']
+        lru_inactive_file = mems[b'Inactive(file):']
+        slab_reclaimable = mems[b'SReclaimable:']
+    except KeyError:
+        return fallback
+    try:
+        f = open_binary('%s/zoneinfo' % get_procfs_path())
+    except IOError:
+        return fallback  # kernel 2.6.13
 
-pmmap_ext = namedtuple(
-    'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+    watermark_low = 0
+    with f:
+        for line in f:
+            line = line.strip()
+            if line.startswith(b'low'):
+                watermark_low += int(line.split()[1])
+    watermark_low *= PAGESIZE
+    watermark_low = watermark_low
 
+    avail = free - watermark_low
+    pagecache = lru_active_file + lru_inactive_file
+    pagecache -= min(pagecache / 2, watermark_low)
+    avail += pagecache
+    avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low)
+    return int(avail)
 
-# --- system memory
 
 def virtual_memory():
-    total, free, buffers, shared, _, _ = cext.linux_sysinfo()
-    cached = active = inactive = None
-    with open('/proc/meminfo', 'rb') as f:
+    """Report virtual memory stats.
+    This implementation matches "free" and "vmstat -s" cmdline
+    utility values and procps-ng-3.3.12 source was used as a reference
+    (2016-09-18):
+    https://gitlab.com/procps-ng/procps/blob/
+        24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c
+    For reference, procps-ng-3.3.10 is the version available on Ubuntu
+    16.04.
+
+    Note about "available" memory: up until psutil 4.3 it was
+    calculated as "avail = (free + buffers + cached)". Now
+    "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as
+    it's more accurate.
+    That matches "available" column in newer versions of "free".
+    """
+    missing_fields = []
+    mems = {}
+    with open_binary('%s/meminfo' % get_procfs_path()) as f:
         for line in f:
-            if line.startswith(b"Cached:"):
-                cached = int(line.split()[1]) * 1024
-            elif line.startswith(b"Active:"):
-                active = int(line.split()[1]) * 1024
-            elif line.startswith(b"Inactive:"):
-                inactive = int(line.split()[1]) * 1024
-            if (cached is not None and
-                    active is not None and
-                    inactive is not None):
-                break
-        else:
-            # we might get here when dealing with exotic Linux flavors, see:
-            # https://github.com/giampaolo/psutil/issues/313
-            msg = "'cached', 'active' and 'inactive' memory stats couldn't " \
-                  "be determined and were set to 0"
-            warnings.warn(msg, RuntimeWarning)
-            cached = active = inactive = 0
-    avail = free + buffers + cached
-    used = total - free
+            fields = line.split()
+            mems[fields[0]] = int(fields[1]) * 1024
+
+    # /proc doc states that the available fields in /proc/meminfo vary
+    # by architecture and compile options, but these 3 values are also
+    # returned by sysinfo(2); as such we assume they are always there.
+    total = mems[b'MemTotal:']
+    free = mems[b'MemFree:']
+    try:
+        buffers = mems[b'Buffers:']
+    except KeyError:
+        # https://github.com/giampaolo/psutil/issues/1010
+        buffers = 0
+        missing_fields.append('buffers')
+    try:
+        cached = mems[b"Cached:"]
+    except KeyError:
+        cached = 0
+        missing_fields.append('cached')
+    else:
+        # "free" cmdline utility sums reclaimable to cached.
+        # Older versions of procps used to add slab memory instead.
+        # This got changed in:
+        # https://gitlab.com/procps-ng/procps/commit/
+        #     05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+        cached += mems.get(b"SReclaimable:", 0)  # since kernel 2.6.19
+
+    try:
+        shared = mems[b'Shmem:']  # since kernel 2.6.32
+    except KeyError:
+        try:
+            shared = mems[b'MemShared:']  # kernels 2.4
+        except KeyError:
+            shared = 0
+            missing_fields.append('shared')
+
+    try:
+        active = mems[b"Active:"]
+    except KeyError:
+        active = 0
+        missing_fields.append('active')
+
+    try:
+        inactive = mems[b"Inactive:"]
+    except KeyError:
+        try:
+            inactive = \
+                mems[b"Inact_dirty:"] + \
+                mems[b"Inact_clean:"] + \
+                mems[b"Inact_laundry:"]
+        except KeyError:
+            inactive = 0
+            missing_fields.append('inactive')
+
+    used = total - free - cached - buffers
+    if used < 0:
+        # May be symptomatic of running within a LCX container where such
+        # values will be dramatically distorted over those of the host.
+        used = total - free
+
+    # - starting from 4.4.0 we match free's "available" column.
+    #   Before 4.4.0 we calculated it as (free + buffers + cached)
+    #   which matched htop.
+    # - free and htop available memory differs as per:
+    #   http://askubuntu.com/a/369589
+    #   http://unix.stackexchange.com/a/65852/168884
+    # - MemAvailable has been introduced in kernel 3.14
+    try:
+        avail = mems[b'MemAvailable:']
+    except KeyError:
+        avail = calculate_avail_vmem(mems)
+
+    if avail < 0:
+        avail = 0
+        missing_fields.append('available')
+
+    # If avail is greater than total or our calculation overflows,
+    # that's symptomatic of running within a LCX container where such
+    # values will be dramatically distorted over those of the host.
+    # https://gitlab.com/procps-ng/procps/blob/
+    #     24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
+    if avail > total:
+        avail = free
+
     percent = usage_percent((total - avail), total, _round=1)
+
+    # Warn about missing metrics which are set to 0.
+    if missing_fields:
+        msg = "%s memory stats couldn't be determined and %s set to 0" % (
+            ", ".join(missing_fields),
+            "was" if len(missing_fields) == 1 else "were")
+        warnings.warn(msg, RuntimeWarning)
+
     return svmem(total, avail, percent, used, free,
-                 active, inactive, buffers, cached)
+                 active, inactive, buffers, cached, shared)
 
 
 def swap_memory():
-    _, _, _, _, total, free = cext.linux_sysinfo()
+    """Return swap memory metrics."""
+    mems = {}
+    with open_binary('%s/meminfo' % get_procfs_path()) as f:
+        for line in f:
+            fields = line.split()
+            mems[fields[0]] = int(fields[1]) * 1024
+    # We prefer /proc/meminfo over sysinfo() syscall so that
+    # psutil.PROCFS_PATH can be used in order to allow retrieval
+    # for linux containers, see:
+    # https://github.com/giampaolo/psutil/issues/1015
+    try:
+        total = mems[b'SwapTotal:']
+        free = mems[b'SwapFree:']
+    except KeyError:
+        _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
+        total *= unit_multiplier
+        free *= unit_multiplier
+
     used = total - free
     percent = usage_percent(used, total, _round=1)
     # get pgin/pgouts
-    with open("/proc/vmstat", "rb") as f:
-        sin = sout = None
-        for line in f:
-            # values are expressed in 4 kilo bytes, we want bytes instead
-            if line.startswith(b'pswpin'):
-                sin = int(line.split(b' ')[1]) * 4 * 1024
-            elif line.startswith(b'pswpout'):
-                sout = int(line.split(b' ')[1]) * 4 * 1024
-            if sin is not None and sout is not None:
-                break
-        else:
-            # we might get here when dealing with exotic Linux flavors, see:
-            # https://github.com/giampaolo/psutil/issues/313
-            msg = "'sin' and 'sout' swap memory stats couldn't " \
-                  "be determined and were set to 0"
-            warnings.warn(msg, RuntimeWarning)
-            sin = sout = 0
+    try:
+        f = open_binary("%s/vmstat" % get_procfs_path())
+    except IOError as err:
+        # see https://github.com/giampaolo/psutil/issues/722
+        msg = "'sin' and 'sout' swap memory stats couldn't " \
+              "be determined and were set to 0 (%s)" % str(err)
+        warnings.warn(msg, RuntimeWarning)
+        sin = sout = 0
+    else:
+        with f:
+            sin = sout = None
+            for line in f:
+                # values are expressed in 4 kilo bytes, we want
+                # bytes instead
+                if line.startswith(b'pswpin'):
+                    sin = int(line.split(b' ')[1]) * 4 * 1024
+                elif line.startswith(b'pswpout'):
+                    sout = int(line.split(b' ')[1]) * 4 * 1024
+                if sin is not None and sout is not None:
+                    break
+            else:
+                # we might get here when dealing with exotic Linux
+                # flavors, see:
+                # https://github.com/giampaolo/psutil/issues/313
+                msg = "'sin' and 'sout' swap memory stats couldn't " \
+                      "be determined and were set to 0"
+                warnings.warn(msg, RuntimeWarning)
+                sin = sout = 0
     return _common.sswap(total, used, free, percent, sin, sout)
 
 
-# --- CPUs
+# =====================================================================
+# --- CPU
+# =====================================================================
+
 
 def cpu_times():
     """Return a named tuple representing the following system-wide
     CPU times:
     (user, nice, system, idle, iowait, irq, softirq [steal, [guest,
      [guest_nice]]])
     Last 3 fields may not be available on all Linux kernel versions.
     """
-    with open('/proc/stat', 'rb') as f:
+    procfs_path = get_procfs_path()
+    set_scputimes_ntuple(procfs_path)
+    with open_binary('%s/stat' % procfs_path) as f:
         values = f.readline().split()
     fields = values[1:len(scputimes._fields) + 1]
     fields = [float(x) / CLOCK_TICKS for x in fields]
     return scputimes(*fields)
 
 
 def per_cpu_times():
     """Return a list of namedtuple representing the CPU times
     for every CPU available on the system.
     """
+    procfs_path = get_procfs_path()
+    set_scputimes_ntuple(procfs_path)
     cpus = []
-    with open('/proc/stat', 'rb') as f:
+    with open_binary('%s/stat' % procfs_path) as f:
         # get rid of the first line which refers to system wide CPU stats
         f.readline()
         for line in f:
             if line.startswith(b'cpu'):
                 values = line.split()
                 fields = values[1:len(scputimes._fields) + 1]
                 fields = [float(x) / CLOCK_TICKS for x in fields]
                 entry = scputimes(*fields)
@@ -248,43 +580,43 @@ def per_cpu_times():
 
 def cpu_count_logical():
     """Return the number of logical CPUs in the system."""
     try:
         return os.sysconf("SC_NPROCESSORS_ONLN")
     except ValueError:
         # as a second fallback we try to parse /proc/cpuinfo
         num = 0
-        with open('/proc/cpuinfo', 'rb') as f:
+        with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
             for line in f:
                 if line.lower().startswith(b'processor'):
                     num += 1
 
         # unknown format (e.g. amrel/sparc architectures), see:
         # https://github.com/giampaolo/psutil/issues/200
         # try to parse /proc/stat as a last resort
         if num == 0:
-            search = re.compile('cpu\d')
-            with open('/proc/stat', 'rt') as f:
+            search = re.compile(r'cpu\d')
+            with open_text('%s/stat' % get_procfs_path()) as f:
                 for line in f:
                     line = line.split(' ')[0]
                     if search.match(line):
                         num += 1
 
         if num == 0:
             # mimic os.cpu_count()
             return None
         return num
 
 
 def cpu_count_physical():
     """Return the number of physical cores in the system."""
     mapping = {}
     current_info = {}
-    with open('/proc/cpuinfo', 'rb') as f:
+    with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
         for line in f:
             line = line.strip().lower()
             if not line:
                 # new section
                 if (b'physical id' in current_info and
                         b'cpu cores' in current_info):
                     mapping[current_info[b'physical id']] = \
                         current_info[b'cpu cores']
@@ -295,61 +627,87 @@ def cpu_count_physical():
                         line.startswith(b'cpu cores')):
                     key, value = line.split(b'\t:', 1)
                     current_info[key] = int(value)
 
     # mimic os.cpu_count()
     return sum(mapping.values()) or None
 
 
-# --- other system functions
-
-def users():
-    """Return currently connected users as a list of namedtuples."""
-    retlist = []
-    rawlist = cext.users()
-    for item in rawlist:
-        user, tty, hostname, tstamp, user_process = item
-        # note: the underlying C function includes entries about
-        # system boot, run level and others.  We might want
-        # to use them in the future.
-        if not user_process:
-            continue
-        if hostname == ':0.0' or hostname == ':0':
-            hostname = 'localhost'
-        nt = _common.suser(user, tty or None, hostname, tstamp)
-        retlist.append(nt)
-    return retlist
+def cpu_stats():
+    """Return various CPU stats as a named tuple."""
+    with open_binary('%s/stat' % get_procfs_path()) as f:
+        ctx_switches = None
+        interrupts = None
+        soft_interrupts = None
+        for line in f:
+            if line.startswith(b'ctxt'):
+                ctx_switches = int(line.split()[1])
+            elif line.startswith(b'intr'):
+                interrupts = int(line.split()[1])
+            elif line.startswith(b'softirq'):
+                soft_interrupts = int(line.split()[1])
+            if ctx_switches is not None and soft_interrupts is not None \
+                    and interrupts is not None:
+                break
+    syscalls = 0
+    return _common.scpustats(
+        ctx_switches, interrupts, soft_interrupts, syscalls)
 
 
-def boot_time():
-    """Return the system boot time expressed in seconds since the epoch."""
-    global BOOT_TIME
-    with open('/proc/stat', 'rb') as f:
-        for line in f:
-            if line.startswith(b'btime'):
-                ret = float(line.strip().split()[1])
-                BOOT_TIME = ret
-                return ret
-        raise RuntimeError("line 'btime' not found in /proc/stat")
+if os.path.exists("/sys/devices/system/cpu/cpufreq") 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)))
+
+        pjoin = os.path.join
+        for path in ls:
+            curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None)
+            if curr is None:
+                # Likely an old RedHat, see:
+                # https://github.com/giampaolo/psutil/issues/1071
+                curr = cat(pjoin(path, "cpuinfo_cur_freq"), fallback=None)
+                if curr is None:
+                    raise NotImplementedError(
+                        "can't find current frequency file")
+            curr = int(curr) / 1000
+            max_ = int(cat(pjoin(path, "scaling_max_freq"))) / 1000
+            min_ = int(cat(pjoin(path, "scaling_min_freq"))) / 1000
+            ret.append(_common.scpufreq(curr, min_, max_))
+        return ret
 
 
-# --- processes
-
-def pids():
-    """Returns a list of PIDs currently running on the system."""
-    return [int(x) for x in os.listdir(b'/proc') if x.isdigit()]
+# =====================================================================
+# --- network
+# =====================================================================
 
 
-def pid_exists(pid):
-    """Check For the existence of a unix pid."""
-    return _psposix.pid_exists(pid)
+net_if_addrs = cext_posix.net_if_addrs
 
 
-# --- network
+class _Ipv6UnsupportedError(Exception):
+    pass
+
 
 class Connections:
     """A wrapper on top of /proc/net/* files, retrieving per-process
     and system-wide open connections (TCP, UDP, UNIX) similarly to
     "netstat -an".
 
     Note: in case of UNIX sockets we're only able to determine the
     local endpoint/path, not the one it's connected to.
@@ -372,22 +730,23 @@ class Connections:
             "udp": (udp4, udp6),
             "udp4": (udp4,),
             "udp6": (udp6,),
             "unix": (unix,),
             "inet": (tcp4, tcp6, udp4, udp6),
             "inet4": (tcp4, udp4),
             "inet6": (tcp6, udp6),
         }
+        self._procfs_path = None
 
     def get_proc_inodes(self, pid):
         inodes = defaultdict(list)
-        for fd in os.listdir("/proc/%s/fd" % pid):
+        for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)):
             try:
-                inode = os.readlink("/proc/%s/fd/%s" % (pid, fd))
+                inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd))
             except OSError as err:
                 # 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:
                     # not a link
@@ -414,17 +773,18 @@ 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
         return inodes
 
-    def decode_address(self, addr, family):
+    @staticmethod
+    def decode_address(addr, family):
         """Accept an "ip:port" address as displayed in /proc/net/*
         and convert it into a human readable form, like:
 
         "0500000A:0016" -> ("10.0.0.5", 22)
         "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521)
 
         The IP address portion is a little or big endian four-byte
         hexadecimal number; that is, the least significant byte is listed
@@ -440,52 +800,60 @@ class Connections:
         # this usually refers to a local socket in listen mode with
         # no end-points connected
         if not port:
             return ()
         if PY3:
             ip = ip.encode('ascii')
         if family == socket.AF_INET:
             # see: https://github.com/giampaolo/psutil/issues/201
-            if sys.byteorder == 'little':
+            if LITTLE_ENDIAN:
                 ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1])
             else:
                 ip = socket.inet_ntop(family, base64.b16decode(ip))
         else:  # IPv6
             # old version - let's keep it, just in case...
             # ip = ip.decode('hex')
             # return socket.inet_ntop(socket.AF_INET6,
             #          ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4)))
             ip = base64.b16decode(ip)
-            # see: https://github.com/giampaolo/psutil/issues/201
-            if sys.byteorder == 'little':
-                ip = socket.inet_ntop(
-                    socket.AF_INET6,
-                    struct.pack('>4I', *struct.unpack('<4I', ip)))
-            else:
-                ip = socket.inet_ntop(
-                    socket.AF_INET6,
-                    struct.pack('<4I', *struct.unpack('<4I', ip)))
-        return (ip, port)
+            try:
+                # see: https://github.com/giampaolo/psutil/issues/201
+                if LITTLE_ENDIAN:
+                    ip = socket.inet_ntop(
+                        socket.AF_INET6,
+                        struct.pack('>4I', *struct.unpack('<4I', ip)))
+                else:
+                    ip = socket.inet_ntop(
+                        socket.AF_INET6,
+                        struct.pack('<4I', *struct.unpack('<4I', ip)))
+            except ValueError:
+                # see: https://github.com/giampaolo/psutil/issues/623
+                if not supports_ipv6():
+                    raise _Ipv6UnsupportedError
+                else:
+                    raise
+        return _common.addr(ip, port)
 
-    def process_inet(self, file, family, type_, inodes, filter_pid=None):
+    @staticmethod
+    def process_inet(file, family, type_, inodes, filter_pid=None):
         """Parse /proc/net/tcp* and /proc/net/udp* files."""
         if file.endswith('6') and not os.path.exists(file):
             # IPv6 not supported
             return
-        with open(file, 'rt') as f:
+        with open_text(file, buffering=BIGFILE_BUFFERING) as f:
             f.readline()  # skip the first line
-            for line in f:
+            for lineno, line in enumerate(f, 1):
                 try:
                     _, laddr, raddr, status, _, _, _, _, _, inode = \
                         line.split()[:10]
                 except ValueError:
                     raise RuntimeError(
-                        "error while parsing %s; malformed line %r" % (
-                            file, line))
+                        "error while parsing %s; malformed line %s %r" % (
+                            file, lineno, line))
                 if inode in inodes:
                     # # We assume inet sockets are unique, so we error
                     # # out if there are multiple references to the
                     # # same inode. We won't do this for UNIX sockets.
                     # if len(inodes[inode]) > 1 and family != socket.AF_UNIX:
                     #     raise ValueError("ambiguos inode with multiple "
                     #                      "PIDs references")
                     pid, fd = inodes[inode][0]
@@ -493,29 +861,36 @@ class Connections:
                     pid, fd = None, -1
                 if filter_pid is not None and filter_pid != pid:
                     continue
                 else:
                     if type_ == socket.SOCK_STREAM:
                         status = TCP_STATUSES[status]
                     else:
                         status = _common.CONN_NONE
-                    laddr = self.decode_address(laddr, family)
-                    raddr = self.decode_address(raddr, family)
+                    try:
+                        laddr = Connections.decode_address(laddr, family)
+                        raddr = Connections.decode_address(raddr, family)
+                    except _Ipv6UnsupportedError:
+                        continue
                     yield (fd, family, type_, laddr, raddr, status, pid)
 
-    def process_unix(self, file, family, inodes, filter_pid=None):
+    @staticmethod
+    def process_unix(file, family, inodes, filter_pid=None):
         """Parse /proc/net/unix files."""
-        with open(file, 'rt') as f:
+        with open_text(file, buffering=BIGFILE_BUFFERING) as f:
             f.readline()  # skip the first line
             for line in f:
                 tokens = line.split()
                 try:
                     _, _, _, _, type_, _, inode = tokens[0:7]
                 except ValueError:
+                    if ' ' not in line:
+                        # see: https://github.com/giampaolo/psutil/issues/766
+                        continue
                     raise RuntimeError(
                         "error while parsing %s; malformed line %r" % (
                             file, line))
                 if inode in inodes:
                     # With UNIX sockets we can have a single inode
                     # referencing many file descriptors.
                     pairs = inodes[inode]
                 else:
@@ -524,39 +899,45 @@ class Connections:
                     if filter_pid is not None and filter_pid != pid:
                         continue
                     else:
                         if len(tokens) == 8:
                             path = tokens[-1]
                         else:
                             path = ""
                         type_ = int(type_)
-                        raddr = None
+                        # XXX: determining the remote endpoint of a
+                        # UNIX socket on Linux is not possible, see:
+                        # https://serverfault.com/questions/252723/
+                        raddr = ""
                         status = _common.CONN_NONE
                         yield (fd, family, type_, path, raddr, status, pid)
 
     def retrieve(self, kind, pid=None):
         if kind not in self.tmap:
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in self.tmap])))
+        self._procfs_path = get_procfs_path()
         if pid is not None:
             inodes = self.get_proc_inodes(pid)
             if not inodes:
                 # no connections for this process
                 return []
         else:
             inodes = self.get_all_inodes()
         ret = set()
         for f, family, type_ in self.tmap[kind]:
             if family in (socket.AF_INET, socket.AF_INET6):
                 ls = self.process_inet(
-                    "/proc/net/%s" % f, family, type_, inodes, filter_pid=pid)
+                    "%s/net/%s" % (self._procfs_path, f),
+                    family, type_, inodes, filter_pid=pid)
             else:
                 ls = self.process_unix(
-                    "/proc/net/%s" % f, family, inodes, filter_pid=pid)
+                    "%s/net/%s" % (self._procfs_path, f),
+                    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,
                                          status)
                 else:
                     conn = _common.sconn(fd, family, type_, laddr, raddr,
                                          status, bound_pid)
                 ret.add(conn)
@@ -570,111 +951,148 @@ def net_connections(kind='inet'):
     """Return system-wide open connections."""
     return _connections.retrieve(kind)
 
 
 def net_io_counters():
     """Return network I/O statistics for every network interface
     installed on the system as a dict of raw tuples.
     """
-    with open("/proc/net/dev", "rt") as f:
+    with open_text("%s/net/dev" % get_procfs_path()) as f:
         lines = f.readlines()
     retdict = {}
     for line in lines[2:]:
         colon = line.rfind(':')
         assert colon > 0, repr(line)
         name = line[:colon].strip()
         fields = line[colon + 1:].strip().split()
-        bytes_recv = int(fields[0])
-        packets_recv = int(fields[1])
-        errin = int(fields[2])
-        dropin = int(fields[3])
-        bytes_sent = int(fields[8])
-        packets_sent = int(fields[9])
-        errout = int(fields[10])
-        dropout = int(fields[11])
+
+        # in
+        (bytes_recv,
+         packets_recv,
+         errin,
+         dropin,
+         fifoin,  # unused
+         framein,  # unused
+         compressedin,  # unused
+         multicastin,  # unused
+         # out
+         bytes_sent,
+         packets_sent,
+         errout,
+         dropout,
+         fifoout,  # unused
+         collisionsout,  # unused
+         carrierout,  # unused
+         compressedout) = map(int, fields)
+
         retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv,
                          errin, errout, dropin, dropout)
     return retdict
 
 
 def net_if_stats():
     """Get NIC stats (isup, duplex, speed, mtu)."""
     duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL,
                   cext.DUPLEX_HALF: NIC_DUPLEX_HALF,
                   cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN}
     names = net_io_counters().keys()
     ret = {}
     for name in names:
-        isup, duplex, speed, mtu = cext.net_if_stats(name)
-        duplex = duplex_map[duplex]
-        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+        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)
     return ret
 
 
-net_if_addrs = cext_posix.net_if_addrs
+# =====================================================================
+# --- disks
+# =====================================================================
 
 
-# --- disks
+disk_usage = _psposix.disk_usage
+
 
 def disk_io_counters():
     """Return disk I/O statistics for every disk installed on the
     system as a dict of raw tuples.
     """
-    # man iostat states that sectors are equivalent with blocks and
-    # have a size of 512 bytes since 2.4 kernels. This value is
-    # needed to calculate the amount of disk I/O in bytes.
-    SECTOR_SIZE = 512
+    # 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
 
-    # determine partitions we want to look for
-    partitions = []
-    with open("/proc/partitions", "rt") 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)
-    #
     retdict = {}
-    with open("/proc/diskstats", "rt") as f:
+    partitions = get_partitions()
+    with open_text("%s/diskstats" % get_procfs_path()) as f:
         lines = f.readlines()
     for line in lines:
-        # http://www.mjmwired.net/kernel/Documentation/iostats.txt
+        # 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.:
+        # "3     0   8 hda 8 8 8 8 8 8 8 8 8 8 8"
+        # On Linux 2.6+ each line *usually* has 14 fields, and the disk
+        # name is in another position, like this:
+        # "3    0   hda 8 8 8 8 8 8 8 8 8 8 8"
+        # ...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"
+        # See:
+        # https://www.kernel.org/doc/Documentation/iostats.txt
+        # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
         fields = line.split()
-        if len(fields) > 7:
-            _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \
-                fields[:11]
+        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:
-            # from kernel 2.6.0 to 2.6.25
-            _, _, name, reads, rbytes, writes, wbytes = fields
-            rtime, wtime = 0, 0
+            raise ValueError("not sure how to interpret line %r" % line)
+
         if name in partitions:
-            rbytes = int(rbytes) * SECTOR_SIZE
-            wbytes = int(wbytes) * SECTOR_SIZE
-            reads = int(reads)
-            writes = int(writes)
-            rtime = int(rtime)
-            wtime = int(wtime)
-            retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime)
+            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"""
+    """Return mounted disk partitions as a list of namedtuples."""
     fstypes = set()
-    with open("/proc/filesystems", "r") as f:
+    with open_text("%s/filesystems" % get_procfs_path()) as f:
         for line in f:
             line = line.strip()
             if not line.startswith("nodev"):
                 fstypes.add(line.strip())
             else:
                 # ignore all lines starting with "nodev" except "nodev zfs"
                 fstype = line.split("\t")[1]
                 if fstype == "zfs":
@@ -689,321 +1107,654 @@ def disk_partitions(all=False):
         if not all:
             if device == '' or fstype not in fstypes:
                 continue
         ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
         retlist.append(ntuple)
     return retlist
 
 
-disk_usage = _psposix.disk_usage
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_temperatures():
+    """Return hardware (CPU and others) temperatures as a dict
+    including hardware name, label, current, max and critical
+    temperatures.
+
+    Implementation notes:
+    - /sys/class/hwmon looks like the most recent interface to
+      retrieve this info, and this implementation relies on it
+      only (old distros will probably use something else)
+    - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon
+    - /sys/class/thermal/thermal_zone* is another one but it's more
+      difficult to parse
+    """
+    ret = collections.defaultdict(list)
+    basenames = glob.glob('/sys/class/hwmon/hwmon*/temp*_*')
+    # CentOS has an intermediate /device directory:
+    # 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 = 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:
+            # A lot of things can go wrong here, so let's just skip the
+            # whole entry.
+            # 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)
+            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
+        if critical is not None:
+            critical = float(critical) / 1000.0
+
+        ret[unit_name].append((label, current, high, critical))
+
+    return ret
+
+
+def sensors_fans():
+    """Return hardware fans info (for CPU and other peripherals) as a
+    dict including hardware label and current speed.
+
+    Implementation notes:
+    - /sys/class/hwmon looks like the most recent interface to
+      retrieve this info, and this implementation relies on it
+      only (old distros will probably use something else)
+    - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon
+    """
+    ret = collections.defaultdict(list)
+    basenames = glob.glob('/sys/class/hwmon/hwmon*/fan*_*')
+    if not basenames:
+        # CentOS has an intermediate /device directory:
+        # https://github.com/giampaolo/psutil/issues/971
+        basenames = glob.glob('/sys/class/hwmon/hwmon*/device/fan*_*')
+
+    basenames = sorted(set([x.split('_')[0] for x in basenames]))
+    for base in basenames:
+        try:
+            current = int(cat(base + '_input'))
+        except (IOError, OSError) as err:
+            warnings.warn("ignoring %r" % err, RuntimeWarning)
+            continue
+        unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
+                        binary=False)
+        label = cat(base + '_label', fallback='', binary=False)
+        ret[unit_name].append(_common.sfan(label, current))
+
+    return dict(ret)
 
 
-# --- decorators
+def sensors_battery():
+    """Return battery information.
+    Implementation note: it appears /sys/class/power_supply/BAT0/
+    directory structure may vary and provide files with the same
+    meaning but under different names, see:
+    https://github.com/giampaolo/psutil/issues/966
+    """
+    null = object()
+
+    def multi_cat(*paths):
+        """Attempt to read the content of multiple files which may
+        not exist. If none of them exist return None.
+        """
+        for path in paths:
+            ret = cat(path, fallback=null)
+            if ret != null:
+                return int(ret) if ret.isdigit() else ret
+        return None
+
+    root = os.path.join(POWER_SUPPLY_PATH, "BAT0")
+    if not os.path.exists(root):
+        return None
+
+    # Base metrics.
+    energy_now = multi_cat(
+        root + "/energy_now",
+        root + "/charge_now")
+    power_now = multi_cat(
+        root + "/power_now",
+        root + "/current_now")
+    energy_full = multi_cat(
+        root + "/energy_full",
+        root + "/charge_full")
+    if energy_now is None or power_now is None:
+        return None
+
+    # Percent. If we have energy_full the percentage will be more
+    # accurate compared to reading /capacity file (float vs. int).
+    if energy_full is not None:
+        try:
+            percent = 100.0 * energy_now / energy_full
+        except ZeroDivisionError:
+            percent = 0.0
+    else:
+        percent = int(cat(root + "/capacity", fallback=-1))
+        if percent == -1:
+            return None
+
+    # Is AC power cable plugged in?
+    # Note: AC0 is not always available and sometimes (e.g. CentOS7)
+    # it's called "AC".
+    power_plugged = None
+    online = multi_cat(
+        os.path.join(POWER_SUPPLY_PATH, "AC0/online"),
+        os.path.join(POWER_SUPPLY_PATH, "AC/online"))
+    if online is not None:
+        power_plugged = online == 1
+    else:
+        status = cat(root + "/status", fallback="", binary=False).lower()
+        if status == "discharging":
+            power_plugged = False
+        elif status in ("charging", "full"):
+            power_plugged = True
+
+    # Seconds left.
+    # Note to self: we may also calculate the charging ETA as per:
+    # https://github.com/thialfihar/dotfiles/blob/
+    #     013937745fd9050c30146290e8f963d65c0179e6/bin/battery.py#L55
+    if power_plugged:
+        secsleft = _common.POWER_TIME_UNLIMITED
+    else:
+        try:
+            secsleft = int(energy_now / power_now * 3600)
+        except ZeroDivisionError:
+            secsleft = _common.POWER_TIME_UNKNOWN
+
+    return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = cext.users()
+    for item in rawlist:
+        user, tty, hostname, tstamp, user_process, pid = item
+        # note: the underlying C function includes entries about
+        # system boot, run level and others.  We might want
+        # to use them in the future.
+        if not user_process:
+            continue
+        if hostname in (':0.0', ':0'):
+            hostname = 'localhost'
+        nt = _common.suser(user, tty or None, hostname, tstamp, pid)
+        retlist.append(nt)
+    return retlist
+
+
+def boot_time():
+    """Return the system boot time expressed in seconds since the epoch."""
+    global BOOT_TIME
+    path = '%s/stat' % get_procfs_path()
+    with open_binary(path) as f:
+        for line in f:
+            if line.startswith(b'btime'):
+                ret = float(line.strip().split()[1])
+                BOOT_TIME = ret
+                return ret
+        raise RuntimeError(
+            "line 'btime' not found in %s" % path)
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+    """Returns a list of PIDs currently running on the system."""
+    return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()]
+
+
+def pid_exists(pid):
+    """Check for the existence of a unix PID. Linux TIDs are not
+    supported (always return False).
+    """
+    if not _psposix.pid_exists(pid):
+        return False
+    else:
+        # Linux's apparently does not distinguish between PIDs and TIDs
+        # (thread IDs).
+        # listdir("/proc") won't show any TID (only PIDs) but
+        # os.stat("/proc/{tid}") will succeed if {tid} exists.
+        # os.kill() can also be passed a TID. This is quite confusing.
+        # In here we want to enforce this distinction and support PIDs
+        # only, see:
+        # https://github.com/giampaolo/psutil/issues/687
+        try:
+            # Note: already checked that this is faster than using a
+            # regular expr. Also (a lot) faster than doing
+            # 'return pid in pids()'
+            path = "%s/%s/status" % (get_procfs_path(), pid)
+            with open_binary(path) as f:
+                for line in f:
+                    if line.startswith(b"Tgid:"):
+                        tgid = int(line.split()[1])
+                        # If tgid and pid are the same then we're
+                        # dealing with a process PID.
+                        return tgid == pid
+                raise ValueError("'Tgid' line not found in %s" % path)
+        except (EnvironmentError, ValueError):
+            return pid in pids()
+
+
+def ppid_map():
+    """Obtain a {pid: ppid, ...} dict for all running processes in
+    one shot. Used to speed up Process.children().
+    """
+    ret = {}
+    procfs_path = get_procfs_path()
+    for pid in pids():
+        try:
+            with open_binary("%s/%s/stat" % (procfs_path, pid)) as f:
+                data = f.read()
+        except EnvironmentError as err:
+            # 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
+        else:
+            rpar = data.rfind(b')')
+            dset = data[rpar + 2:].split()
+            ppid = int(dset[1])
+            ret[pid] = ppid
+    return ret
+
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError and IOError exceptions
     into NoSuchProcess and AccessDenied.
     """
     @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:
-                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):
-                raise NoSuchProcess(self.pid, self._name)
             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)):
+                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.
             raise
     return wrapper
 
 
-def wrap_exceptions_w_zombie(fun):
-    """Same as above but also handles zombies."""
-    @functools.wraps(fun)
-    def wrapper(self, *args, **kwargs):
-        try:
-            return wrap_exceptions(fun)(self)
-        except NoSuchProcess:
-            if not pid_exists(self.pid):
-                raise
-            else:
-                raise ZombieProcess(self.pid, self._name, self._ppid)
-    return wrapper
-
-
 class Process(object):
     """Linux process implementation."""
 
-    __slots__ = ["pid", "_name", "_ppid"]
+    __slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
         self._ppid = None
+        self._procfs_path = get_procfs_path()
+
+    @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.
+        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).
+        The return value is cached in case oneshot() ctx manager is
+        in use.
+        """
+        with open_binary("%s/%s/stat" % (self._procfs_path, self.pid)) as f:
+            data = f.read()
+        # Process name is between parentheses. It can contain spaces and
+        # other parentheses. This is taken into account by looking for
+        # 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
+
+    @memoize_when_activated
+    def _read_status_file(self):
+        """Read /proc/{pid}/stat file and return its content.
+        The return value is cached in case oneshot() ctx manager is
+        in use.
+        """
+        with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f:
+            return f.read()
+
+    @memoize_when_activated
+    def _read_smaps_file(self):
+        with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid),
+                         buffering=BIGFILE_BUFFERING) as f:
+            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()
+
+    def oneshot_exit(self):
+        self._parse_stat_file.cache_deactivate()
+        self._read_status_file.cache_deactivate()
+        self._read_smaps_file.cache_deactivate()
 
     @wrap_exceptions
     def name(self):
-        fname = "/proc/%s/stat" % self.pid
-        kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict()
-        with open(fname, "rt", **kw) as f:
-            data = f.read()
+        name = self._parse_stat_file()[0]
+        if PY3:
+            name = decode(name)
         # XXX - gets changed later and probably needs refactoring
-        return data[data.find('(') + 1:data.rfind(')')]
+        return name
 
     def exe(self):
         try:
-            exe = os.readlink("/proc/%s/exe" % self.pid)
+            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("/proc/%s" % self.pid):
+                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:
                         raise ZombieProcess(self.pid, self._name, self._ppid)
             if err.errno in (errno.EPERM, errno.EACCES):
                 raise AccessDenied(self.pid, self._name)
             raise
 
-        # readlink() might return paths containing null bytes ('\x00').
-        # Certain names have ' (deleted)' appended. Usually this is
-        # bogus as the file actually exists. Either way that's not
-        # important as we don't want to discriminate executables which
-        # have been deleted.
-        exe = exe.split('\x00')[0]
-        if exe.endswith(' (deleted)') and not os.path.exists(exe):
-            exe = exe[:-10]
-        return exe
+    @wrap_exceptions
+    def cmdline(self):
+        with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f:
+            data = f.read()
+        if not data:
+            # may happen in case of zombie process
+            return []
+        # 'man proc' states that args are separated by null bytes '\0'
+        # and last char is supposed to be a null byte. Nevertheless
+        # some processes may change their cmdline after being started
+        # (via setproctitle() or similar), they are usually not
+        # compliant with this rule and use spaces instead. Google
+        # Chrome process is an example. See:
+        # https://github.com/giampaolo/psutil/issues/1179
+        sep = '\x00' if data.endswith('\x00') else ' '
+        if data.endswith(sep):
+            data = data[:-1]
+        return [x for x in data.split(sep)]
 
     @wrap_exceptions
-    def cmdline(self):
-        fname = "/proc/%s/cmdline" % self.pid
-        kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict()
-        with open(fname, "rt", **kw) as f:
+    def environ(self):
+        with open_text("%s/%s/environ" % (self._procfs_path, self.pid)) as f:
             data = f.read()
-        if data.endswith('\x00'):
-            data = data[:-1]
-        return [x for x in data.split('\x00')]
+        return parse_environ_block(data)
 
     @wrap_exceptions
     def terminal(self):
-        tmap = _psposix._get_terminal_map()
-        with open("/proc/%s/stat" % self.pid, 'rb') as f:
-            tty_nr = int(f.read().split(b' ')[6])
+        tty_nr = int(self._parse_stat_file()[5])
+        tmap = _psposix.get_terminal_map()
         try:
             return tmap[tty_nr]
         except KeyError:
             return None
 
     if os.path.exists('/proc/%s/io' % os.getpid()):
         @wrap_exceptions
         def io_counters(self):
-            fname = "/proc/%s/io" % self.pid
-            with open(fname, 'rb') as f:
-                rcount = wcount = rbytes = wbytes = None
+            fname = "%s/%s/io" % (self._procfs_path, self.pid)
+            fields = {}
+            with open_binary(fname) as f:
                 for line in f:
-                    if rcount is None and line.startswith(b"syscr"):
-                        rcount = int(line.split()[1])
-                    elif wcount is None and line.startswith(b"syscw"):
-                        wcount = int(line.split()[1])
-                    elif rbytes is None and line.startswith(b"read_bytes"):
-                        rbytes = int(line.split()[1])
-                    elif wbytes is None and line.startswith(b"write_bytes"):
-                        wbytes = int(line.split()[1])
-                for x in (rcount, wcount, rbytes, wbytes):
-                    if x is None:
-                        raise NotImplementedError(
-                            "couldn't read all necessary info from %r" % fname)
-                return _common.pio(rcount, wcount, rbytes, wbytes)
+                    # https://github.com/giampaolo/psutil/issues/1004
+                    line = line.strip()
+                    if line:
+                        name, value = line.split(b': ')
+                        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)
 
     @wrap_exceptions
     def cpu_times(self):
-        with open("/proc/%s/stat" % self.pid, 'rb') as f:
-            st = f.read().strip()
-        # ignore the first two values ("pid (exe)")
-        st = st[st.find(b')') + 2:]
-        values = st.split(b' ')
-        utime = float(values[11]) / CLOCK_TICKS
-        stime = float(values[12]) / CLOCK_TICKS
-        return _common.pcputimes(utime, stime)
+        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)
+
+    @wrap_exceptions
+    def cpu_num(self):
+        """What CPU the process is on."""
+        return int(self._parse_stat_file()[37])
 
     @wrap_exceptions
     def wait(self, timeout=None):
-        try:
-            return _psposix.wait_pid(self.pid, timeout)
-        except _psposix.TimeoutExpired:
-            # support for private module import
-            if TimeoutExpired is None:
-                raise
-            raise TimeoutExpired(timeout, self.pid, self._name)
+        return _psposix.wait_pid(self.pid, timeout, self._name)
 
     @wrap_exceptions
     def create_time(self):
-        with open("/proc/%s/stat" % self.pid, 'rb') as f:
-            st = f.read().strip()
-        # ignore the first two values ("pid (exe)")
-        st = st[st.rfind(b')') + 2:]
-        values = st.split(b' ')
+        values = self._parse_stat_file()
         # 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[19]) / CLOCK_TICKS) + bt
+        return (float(values[20]) / CLOCK_TICKS) + bt
 
     @wrap_exceptions
     def memory_info(self):
-        with open("/proc/%s/statm" % self.pid, 'rb') as f:
-            vms, rss = f.readline().split()[:2]
-            return _common.pmem(int(rss) * PAGESIZE,
-                                int(vms) * PAGESIZE)
-
-    @wrap_exceptions
-    def memory_info_ex(self):
         #  ============================================================
         # | FIELD  | DESCRIPTION                         | AKA  | TOP  |
         #  ============================================================
         # | rss    | resident set size                   |      | RES  |
         # | vms    | total program size                  | size | VIRT |
         # | shared | shared pages (from shared mappings) |      | SHR  |
         # | text   | text ('code')                       | trs  | CODE |
         # | lib    | library (unused in Linux 2.6)       | lrs  |      |
         # | data   | data + stack                        | drs  | DATA |
         # | dirty  | dirty pages (unused in Linux 2.6)   | dt   |      |
         #  ============================================================
-        with open("/proc/%s/statm" % self.pid, "rb") as f:
+        with open_binary("%s/%s/statm" % (self._procfs_path, self.pid)) as f:
             vms, rss, shared, text, lib, data, dirty = \
                 [int(x) * PAGESIZE for x in f.readline().split()[:7]]
-        return pextmem(rss, vms, shared, text, lib, data, dirty)
+        return pmem(rss, vms, shared, text, lib, data, dirty)
+
+    # /proc/pid/smaps does not exist on kernels < 2.6.14 or if
+    # CONFIG_MMU kernel configuration option is not enabled.
+    if HAS_SMAPS:
 
-    if os.path.exists('/proc/%s/smaps' % os.getpid()):
+        @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+)")):
+            basic_mem = self.memory_info()
+            # Note: using 3 regexes is faster than reading the file
+            # line by line.
+            # XXX: on Python 3 the 2 regexes are 30% slower than on
+            # Python 2 though. Figure out why.
+            #
+            # You might be tempted to calculate USS by subtracting
+            # the "shared" value from the "resident" value in
+            # /proc/<pid>/statm. But at least on Linux, statm's "shared"
+            # value actually counts pages backed by files, which has
+            # little to do with whether the pages are actually shared.
+            # /proc/self/smaps on the other hand appears to give us the
+            # correct information.
+            smaps_data = self._read_smaps_file()
+            # Note: smaps file can be empty for certain processes.
+            # The code below will not crash though and will result to 0.
+            uss = sum(map(int, _private_re.findall(smaps_data))) * 1024
+            pss = sum(map(int, _pss_re.findall(smaps_data))) * 1024
+            swap = sum(map(int, _swap_re.findall(smaps_data))) * 1024
+            return pfullmem(*basic_mem + (uss, pss, swap))
+
+    else:
+        memory_full_info = memory_info
+
+    if HAS_SMAPS:
 
         @wrap_exceptions
         def memory_maps(self):
-            """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
+            """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
             """
-            with open("/proc/%s/smaps" % self.pid, "rt") as f:
-                first_line = f.readline()
-                current_block = [first_line]
+            def get_blocks(lines, current_block):
+                data = {}
+                for line in lines:
+                    fields = line.split(None, 5)
+                    if not fields[0].endswith(b':'):
+                        # new block section
+                        yield (current_block.pop(), data)
+                        current_block.append(line)
+                    else:
+                        try:
+                            data[fields[0]] = int(fields[1]) * 1024
+                        except ValueError:
+                            if fields[0].startswith(b'VmFlags:'):
+                                # see issue #369
+                                continue
+                            else:
+                                raise ValueError("don't know how to inte"
+                                                 "rpret line %r" % line)
+                yield (current_block.pop(), data)
 
-                def get_blocks():
-                    data = {}
-                    for line in f:
-                        fields = line.split(None, 5)
-                        if not fields[0].endswith(':'):
-                            # new block section
-                            yield (current_block.pop(), data)
-                            current_block.append(line)
-                        else:
-                            try:
-                                data[fields[0]] = int(fields[1]) * 1024
-                            except ValueError:
-                                if fields[0].startswith('VmFlags:'):
-                                    # see issue #369
-                                    continue
-                                else:
-                                    raise ValueError("don't know how to inte"
-                                                     "rpret line %r" % line)
-                    yield (current_block.pop(), data)
-
-                ls = []
-                if first_line:  # smaps file can be empty
-                    for header, data in get_blocks():
-                        hfields = header.split(None, 5)
-                        try:
-                            addr, perms, offset, dev, inode, path = hfields
-                        except ValueError:
-                            addr, perms, offset, dev, inode, path = \
-                                hfields + ['']
-                        if not path:
-                            path = '[anon]'
-                        else:
-                            path = path.strip()
-                        ls.append((
-                            addr, perms, path,
-                            data['Rss:'],
-                            data.get('Size:', 0),
-                            data.get('Pss:', 0),
-                            data.get('Shared_Clean:', 0),
-                            data.get('Shared_Dirty:', 0),
-                            data.get('Private_Clean:', 0),
-                            data.get('Private_Dirty:', 0),
-                            data.get('Referenced:', 0),
-                            data.get('Anonymous:', 0),
-                            data.get('Swap:', 0)
-                        ))
+            data = self._read_smaps_file()
+            # Note: smaps file can be empty for certain processes.
+            if not data:
+                return []
+            lines = data.split(b'\n')
+            ls = []
+            first_line = lines.pop(0)
+            current_block = [first_line]
+            for header, data in get_blocks(lines, current_block):
+                hfields = header.split(None, 5)
+                try:
+                    addr, perms, offset, dev, inode, path = hfields
+                except ValueError:
+                    addr, perms, offset, dev, inode, path = \
+                        hfields + ['']
+                if not path:
+                    path = '[anon]'
+                else:
+                    if PY3:
+                        path = decode(path)
+                    path = path.strip()
+                    if (path.endswith(' (deleted)') and not
+                            path_exists_strict(path)):
+                        path = path[:-10]
+                ls.append((
+                    decode(addr), decode(perms), path,
+                    data[b'Rss:'],
+                    data.get(b'Size:', 0),
+                    data.get(b'Pss:', 0),
+                    data.get(b'Shared_Clean:', 0),
+                    data.get(b'Shared_Dirty:', 0),
+                    data.get(b'Private_Clean:', 0),
+                    data.get(b'Private_Dirty:', 0),
+                    data.get(b'Referenced:', 0),
+                    data.get(b'Anonymous:', 0),
+                    data.get(b'Swap:', 0)
+                ))
             return ls
 
-    else:
+    else:  # pragma: no cover
         def memory_maps(self):
-            msg = "couldn't find /proc/%s/smaps; kernel < 2.6.14 or "  \
-                  "CONFIG_MMU kernel configuration option is not enabled" \
-                  % self.pid
-            raise NotImplementedError(msg)
+            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_w_zombie
+    @wrap_exceptions
     def cwd(self):
-        # readlink() might return paths containing null bytes causing
-        # problems when used with other fs-related functions (os.*,
-        # open(), ...)
-        path = os.readlink("/proc/%s/cwd" % self.pid)
-        return path.replace('\x00', '')
+        try:
+            return readlink("%s/%s/cwd" % (self._procfs_path, self.pid))
+        except OSError as err:
+            # 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
 
     @wrap_exceptions
-    def num_ctx_switches(self):
-        vol = unvol = None
-        with open("/proc/%s/status" % self.pid, "rb") as f:
-            for line in f:
-                if line.startswith(b"voluntary_ctxt_switches"):
-                    vol = int(line.split()[1])
-                elif line.startswith(b"nonvoluntary_ctxt_switches"):
-                    unvol = int(line.split()[1])
-                if vol is not None and unvol is not None:
-                    return _common.pctxsw(vol, unvol)
+    def num_ctx_switches(self,
+                         _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)')):
+        data = self._read_status_file()
+        ctxsw = _ctxsw_re.findall(data)
+        if not ctxsw:
             raise NotImplementedError(
                 "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'"
-                "fields were not found in /proc/%s/status; the kernel is "
-                "probably older than 2.6.23" % self.pid)
+                "lines were not found in %s/%s/status; the kernel is "
+                "probably older than 2.6.23" % (
+                    self._procfs_path, self.pid))
+        else:
+            return _common.pctxsw(int(ctxsw[0]), int(ctxsw[1]))
 
     @wrap_exceptions
-    def num_threads(self):
-        with open("/proc/%s/status" % self.pid, "rb") as f:
-            for line in f:
-                if line.startswith(b"Threads:"):
-                    return int(line.split()[1])
-            raise NotImplementedError("line not found")
+    def num_threads(self, _num_threads_re=re.compile(br'Threads:\t(\d+)')):
+        # Note: on Python 3 using a re is faster than iterating over file
+        # line by line. On Python 2 is the exact opposite, and iterating
+        # over a file on Python 3 is slower than on Python 2.
+        data = self._read_status_file()
+        return int(_num_threads_re.findall(data)[0])
 
     @wrap_exceptions
     def threads(self):
-        thread_ids = os.listdir("/proc/%s/task" % self.pid)
+        thread_ids = os.listdir("%s/%s/task" % (self._procfs_path, self.pid))
         thread_ids.sort()
         retlist = []
         hit_enoent = False
         for thread_id in thread_ids:
-            fname = "/proc/%s/task/%s/stat" % (self.pid, thread_id)
+            fname = "%s/%s/task/%s/stat" % (
+                self._procfs_path, self.pid, thread_id)
             try:
-                with open(fname, 'rb') as f:
+                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
@@ -1011,47 +1762,63 @@ class Process(object):
             st = st[st.find(b')') + 2:]
             values = st.split(b' ')
             utime = float(values[11]) / CLOCK_TICKS
             stime = float(values[12]) / CLOCK_TICKS
             ntuple = _common.pthread(int(thread_id), utime, stime)
             retlist.append(ntuple)
         if hit_enoent:
             # raise NSP if the process disappeared on us
-            os.stat('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (self._procfs_path, self.pid))
         return retlist
 
     @wrap_exceptions
     def nice_get(self):
-        # with open('/proc/%s/stat' % self.pid, 'r') as f:
+        # with open_text('%s/%s/stat' % (self._procfs_path, self.pid)) as f:
         #   data = f.read()
         #   return int(data.split()[18])
 
         # Use C implementation
         return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     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)
 
+    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 as err:
-            if err.errno == errno.EINVAL:
-                allcpus = tuple(range(len(per_cpu_times())))
+        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 allcpus:
-                        raise ValueError("invalid CPU #%i (choose between %s)"
-                                         % (cpu, allcpus))
+                    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"):
 
         @wrap_exceptions
         def ionice_get(self):
             ioclass, value = cext.proc_ioprio_get(self.pid)
@@ -1060,19 +1827,19 @@ class Process(object):
             return _common.pionice(ioclass, value)
 
         @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 <= 8:
+                if not 0 <= value <= 7:
                     raise ValueError(
-                        "value argument range expected is between 0 and 8")
+                        "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
                 value = 0
@@ -1117,90 +1884,90 @@ class Process(object):
                     # I saw this happening on Travis:
                     # https://travis-ci.org/giampaolo/psutil/jobs/51368273
                     raise ZombieProcess(self.pid, self._name, self._ppid)
                 else:
                     raise
 
     @wrap_exceptions
     def status(self):
-        with open("/proc/%s/status" % self.pid, 'rb') as f:
-            for line in f:
-                if line.startswith(b"State:"):
-                    letter = line.split()[1]
-                    if PY3:
-                        letter = letter.decode()
-                    # XXX is '?' legit? (we're not supposed to return
-                    # it anyway)
-                    return PROC_STATUSES.get(letter, '?')
+        letter = self._parse_stat_file()[1]
+        if PY3:
+            letter = letter.decode()
+        # XXX is '?' legit? (we're not supposed to return it anyway)
+        return PROC_STATUSES.get(letter, '?')
 
     @wrap_exceptions
     def open_files(self):
         retlist = []
-        files = os.listdir("/proc/%s/fd" % self.pid)
+        files = os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))
         hit_enoent = False
         for fd in files:
-            file = "/proc/%s/fd/%s" % (self.pid, fd)
+            file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd)
             try:
-                file = os.readlink(file)
+                path = readlink(file)
             except OSError as err:
                 # 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:
                     # not a link
                     continue
                 else:
                     raise
             else:
-                # If file is not an absolute path there's no way
-                # to tell whether it's a regular file or not,
-                # so we skip it. A regular file is always supposed
-                # to be absolutized though.
-                if file.startswith('/') and isfile_strict(file):
-                    ntuple = _common.popenfile(file, int(fd))
-                    retlist.append(ntuple)
+                # If path is not an absolute there's no way to tell
+                # whether it's a regular file or not, so we skip it.
+                # A regular file is always supposed to be have an
+                # absolute path though.
+                if path.startswith('/') and isfile_strict(path):
+                    # Get file position and flags.
+                    file = "%s/%s/fdinfo/%s" % (
+                        self._procfs_path, self.pid, fd)
+                    try:
+                        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
+                    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('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (self._procfs_path, self.pid))
         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('/proc/%s' % self.pid)
+        os.stat('%s/%s' % (self._procfs_path, self.pid))
         return ret
 
     @wrap_exceptions
     def num_fds(self):
-        return len(os.listdir("/proc/%s/fd" % self.pid))
+        return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
 
     @wrap_exceptions
     def ppid(self):
-        fpath = "/proc/%s/status" % self.pid
-        with open(fpath, 'rb') as f:
-            for line in f:
-                if line.startswith(b"PPid:"):
-                    # PPid: nnnn
-                    return int(line.split()[1])
-            raise NotImplementedError("line 'PPid' not found in %s" % fpath)
+        return int(self._parse_stat_file()[2])
 
     @wrap_exceptions
-    def uids(self):
-        fpath = "/proc/%s/status" % self.pid
-        with open(fpath, 'rb') as f:
-            for line in f:
-                if line.startswith(b'Uid:'):
-                    _, real, effective, saved, fs = line.split()
-                    return _common.puids(int(real), int(effective), int(saved))
-            raise NotImplementedError("line 'Uid' not found in %s" % fpath)
+    def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')):
+        data = self._read_status_file()
+        real, effective, saved = _uids_re.findall(data)[0]
+        return _common.puids(int(real), int(effective), int(saved))
 
     @wrap_exceptions
-    def gids(self):
-        fpath = "/proc/%s/status" % self.pid
-        with open(fpath, 'rb') as f:
-            for line in f:
-                if line.startswith(b'Gid:'):
-                    _, real, effective, saved, fs = line.split()
-                    return _common.pgids(int(real), int(effective), int(saved))
-            raise NotImplementedError("line 'Gid' not found in %s" % fpath)
+    def gids(self, _gids_re=re.compile(br'Gid:\t(\d+)\t(\d+)\t(\d+)')):
+        data = self._read_status_file()
+        real, effective, saved = _gids_re.findall(data)[0]
+        return _common.pgids(int(real), int(effective), int(saved))
--- a/third_party/python/psutil/psutil/_psosx.py
+++ b/third_party/python/psutil/psutil/_psosx.py
@@ -1,37 +1,49 @@
-#!/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.
 
 """OSX 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 conn_tmap, usage_percent, isfile_strict
-from ._common import sockfam_to_enum, socktype_to_enum
+from ._common import AF_INET6
+from ._common import conn_tmap
+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 ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
 
 
 __extra__all__ = []
 
-# --- constants
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
 
 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
 AF_LINK = cext_posix.AF_LINK
 
-# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
 TCP_STATUSES = {
     cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
     cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
     cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
     cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
     cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
     cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
     cext.TCPS_CLOSED: _common.CONN_CLOSE,
@@ -45,39 +57,70 @@ TCP_STATUSES = {
 PROC_STATUSES = {
     cext.SIDL: _common.STATUS_IDLE,
     cext.SRUN: _common.STATUS_RUNNING,
     cext.SSLEEP: _common.STATUS_SLEEPING,
     cext.SSTOP: _common.STATUS_STOPPED,
     cext.SZOMB: _common.STATUS_ZOMBIE,
 }
 
+kinfo_proc_map = dict(
+    ppid=0,
+    ruid=1,
+    euid=2,
+    suid=3,
+    rgid=4,
+    egid=5,
+    sgid=6,
+    ttynr=7,
+    ctime=8,
+    status=9,
+    name=10,
+)
+
+pidtaskinfo_map = dict(
+    cpuutime=0,
+    cpustime=1,
+    rss=2,
+    vms=3,
+    pfaults=4,
+    pageins=5,
+    numthreads=6,
+    volctxsw=7,
+)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.cpu_times()
 scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
-
+# psutil.virtual_memory()
 svmem = namedtuple(
     'svmem', ['total', 'available', 'percent', 'used', 'free',
               'active', 'inactive', 'wired'])
-
-pextmem = namedtuple('pextmem', ['rss', 'vms', 'pfaults', 'pageins'])
-
+# psutil.Process.memory_info()
+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))
 
-# set later from __init__.py
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
 
+# =====================================================================
+# --- memory
+# =====================================================================
 
-# --- functions
 
 def virtual_memory():
     """System virtual memory as a namedtuple."""
     total, active, inactive, wired, free = cext.virtual_mem()
     avail = inactive + free
     used = active + inactive + wired
     percent = usage_percent((total - avail), total, _round=1)
     return svmem(total, avail, percent, used, free,
@@ -86,16 +129,21 @@ 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)
     return _common.sswap(total, used, free, percent, sin, sout)
 
 
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
 def cpu_times():
     """Return system CPU times as a namedtuple."""
     user, nice, system, idle = cext.cpu_times()
     return scputimes(user, nice, system, idle)
 
 
 def per_cpu_times():
     """Return system CPU times as a named tuple"""
@@ -112,51 +160,92 @@ def cpu_count_logical():
     return cext.cpu_count_logical()
 
 
 def cpu_count_physical():
     """Return the number of physical CPUs in the system."""
     return cext.cpu_count_phys()
 
 
-def boot_time():
-    """The system boot time expressed in seconds since the epoch."""
-    return cext.boot_time()
+def cpu_stats():
+    ctx_switches, interrupts, soft_interrupts, syscalls, traps = \
+        cext.cpu_stats()
+    return _common.scpustats(
+        ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+def cpu_freq():
+    """Return CPU frequency.
+    On OSX per-cpu frequency is not supported.
+    Also, the returned frequency never changes, see:
+    https://arstechnica.com/civis/viewtopic.php?f=19&t=465002
+    """
+    curr, min_, max_ = cext.cpu_freq()
+    return [_common.scpufreq(curr, min_, max_)]
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_usage = _psposix.disk_usage
+disk_io_counters = cext.disk_io_counters
 
 
 def disk_partitions(all=False):
+    """Return mounted disk partitions as a list of namedtuples."""
     retlist = []
     partitions = cext.disk_partitions()
     for partition in partitions:
         device, mountpoint, fstype, opts = partition
         if device == 'none':
             device = ''
         if not all:
             if not os.path.isabs(device) or not os.path.exists(device):
                 continue
         ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
         retlist.append(ntuple)
     return retlist
 
 
-def users():
-    retlist = []
-    rawlist = cext.users()
-    for item in rawlist:
-        user, tty, hostname, tstamp = item
-        if tty == '~':
-            continue  # reboot or shutdown
-        if not tstamp:
-            continue
-        nt = _common.suser(user, tty or None, hostname or None, tstamp)
-        retlist.append(nt)
-    return retlist
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_battery():
+    """Return battery information.
+    """
+    try:
+        percent, minsleft, power_plugged = cext.sensors_battery()
+    except NotImplementedError:
+        # no power source - return None according to interface
+        return None
+    power_plugged = power_plugged == 1
+    if power_plugged:
+        secsleft = _common.POWER_TIME_UNLIMITED
+    elif minsleft == -1:
+        secsleft = _common.POWER_TIME_UNKNOWN
+    else:
+        secsleft = minsleft * 60
+    return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_io_counters = cext.net_io_counters
+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
     # the process is owned by root.
     ret = []
     for pid in pids():
         try:
             cons = Process(pid).connections(kind)
         except NoSuchProcess:
             continue
@@ -168,196 +257,315 @@ def net_connections(kind='inet'):
     return ret
 
 
 def net_if_stats():
     """Get NIC stats (isup, duplex, speed, mtu)."""
     names = net_io_counters().keys()
     ret = {}
     for name in names:
-        isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
+        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)
     return ret
 
 
-pids = cext.pids
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+    """The system boot time expressed in seconds since the epoch."""
+    return cext.boot_time()
+
+
+def users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = cext.users()
+    for item in rawlist:
+        user, tty, hostname, tstamp, pid = item
+        if tty == '~':
+            continue  # reboot or shutdown
+        if not tstamp:
+            continue
+        nt = _common.suser(user, tty or None, hostname or None, tstamp, pid)
+        retlist.append(nt)
+    return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+    ls = cext.pids()
+    if 0 not in ls:
+        # On certain OSX 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)
+        except NoSuchProcess:
+            pass
+        except AccessDenied:
+            ls.append(0)
+    return ls
+
+
 pid_exists = _psposix.pid_exists
-disk_usage = _psposix.disk_usage
-net_io_counters = cext.net_io_counters
-disk_io_counters = cext.disk_io_counters
-net_if_addrs = cext_posix.net_if_addrs
 
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError exceptions into
     NoSuchProcess and AccessDenied.
     """
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
         except OSError as err:
-            # support for private module import
-            if (NoSuchProcess is None or AccessDenied is None or
-                    ZombieProcess is None):
-                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)
+                raise NoSuchProcess(self.pid, self._name)
             if err.errno in (errno.EPERM, errno.EACCES):
                 raise AccessDenied(self.pid, self._name)
             raise
     return wrapper
 
 
+@contextlib.contextmanager
+def catch_zombie(proc):
+    """There are some poor C APIs which incorrectly raise ESRCH when
+    the process is still alive or it's a zombie, or even RuntimeError
+    (those who don't set errno). This is here in order to solve:
+    https://github.com/giampaolo/psutil/issues/1044
+    """
+    try:
+        yield
+    except (OSError, RuntimeError) as err:
+        if isinstance(err, RuntimeError) or err.errno == errno.ESRCH:
+            try:
+                # status() is not supposed to lie and correctly detect
+                # zombies so if it raises ESRCH it's true.
+                status = proc.status()
+            except NoSuchProcess:
+                raise err
+            else:
+                if status == _common.STATUS_ZOMBIE:
+                    raise ZombieProcess(proc.pid, proc._name, proc._ppid)
+                else:
+                    raise AccessDenied(proc.pid, proc._name)
+        else:
+            raise
+
+
 class Process(object):
     """Wrapper class around underlying C implementation."""
 
     __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
         self._ppid = None
 
+    @memoize_when_activated
+    def _get_kinfo_proc(self):
+        # Note: should work with all PIDs without permission issues.
+        ret = cext.proc_kinfo_oneshot(self.pid)
+        assert len(ret) == len(kinfo_proc_map)
+        return ret
+
+    @memoize_when_activated
+    def _get_pidtaskinfo(self):
+        # Note: should work for PIDs owned by user only.
+        with catch_zombie(self):
+            ret = cext.proc_pidtaskinfo_oneshot(self.pid)
+        assert len(ret) == len(pidtaskinfo_map)
+        return ret
+
+    def oneshot_enter(self):
+        self._get_kinfo_proc.cache_activate()
+        self._get_pidtaskinfo.cache_activate()
+
+    def oneshot_exit(self):
+        self._get_kinfo_proc.cache_deactivate()
+        self._get_pidtaskinfo.cache_deactivate()
+
     @wrap_exceptions
     def name(self):
-        return cext.proc_name(self.pid)
+        name = self._get_kinfo_proc()[kinfo_proc_map['name']]
+        return name if name is not None else cext.proc_name(self.pid)
 
     @wrap_exceptions
     def exe(self):
-        return cext.proc_exe(self.pid)
+        with catch_zombie(self):
+            return cext.proc_exe(self.pid)
 
     @wrap_exceptions
     def cmdline(self):
-        if not pid_exists(self.pid):
-            raise NoSuchProcess(self.pid, self._name)
-        return cext.proc_cmdline(self.pid)
+        with catch_zombie(self):
+            return cext.proc_cmdline(self.pid)
+
+    @wrap_exceptions
+    def environ(self):
+        with catch_zombie(self):
+            return parse_environ_block(cext.proc_environ(self.pid))
 
     @wrap_exceptions
     def ppid(self):
-        return cext.proc_ppid(self.pid)
+        self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
+        return self._ppid
 
     @wrap_exceptions
     def cwd(self):
-        return cext.proc_cwd(self.pid)
+        with catch_zombie(self):
+            return cext.proc_cwd(self.pid)
 
     @wrap_exceptions
     def uids(self):
-        real, effective, saved = cext.proc_uids(self.pid)
-        return _common.puids(real, effective, saved)
+        rawtuple = self._get_kinfo_proc()
+        return _common.puids(
+            rawtuple[kinfo_proc_map['ruid']],
+            rawtuple[kinfo_proc_map['euid']],
+            rawtuple[kinfo_proc_map['suid']])
 
     @wrap_exceptions
     def gids(self):
-        real, effective, saved = cext.proc_gids(self.pid)
-        return _common.pgids(real, effective, saved)
+        rawtuple = self._get_kinfo_proc()
+        return _common.puids(
+            rawtuple[kinfo_proc_map['rgid']],
+            rawtuple[kinfo_proc_map['egid']],
+            rawtuple[kinfo_proc_map['sgid']])
 
     @wrap_exceptions
     def terminal(self):
-        tty_nr = cext.proc_tty_nr(self.pid)
-        tmap = _psposix._get_terminal_map()
+        tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']]
+        tmap = _psposix.get_terminal_map()
         try:
             return tmap[tty_nr]
         except KeyError:
             return None
 
     @wrap_exceptions
     def memory_info(self):
-        rss, vms = cext.proc_memory_info(self.pid)[:2]
-        return _common.pmem(rss, vms)
+        rawtuple = self._get_pidtaskinfo()
+        return pmem(
+            rawtuple[pidtaskinfo_map['rss']],
+            rawtuple[pidtaskinfo_map['vms']],
+            rawtuple[pidtaskinfo_map['pfaults']],
+            rawtuple[pidtaskinfo_map['pageins']],
+        )
 
     @wrap_exceptions
-    def memory_info_ex(self):
-        rss, vms, pfaults, pageins = cext.proc_memory_info(self.pid)
-        return pextmem(rss, vms, pfaults * PAGESIZE, pageins * PAGESIZE)
+    def memory_full_info(self):
+        basic_mem = self.memory_info()
+        uss = cext.proc_memory_uss(self.pid)
+        return pfullmem(*basic_mem + (uss, ))
 
     @wrap_exceptions
     def cpu_times(self):
-        user, system = cext.proc_cpu_times(self.pid)
-        return _common.pcputimes(user, system)
+        rawtuple = self._get_pidtaskinfo()
+        return _common.pcputimes(
+            rawtuple[pidtaskinfo_map['cpuutime']],
+            rawtuple[pidtaskinfo_map['cpustime']],
+            # children user / system times are not retrievable (set to 0)
+            0.0, 0.0)
 
     @wrap_exceptions
     def create_time(self):
-        return cext.proc_create_time(self.pid)
+        return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
 
     @wrap_exceptions
     def num_ctx_switches(self):
-        return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
+        # Unvoluntary value seems not to be available;
+        # getrusage() numbers seems to confirm this theory.
+        # We set it to 0.
+        vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']]
+        return _common.pctxsw(vol, 0)
 
     @wrap_exceptions
     def num_threads(self):
-        return cext.proc_num_threads(self.pid)
+        return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
 
     @wrap_exceptions
     def open_files(self):
         if self.pid == 0:
             return []
         files = []
-        rawlist = cext.proc_open_files(self.pid)
+        with catch_zombie(self):
+            rawlist = cext.proc_open_files(self.pid)
         for path, fd in rawlist:
             if isfile_strict(path):
                 ntuple = _common.popenfile(path, fd)
                 files.append(ntuple)
         return files
 
     @wrap_exceptions
     def connections(self, kind='inet'):
         if kind not in conn_tmap:
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in conn_tmap])))
         families, types = conn_tmap[kind]
-        rawlist = cext.proc_connections(self.pid, families, types)
+        with catch_zombie(self):
+            rawlist = cext.proc_connections(self.pid, families, types)
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
             status = TCP_STATUSES[status]
             fam = sockfam_to_enum(fam)
             type = socktype_to_enum(type)
+            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)
             ret.append(nt)
         return ret
 
     @wrap_exceptions
     def num_fds(self):
         if self.pid == 0:
             return 0
-        return cext.proc_num_fds(self.pid)
+        with catch_zombie(self):
+            return cext.proc_num_fds(self.pid)
 
     @wrap_exceptions
     def wait(self, timeout=None):
-        try:
-            return _psposix.wait_pid(self.pid, timeout)
-        except _psposix.TimeoutExpired:
-            # support for private module import
-            if TimeoutExpired is None:
-                raise
-            raise TimeoutExpired(timeout, self.pid, self._name)
+        return _psposix.wait_pid(self.pid, timeout, self._name)
 
     @wrap_exceptions
     def nice_get(self):
-        return cext_posix.getpriority(self.pid)
+        with catch_zombie(self):
+            return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     def nice_set(self, value):
-        return cext_posix.setpriority(self.pid, value)
+        with catch_zombie(self):
+            return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def status(self):
-        code = cext.proc_status(self.pid)
+        code = self._get_kinfo_proc()[kinfo_proc_map['status']]
         # XXX is '?' legit? (we're not supposed to return it anyway)
         return PROC_STATUSES.get(code, '?')
 
     @wrap_exceptions
     def threads(self):
-        rawlist = cext.proc_threads(self.pid)
+        with catch_zombie(self):
+            rawlist = cext.proc_threads(self.pid)
         retlist = []
         for thread_id, utime, stime in rawlist:
             ntuple = _common.pthread(thread_id, utime, stime)
             retlist.append(ntuple)
         return retlist
 
     @wrap_exceptions
     def memory_maps(self):
-        return cext.proc_memory_maps(self.pid)
+        with catch_zombie(self):
+            return cext.proc_memory_maps(self.pid)
--- a/third_party/python/psutil/psutil/_psposix.py
+++ b/third_party/python/psutil/psutil/_psposix.py
@@ -1,28 +1,29 @@
-#!/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.
 
 """Routines common to all posix systems."""
 
 import errno
 import glob
 import os
 import sys
 import time
 
-from ._common import sdiskusage, usage_percent, memoize
-from ._compat import PY3, unicode
+from ._common import memoize
+from ._common import sdiskusage
+from ._common import usage_percent
+from ._compat import PY3
+from ._compat import unicode
+from ._exceptions import TimeoutExpired
 
 
-class TimeoutExpired(Exception):
-    pass
+__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
 
 
 def pid_exists(pid):
     """Check whether pid exists in the current process table."""
     if pid == 0:
         # According to "man 2 kill" PID 0 has a special meaning:
         # it refers to <<every process in the process group of the
         # calling process>> so we don't want to go any further.
@@ -43,31 +44,31 @@ def pid_exists(pid):
             # (EINVAL, EPERM, ESRCH) therefore we should never get
             # here. If we do let's be explicit in considering this
             # an error.
             raise err
     else:
         return True
 
 
-def wait_pid(pid, timeout=None):
+def wait_pid(pid, timeout=None, proc_name=None):
     """Wait for process with pid 'pid' to terminate and return its
     exit status code as an integer.
 
     If pid is not a children of os.getpid() (current process) just
     waits until the process disappears and return None.
 
     If pid does not exist at all return None immediately.
 
     Raise TimeoutExpired on timeout expired.
     """
     def check_timeout(delay):
         if timeout is not None:
             if timer() >= stop_at:
-                raise TimeoutExpired()
+                raise TimeoutExpired(timeout, pid=pid, name=proc_name)
         time.sleep(delay)
         return min(delay * 2, 0.04)
 
     timer = getattr(time, 'monotonic', time.time)
     if timeout is not None:
         def waitcall():
             return os.waitpid(pid, os.WNOHANG)
         stop_at = timer() + timeout
@@ -100,57 +101,82 @@ def wait_pid(pid, timeout=None):
         else:
             if retpid == 0:
                 # WNOHANG was used, pid is still running
                 delay = check_timeout(delay)
                 continue
             # process exited due to a signal; return the integer of
             # that signal
             if os.WIFSIGNALED(status):
-                return os.WTERMSIG(status)
+                return -os.WTERMSIG(status)
             # process exited using exit(2) system call; return the
             # integer exit(2) system call has been called with
             elif os.WIFEXITED(status):
                 return os.WEXITSTATUS(status)
             else:
                 # should never happen
-                raise RuntimeError("unknown process exit status")
+                raise ValueError("unknown process exit status %r" % status)
 
 
 def disk_usage(path):
-    """Return disk usage associated with path."""
-    try:
+    """Return disk usage associated with path.
+    Note: UNIX usually reserves 5% disk space which is not accessible
+    by user. In this function "total" and "used" values reflect the
+    total and used disk space whereas "free" and "percent" represent
+    the "free" and "used percent" user disk space.
+    """
+    if PY3:
         st = os.statvfs(path)
-    except UnicodeEncodeError:
-        if not PY3 and isinstance(path, unicode):
-            # this is a bug with os.statvfs() and unicode on
-            # Python 2, see:
-            # - https://github.com/giampaolo/psutil/issues/416
-            # - http://bugs.python.org/issue18695
-            try:
-                path = path.encode(sys.getfilesystemencoding())
-            except UnicodeEncodeError:
-                pass
+    else:
+        # os.statvfs() does not support unicode on Python 2:
+        # - https://github.com/giampaolo/psutil/issues/416
+        # - http://bugs.python.org/issue18695
+        try:
             st = os.statvfs(path)
-        else:
-            raise
-    free = (st.f_bavail * st.f_frsize)
+        except UnicodeEncodeError:
+            if isinstance(path, unicode):
+                try:
+                    path = path.encode(sys.getfilesystemencoding())
+                except UnicodeEncodeError:
+                    pass
+                st = os.statvfs(path)
+            else:
+                raise
+
+    # Total space which is only available to root (unless changed
+    # at system level).
     total = (st.f_blocks * st.f_frsize)
-    used = (st.f_blocks - st.f_bfree) * st.f_frsize
-    percent = usage_percent(used, total, _round=1)
+    # Remaining free space usable by root.
+    avail_to_root = (st.f_bfree * st.f_frsize)
+    # Remaining free space usable by user.
+    avail_to_user = (st.f_bavail * st.f_frsize)
+    # Total space being used in general.
+    used = (total - avail_to_root)
+    # Total space which is available to user (same as 'total' but
+    # for the user).
+    total_user = used + avail_to_user
+    # 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)
+
     # NB: the percentage is -5% than what shown by df due to
     # reserved blocks that we are currently not considering:
-    # http://goo.gl/sWGbH
-    return sdiskusage(total, used, free, percent)
+    # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462
+    return sdiskusage(
+        total=total, used=used, free=avail_to_user, percent=usage_percent_user)
 
 
 @memoize
-def _get_terminal_map():
+def get_terminal_map():
+    """Get a map of device-id -> path as a dict.
+    Used by Process.terminal()
+    """
     ret = {}
     ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
     for name in ls:
-        assert name not in ret
+        assert name not in ret, name
         try:
             ret[os.stat(name).st_rdev] = name
         except OSError as err:
             if err.errno != errno.ENOENT:
                 raise
     return ret
--- a/third_party/python/psutil/psutil/_pssunos.py
+++ b/third_party/python/psutil/psutil/_pssunos.py
@@ -1,36 +1,50 @@
-#!/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.
 
 """Sun OS Solaris platform implementation."""
 
 import errno
 import os
 import socket
 import subprocess
 import sys
 from collections import namedtuple
+from socket import AF_INET
 
 from . import _common
 from . import _psposix
 from . import _psutil_posix as cext_posix
 from . import _psutil_sunos as cext
-from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum
+from ._common import AF_INET6
+from ._common import isfile_strict
+from ._common import memoize_when_activated
+from ._common import sockfam_to_enum
+from ._common import socktype_to_enum
 from ._common import usage_percent
+from ._compat import b
 from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
 
 
-__extra__all__ = ["CONN_IDLE", "CONN_BOUND"]
+__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
 
 PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
 AF_LINK = cext_posix.AF_LINK
+IS_64_BIT = sys.maxsize > 2**32
 
 CONN_IDLE = "IDLE"
 CONN_BOUND = "BOUND"
 
 PROC_STATUSES = {
     cext.SSLEEP: _common.STATUS_SLEEPING,
     cext.SRUN: _common.STATUS_RUNNING,
     cext.SZOMB: _common.STATUS_ZOMBIE,
@@ -52,89 +66,112 @@ TCP_STATUSES = {
     cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
     cext.TCPS_LISTEN: _common.CONN_LISTEN,
     cext.TCPS_CLOSING: _common.CONN_CLOSING,
     cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
     cext.TCPS_IDLE: CONN_IDLE,  # sunos specific
     cext.TCPS_BOUND: CONN_BOUND,  # sunos specific
 }
 
+proc_info_map = dict(
+    ppid=0,
+    rss=1,
+    vms=2,
+    create_time=3,
+    nice=4,
+    num_threads=5,
+    status=6,
+    ttynr=7)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.cpu_times()
 scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
+# psutil.cpu_times(percpu=True)
+pcputimes = namedtuple('pcputimes',
+                       ['user', 'system', 'children_user', 'children_system'])
+# psutil.virtual_memory()
 svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
-pextmem = namedtuple('pextmem', ['rss', 'vms'])
-pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms'])
+pfullmem = pmem
+# psutil.Process.memory_maps(grouped=True)
+pmmap_grouped = namedtuple('pmmap_grouped',
+                           ['path', 'rss', 'anonymous', 'locked'])
+# psutil.Process.memory_maps(grouped=False)
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
 
-# set later from __init__.py
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
 
-# --- functions
+def get_procfs_path():
+    """Return updated psutil.PROCFS_PATH constant."""
+    return sys.modules['psutil'].PROCFS_PATH
 
-disk_io_counters = cext.disk_io_counters
-net_io_counters = cext.net_io_counters
-disk_usage = _psposix.disk_usage
-net_if_addrs = cext_posix.net_if_addrs
+
+# =====================================================================
+# --- memory
+# =====================================================================
 
 
 def virtual_memory():
-    # we could have done this with kstat, but imho this is good enough
+    """Report virtual memory metrics."""
+    # we could have done this with kstat, but IMHO this is good enough
     total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
     # 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)
     return svmem(total, avail, percent, used, free)
 
 
 def swap_memory():
+    """Report swap memory metrics."""
     sin, sout = cext.swap_mem()
     # XXX
     # we are supposed to get total/free by doing so:
     # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
     #     usr/src/cmd/swap/swap.c
     # ...nevertheless I can't manage to obtain the same numbers as 'swap'
     # cmdline utility, so let's parse its output (sigh!)
     p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
-                          os.environ['PATH'], 'swap', '-l', '-k'],
+                          os.environ['PATH'], 'swap', '-l'],
                          stdout=subprocess.PIPE)
     stdout, stderr = p.communicate()
     if PY3:
         stdout = stdout.decode(sys.stdout.encoding)
     if p.returncode != 0:
-        raise RuntimeError("'swap -l -k' failed (retcode=%s)" % p.returncode)
+        raise RuntimeError("'swap -l' failed (retcode=%s)" % p.returncode)
 
     lines = stdout.strip().split('\n')[1:]
     if not lines:
         raise RuntimeError('no swap device(s) configured')
     total = free = 0
     for line in lines:
         line = line.split()
         t, f = line[-2:]
-        t = t.replace('K', '')
-        f = f.replace('K', '')
-        total += int(int(t) * 1024)
-        free += int(int(f) * 1024)
+        total += int(int(t) * 512)
+        free += int(int(f) * 512)
     used = total - free
     percent = usage_percent(used, total, _round=1)
     return _common.sswap(total, used, free, percent,
                          sin * PAGE_SIZE, sout * PAGE_SIZE)
 
 
-def pids():
-    """Returns a list of PIDs currently running on the system."""
-    return [int(x) for x in os.listdir('/proc') if x.isdigit()]
-
-
-def pid_exists(pid):
-    """Check for the existence of a unix pid."""
-    return _psposix.pid_exists(pid)
+# =====================================================================
+# --- CPU
+# =====================================================================
 
 
 def cpu_times():
     """Return system-wide CPU times as a named tuple"""
     ret = cext.per_cpu_times()
     return scputimes(*[sum(x) for x in zip(*ret)])
 
 
@@ -153,38 +190,31 @@ def cpu_count_logical():
         return None
 
 
 def cpu_count_physical():
     """Return the number of physical CPUs in the system."""
     return cext.cpu_count_phys()
 
 
-def boot_time():
-    """The system boot time expressed in seconds since the epoch."""
-    return cext.boot_time()
+def cpu_stats():
+    """Return various CPU stats as a named tuple."""
+    ctx_switches, interrupts, syscalls, traps = cext.cpu_stats()
+    soft_interrupts = 0
+    return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
+                             syscalls)
 
 
-def users():
-    """Return currently connected users as a list of namedtuples."""
-    retlist = []
-    rawlist = cext.users()
-    localhost = (':0.0', ':0')
-    for item in rawlist:
-        user, tty, hostname, tstamp, user_process = item
-        # note: the underlying C function includes entries about
-        # system boot, run level and others.  We might want
-        # to use them in the future.
-        if not user_process:
-            continue
-        if hostname in localhost:
-            hostname = 'localhost'
-        nt = _common.suser(user, tty, hostname, tstamp)
-        retlist.append(nt)
-    return retlist
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+disk_usage = _psposix.disk_usage
 
 
 def disk_partitions(all=False):
     """Return system disk partitions."""
     # TODO - the filtering logic should be better checked so that
     # it tries to reflect 'df' as much as possible
     retlist = []
     partitions = cext.disk_partitions()
@@ -198,36 +228,50 @@ def disk_partitions(all=False):
             # filter by filesystem having a total size > 0.
             if not disk_usage(mountpoint).total:
                 continue
         ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
         retlist.append(ntuple)
     return retlist
 
 
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_io_counters = cext.net_io_counters
+net_if_addrs = cext_posix.net_if_addrs
+
+
 def net_connections(kind, _pid=-1):
     """Return socket connections.  If pid == -1 return system-wide
     connections (as opposed to connections opened by one process only).
     Only INET sockets are returned (UNIX are not).
     """
     cmap = _common.conn_tmap.copy()
     if _pid == -1:
         cmap.pop('unix', 0)
     if kind not in cmap:
         raise ValueError("invalid %r kind argument; choose between %s"
                          % (kind, ', '.join([repr(x) for x in cmap])))
     families, types = _common.conn_tmap[kind]
-    rawlist = cext.net_connections(_pid, families, types)
+    rawlist = cext.net_connections(_pid)
     ret = set()
     for item in rawlist:
         fd, fam, type_, laddr, raddr, status, pid = item
         if fam not in families:
             continue
         if type_ not in types:
             continue
+        if fam in (AF_INET, AF_INET6):
+            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)
         ret.add(nt)
@@ -240,28 +284,74 @@ def net_if_stats():
     for name, items in ret.items():
         isup, duplex, speed, mtu = items
         if hasattr(_common, 'NicDuplex'):
             duplex = _common.NicDuplex(duplex)
         ret[name] = _common.snicstats(isup, duplex, speed, mtu)
     return ret
 
 
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+    """The system boot time expressed in seconds since the epoch."""
+    return cext.boot_time()
+
+
+def users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = cext.users()
+    localhost = (':0.0', ':0')
+    for item in rawlist:
+        user, tty, hostname, tstamp, user_process, pid = item
+        # note: the underlying C function includes entries about
+        # system boot, run level and others.  We might want
+        # to use them in the future.
+        if not user_process:
+            continue
+        if hostname in localhost:
+            hostname = 'localhost'
+        nt = _common.suser(user, tty, hostname, tstamp, pid)
+        retlist.append(nt)
+    return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+    """Returns a list of PIDs currently running on the system."""
+    return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()]
+
+
+def pid_exists(pid):
+    """Check for the existence of a unix pid."""
+    return _psposix.pid_exists(pid)
+
+
 def wrap_exceptions(fun):
     """Call callable into a try/except clause and translate ENOENT,
     EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
     """
+
     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
+            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)
@@ -269,57 +359,96 @@ def wrap_exceptions(fun):
                 raise AccessDenied(self.pid, self._name)
             raise
     return wrapper
 
 
 class Process(object):
     """Wrapper class around underlying C implementation."""
 
-    __slots__ = ["pid", "_name", "_ppid"]
+    __slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
         self._ppid = None
+        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()
+
+    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)
+
+    @memoize_when_activated
+    def _proc_basic_info(self):
+        ret = cext.proc_basic_info(self.pid, self._procfs_path)
+        assert len(ret) == len(proc_info_map)
+        return ret
+
+    @memoize_when_activated
+    def _proc_cred(self):
+        return cext.proc_cred(self.pid, self._procfs_path)
 
     @wrap_exceptions
     def name(self):
         # note: max len == 15
-        return cext.proc_name_and_args(self.pid)[0]
+        return self._proc_name_and_args()[0]
 
     @wrap_exceptions
     def exe(self):
-        # Will be guess later from cmdline but we want to explicitly
+        try:
+            return os.readlink(
+                "%s/%s/path/a.out" % (self._procfs_path, self.pid))
+        except OSError:
+            pass    # continue and guess the exe name from the cmdline
+        # Will be guessed later from cmdline but we want to explicitly
         # invoke cmdline here in order to get an AccessDenied
         # exception if the user has not enough privileges.
         self.cmdline()
         return ""
 
     @wrap_exceptions
     def cmdline(self):
-        return cext.proc_name_and_args(self.pid)[1].split(' ')
+        return self._proc_name_and_args()[1].split(' ')
+
+    @wrap_exceptions
+    def environ(self):
+        return cext.proc_environ(self.pid, self._procfs_path)
 
     @wrap_exceptions
     def create_time(self):
-        return cext.proc_basic_info(self.pid)[3]
+        return self._proc_basic_info()[proc_info_map['create_time']]
 
     @wrap_exceptions
     def num_threads(self):
-        return cext.proc_basic_info(self.pid)[5]
+        return self._proc_basic_info()[proc_info_map['num_threads']]
 
     @wrap_exceptions
     def nice_get(self):
-        # For some reason getpriority(3) return ESRCH (no such process)
-        # for certain low-pid processes, no matter what (even as root).
+        # 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)
@@ -332,126 +461,158 @@ class Process(object):
             # (no such process), no matter what.
             # The process actually exists though, as it has a name,
             # creation time, etc.
             raise AccessDenied(self.pid, self._name)
         return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def ppid(self):
-        return cext.proc_basic_info(self.pid)[0]
+        self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
+        return self._ppid
 
     @wrap_exceptions
     def uids(self):
-        real, effective, saved, _, _, _ = cext.proc_cred(self.pid)
+        real, effective, saved, _, _, _ = self._proc_cred()
         return _common.puids(real, effective, saved)
 
     @wrap_exceptions
     def gids(self):
-        _, _, _, real, effective, saved = cext.proc_cred(self.pid)
+        _, _, _, real, effective, saved = self._proc_cred()
         return _common.puids(real, effective, saved)
 
     @wrap_exceptions
     def cpu_times(self):
-        user, system = cext.proc_cpu_times(self.pid)
-        return _common.pcputimes(user, system)
+        try:
+            times = cext.proc_cpu_times(self.pid, self._procfs_path)
+        except OSError as err:
+            if err.errno == errno.EOVERFLOW and not IS_64_BIT:
+                # We may get here if we attempt to query a 64bit process
+                # with a 32bit python.
+                # Error originates from read() and also tools like "cat"
+                # fail in the same way (!).
+                # Since there simply is no way to determine CPU times we
+                # return 0.0 as a fallback. See:
+                # https://github.com/giampaolo/psutil/issues/857
+                times = (0.0, 0.0, 0.0, 0.0)
+            else:
+                raise
+        return _common.pcputimes(*times)
+
+    @wrap_exceptions
+    def cpu_num(self):
+        return cext.proc_cpu_num(self.pid, self._procfs_path)
 
     @wrap_exceptions
     def terminal(self):
+        procfs_path = self._procfs_path
         hit_enoent = False
         tty = wrap_exceptions(
-            cext.proc_basic_info(self.pid)[0])
+            self._proc_basic_info()[proc_info_map['ttynr']])
         if tty != cext.PRNODEV:
             for x in (0, 1, 2, 255):
                 try:
-                    return os.readlink('/proc/%d/path/%d' % (self.pid, x))
+                    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
         if hit_enoent:
             # raise NSP if the process disappeared on us
-            os.stat('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (procfs_path, self.pid))
 
     @wrap_exceptions
     def cwd(self):
         # /proc/PID/path/cwd may not be resolved by readlink() even if
         # it exists (ls shows it). If that's the case and the process
         # is still alive return None (we can return None also on BSD).
         # Reference: http://goo.gl/55XgO
+        procfs_path = self._procfs_path
         try:
-            return os.readlink("/proc/%s/path/cwd" % self.pid)
+            return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
         except OSError as err:
             if err.errno == errno.ENOENT:
-                os.stat("/proc/%s" % self.pid)
+                os.stat("%s/%s" % (procfs_path, self.pid))  # raise NSP or AD
                 return None
             raise
 
     @wrap_exceptions
     def memory_info(self):
-        ret = cext.proc_basic_info(self.pid)
-        rss, vms = ret[1] * 1024, ret[2] * 1024
-        return _common.pmem(rss, vms)
+        ret = self._proc_basic_info()
+        rss = ret[proc_info_map['rss']] * 1024
+        vms = ret[proc_info_map['vms']] * 1024
+        return pmem(rss, vms)
 
-    # it seems Solaris uses rss and vms only
-    memory_info_ex = memory_info
+    memory_full_info = memory_info
 
     @wrap_exceptions
     def status(self):
-        code = cext.proc_basic_info(self.pid)[6]
+        code = self._proc_basic_info()[proc_info_map['status']]
         # XXX is '?' legit? (we're not supposed to return it anyway)
         return PROC_STATUSES.get(code, '?')
 
     @wrap_exceptions
     def threads(self):
+        procfs_path = self._procfs_path
         ret = []
-        tids = os.listdir('/proc/%d/lwp' % self.pid)
+        tids = os.listdir('%s/%d/lwp' % (procfs_path, self.pid))
         hit_enoent = False
         for tid in tids:
             tid = int(tid)
             try:
                 utime, stime = cext.query_process_thread(
-                    self.pid, tid)
+                    self.pid, tid, procfs_path)
             except EnvironmentError as err:
+                if err.errno == errno.EOVERFLOW and not IS_64_BIT:
+                    # We may get here if we attempt to query a 64bit process
+                    # with a 32bit python.
+                    # Error originates from read() and also tools like "cat"
+                    # fail in the same way (!).
+                    # Since there simply is no way to determine CPU times we
+                    # return 0.0 as a fallback. See:
+                    # https://github.com/giampaolo/psutil/issues/857
+                    continue
                 # ENOENT == thread gone in meantime
                 if err.errno == errno.ENOENT:
                     hit_enoent = True
                     continue
                 raise
             else:
                 nt = _common.pthread(tid, utime, stime)
                 ret.append(nt)
         if hit_enoent:
             # raise NSP if the process disappeared on us
-            os.stat('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (procfs_path, self.pid))
         return ret
 
     @wrap_exceptions
     def open_files(self):
         retlist = []
         hit_enoent = False
-        pathdir = '/proc/%d/path' % self.pid
-        for fd in os.listdir('/proc/%d/fd' % self.pid):
+        procfs_path = self._procfs_path
+        pathdir = '%s/%d/path' % (procfs_path, self.pid)
+        for fd in os.listdir('%s/%d/fd' % (procfs_path, self.pid)):
             path = os.path.join(pathdir, fd)
             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
                 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('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (procfs_path, self.pid))
         return retlist
 
     def _get_unix_sockets(self, pid):
         """Get UNIX sockets used by process by parsing 'pfiles' output."""
         # TODO: rewrite this in C (...but the damn netstat source code
         # does not include this part! Argh!!)
         cmd = "pfiles %s" % pid
         p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
@@ -485,69 +646,80 @@ class Process(object):
     def connections(self, kind='inet'):
         ret = net_connections(kind, _pid=self.pid)
         # The underlying C implementation retrieves all OS connections
         # and filters them by PID.  At this point we can't tell whether
         # an empty list means there were no connections for process or
         # process is no longer active so we force NSP in case the PID
         # is no longer there.
         if not ret:
-            os.stat('/proc/%s' % self.pid)  # will raise NSP if process is gone
+            # will raise NSP if process is gone
+            os.stat('%s/%s' % (self._procfs_path, self.pid))
 
         # UNIX sockets
         if kind in ('all', 'unix'):
             ret.extend([_common.pconn(*conn) for conn in
                         self._get_unix_sockets(self.pid)])
         return ret
 
     nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
     nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')
 
     @wrap_exceptions
     def memory_maps(self):
         def toaddr(start, end):
             return '%s-%s' % (hex(start)[2:].strip('L'),
                               hex(end)[2:].strip('L'))
 
+        procfs_path = self._procfs_path
         retlist = []
-        rawlist = cext.proc_memory_maps(self.pid)
+        try:
+            rawlist = cext.proc_memory_maps(self.pid, procfs_path)
+        except OSError as err:
+            if err.errno == errno.EOVERFLOW and not IS_64_BIT:
+                # We may get here if we attempt to query a 64bit process
+                # with a 32bit python.
+                # Error originates from read() and also tools like "cat"
+                # fail in the same way (!).
+                # Since there simply is no way to determine CPU times we
+                # return 0.0 as a fallback. See:
+                # https://github.com/giampaolo/psutil/issues/857
+                return []
+            else:
+                raise
         hit_enoent = False
         for item in rawlist:
             addr, addrsize, perm, name, rss, anon, locked = item
             addr = toaddr(addr, addrsize)
             if not name.startswith('['):
                 try:
-                    name = os.readlink('/proc/%s/path/%s' % (self.pid, name))
+                    name = os.readlink(
+                        '%s/%s/path/%s' % (procfs_path, self.pid, name))
                 except OSError as err:
                     if err.errno == errno.ENOENT:
                         # sometimes the link may not be resolved by
                         # readlink() even if it exists (ls shows it).
                         # If that's the case we just return the
                         # unresolved link path.
                         # This seems an incosistency with /proc similar
                         # to: http://goo.gl/55XgO
-                        name = '/proc/%s/path/%s' % (self.pid, name)
+                        name = '%s/%s/path/%s' % (procfs_path, self.pid, name)
                         hit_enoent = True
                     else:
                         raise
             retlist.append((addr, perm, name, rss, anon, locked))
         if hit_enoent:
             # raise NSP if the process disappeared on us
-            os.stat('/proc/%s' % self.pid)
+            os.stat('%s/%s' % (procfs_path, self.pid))
         return retlist
 
     @wrap_exceptions
     def num_fds(self):
-        return len(os.listdir("/proc/%s/fd" % self.pid))
+        return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
 
     @wrap_exceptions
     def num_ctx_switches(self):
-        return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
+        return _common.pctxsw(
+            *cext.proc_num_ctx_switches(self.pid, self._procfs_path))
 
     @wrap_exceptions
     def wait(self, timeout=None):
-        try:
-            return _psposix.wait_pid(self.pid, timeout)
-        except _psposix.TimeoutExpired:
-            # support for private module import
-            if TimeoutExpired is None:
-                raise
-            raise TimeoutExpired(timeout, self.pid, self._name)
+        return _psposix.wait_pid(self.pid, timeout, self._name)
--- a/third_party/python/psutil/psutil/_psutil_bsd.c
+++ b/third_party/python/psutil/psutil/_psutil_bsd.c
@@ -1,1477 +1,713 @@
 /*
- * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil (OpenBSD).
+ * All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  *
- * FreeBSD platform-specific module methods for _psutil_bsd
+ * Platform-specific module methods for FreeBSD and OpenBSD.
+
+ * OpenBSD references:
+ * - OpenBSD source code: https://github.com/openbsd/src
+ *
+ * OpenBSD / NetBSD: missing APIs compared to FreeBSD implementation:
+ * - psutil.net_connections()
+ * - psutil.Process.get/set_cpu_affinity()  (not supported natively)
+ * - psutil.Process.memory_maps()
  */
 
+#if defined(PSUTIL_NETBSD)
+    #define _KMEMUSER
+#endif
 
 #include <Python.h>
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <signal.h>
 #include <fcntl.h>
 #include <paths.h>
 #include <sys/types.h>
+#include <sys/param.h>
 #include <sys/sysctl.h>
-#include <sys/param.h>
 #include <sys/user.h>
 #include <sys/proc.h>
 #include <sys/file.h>
-#include <sys/cpuset.h>
+#include <sys/socket.h>
 #include <net/route.h>
-
-#include <sys/socket.h>
 #include <sys/socketvar.h>    // for struct xsocket
 #include <sys/un.h>
 #include <sys/unpcb.h>
-#include <sys/sockio.h>
 // for xinpcb struct
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/in_pcb.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/ip_var.h>
 #include <netinet/tcp_var.h>   // for struct xtcpcb
 #include <netinet/tcp_fsm.h>   // for TCP connection states
 #include <arpa/inet.h>         // for inet_ntop()
 
-#if __FreeBSD_version < 900000
-#include <utmp.h>         // system users
-#else
-#include <utmpx.h>
-#endif
-#include <devstat.h>      // get io counters
-#include <sys/vmmeter.h>  // needed for vmtotal struct
-#include <libutil.h>      // process open files, shared libs (kinfo_getvmmap)
 #include <sys/mount.h>
 
 #include <net/if.h>       // net io counters
 #include <net/if_dl.h>
 #include <net/route.h>
-#include <net/if_media.h>
 
 #include <netinet/in.h>   // process open files/connections
 #include <sys/un.h>
 
-#include "_psutil_bsd.h"
 #include "_psutil_common.h"
-#include "arch/bsd/process_info.h"
+#include "_psutil_posix.h"
+
+#ifdef PSUTIL_FREEBSD
+    #include "arch/freebsd/specific.h"
+    #include "arch/freebsd/sys_socks.h"
+    #include "arch/freebsd/proc_socks.h"
+
+    #include <net/if_media.h>
+    #include <devstat.h>  // get io counters
+    #include <libutil.h>  // process open files, shared libs (kinfo_getvmmap)
+    #if __FreeBSD_version < 900000
+        #include <utmp.h>  // system users
+    #else
+        #include <utmpx.h>
+    #endif
+#elif PSUTIL_OPENBSD
+    #include "arch/openbsd/specific.h"
+
+    #include <utmp.h>
+    #include <sys/vnode.h>  // for VREG
+    #define _KERNEL  // for DTYPE_VNODE
+    #include <sys/file.h>
+    #undef _KERNEL
+    #include <sys/sched.h>  // for CPUSTATES & CP_*
+#elif PSUTIL_NETBSD
+    #include "arch/netbsd/specific.h"
+    #include "arch/netbsd/socks.h"
+
+    #include <utmpx.h>
+    #include <sys/vnode.h>  // for VREG
+    #include <sys/sched.h>  // for CPUSTATES & CP_*
+    #ifndef DTYPE_VNODE
+        #define DTYPE_VNODE 1
+    #endif
+#endif
+
 
 
 // convert a timeval struct to a double
-#define TV2DOUBLE(t)    ((t).tv_sec + (t).tv_usec / 1000000.0)
-
-
-/*
- * Utility function which fills a kinfo_proc struct based on process pid
- */
-static int
-psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc)
-{
-    int mib[4];
-    size_t size;
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_PROC;
-    mib[2] = KERN_PROC_PID;
-    mib[3] = pid;
-
-    size = sizeof(struct kinfo_proc);
+#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
 
-    if (sysctl((int *)mib, 4, proc, &size, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return -1;
-    }
+#ifdef PSUTIL_FREEBSD
+    // convert a bintime struct to milliseconds
+    #define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * \
+                           (uint32_t) (bt.frac >> 32) ) >> 32 ) / 1000000)
+#endif
 
-    // sysctl stores 0 in the size if we can't find the process information.
-    if (size == 0) {
-        NoSuchProcess();
-        return -1;
-    }
-    return 0;
-}
-
-
-/*
- * Set exception to AccessDenied if pid exists else NoSuchProcess.
- */
-void
-psutil_raise_ad_or_nsp(long pid) {
-    if (psutil_pid_exists(pid) == 0)
-        NoSuchProcess();
-    else
-        AccessDenied();
-}
+#if defined(PSUTIL_OPENBSD) || defined (PSUTIL_NETBSD)
+    #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
+#endif
 
 
 /*
  * Return a Python list of all the PIDs running on the system.
  */
 static PyObject *
-psutil_pids(PyObject *self, PyObject *args)
-{
+psutil_pids(PyObject *self, PyObject *args) {
     kinfo_proc *proclist = NULL;
     kinfo_proc *orig_address = NULL;
     size_t num_processes;
     size_t idx;
-    PyObject *retlist = PyList_New(0);
-    PyObject *pid = NULL;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_pid = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
 
-    if (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) {
-        PyErr_SetString(PyExc_RuntimeError,
-                        "failed to retrieve process list.");
+        if (errno != 0) {
+            PyErr_SetFromErrno(PyExc_OSError);
+        }
+        else {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "failed to retrieve process list");
+        }
         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++) {
-            pid = Py_BuildValue("i", proclist->ki_pid);
-            if (!pid)
+#ifdef PSUTIL_FREEBSD
+            py_pid = Py_BuildValue("i", proclist->ki_pid);
+#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+            py_pid = Py_BuildValue("i", proclist->p_pid);
+#endif
+            if (!py_pid)
                 goto error;
-            if (PyList_Append(retlist, pid))
+            if (PyList_Append(py_retlist, py_pid))
                 goto error;
-            Py_DECREF(pid);
+            Py_DECREF(py_pid);
             proclist++;
         }
         free(orig_address);
     }
 
-    return retlist;
+    return py_retlist;
 
 error:
-    Py_XDECREF(pid);
-    Py_DECREF(retlist);
+    Py_XDECREF(py_pid);
+    Py_DECREF(py_retlist);
     if (orig_address != NULL)
         free(orig_address);
     return NULL;
 }
 
 
 /*
  * Return a Python float indicating the system boot time expressed in
  * seconds since the epoch.
  */
 static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args)
-{
+psutil_boot_time(PyObject *self, PyObject *args) {
     // fetch sysctl "kern.boottime"
     static int request[2] = { CTL_KERN, KERN_BOOTTIME };
     struct timeval boottime;
     size_t len = sizeof(boottime);
 
-    if (sysctl(request, 2, &boottime, &len, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
+    if (sysctl(request, 2, &boottime, &len, NULL, 0) == -1)
+        return PyErr_SetFromErrno(PyExc_OSError);
+    return Py_BuildValue("d", (double)boottime.tv_sec);
+}
+
+
+/*
+ * Collect different info about a process in one shot and return
+ * them as a big Python tuple.
+ */
+static PyObject *
+psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
+    long pid;
+    long rss;
+    long vms;
+    long memtext;
+    long memdata;
+    long memstack;
+    int oncpu;
+    kinfo_proc kp;
+    long pagesize = sysconf(_SC_PAGESIZE);
+    char str[1000];
+    PyObject *py_name;
+    PyObject *py_retlist;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
+
+    // Process
+#ifdef PSUTIL_FREEBSD
+    sprintf(str, "%s", kp.ki_comm);
+#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+    sprintf(str, "%s", kp.p_comm);
+#endif
+    py_name = PyUnicode_DecodeFSDefault(str);
+    if (! py_name) {
+        // Likely a decoding error. We don't want to fail the whole
+        // operation. The python module may retry with proc_name().
+        PyErr_Clear();
+        py_name = Py_None;
     }
-    return Py_BuildValue("d", (double)boottime.tv_sec);
+
+    // Calculate memory.
+#ifdef PSUTIL_FREEBSD
+    rss = (long)kp.ki_rssize * pagesize;
+    vms = (long)kp.ki_size;
+    memtext = (long)kp.ki_tsize * pagesize;
+    memdata = (long)kp.ki_dsize * pagesize;
+    memstack = (long)kp.ki_ssize * pagesize;
+#else
+    rss = (long)kp.p_vm_rssize * pagesize;
+    #ifdef PSUTIL_OPENBSD
+        // VMS, this is how ps determines it on OpenBSD:
+        // https://github.com/openbsd/src/blob/
+        //     588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L505
+        vms = (long)(kp.p_vm_dsize + kp.p_vm_ssize + kp.p_vm_tsize) * pagesize;
+    #elif PSUTIL_NETBSD
+        // VMS, this is how top determines it on NetBSD:
+        // https://github.com/IIJ-NetBSD/netbsd-src/blob/master/external/
+        //     bsd/top/dist/machine/m_netbsd.c
+        vms = (long)kp.p_vm_msize * pagesize;
+    #endif
+        memtext = (long)kp.p_vm_tsize * pagesize;
+        memdata = (long)kp.p_vm_dsize * pagesize;
+        memstack = (long)kp.p_vm_ssize * pagesize;
+#endif
+
+#ifdef PSUTIL_FREEBSD
+    // what CPU we're on; top was used as an example:
+    // https://svnweb.freebsd.org/base/head/usr.bin/top/machine.c?
+    //     view=markup&pathrev=273835
+    // XXX - note: for "intr" PID this is -1.
+    if (kp.ki_stat == SRUN && kp.ki_oncpu != NOCPU)
+        oncpu = kp.ki_oncpu;
+    else
+        oncpu = kp.ki_lastcpu;
+#else
+    // On Net/OpenBSD we have kp.p_cpuid but it appears it's always
+    // set to KI_NOCPU. Even if it's not, ki_lastcpu does not exist
+    // so there's no way to determine where "sleeping" processes
+    // were. Not supported.
+    oncpu = -1;
+#endif
+
+    // Return a single big tuple with all process info.
+    py_retlist = Py_BuildValue(
+        "(lillllllidllllddddlllllbO)",
+#ifdef PSUTIL_FREEBSD
+        //
+        (long)kp.ki_ppid,                // (long) ppid
+        (int)kp.ki_stat,                 // (int) status
+        // UIDs
+        (long)kp.ki_ruid,                // (long) real uid
+        (long)kp.ki_uid,                 // (long) effective uid
+        (long)kp.ki_svuid,               // (long) saved uid
+        // GIDs
+        (long)kp.ki_rgid,                // (long) real gid
+        (long)kp.ki_groups[0],           // (long) effective gid
+        (long)kp.ki_svuid,               // (long) saved gid
+        //
+        kp.ki_tdev,                      // (int) tty nr
+        PSUTIL_TV2DOUBLE(kp.ki_start),   // (double) create time
+        // ctx switches
+        kp.ki_rusage.ru_nvcsw,           // (long) ctx switches (voluntary)
+        kp.ki_rusage.ru_nivcsw,          // (long) ctx switches (unvoluntary)
+        // IO count
+        kp.ki_rusage.ru_inblock,         // (long) read io count
+        kp.ki_rusage.ru_oublock,         // (long) write io count
+        // CPU times: convert from micro seconds to seconds.
+        PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_utime),     // (double) user time
+        PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_stime),     // (double) sys time
+        PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_utime),  // (double) children utime
+        PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_stime),  // (double) children stime
+        // memory
+        rss,                              // (long) rss
+        vms,                              // (long) vms
+        memtext,                          // (long) mem text
+        memdata,                          // (long) mem data
+        memstack,                         // (long) mem stack
+        // others
+        oncpu,                            // (int) the CPU we are on
+#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+        //
+        (long)kp.p_ppid,                 // (long) ppid
+        (int)kp.p_stat,                  // (int) status
+        // UIDs
+        (long)kp.p_ruid,                 // (long) real uid
+        (long)kp.p_uid,                  // (long) effective uid
+        (long)kp.p_svuid,                // (long) saved uid
+        // GIDs
+        (long)kp.p_rgid,                 // (long) real gid
+        (long)kp.p_groups[0],            // (long) effective gid
+        (long)kp.p_svuid,                // (long) saved gid
+        //
+        kp.p_tdev,                       // (int) tty nr
+        PSUTIL_KPT2DOUBLE(kp.p_ustart),  // (double) create time
+        // ctx switches
+        kp.p_uru_nvcsw,                  // (long) ctx switches (voluntary)
+        kp.p_uru_nivcsw,                 // (long) ctx switches (unvoluntary)
+        // IO count
+        kp.p_uru_inblock,                // (long) read io count
+        kp.p_uru_oublock,                // (long) write io count
+        // CPU times: convert from micro seconds to seconds.
+        PSUTIL_KPT2DOUBLE(kp.p_uutime),  // (double) user time
+        PSUTIL_KPT2DOUBLE(kp.p_ustime),  // (double) sys time
+        // OpenBSD and NetBSD provide children user + system times summed
+        // together (no distinction).
+        kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0,  // (double) ch utime
+        kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0,  // (double) ch stime
+        // memory
+        rss,                              // (long) rss
+        vms,                              // (long) vms
+        memtext,                          // (long) mem text
+        memdata,                          // (long) mem data
+        memstack,                         // (long) mem stack
+        // others
+        oncpu,                            // (int) the CPU we are on
+#endif
+        py_name                           // (pystr) name
+    );
+
+    if (py_retlist != NULL) {
+        // XXX shall we decref() also in case of Py_BuildValue() error?
+        Py_DECREF(py_name);
+    }
+    return py_retlist;
 }
 
 
 /*
  * Return process name from kinfo_proc as a Python string.
  */
 static PyObject *
-psutil_proc_name(PyObject *self, PyObject *args)
-{
+psutil_proc_name(PyObject *self, PyObject *args) {
     long pid;
-    struct kinfo_proc kp;
+    kinfo_proc kp;
+    char str[1000];
+
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    return Py_BuildValue("s", kp.ki_comm);
-}
 
-
-/*
- * Return process pathname executable.
- * Thanks to Robert N. M. Watson:
- * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
- */
-static PyObject *
-psutil_proc_exe(PyObject *self, PyObject *args)
-{
-    long pid;
-    char pathname[PATH_MAX];
-    int error;
-    int mib[4];
-    size_t size;
-
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_PROC;
-    mib[2] = KERN_PROC_PATHNAME;
-    mib[3] = pid;
-
-    size = sizeof(pathname);
-    error = sysctl(mib, 4, pathname, &size, NULL, 0);
-    if (error == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
-    }
-    if (size == 0 || strlen(pathname) == 0) {
-        if (psutil_pid_exists(pid) == 0)
-            return NoSuchProcess();
-        else
-            strcpy(pathname, "");
-    }
-    return Py_BuildValue("s", pathname);
+#ifdef PSUTIL_FREEBSD
+    sprintf(str, "%s", kp.ki_comm);
+#elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+    sprintf(str, "%s", kp.p_comm);
+#endif
+    return PyUnicode_DecodeFSDefault(str);
 }
 
 
 /*
  * Return process cmdline as a Python list of cmdline arguments.
  */
 static PyObject *
-psutil_proc_cmdline(PyObject *self, PyObject *args)
-{
+psutil_proc_cmdline(PyObject *self, PyObject *args) {
     long pid;
-    PyObject *arglist = NULL;
+    PyObject *py_retlist = NULL;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-
-    // get the commandline, defined in arch/bsd/process_info.c
-    arglist = psutil_get_arg_list(pid);
-
-    // psutil_get_arg_list() returns NULL only if psutil_cmd_args
-    // failed with ESRCH (no process with that PID)
-    if (NULL == arglist)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    return Py_BuildValue("N", arglist);
-}
-
-
-/*
- * Return process parent pid from kinfo_proc as a Python integer.
- */
-static PyObject *
-psutil_proc_ppid(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("l", (long)kp.ki_ppid);
-}
-
-
-/*
- * Return process status as a Python integer.
- */
-static PyObject *
-psutil_proc_status(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("i", (int)kp.ki_stat);
-}
-
-
-/*
- * Return process real, effective and saved user ids from kinfo_proc
- * as a Python tuple.
- */
-static PyObject *
-psutil_proc_uids(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("lll",
-                         (long)kp.ki_ruid,
-                         (long)kp.ki_uid,
-                         (long)kp.ki_svuid);
-}
-
-
-/*
- * Return process real, effective and saved group ids from kinfo_proc
- * as a Python tuple.
- */
-static PyObject *
-psutil_proc_gids(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("lll",
-                         (long)kp.ki_rgid,
-                         (long)kp.ki_groups[0],
-                         (long)kp.ki_svuid);
-}
-
-
-/*
- * Return process real, effective and saved group ids from kinfo_proc
- * as a Python tuple.
- */
-static PyObject *
-psutil_proc_tty_nr(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("i", kp.ki_tdev);
-}
-
-
-/*
- * Return the number of context switches performed by process as a tuple.
- */
-static PyObject *
-psutil_proc_num_ctx_switches(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
+    py_retlist = psutil_get_cmdline(pid);
+    if (py_retlist == NULL)
         return NULL;
-    return Py_BuildValue("(ll)",
-                         kp.ki_rusage.ru_nvcsw,
-                         kp.ki_rusage.ru_nivcsw);
-}
-
-
-/*
- * Return number of threads used by process as a Python integer.
- */
-static PyObject *
-psutil_proc_num_threads(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("l", (long)kp.ki_numthreads);
-}
-
-
-/*
- * Retrieves all threads used by process returning a list of tuples
- * including thread id, user time and system time.
- * Thanks to Robert N. M. Watson:
- * http://fxr.googlebit.com/source/usr.bin/procstat/
- *     procstat_threads.c?v=8-CURRENT
- */
-static PyObject *
-psutil_proc_threads(PyObject *self, PyObject *args)
-{
-    long pid;
-    int mib[4];
-    struct kinfo_proc *kip = NULL;
-    struct kinfo_proc *kipp = NULL;
-    int error;
-    unsigned int i;
-    size_t size;
-    PyObject *retList = PyList_New(0);
-    PyObject *pyTuple = NULL;
-
-    if (retList == NULL)
-        return NULL;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        goto error;
-
-    // we need to re-query for thread information, so don't use *kipp
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_PROC;
-    mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;
-    mib[3] = pid;
-
-    size = 0;
-    error = sysctl(mib, 4, NULL, &size, NULL, 0);
-    if (error == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-    if (size == 0) {
-        NoSuchProcess();
-        goto error;
-    }
-
-    kip = malloc(size);
-    if (kip == NULL) {
-        PyErr_NoMemory();
-        goto error;
-    }
-
-    error = sysctl(mib, 4, kip, &size, NULL, 0);
-    if (error == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-    if (size == 0) {
-        NoSuchProcess();
-        goto error;
-    }
-
-    for (i = 0; i < size / sizeof(*kipp); i++) {
-        kipp = &kip[i];
-        pyTuple = Py_BuildValue("Idd",
-                                kipp->ki_tid,
-                                TV2DOUBLE(kipp->ki_rusage.ru_utime),
-                                TV2DOUBLE(kipp->ki_rusage.ru_stime));
-        if (pyTuple == NULL)
-            goto error;
-        if (PyList_Append(retList, pyTuple))
-            goto error;
-        Py_DECREF(pyTuple);
-    }
-    free(kip);
-    return retList;
-
-error:
-    Py_XDECREF(pyTuple);
-    Py_DECREF(retList);
-    if (kip != NULL)
-        free(kip);
-    return NULL;
-}
-
-
-/*
- * Return a Python tuple (user_time, kernel_time)
- */
-static PyObject *
-psutil_proc_cpu_times(PyObject *self, PyObject *args)
-{
-    long pid;
-    double user_t, sys_t;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    // convert from microseconds to seconds
-    user_t = TV2DOUBLE(kp.ki_rusage.ru_utime);
-    sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime);
-    return Py_BuildValue("(dd)", user_t, sys_t);
+    return Py_BuildValue("N", py_retlist);
 }
 
 
 /*
  * Return the number of logical CPUs in the system.
  * XXX this could be shared with OSX
  */
 static PyObject *
-psutil_cpu_count_logical(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
     int mib[2];
     int ncpu;
     size_t len;
 
     mib[0] = CTL_HW;
     mib[1] = HW_NCPU;
     len = sizeof(ncpu);
 
     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
         Py_RETURN_NONE;  // mimic os.cpu_count()
     else
         return Py_BuildValue("i", ncpu);
 }
 
 
 /*
- * Return an XML string from which we'll determine the number of
- * physical CPU cores in the system.
- */
-static PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args)
-{
-    void *topology = NULL;
-    size_t size = 0;
-    PyObject *py_str;
-
-    if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0))
-        goto error;
-
-    topology = malloc(size);
-    if (!topology) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-
-    if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0))
-        goto error;
-
-    py_str = Py_BuildValue("s", topology);
-    free(topology);
-    return py_str;
-
-error:
-    if (topology != NULL)
-        free(topology);
-    Py_RETURN_NONE;
-}
-
-
-/*
- * 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;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("d", TV2DOUBLE(kp.ki_start));
-}
-
-
-/*
- * Return a Python float indicating the process create time expressed in
- * seconds since the epoch.
- */
-static PyObject *
-psutil_proc_io_counters(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    // there's apparently no way to determine bytes count, hence return -1.
-    return Py_BuildValue("(llll)",
-                         kp.ki_rusage.ru_inblock,
-                         kp.ki_rusage.ru_oublock,
-                         -1,
-                         -1);
-}
-
-
-/*
- * Return extended memory info for a process as a Python tuple.
- */
-static PyObject *
-psutil_proc_memory_info(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("(lllll)",
-                         ptoa(kp.ki_rssize),    // rss
-                         (long)kp.ki_size,      // vms
-                         ptoa(kp.ki_tsize),     // text
-                         ptoa(kp.ki_dsize),     // data
-                         ptoa(kp.ki_ssize));    // stack
-}
-
-
-/*
- * Return virtual memory usage statistics.
- */
-static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args)
-{
-    unsigned int   total, active, inactive, wired, cached, free;
-    size_t         size = sizeof(total);
-    struct vmtotal vm;
-    int            mib[] = {CTL_VM, VM_METER};
-    long           pagesize = getpagesize();
-#if __FreeBSD_version > 702101
-    long buffers;
-#else
-    int buffers;
-#endif
-    size_t buffers_size = sizeof(buffers);
-
-    if (sysctlbyname("vm.stats.vm.v_page_count", &total, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vm.stats.vm.v_inactive_count",
-                     &inactive, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0))
-        goto error;
-    if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0))
-        goto error;
-
-    size = sizeof(vm);
-    if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0)
-        goto error;
-
-    return Py_BuildValue("KKKKKKKK",
-        (unsigned long long) total    * pagesize,
-        (unsigned long long) free     * pagesize,
-        (unsigned long long) active   * pagesize,
-        (unsigned long long) inactive * pagesize,
-        (unsigned long long) wired    * pagesize,
-        (unsigned long long) cached   * pagesize,
-        (unsigned long long) buffers,
-        (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize  // shared
-    );
-
-error:
-    PyErr_SetFromErrno(PyExc_OSError);
-    return NULL;
-}
-
-
-#ifndef _PATH_DEVNULL
-#define _PATH_DEVNULL "/dev/null"
-#endif
-
-/*
- * Return swap memory stats (see 'swapinfo' cmdline tool)
- */
-static PyObject *
-psutil_swap_mem(PyObject *self, PyObject *args)
-{
-    kvm_t *kd;
-    struct kvm_swap kvmsw[1];
-    unsigned int swapin, swapout, nodein, nodeout;
-    size_t size = sizeof(unsigned int);
-
-    kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
-    if (kd == NULL) {
-        PyErr_SetString(PyExc_RuntimeError, "kvm_open failed");
-        return NULL;
-    }
-
-    if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
-        kvm_close(kd);
-        PyErr_SetString(PyExc_RuntimeError, "kvm_getswapinfo failed");
-        return NULL;
-    }
-
-    kvm_close(kd);
-
-    if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1)
-        goto sbn_error;
-    if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1)
-        goto sbn_error;
-    if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1)
-        goto sbn_error;
-    if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1)
-        goto sbn_error;
-
-    return Py_BuildValue("(iiiII)",
-                         kvmsw[0].ksw_total,                     // total
-                         kvmsw[0].ksw_used,                      // used
-                         kvmsw[0].ksw_total - kvmsw[0].ksw_used, // free
-                         swapin + swapout,                       // swap in
-                         nodein + nodeout);                      // swap out
-
-sbn_error:
-    PyErr_SetFromErrno(PyExc_OSError);
-    return NULL;
-}
-
-
-/*
  * Return a Python tuple representing user, kernel and idle CPU times
  */
 static PyObject *
-psutil_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_cpu_times(PyObject *self, PyObject *args) {
+#ifdef PSUTIL_NETBSD
+    u_int64_t cpu_time[CPUSTATES];
+#else
     long cpu_time[CPUSTATES];
-    size_t size;
+#endif
+    size_t size = sizeof(cpu_time);
+    int ret;
 
-    size = sizeof(cpu_time);
-
-    if (sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
-    }
-
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
+    ret = sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0);
+#elif PSUTIL_OPENBSD
+    int mib[] = {CTL_KERN, KERN_CPTIME};
+    ret = sysctl(mib, 2, &cpu_time, &size, NULL, 0);
+#endif
+    if (ret == -1)
+        return PyErr_SetFromErrno(PyExc_OSError);
     return Py_BuildValue("(ddddd)",
                          (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
                          (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
                          (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
                          (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
                          (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC
                         );
 }
 
 
-/*
- * XXX
- * These functions are available on FreeBSD 8 only.
- * In the upper python layer we do various tricks to avoid crashing
- * and/or to provide alternatives where possible.
- */
-
-
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
-/*
+ /*
  * Return files opened by process as a list of (path, fd) tuples.
  * TODO: this is broken as it may report empty paths. 'procstat'
  * utility has the same problem see:
  * https://github.com/giampaolo/psutil/issues/595
  */
+#if (defined(__FreeBSD_version) && __FreeBSD_version >= 800000) || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD)
 static PyObject *
-psutil_proc_open_files(PyObject *self, PyObject *args)
-{
+psutil_proc_open_files(PyObject *self, PyObject *args) {
     long pid;
-    int i, cnt;
+    int i;
+    int cnt;
+    int regular;
+    int fd;
+    char *path;
     struct kinfo_file *freep = NULL;
     struct kinfo_file *kif;
-    struct kinfo_proc kipp;
-    PyObject *retList = PyList_New(0);
-    PyObject *tuple = NULL;
+    kinfo_proc kipp;
+    PyObject *py_tuple = NULL;
+    PyObject *py_path = NULL;
+    PyObject *py_retlist = PyList_New(0);
 
-    if (retList == NULL)
+    if (py_retlist == NULL)
         return NULL;
     if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
     if (psutil_kinfo_proc(pid, &kipp) == -1)
         goto error;
 
+    errno = 0;
     freep = kinfo_getfile(pid, &cnt);
     if (freep == NULL) {
-        psutil_raise_ad_or_nsp(pid);
-        goto error;
-    }
-
-    for (i = 0; i < cnt; i++) {
-        kif = &freep[i];
-        if ((kif->kf_type == KF_TYPE_VNODE) &&
-                (kif->kf_vnode_type == KF_VTYPE_VREG))
-        {
-            tuple = Py_BuildValue("(si)", kif->kf_path, kif->kf_fd);
-            if (tuple == NULL)
-                goto error;
-            if (PyList_Append(retList, tuple))
-                goto error;
-            Py_DECREF(tuple);
-        }
-    }
-    free(freep);
-    return retList;
-
-error:
-    Py_XDECREF(tuple);
-    Py_DECREF(retList);
-    if (freep != NULL)
-        free(freep);
-    return NULL;
-}
-
-
-/*
- * Return files opened by process as a list of (path, fd) tuples
- */
-static PyObject *
-psutil_proc_num_fds(PyObject *self, PyObject *args)
-{
-    long pid;
-    int cnt;
-
-    struct kinfo_file *freep;
-    struct kinfo_proc kipp;
-
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_kinfo_proc(pid, &kipp) == -1)
-        return NULL;
-
-    freep = kinfo_getfile(pid, &cnt);
-    if (freep == NULL) {
-        psutil_raise_ad_or_nsp(pid);
-        return NULL;
-    }
-    free(freep);
-
-    return Py_BuildValue("i", cnt);
-}
-
-
-/*
- * Return process current working directory.
- */
-static PyObject *
-psutil_proc_cwd(PyObject *self, PyObject *args)
-{
-    long pid;
-    PyObject *path = NULL;
-    struct kinfo_file *freep = NULL;
-    struct kinfo_file *kif;
-    struct kinfo_proc kipp;
-
-    int i, cnt;
-
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        goto error;
-    if (psutil_kinfo_proc(pid, &kipp) == -1)
-        goto error;
-
-    freep = kinfo_getfile(pid, &cnt);
-    if (freep == NULL) {
-        psutil_raise_ad_or_nsp(pid);
+        psutil_raise_for_pid(pid, "kinfo_getfile()");
         goto error;
     }
 
     for (i = 0; i < cnt; i++) {
         kif = &freep[i];
-        if (kif->kf_fd == KF_FD_TYPE_CWD) {
-            path = Py_BuildValue("s", kif->kf_path);
-            if (!path)
+
+#ifdef PSUTIL_FREEBSD
+        regular = (kif->kf_type == KF_TYPE_VNODE) && \
+            (kif->kf_vnode_type == KF_VTYPE_VREG);
+        fd = kif->kf_fd;
+        path = kif->kf_path;
+#elif PSUTIL_OPENBSD
+        regular = (kif->f_type == DTYPE_VNODE) && (kif->v_type == VREG);
+        fd = kif->fd_fd;
+        // XXX - it appears path is not exposed in the kinfo_file struct.
+        path = "";
+#elif PSUTIL_NETBSD
+        regular = (kif->ki_ftype == DTYPE_VNODE) && (kif->ki_vtype == VREG);
+        fd = kif->ki_fd;
+        // XXX - it appears path is not exposed in the kinfo_file struct.
+        path = "";
+#endif
+        if (regular == 1) {
+            py_path = PyUnicode_DecodeFSDefault(path);
+            if (! py_path)
                 goto error;
-            break;
-        }
-    }
-    /*
-     * For lower pids it seems we can't retrieve any information
-     * (lsof can't do that it either).  Since this happens even
-     * as root we return an empty string instead of AccessDenied.
-     */
-    if (path == NULL)
-        path = Py_BuildValue("s", "");
-    free(freep);
-    return path;
-
-error:
-    Py_XDECREF(path);
-    if (freep != NULL)
-        free(freep);
-    return NULL;
-}
-
-
-// The tcplist fetching and walking is borrowed from netstat/inet.c.
-static char *
-psutil_fetch_tcplist(void)
-{
-    char *buf;
-    size_t len;
-
-    for (;;) {
-        if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) {
-            PyErr_SetFromErrno(PyExc_OSError);
-            return NULL;
-        }
-        buf = malloc(len);
-        if (buf == NULL) {
-            PyErr_NoMemory();
-            return NULL;
-        }
-        if (sysctlbyname("net.inet.tcp.pcblist", buf, &len, NULL, 0) < 0) {
-            free(buf);
-            PyErr_SetFromErrno(PyExc_OSError);
-            return NULL;
-        }
-        return buf;
-    }
-}
-
-static int
-psutil_sockaddr_port(int family, struct sockaddr_storage *ss)
-{
-    struct sockaddr_in6 *sin6;
-    struct sockaddr_in *sin;
-
-    if (family == AF_INET) {
-        sin = (struct sockaddr_in *)ss;
-        return (sin->sin_port);
-    }
-    else {
-        sin6 = (struct sockaddr_in6 *)ss;
-        return (sin6->sin6_port);
-    }
-}
-
-static void *
-psutil_sockaddr_addr(int family, struct sockaddr_storage *ss)
-{
-    struct sockaddr_in6 *sin6;
-    struct sockaddr_in *sin;
-
-    if (family == AF_INET) {
-        sin = (struct sockaddr_in *)ss;
-        return (&sin->sin_addr);
-    }
-    else {
-        sin6 = (struct sockaddr_in6 *)ss;
-        return (&sin6->sin6_addr);
-    }
-}
-
-static socklen_t
-psutil_sockaddr_addrlen(int family)
-{
-    if (family == AF_INET)
-        return (sizeof(struct in_addr));
-    else
-        return (sizeof(struct in6_addr));
-}
-
-static int
-psutil_sockaddr_matches(int family, int port, void *pcb_addr,
-                        struct sockaddr_storage *ss)
-{
-    if (psutil_sockaddr_port(family, ss) != port)
-        return (0);
-    return (memcmp(psutil_sockaddr_addr(family, ss), pcb_addr,
-                   psutil_sockaddr_addrlen(family)) == 0);
-}
-
-static struct tcpcb *
-psutil_search_tcplist(char *buf, struct kinfo_file *kif)
-{
-    struct tcpcb *tp;
-    struct inpcb *inp;
-    struct xinpgen *xig, *oxig;
-    struct xsocket *so;
-
-    oxig = xig = (struct xinpgen *)buf;
-    for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
-            xig->xig_len > sizeof(struct xinpgen);
-            xig = (struct xinpgen *)((char *)xig + xig->xig_len)) {
-        tp = &((struct xtcpcb *)xig)->xt_tp;
-        inp = &((struct xtcpcb *)xig)->xt_inp;
-        so = &((struct xtcpcb *)xig)->xt_socket;
-
-        if (so->so_type != kif->kf_sock_type ||
-                so->xso_family != kif->kf_sock_domain ||
-                so->xso_protocol != kif->kf_sock_protocol)
-            continue;
-
-        if (kif->kf_sock_domain == AF_INET) {
-            if (!psutil_sockaddr_matches(
-                    AF_INET, inp->inp_lport, &inp->inp_laddr,
-                    &kif->kf_sa_local))
-                continue;
-            if (!psutil_sockaddr_matches(
-                    AF_INET, inp->inp_fport, &inp->inp_faddr,
-                    &kif->kf_sa_peer))
-                continue;
-        } else {
-            if (!psutil_sockaddr_matches(
-                    AF_INET6, inp->inp_lport, &inp->in6p_laddr,
-                    &kif->kf_sa_local))
-                continue;
-            if (!psutil_sockaddr_matches(
-                    AF_INET6, inp->inp_fport, &inp->in6p_faddr,
-                    &kif->kf_sa_peer))
-                continue;
-        }
-
-        return (tp);
-    }
-    return NULL;
-}
-
-
-// a signaler for connections without an actual status
-static int PSUTIL_CONN_NONE = 128;
-
-/*
- * Return connections opened by process.
- */
-static PyObject *
-psutil_proc_connections(PyObject *self, PyObject *args)
-{
-    long pid;
-    int i, cnt;
-
-    struct kinfo_file *freep = NULL;
-    struct kinfo_file *kif;
-    char *tcplist = NULL;
-    struct tcpcb *tcp;
-
-    PyObject *retList = PyList_New(0);
-    PyObject *tuple = NULL;
-    PyObject *laddr = NULL;
-    PyObject *raddr = NULL;
-    PyObject *af_filter = NULL;
-    PyObject *type_filter = NULL;
-    PyObject *_family = NULL;
-    PyObject *_type = NULL;
-
-    if (retList == NULL)
-        return NULL;
-    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter))
-        goto error;
-    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
-        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
-        goto error;
-    }
-
-    freep = kinfo_getfile(pid, &cnt);
-    if (freep == NULL) {
-        psutil_raise_ad_or_nsp(pid);
-        goto error;
-    }
-
-    tcplist = psutil_fetch_tcplist();
-    if (tcplist == NULL) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-
-    for (i = 0; i < cnt; i++) {
-        int lport, rport, state;
-        char lip[200], rip[200];
-        char path[PATH_MAX];
-        int inseq;
-        tuple = NULL;
-        laddr = NULL;
-        raddr = NULL;
-
-        kif = &freep[i];
-        if (kif->kf_type == KF_TYPE_SOCKET) {
-            // apply filters
-            _family = PyLong_FromLong((long)kif->kf_sock_domain);
-            inseq = PySequence_Contains(af_filter, _family);
-            Py_DECREF(_family);
-            if (inseq == 0)
-                continue;
-            _type = PyLong_FromLong((long)kif->kf_sock_type);
-            inseq = PySequence_Contains(type_filter, _type);
-            Py_DECREF(_type);
-            if (inseq == 0)
-                continue;
-            // IPv4 / IPv6 socket
-            if ((kif->kf_sock_domain == AF_INET) ||
-                    (kif->kf_sock_domain == AF_INET6)) {
-                // fill status
-                state = PSUTIL_CONN_NONE;
-                if (kif->kf_sock_type == SOCK_STREAM) {
-                    tcp = psutil_search_tcplist(tcplist, kif);
-                    if (tcp != NULL)
-                        state = (int)tcp->t_state;
-                }
-
-                // build addr and port
-                inet_ntop(
-                    kif->kf_sock_domain,
-                    psutil_sockaddr_addr(kif->kf_sock_domain,
-                                         &kif->kf_sa_local),
-                    lip,
-                    sizeof(lip));
-                inet_ntop(
-                    kif->kf_sock_domain,
-                    psutil_sockaddr_addr(kif->kf_sock_domain,
-                                         &kif->kf_sa_peer),
-                    rip,
-                    sizeof(rip));
-                lport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
-                                                   &kif->kf_sa_local));
-                rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
-                                                   &kif->kf_sa_peer));
-
-                // construct python tuple/list
-                laddr = Py_BuildValue("(si)", lip, lport);
-                if (!laddr)
-                    goto error;
-                if (rport != 0)
-                    raddr = Py_BuildValue("(si)", rip, rport);
-                else
-                    raddr = Py_BuildValue("()");
-                if (!raddr)
-                    goto error;
-                tuple = Py_BuildValue("(iiiNNi)",
-                                      kif->kf_fd,
-                                      kif->kf_sock_domain,
-                                      kif->kf_sock_type,
-                                      laddr,
-                                      raddr,
-                                      state);
-                if (!tuple)
-                    goto error;
-                if (PyList_Append(retList, tuple))
-                    goto error;
-                Py_DECREF(tuple);
-            }
-            // UNIX socket
-            else if (kif->kf_sock_domain == AF_UNIX) {
-                struct sockaddr_un *sun;
-
-                sun = (struct sockaddr_un *)&kif->kf_sa_local;
-                snprintf(
-                    path, sizeof(path), "%.*s",
-                    (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
-                    sun->sun_path);
-
-                tuple = Py_BuildValue("(iiisOi)",
-                                      kif->kf_fd,
-                                      kif->kf_sock_domain,
-                                      kif->kf_sock_type,
-                                      path,
-                                      Py_None,
-                                      PSUTIL_CONN_NONE);
-                if (!tuple)
-                    goto error;
-                if (PyList_Append(retList, tuple))
-                    goto error;
-                Py_DECREF(tuple);
-                Py_INCREF(Py_None);
-            }
+            py_tuple = Py_BuildValue("(Oi)", py_path, fd);
+            if (py_tuple == NULL)
+                goto error;
+            if (PyList_Append(py_retlist, py_tuple))
+                goto error;
+            Py_DECREF(py_path);
+            Py_DECREF(py_tuple);
         }
     }
     free(freep);
-    free(tcplist);
-    return retList;
-
-error:
-    Py_XDECREF(tuple);
-    Py_XDECREF(laddr);
-    Py_XDECREF(raddr);
-    Py_DECREF(retList);
-    if (freep != NULL)
-        free(freep);
-    if (tcplist != NULL)
-        free(tcplist);
-    return NULL;
-}
-
-
-/*
- * Return a Python list of tuple representing per-cpu times
- */
-static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args)
-{
-    static int maxcpus;
-    int mib[2];
-    int ncpu;
-    size_t len;
-    size_t size;
-    int i;
-    PyObject *py_retlist = PyList_New(0);
-    PyObject *py_cputime = NULL;
-
-    if (py_retlist == NULL)
-        return NULL;
-
-    // retrieve maxcpus value
-    size = sizeof(maxcpus);
-    if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
-        Py_DECREF(py_retlist);
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
-    }
-    long cpu_time[maxcpus][CPUSTATES];
-
-    // retrieve the number of cpus
-    mib[0] = CTL_HW;
-    mib[1] = HW_NCPU;
-    len = sizeof(ncpu);
-    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-
-    // per-cpu info
-    size = sizeof(cpu_time);
-    if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-
-    for (i = 0; i < ncpu; i++) {
-        py_cputime = Py_BuildValue(
-            "(ddddd)",
-            (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
-            (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
-            (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
-            (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
-            (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC);
-        if (!py_cputime)
-            goto error;
-        if (PyList_Append(py_retlist, py_cputime))
-            goto error;
-        Py_DECREF(py_cputime);
-    }
-
     return py_retlist;
 
 error:
-    Py_XDECREF(py_cputime);
+    Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
-    return NULL;
-}
-
-
-// remove spaces from string
-void remove_spaces(char *str) {
-    char *p1 = str;
-    char *p2 = str;
-    do
-        while (*p2 == ' ')
-            p2++;
-    while ((*p1++ = *p2++));
-}
-
-
-/*
- * Return a list of tuples for every process memory maps.
- * 'procstat' cmdline utility has been used as an example.
- */
-static PyObject *
-psutil_proc_memory_maps(PyObject *self, PyObject *args)
-{
-    long pid;
-    int ptrwidth;
-    int i, cnt;
-    char addr[1000];
-    char perms[4];
-    const char *path;
-    struct kinfo_proc kp;
-    struct kinfo_vmentry *freep = NULL;
-    struct kinfo_vmentry *kve;
-    ptrwidth = 2 * sizeof(void *);
-    PyObject *pytuple = NULL;
-    PyObject *retlist = PyList_New(0);
-
-    if (retlist == NULL)
-        return NULL;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        goto error;
-    if (psutil_kinfo_proc(pid, &kp) == -1)
-        goto error;
-
-    freep = kinfo_getvmmap(pid, &cnt);
-    if (freep == NULL) {
-        psutil_raise_ad_or_nsp(pid);
-        goto error;
-    }
-    for (i = 0; i < cnt; i++) {
-        pytuple = NULL;
-        kve = &freep[i];
-        addr[0] = '\0';
-        perms[0] = '\0';
-        sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
-                ptrwidth, (uintmax_t)kve->kve_end);
-        remove_spaces(addr);
-        strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
-                sizeof(perms));
-        strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
-                sizeof(perms));
-        strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
-                sizeof(perms));
-
-        if (strlen(kve->kve_path) == 0) {
-            switch (kve->kve_type) {
-                case KVME_TYPE_NONE:
-                    path = "[none]";
-                    break;
-                case KVME_TYPE_DEFAULT:
-                    path = "[default]";
-                    break;
-                case KVME_TYPE_VNODE:
-                    path = "[vnode]";
-                    break;
-                case KVME_TYPE_SWAP:
-                    path = "[swap]";
-                    break;
-                case KVME_TYPE_DEVICE:
-                    path = "[device]";
-                    break;
-                case KVME_TYPE_PHYS:
-                    path = "[phys]";
-                    break;
-                case KVME_TYPE_DEAD:
-                    path = "[dead]";
-                    break;
-                case KVME_TYPE_SG:
-                    path = "[sg]";
-                    break;
-                case KVME_TYPE_UNKNOWN:
-                    path = "[unknown]";
-                    break;
-                default:
-                    path = "[?]";
-                    break;
-            }
-        }
-        else {
-            path = kve->kve_path;
-        }
-
-        pytuple = Py_BuildValue("sssiiii",
-            addr,                       // "start-end" address
-            perms,                      // "rwx" permissions
-            path,                       // path
-            kve->kve_resident,          // rss
-            kve->kve_private_resident,  // private
-            kve->kve_ref_count,         // ref count
-            kve->kve_shadow_count);     // shadow count
-        if (!pytuple)
-            goto error;
-        if (PyList_Append(retlist, pytuple))
-            goto error;
-        Py_DECREF(pytuple);
-    }
-    free(freep);
-    return retlist;
-
-error:
-    Py_XDECREF(pytuple);
-    Py_DECREF(retlist);
     if (freep != NULL)
         free(freep);
     return NULL;
 }
 #endif
 
 
 /*
  * Return a list of tuples including device, mount point and fs type
  * for all partitions mounted on the system.
  */
 static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args)
-{
+psutil_disk_partitions(PyObject *self, PyObject *args) {
     int num;
     int i;
     long len;
     uint64_t flags;
     char opts[200];
+#ifdef PSUTIL_NETBSD
+    struct statvfs *fs = NULL;
+#else
     struct statfs *fs = NULL;
+#endif
     PyObject *py_retlist = PyList_New(0);
+    PyObject *py_dev = NULL;
+    PyObject *py_mountp = NULL;
     PyObject *py_tuple = NULL;
 
     if (py_retlist == NULL)
         return NULL;
 
     // get the number of mount points
     Py_BEGIN_ALLOW_THREADS
+#ifdef PSUTIL_NETBSD
+    num = getvfsstat(NULL, 0, MNT_NOWAIT);
+#else
     num = getfsstat(NULL, 0, MNT_NOWAIT);
+#endif
     Py_END_ALLOW_THREADS
     if (num == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     len = sizeof(*fs) * num;
     fs = malloc(len);
     if (fs == NULL) {
         PyErr_NoMemory();
         goto error;
     }
 
     Py_BEGIN_ALLOW_THREADS
+#ifdef PSUTIL_NETBSD
+    num = getvfsstat(fs, len, MNT_NOWAIT);
+#else
     num = getfsstat(fs, len, MNT_NOWAIT);
+#endif
     Py_END_ALLOW_THREADS
     if (num == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     for (i = 0; i < num; i++) {
         py_tuple = NULL;
         opts[0] = 0;
+#ifdef PSUTIL_NETBSD
+        flags = fs[i].f_flag;
+#else
         flags = fs[i].f_flags;
+#endif
 
         // see sys/mount.h
         if (flags & MNT_RDONLY)
             strlcat(opts, "ro", sizeof(opts));
         else
             strlcat(opts, "rw", sizeof(opts));
         if (flags & MNT_SYNCHRONOUS)
             strlcat(opts, ",sync", sizeof(opts));
         if (flags & MNT_NOEXEC)
             strlcat(opts, ",noexec", sizeof(opts));
         if (flags & MNT_NOSUID)
             strlcat(opts, ",nosuid", sizeof(opts));
+        if (flags & MNT_ASYNC)
+            strlcat(opts, ",async", sizeof(opts));
+        if (flags & MNT_NOATIME)
+            strlcat(opts, ",noatime", sizeof(opts));
+        if (flags & MNT_SOFTDEP)
+            strlcat(opts, ",softdep", sizeof(opts));
+#ifdef PSUTIL_FREEBSD
         if (flags & MNT_UNION)
             strlcat(opts, ",union", sizeof(opts));
-        if (flags & MNT_ASYNC)
-            strlcat(opts, ",async", sizeof(opts));
         if (flags & MNT_SUIDDIR)
             strlcat(opts, ",suiddir", sizeof(opts));
         if (flags & MNT_SOFTDEP)
             strlcat(opts, ",softdep", sizeof(opts));
         if (flags & MNT_NOSYMFOLLOW)
             strlcat(opts, ",nosymfollow", sizeof(opts));
         if (flags & MNT_GJOURNAL)
             strlcat(opts, ",gjournal", sizeof(opts));
         if (flags & MNT_MULTILABEL)
             strlcat(opts, ",multilabel", sizeof(opts));
         if (flags & MNT_ACLS)
             strlcat(opts, ",acls", sizeof(opts));
-        if (flags & MNT_NOATIME)
-            strlcat(opts, ",noatime", sizeof(opts));
         if (flags & MNT_NOCLUSTERR)
             strlcat(opts, ",noclusterr", sizeof(opts));
         if (flags & MNT_NOCLUSTERW)
             strlcat(opts, ",noclusterw", sizeof(opts));
         if (flags & MNT_NFS4ACLS)
             strlcat(opts, ",nfs4acls", sizeof(opts));
-
-        py_tuple = Py_BuildValue("(ssss)",
-                                 fs[i].f_mntfromname,  // device
-                                 fs[i].f_mntonname,    // mount point
+#elif PSUTIL_NETBSD
+        if (flags & MNT_NODEV)
+            strlcat(opts, ",nodev", sizeof(opts));
+        if (flags & MNT_UNION)
+            strlcat(opts, ",union", sizeof(opts));
+        if (flags & MNT_NOCOREDUMP)
+            strlcat(opts, ",nocoredump", sizeof(opts));
+#ifdef MNT_RELATIME
+        if (flags & MNT_RELATIME)
+            strlcat(opts, ",relatime", sizeof(opts));
+#endif
+        if (flags & MNT_IGNORE)
+            strlcat(opts, ",ignore", sizeof(opts));
+#ifdef MNT_DISCARD
+        if (flags & MNT_DISCARD)
+            strlcat(opts, ",discard", sizeof(opts));
+#endif
+#ifdef MNT_EXTATTR
+        if (flags & MNT_EXTATTR)
+            strlcat(opts, ",extattr", sizeof(opts));
+#endif
+        if (flags & MNT_LOG)
+            strlcat(opts, ",log", sizeof(opts));
+        if (flags & MNT_SYMPERM)
+            strlcat(opts, ",symperm", sizeof(opts));
+        if (flags & MNT_NODEVMTIME)
+            strlcat(opts, ",nodevmtime", sizeof(opts));
+#endif
+        py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
+        if (! py_dev)
+            goto error;
+        py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
+        if (! py_mountp)
+            goto error;
+        py_tuple = Py_BuildValue("(OOss)",
+                                 py_dev,               // device
+                                 py_mountp,            // mount point
                                  fs[i].f_fstypename,   // fs type
                                  opts);                // options
         if (!py_tuple)
             goto error;
         if (PyList_Append(py_retlist, py_tuple))
             goto error;
+        Py_DECREF(py_dev);
+        Py_DECREF(py_mountp);
         Py_DECREF(py_tuple);
     }
 
     free(fs);
     return py_retlist;
 
 error:
+    Py_XDECREF(py_dev);
+    Py_XDECREF(py_mountp);
     Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     if (fs != NULL)
         free(fs);
     return NULL;
 }
 
 
 /*
  * Return a Python list of named tuples with overall network I/O information
  */
 static PyObject *
-psutil_net_io_counters(PyObject *self, PyObject *args)
-{
+psutil_net_io_counters(PyObject *self, PyObject *args) {
     char *buf = NULL, *lim, *next;
     struct if_msghdr *ifm;
     int mib[6];
     size_t len;
     PyObject *py_retdict = PyDict_New();
     PyObject *py_ifc_info = NULL;
-
     if (py_retdict == NULL)
         return NULL;
+
     mib[0] = CTL_NET;          // networking subsystem
     mib[1] = PF_ROUTE;         // type of information
     mib[2] = 0;                // protocol (IPPROTO_xxx)
     mib[3] = 0;                // address family
     mib[4] = NET_RT_IFLIST;   // operation
     mib[5] = 0;
 
     if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
@@ -1514,17 +750,22 @@ psutil_net_io_counters(PyObject *self, P
             py_ifc_info = Py_BuildValue("(kkkkkkki)",
                                         if2m->ifm_data.ifi_obytes,
                                         if2m->ifm_data.ifi_ibytes,
                                         if2m->ifm_data.ifi_opackets,
                                         if2m->ifm_data.ifi_ipackets,
                                         if2m->ifm_data.ifi_ierrors,
                                         if2m->ifm_data.ifi_oerrors,
                                         if2m->ifm_data.ifi_iqdrops,
-                                        0);  // dropout not supported
+#ifdef _IFI_OQDROPS
+                                        if2m->ifm_data.ifi_oqdrops
+#else
+                                        0
+#endif
+                                        );
             if (!py_ifc_info)
                 goto error;
             if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
                 goto error;
             Py_DECREF(py_ifc_info);
         }
         else {
             continue;
@@ -1539,680 +780,217 @@ error:
     Py_DECREF(py_retdict);
     if (buf != NULL)
         free(buf);
     return NULL;
 }
 
 
 /*
- * Return a Python dict of tuples for disk I/O information
- */
-static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args)
-{
-    int i;
-    struct statinfo stats;
-
-    PyObject *py_retdict = PyDict_New();
-    PyObject *py_disk_info = NULL;
-
-    if (py_retdict == NULL)
-        return NULL;
-    if (devstat_checkversion(NULL) < 0) {
-        PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed");
-        goto error;
-    }
-
-    stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
-    if (stats.dinfo == NULL) {
-        PyErr_NoMemory();
-        goto error;
-    }
-    bzero(stats.dinfo, sizeof(struct devinfo));
-
-    if (devstat_getdevs(NULL, &stats) == -1) {
-        PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() failed");
-        goto error;
-    }
-
-    for (i = 0; i < stats.dinfo->numdevs; i++) {
-        py_disk_info = NULL;
-        struct devstat current;
-        char disk_name[128];
-        current = stats.dinfo->devices[i];
-        snprintf(disk_name, sizeof(disk_name), "%s%d",
-                 current.device_name,
-                 current.unit_number);
-
-        py_disk_info = Py_BuildValue(
-            "(KKKKLL)",
-            current.operations[DEVSTAT_READ],   // no reads
-            current.operations[DEVSTAT_WRITE],  // no writes
-            current.bytes[DEVSTAT_READ],        // bytes read
-            current.bytes[DEVSTAT_WRITE],       // bytes written
-            (long long)devstat_compute_etime(
-                &current.duration[DEVSTAT_READ], NULL),  // r time
-            (long long)devstat_compute_etime(
-                &current.duration[DEVSTAT_WRITE], NULL));  // w time
-        if (!py_disk_info)
-            goto error;
-        if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
-            goto error;
-        Py_DECREF(py_disk_info);
-    }
-
-    if (stats.dinfo->mem_ptr)
-        free(stats.dinfo->mem_ptr);
-    free(stats.dinfo);
-    return py_retdict;
-
-error:
-    Py_XDECREF(py_disk_info);
-    Py_DECREF(py_retdict);
-    if (stats.dinfo != NULL)
-        free(stats.dinfo);
-    return NULL;
-}
-
-
-/*
  * Return currently connected users as a list of tuples.
  */
 static PyObject *
-psutil_users(PyObject *self, PyObject *args)
-{
-    PyObject *ret_list = PyList_New(0);
-    PyObject *tuple = NULL;
+psutil_users(PyObject *self, PyObject *args) {
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_username = NULL;
+    PyObject *py_tty = NULL;
+    PyObject *py_hostname = NULL;
+    PyObject *py_tuple = NULL;
 
-    if (ret_list == NULL)
+    if (py_retlist == NULL)
         return NULL;
 
-#if __FreeBSD_version < 900000
+#if (defined(__FreeBSD_version) && (__FreeBSD_version < 900000)) || PSUTIL_OPENBSD
     struct utmp ut;
     FILE *fp;
 
     fp = fopen(_PATH_UTMP, "r");
     if (fp == NULL) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     while (fread(&ut, sizeof(ut), 1, fp) == 1) {
         if (*ut.ut_name == '\0')
             continue;
-        tuple = Py_BuildValue(
-            "(sssf)",
-            ut.ut_name,         // username
-            ut.ut_line,         // tty
-            ut.ut_host,         // hostname
-           (float)ut.ut_time);  // start time
-        if (!tuple) {
+        py_username = PyUnicode_DecodeFSDefault(ut.ut_name);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(ut.ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(ut.ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfi)",
+            py_username,        // username
+            py_tty,             // tty
+            py_hostname,        // hostname
+            (float)ut.ut_time,  // start time
+#ifdef PSUTIL_OPENBSD
+            -1                  // process id (set to None later)
+#else
+            ut.ut_pid           // process id
+#endif
+        );
+        if (!py_tuple) {
             fclose(fp);
             goto error;
         }
-        if (PyList_Append(ret_list, tuple)) {
+        if (PyList_Append(py_retlist, py_tuple)) {
             fclose(fp);
             goto error;
         }
-        Py_DECREF(tuple);
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
     }
 
     fclose(fp);
 #else
     struct utmpx *utx;
-
+    setutxent();
     while ((utx = getutxent()) != NULL) {
         if (utx->ut_type != USER_PROCESS)
             continue;
-        tuple = Py_BuildValue(
-            "(sssf)",
-            utx->ut_user,  // username
-            utx->ut_line,  // tty
-            utx->ut_host,  // hostname
-            (float)utx->ut_tv.tv_sec  // start time
+        py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfi)",
+            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
         );
 
-        if (!tuple) {
+        if (!py_tuple) {
             endutxent();
             goto error;
         }
-        if (PyList_Append(ret_list, tuple)) {
+        if (PyList_Append(py_retlist, py_tuple)) {
             endutxent();
             goto error;
         }
-        Py_DECREF(tuple);
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
     }
 
     endutxent();
 #endif
-    return ret_list;
-
-error:
-    Py_XDECREF(tuple);
-    Py_DECREF(ret_list);
-    return NULL;
-}
-
-
-
-/*
- * System-wide open connections.
- */
-
-#define HASHSIZE 1009
-static struct xfile *psutil_xfiles;
-static int psutil_nxfiles;
-
-int
-psutil_populate_xfiles()
-{
-    size_t len;
-
-    if ((psutil_xfiles = malloc(len = sizeof *psutil_xfiles)) == NULL) {
-        PyErr_NoMemory();
-        return 0;
-    }
-    while (sysctlbyname("kern.file", psutil_xfiles, &len, 0, 0) == -1) {
-        if (errno != ENOMEM) {
-            PyErr_SetFromErrno(0);
-            return 0;
-        }
-        len *= 2;
-        if ((psutil_xfiles = realloc(psutil_xfiles, len)) == NULL) {
-            PyErr_NoMemory();
-            return 0;
-        }
-    }
-    if (len > 0 && psutil_xfiles->xf_size != sizeof *psutil_xfiles) {
-        PyErr_Format(PyExc_RuntimeError, "struct xfile size mismatch");
-        return 0;
-    }
-    psutil_nxfiles = len / sizeof *psutil_xfiles;
-    return 1;
-}
-
-int
-psutil_get_pid_from_sock(int sock_hash)
-{
-    struct xfile *xf;
-    int hash, n;
-    for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {
-        if (xf->xf_data == NULL)
-            continue;
-        hash = (int)((uintptr_t)xf->xf_data % HASHSIZE);
-        if (sock_hash == hash)
-            return xf->xf_pid;
-    }
-    return -1;
-}
-
-
-// Reference:
-// https://gitorious.org/freebsd/freebsd/source/
-//     f1d6f4778d2044502209708bc167c05f9aa48615:usr.bin/sockstat/sockstat.c
-int psutil_gather_inet(int proto, PyObject *py_retlist)
-{
-    struct xinpgen *xig, *exig;
-    struct xinpcb *xip;
-    struct xtcpcb *xtp;
-    struct inpcb *inp;
-    struct xsocket *so;
-    const char *varname = NULL;
-    size_t len, bufsize;
-    void *buf;
-    int hash;
-    int retry;
-    int type;
-
-    PyObject *tuple = NULL;
-    PyObject *laddr = NULL;
-    PyObject *raddr = NULL;
-
-    switch (proto) {
-        case IPPROTO_TCP:
-            varname = "net.inet.tcp.pcblist";
-            type = SOCK_STREAM;
-            break;
-        case IPPROTO_UDP:
-            varname = "net.inet.udp.pcblist";
-            type = SOCK_DGRAM;
-            break;
-    }
-
-    buf = NULL;
-    bufsize = 8192;
-    retry = 5;
-    do {
-        for (;;) {
-            buf = realloc(buf, bufsize);
-            if (buf == NULL)
-                continue;  // XXX
-            len = bufsize;
-            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
-                break;
-            if (errno != ENOMEM) {
-                PyErr_SetFromErrno(0);
-                goto error;
-            }
-            bufsize *= 2;
-        }
-        xig = (struct xinpgen *)buf;
-        exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);
-        if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {
-            PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
-            goto error;
-        }
-    } while (xig->xig_gen != exig->xig_gen && retry--);
-
-
-    for (;;) {
-        int lport, rport, pid, status, family;
-
-        xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
-        if (xig >= exig)
-            break;
-
-        switch (proto) {
-            case IPPROTO_TCP:
-                xtp = (struct xtcpcb *)xig;
-                if (xtp->xt_len != sizeof *xtp) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "struct xtcpcb size mismatch");
-                    goto error;
-                }
-                inp = &xtp->xt_inp;
-                so = &xtp->xt_socket;
-                status = xtp->xt_tp.t_state;
-                break;
-            case IPPROTO_UDP:
-                xip = (struct xinpcb *)xig;
-                if (xip->xi_len != sizeof *xip) {
-                    PyErr_Format(PyExc_RuntimeError,
-                                 "struct xinpcb size mismatch");
-                    goto error;
-                }
-                inp = &xip->xi_inp;
-                so = &xip->xi_socket;
-                status = PSUTIL_CONN_NONE;
-                break;
-            default:
-                PyErr_Format(PyExc_RuntimeError, "invalid proto");
-                goto error;
-        }
-
-        char lip[200], rip[200];
-
-        hash = (int)((uintptr_t)so->xso_so % HASHSIZE);
-        pid = psutil_get_pid_from_sock(hash);
-        if (pid < 0)
-            continue;
-        lport = ntohs(inp->inp_lport);
-        rport = ntohs(inp->inp_fport);
-
-        if (inp->inp_vflag & INP_IPV4) {
-            family = AF_INET;
-            inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));
-            inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));
-        }
-        else if (inp->inp_vflag & INP_IPV6) {
-            family = AF_INET6;
-            inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));
-            inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));
-        }
-
-        // construct python tuple/list
-        laddr = Py_BuildValue("(si)", lip, lport);
-        if (!laddr)
-            goto error;
-        if (rport != 0)
-            raddr = Py_BuildValue("(si)", rip, rport);
-        else
-            raddr = Py_BuildValue("()");
-        if (!raddr)
-            goto error;
-        tuple = Py_BuildValue("(iiiNNii)", -1, family, type, laddr, raddr,
-                                               status, pid);
-        if (!tuple)
-            goto error;
-        if (PyList_Append(py_retlist, tuple))
-            goto error;
-        Py_DECREF(tuple);
-    }
-
-    free(buf);
-    return 1;
-
-error:
-    Py_XDECREF(tuple);
-    Py_XDECREF(laddr);
-    Py_XDECREF(raddr);
-    free(buf);
-    return 0;
-}
-
-
-int psutil_gather_unix(int proto, PyObject *py_retlist)
-{
-    struct xunpgen *xug, *exug;
-    struct xunpcb *xup;
-    const char *varname = NULL;
-    const char *protoname = NULL;
-    size_t len;
-    size_t bufsize;
-    void *buf;
-    int hash;
-    int retry;
-    int pid;
-    struct sockaddr_un *sun;
-    char path[PATH_MAX];
-
-    PyObject *tuple = NULL;
-    PyObject *laddr = NULL;
-    PyObject *raddr = NULL;
-
-    switch (proto) {
-        case SOCK_STREAM:
-            varname = "net.local.stream.pcblist";
-            protoname = "stream";
-            break;
-        case SOCK_DGRAM:
-            varname = "net.local.dgram.pcblist";
-            protoname = "dgram";
-            break;
-    }
-
-    buf = NULL;
-    bufsize = 8192;
-    retry = 5;
-
-    do {
-        for (;;) {
-            buf = realloc(buf, bufsize);
-            if (buf == NULL) {
-                PyErr_NoMemory();
-                goto error;
-            }
-            len = bufsize;
-            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
-                break;
-            if (errno != ENOMEM) {
-                PyErr_SetFromErrno(0);
-                goto error;
-            }
-            bufsize *= 2;
-        }
-        xug = (struct xunpgen *)buf;
-        exug = (struct xunpgen *)(void *)
-            ((char *)buf + len - sizeof *exug);
-        if (xug->xug_len != sizeof *xug || exug->xug_len != sizeof *exug) {
-            PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
-            goto error;
-        }
-    } while (xug->xug_gen != exug->xug_gen && retry--);
-
-    for (;;) {
-        xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
-        if (xug >= exug)
-            break;
-        xup = (struct xunpcb *)xug;
-        if (xup->xu_len != sizeof *xup)
-            goto error;
-
-        hash = (int)((uintptr_t) xup->xu_socket.xso_so % HASHSIZE);
-        pid = psutil_get_pid_from_sock(hash);
-        if (pid < 0)
-            continue;
-
-        sun = (struct sockaddr_un *)&xup->xu_addr;
-        snprintf(path, sizeof(path), "%.*s",
-                 (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
-                 sun->sun_path);
-
-        tuple = Py_BuildValue("(iiisOii)", -1, AF_UNIX, proto, path, Py_None,
-                                               PSUTIL_CONN_NONE, pid);
-        if (!tuple)
-            goto error;
-        if (PyList_Append(py_retlist, tuple))
-            goto error;
-        Py_DECREF(tuple);
-        Py_INCREF(Py_None);
-    }
-
-    free(buf);
-    return 1;
-
-error:
-    Py_XDECREF(tuple);
-    Py_XDECREF(laddr);
-    Py_XDECREF(raddr);
-    free(buf);
-    return 0;
-}
-
-
-/*
- * Return system-wide open connections.
- */
-static PyObject*
-psutil_net_connections(PyObject* self, PyObject* args)
-{
-    PyObject *py_retlist = PyList_New(0);
-
-    if (py_retlist == NULL)
-        return NULL;
-    if (psutil_populate_xfiles() != 1)
-        goto error;
-    if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0)
-        goto error;
-    if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0)
-        goto error;
-    if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0)
-       goto error;
-    if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0)
-        goto error;
-
-    free(psutil_xfiles);
     return py_retlist;
 
 error:
-    Py_DECREF(py_retlist);
-    free(psutil_xfiles);
-    return NULL;
-}
-
-
-/*
- * Get process CPU affinity.
- * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
- */
-static PyObject*
-psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args)
-{
-    long pid;
-    int ret;
-    int i;
-    cpuset_t mask;
-    PyObject* py_retlist;
-    PyObject* py_cpu_num;
-
-    if (!PyArg_ParseTuple(args, "i", &pid))
-        return NULL;
-    ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
-                             sizeof(mask), &mask);
-    if (ret != 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
-    }
-
-    py_retlist = PyList_New(0);
-    if (py_retlist == NULL)
-        return NULL;
-
-    for (i = 0; i < CPU_SETSIZE; i++) {
-        if (CPU_ISSET(i, &mask)) {
-            py_cpu_num = Py_BuildValue("i", i);
-            if (py_cpu_num == NULL)
-                goto error;
-            if (PyList_Append(py_retlist, py_cpu_num))
-                goto error;
-        }
-    }
-
-    return py_retlist;
-
-error:
-    Py_XDECREF(py_cpu_num);
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tty);
+    Py_XDECREF(py_hostname);
+    Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     return NULL;
 }
 
 
 /*
- * Set process CPU affinity.
- * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
- */
-static PyObject *
-psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args)
-{
-    long pid;
-    int i;
-    int seq_len;
-    int ret;
-    cpuset_t cpu_set;
-    PyObject *py_cpu_set;
-    PyObject *py_cpu_seq = NULL;
-
-    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
-        return NULL;
-
-    py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
-    if (!py_cpu_seq)
-        return NULL;
-    seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
-
-    // calculate the mask
-    CPU_ZERO(&cpu_set);
-    for (i = 0; i < seq_len; i++) {
-        PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
-#if PY_MAJOR_VERSION >= 3
-        long value = PyLong_AsLong(item);
-#else
-        long value = PyInt_AsLong(item);
-#endif
-        if (value == -1 && PyErr_Occurred())
-            goto error;
-        CPU_SET(value, &cpu_set);
-    }
-
-    // set affinity
-    ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
-                             sizeof(cpu_set), &cpu_set);
-    if (ret != 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        goto error;
-    }
-
-    Py_DECREF(py_cpu_seq);
-    Py_RETURN_NONE;
-
-error:
-    if (py_cpu_seq != NULL)
-        Py_DECREF(py_cpu_seq);
-    return NULL;
-}
-
-
-/*
  * define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     // --- per-process functions
 
+    {"proc_oneshot_info", psutil_proc_oneshot_info, METH_VARARGS,
+     "Return multiple info about a process"},
     {"proc_name", psutil_proc_name, METH_VARARGS,
      "Return process name"},
-    {"proc_connections", psutil_proc_connections, METH_VARARGS,
-     "Return connections opened by process"},
-    {"proc_exe", psutil_proc_exe, METH_VARARGS,
-     "Return process pathname executable"},
     {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
      "Return process cmdline as a list of cmdline arguments"},
-    {"proc_ppid", psutil_proc_ppid, METH_VARARGS,
-     "Return process ppid as an integer"},
-    {"proc_uids", psutil_proc_uids, METH_VARARGS,
-     "Return process real effective and saved user ids as a Python tuple"},
-    {"proc_gids", psutil_proc_gids, METH_VARARGS,
-     "Return process real effective and saved group ids as a Python tuple"},
-    {"proc_cpu_times", psutil_proc_cpu_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 extended memory info for a process as a Python tuple."},
+    {"proc_threads", psutil_proc_threads, METH_VARARGS,
+     "Return process threads"},
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD)
+    {"proc_connections", psutil_proc_connections, METH_VARARGS,
+     "Return connections opened by process"},
+    {"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"},
+    {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
+     "Return files opened by process as a list of (path, fd) tuples"},
+#endif
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
     {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS,
      "Return number of threads used by process"},
-    {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
-     "Return the number of context switches performed by process"},
-    {"proc_threads", psutil_proc_threads, METH_VARARGS,
-     "Return process threads"},
-    {"proc_status", psutil_proc_status, METH_VARARGS,
-     "Return process status as an integer"},
-    {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
-     "Return process IO counters"},
-    {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS,
-     "Return process tty (terminal) number"},
+#endif
+#if defined(PSUTIL_FREEBSD)
+    {"proc_exe", psutil_proc_exe, METH_VARARGS,
+     "Return process pathname executable"},
+    {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
+     "Return a list of tuples for every process's memory map"},
     {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
      "Return process CPU affinity."},
     {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
      "Set process CPU affinity."},
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
-    {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
-     "Return files opened by process as a list of (path, fd) tuples"},
-    {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
-     "Return process current working directory."},
-    {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
-     "Return a list of tuples for every process's memory map"},
-    {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
-     "Return the number of file descriptors opened by this process"},
+    {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
+     "Return an XML string to determine the number physical CPUs."},
 #endif
 
     // --- system-related functions
 
     {"pids", psutil_pids, METH_VARARGS,
      "Returns a list of PIDs currently running on the system"},
     {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
      "Return number of logical CPUs on the system"},
-    {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
-     "Return an XML string to determine the number physical CPUs."},
     {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
      "Return system virtual memory usage statistics"},
     {"swap_mem", psutil_swap_mem, METH_VARARGS,
      "Return swap mem stats"},
     {"cpu_times", psutil_cpu_times, METH_VARARGS,
      "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
     {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
      "Return system per-cpu times as a list of tuples"},
-#endif
     {"boot_time", psutil_boot_time, METH_VARARGS,
      "Return the system boot time expressed in seconds since the epoch."},
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return a list of tuples including device, mount point and "
      "fs type for all partitions mounted on the system."},
     {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
      "Return dict of tuples of networks I/O information."},
     {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
      "Return a Python dict of tuples for disk I/O information"},
     {"users", psutil_users, METH_VARARGS,
      "Return currently connected users as a list of tuples"},
+    {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+     "Return CPU statistics"},
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
     {"net_connections", psutil_net_connections, METH_VARARGS,
      "Return system-wide open connections."},
+#endif
+#if defined(PSUTIL_FREEBSD)
+    {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
+     "Return battery information."},
+#endif
+
+    // --- others
+    {"set_testing", psutil_set_testing, METH_NOARGS,
+     "Set psutil in testing mode"},
 
     {NULL, NULL, 0, NULL}
 };
 
 struct module_state {
     PyObject *error;
 };
 
@@ -2260,37 +1038,61 @@ void init_psutil_bsd(void)
 #endif
 {
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
     PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods);
 #endif
     PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+    // process status constants
 
-    // 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, "SSLEEP", SSLEEP);
-    PyModule_AddIntConstant(module, "SRUN", SRUN);
-    PyModule_AddIntConstant(module, "SIDL", SIDL);
+    PyModule_AddIntConstant(module, "SZOMB", SZOMB);
     PyModule_AddIntConstant(module, "SWAIT", SWAIT);
     PyModule_AddIntConstant(module, "SLOCK", SLOCK);
-    PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+#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);
+#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);
+    // unique to NetBSD
+    PyModule_AddIntConstant(module, "SSUSPENDED", LSSUSPENDED);
+#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);
-    PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+    // PSUTIL_CONN_NONE
+    PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", 128);
+
+    psutil_setup();
 
     if (module == NULL)
         INITERROR;
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
deleted file mode 100644
--- a/third_party/python/psutil/psutil/_psutil_bsd.h
+++ /dev/null
@@ -1,53 +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.
- */
-
-#include <Python.h>
-
-// --- per-process functions
-
-static PyObject* psutil_proc_cmdline(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_create_time(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_gids(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_name(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_ctx_switches(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_ppid(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_status(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_tty_nr(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_uids(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
-
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
-static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
-#endif
-
-// --- system-related functions
-
-static PyObject* psutil_boot_time(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_logical(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
-static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_pids(PyObject* self, PyObject* args);
-static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
-static PyObject* psutil_users(PyObject* self, PyObject* args);
-static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
-
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
-static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
-#endif
--- a/third_party/python/psutil/psutil/_psutil_common.c
+++ b/third_party/python/psutil/psutil/_psutil_common.c
@@ -2,36 +2,103 @@
  * 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.
  *
  * Routines common to all platforms.
  */
 
 #include <Python.h>
+#include <stdio.h>
+
+
+// Global vars.
+int PSUTIL_DEBUG = 0;
+int PSUTIL_TESTING = 0;
+
+
+/*
+ * 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
+PyObject *
+PyUnicode_DecodeFSDefault(char *s) {
+    return PyString_FromString(s);
+}
+
+
+PyObject *
+PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) {
+    return PyString_FromStringAndSize(s, size);
+}
+#endif
 
 
 /*
  * Set OSError(errno=ESRCH, strerror="No such process") Python exception.
+ * If msg != "" the exception message will change in accordance.
  */
 PyObject *
-NoSuchProcess(void) {
+NoSuchProcess(char *msg) {
     PyObject *exc;
-    char *msg = strerror(ESRCH);
-    exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
+    exc = PyObject_CallFunction(
+        PyExc_OSError, "(is)", ESRCH, strlen(msg) ? msg : strerror(ESRCH));
     PyErr_SetObject(PyExc_OSError, exc);
     Py_XDECREF(exc);
     return NULL;
 }
 
 
 /*
  * Set OSError(errno=EACCES, strerror="Permission denied") Python exception.
+ * If msg != "" the exception message will change in accordance.
  */
 PyObject *
-AccessDenied(void) {
+AccessDenied(char *msg) {
     PyObject *exc;
-    char *msg = strerror(EACCES);
-    exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
+    exc = PyObject_CallFunction(
+        PyExc_OSError, "(is)", EACCES, strlen(msg) ? msg : strerror(EACCES));
     PyErr_SetObject(PyExc_OSError, exc);
     Py_XDECREF(exc);
     return NULL;
 }
+
+
+/*
+ * Enable testing mode. This has the same effect as setting PSUTIL_TESTING
+ * env var. This dual method exists because updating os.environ on
+ * Windows has no effect. Called on unit tests setup.
+ */
+PyObject *
+psutil_set_testing(PyObject *self, PyObject *args) {
+    PSUTIL_TESTING = 1;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+/*
+ * Print a debug message on stderr. No-op if PSUTIL_DEBUG env var is not set.
+ */
+void
+psutil_debug(const char* format, ...) {
+    va_list argptr;
+    va_start(argptr, format);
+    fprintf(stderr, "psutil-dubug> ");
+    vfprintf(stderr, format, argptr);
+    fprintf(stderr, "\n");
+    va_end(argptr);
+}
+
+
+/*
+ * Called on module import on all platforms.
+ */
+void
+psutil_setup(void) {
+    if (getenv("PSUTIL_DEBUG") != NULL)
+        PSUTIL_DEBUG = 1;
+    if (getenv("PSUTIL_TESTING") != NULL)
+        PSUTIL_TESTING = 1;
+}
--- a/third_party/python/psutil/psutil/_psutil_common.h
+++ b/third_party/python/psutil/psutil/_psutil_common.h
@@ -1,10 +1,25 @@
 /*
  * 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 <Python.h>
 
-PyObject* AccessDenied(void);
-PyObject* NoSuchProcess(void);
+extern int PSUTIL_TESTING;
+extern int PSUTIL_DEBUG;
+
+// a signaler for connections without an actual status
+static const int PSUTIL_CONN_NONE = 128;
+
+#if PY_MAJOR_VERSION < 3
+PyObject* PyUnicode_DecodeFSDefault(char *s);
+PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size);
+#endif
+
+PyObject* AccessDenied(char *msg);
+PyObject* NoSuchProcess(char *msg);
+
+PyObject* psutil_set_testing(PyObject *self, PyObject *args);
+void psutil_debug(const char* format, ...);
+void psutil_setup(void);
--- a/third_party/python/psutil/psutil/_psutil_linux.c
+++ b/third_party/python/psutil/psutil/_psutil_linux.c
@@ -18,20 +18,29 @@
 #include <sched.h>
 #include <linux/version.h>
 #include <sys/syscall.h>
 #include <sys/sysinfo.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <linux/sockios.h>
 #include <linux/if.h>
+
+// see: https://github.com/giampaolo/psutil/issues/659
+#ifdef PSUTIL_ETHTOOL_MISSING_TYPES
+    #include <linux/types.h>
+    typedef __u64 u64;
+    typedef __u32 u32;
+    typedef __u16 u16;
+    typedef __u8 u8;
+#endif
+/* Avoid redefinition of struct sysinfo with musl libc */
+#define _LINUX_SYSINFO_H
 #include <linux/ethtool.h>
 
-#include "_psutil_linux.h"
-
 /* The minimum number of CPUs allocated in a cpu_set_t */
 static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT;
 
 // Linux >= 2.6.13
 #define PSUTIL_HAVE_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
 
 // Linux >= 2.6.36 (supposedly) and glibc >= 13
 #define PSUTIL_HAVE_PRLIMIT \
@@ -40,54 +49,54 @@ static const int NCPUS_START = sizeof(un
     defined(__NR_prlimit64)
 
 #if PSUTIL_HAVE_PRLIMIT
     #define _FILE_OFFSET_BITS 64
     #include <time.h>
     #include <sys/resource.h>
 #endif
 
-
-#if PSUTIL_HAVE_IOPRIO
-enum {
-    IOPRIO_WHO_PROCESS = 1,
-};
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
 
 // May happen on old RedHat versions, see:
 // https://github.com/giampaolo/psutil/issues/607
 #ifndef DUPLEX_UNKNOWN
     #define DUPLEX_UNKNOWN 0xff
 #endif
 
+
+#if PSUTIL_HAVE_IOPRIO
+enum {
+    IOPRIO_WHO_PROCESS = 1,
+};
+
 static inline int
-ioprio_get(int which, int who)
-{
+ioprio_get(int which, int who) {
     return syscall(__NR_ioprio_get, which, who);
 }
 
 static inline int
-ioprio_set(int which, int who, int ioprio)
-{
+ioprio_set(int which, int who, int ioprio) {
     return syscall(__NR_ioprio_set, which, who, ioprio);
 }
 
 #define IOPRIO_CLASS_SHIFT 13
 #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
 
 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
 #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
 #define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
 
 
 /*
  * Return a (ioclass, iodata) Python tuple representing process I/O priority.
  */
 static PyObject *
-psutil_proc_ioprio_get(PyObject *self, PyObject *args)
-{
+psutil_proc_ioprio_get(PyObject *self, PyObject *args) {
     long pid;
     int ioprio, ioclass, iodata;
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
     if (ioprio == -1)
         return PyErr_SetFromErrno(PyExc_OSError);
     ioclass = IOPRIO_PRIO_CLASS(ioprio);
@@ -97,18 +106,17 @@ psutil_proc_ioprio_get(PyObject *self, P
 
 
 /*
  * A wrapper around ioprio_set(); sets process I/O priority.
  * ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE
  * or 0. iodata goes from 0 to 7 depending on ioclass specified.
  */
 static PyObject *
-psutil_proc_ioprio_set(PyObject *self, PyObject *args)
-{
+psutil_proc_ioprio_set(PyObject *self, PyObject *args) {
     long pid;
     int ioprio, ioclass, iodata;
     int retval;
 
     if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata))
         return NULL;
     ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
     retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
@@ -121,57 +129,56 @@ psutil_proc_ioprio_set(PyObject *self, P
 
 #if PSUTIL_HAVE_PRLIMIT
 /*
  * A wrapper around prlimit(2); sets process resource limits.
  * This can be used for both get and set, in which case extra
  * 'soft' and 'hard' args must be provided.
  */
 static PyObject *
-psutil_linux_prlimit(PyObject *self, PyObject *args)
-{
+psutil_linux_prlimit(PyObject *self, PyObject *args) {
     long pid;
     int ret, resource;
     struct rlimit old, new;
     struct rlimit *newp = NULL;
-    PyObject *soft = NULL;
-    PyObject *hard = NULL;
+    PyObject *py_soft = NULL;
+    PyObject *py_hard = NULL;
 
-    if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &soft, &hard))
+    if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &py_soft, &py_hard))
         return NULL;
 
     // get
-    if (soft == NULL && hard == NULL) {
+    if (py_soft == NULL && py_hard == NULL) {
         ret = prlimit(pid, resource, NULL, &old);
         if (ret == -1)
             return PyErr_SetFromErrno(PyExc_OSError);
 #if defined(PSUTIL_HAVE_LONG_LONG)
         if (sizeof(old.rlim_cur) > sizeof(long)) {
             return Py_BuildValue("LL",
                                  (PY_LONG_LONG)old.rlim_cur,
                                  (PY_LONG_LONG)old.rlim_max);
         }
 #endif
         return Py_BuildValue("ll", (long)old.rlim_cur, (long)old.rlim_max);
     }
 
     // set
     else {
 #if defined(PSUTIL_HAVE_LARGEFILE_SUPPORT)
-        new.rlim_cur = PyLong_AsLongLong(soft);
+        new.rlim_cur = PyLong_AsLongLong(py_soft);
         if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
             return NULL;
-        new.rlim_max = PyLong_AsLongLong(hard);
+        new.rlim_max = PyLong_AsLongLong(py_hard);
         if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
             return NULL;
 #else
-        new.rlim_cur = PyLong_AsLong(soft);
+        new.rlim_cur = PyLong_AsLong(py_soft);
         if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
             return NULL;
-        new.rlim_max = PyLong_AsLong(hard);
+        new.rlim_max = PyLong_AsLong(py_hard);
         if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
             return NULL;
 #endif
         newp = &new;
         ret = prlimit(pid, resource, newp, &old);
         if (ret == -1)
             return PyErr_SetFromErrno(PyExc_OSError);
         Py_RETURN_NONE;
@@ -180,103 +187,114 @@ psutil_linux_prlimit(PyObject *self, PyO
 #endif
 
 
 /*
  * Return disk mounted partitions as a list of tuples including device,
  * mount point and filesystem type
  */
 static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args)
-{
+psutil_disk_partitions(PyObject *self, PyObject *args) {
     FILE *file = NULL;
     struct mntent *entry;
+    PyObject *py_dev = NULL;
+    PyObject *py_mountp = NULL;
+    PyObject *py_tuple = NULL;
     PyObject *py_retlist = PyList_New(0);
-    PyObject *py_tuple = NULL;
 
     if (py_retlist == NULL)
         return NULL;
 
     // MOUNTED constant comes from mntent.h and it's == '/etc/mtab'
     Py_BEGIN_ALLOW_THREADS
     file = setmntent(MOUNTED, "r");
     Py_END_ALLOW_THREADS
     if ((file == 0) || (file == NULL)) {
         PyErr_SetFromErrnoWithFilename(PyExc_OSError, MOUNTED);
         goto error;
     }
 
     while ((entry = getmntent(file))) {
         if (entry == NULL) {
-            PyErr_Format(PyExc_RuntimeError, "getmntent() failed");
+            PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
             goto error;
         }
-        py_tuple = Py_BuildValue("(ssss)",
-                                 entry->mnt_fsname,  // device
-                                 entry->mnt_dir,     // mount point
+        py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname);
+        if (! py_dev)
+            goto error;
+        py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir);
+        if (! py_mountp)
+            goto error;
+        py_tuple = Py_BuildValue("(OOss)",
+                                 py_dev,             // device
+                                 py_mountp,          // mount point
                                  entry->mnt_type,    // fs type
                                  entry->mnt_opts);   // options
         if (! py_tuple)
             goto error;
         if (PyList_Append(py_retlist, py_tuple))
             goto error;
+        Py_DECREF(py_dev);
+        Py_DECREF(py_mountp);
         Py_DECREF(py_tuple);
     }
     endmntent(file);
     return py_retlist;
 
 error:
     if (file != NULL)
         endmntent(file);
+    Py_XDECREF(py_dev);
+    Py_XDECREF(py_mountp);
     Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     return NULL;
 }
 
 
 /*
  * A wrapper around sysinfo(), return system memory usage statistics.
  */
 static PyObject *
-psutil_linux_sysinfo(PyObject *self, PyObject *args)
-{
+psutil_linux_sysinfo(PyObject *self, PyObject *args) {
     struct sysinfo info;
 
     if (sysinfo(&info) != 0)
         return PyErr_SetFromErrno(PyExc_OSError);
     // note: boot time might also be determined from here
     return Py_BuildValue(
-        "(KKKKKK)",
-        (unsigned long long)info.totalram  * info.mem_unit,   // total
-        (unsigned long long)info.freeram   * info.mem_unit,   // free
-        (unsigned long long)info.bufferram * info.mem_unit,   // buffer
-        (unsigned long long)info.sharedram * info.mem_unit,   // shared
-        (unsigned long long)info.totalswap * info.mem_unit,   // swap tot
-        (unsigned long long)info.freeswap  * info.mem_unit);  // swap free
+        "(kkkkkkI)",
+        info.totalram,  // total
+        info.freeram,  // free
+        info.bufferram, // buffer
+        info.sharedram, // shared
+        info.totalswap, // swap tot
+        info.freeswap,  // swap free
+        info.mem_unit  // multiplier
+    );
 }
 
 
 /*
  * Return process CPU affinity as a Python list
  * The dual implementation exists because of:
  * https://github.com/giampaolo/psutil/issues/536
  */
 
 #ifdef CPU_ALLOC
 
 static PyObject *
-psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
     int cpu, ncpus, count, cpucount_s;
     long pid;
     size_t setsize;
     cpu_set_t *mask = NULL;
-    PyObject *res = NULL;
+    PyObject *py_list = NULL;
 
-    if (!PyArg_ParseTuple(args, "i", &pid))
+    if (!PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     ncpus = NCPUS_START;
     while (1) {
         setsize = CPU_ALLOC_SIZE(ncpus);
         mask = CPU_ALLOC(ncpus);
         if (mask == NULL)
             return PyErr_NoMemory();
         if (sched_getaffinity(pid, setsize, mask) == 0)
@@ -287,64 +305,63 @@ psutil_proc_cpu_affinity_get(PyObject *s
         if (ncpus > INT_MAX / 2) {
             PyErr_SetString(PyExc_OverflowError, "could not allocate "
                             "a large enough CPU set");
             return NULL;
         }
         ncpus = ncpus * 2;
     }
 
-    res = PyList_New(0);
-    if (res == NULL)
+    py_list = PyList_New(0);
+    if (py_list == NULL)
         goto error;
 
     cpucount_s = CPU_COUNT_S(setsize, mask);
     for (cpu = 0, count = cpucount_s; count; cpu++) {
         if (CPU_ISSET_S(cpu, setsize, mask)) {
 #if PY_MAJOR_VERSION >= 3
             PyObject *cpu_num = PyLong_FromLong(cpu);
 #else
             PyObject *cpu_num = PyInt_FromLong(cpu);
 #endif
             if (cpu_num == NULL)
                 goto error;
-            if (PyList_Append(res, cpu_num)) {
+            if (PyList_Append(py_list, cpu_num)) {
                 Py_DECREF(cpu_num);
                 goto error;
             }
             Py_DECREF(cpu_num);
             --count;
         }
     }
     CPU_FREE(mask);
-    return res;
+    return py_list;
 
 error:
     if (mask)
         CPU_FREE(mask);
-    Py_XDECREF(res);
+    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)
-{
+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, "i", &pid))
+    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;
@@ -358,27 +375,26 @@ psutil_proc_cpu_affinity_get(PyObject *s
             Py_DECREF(py_cpu_num);
         }
     }
 
     return py_retlist;
 
 error:
     Py_XDECREF(py_cpu_num);
-    Py_DECREF(py_retlist);
+    Py_XDECREF(py_retlist);
     return NULL;
 }
 #endif
 
 /*
  * Set process CPU affinity; expects a bitmask
  */
 static PyObject *
-psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
     cpu_set_t cpu_set;
     size_t len;
     long pid;
     int i, seq_len;
     PyObject *py_cpu_set;
     PyObject *py_cpu_seq = NULL;
 
     if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
@@ -397,21 +413,25 @@ psutil_proc_cpu_affinity_set(PyObject *s
     CPU_ZERO(&cpu_set);
     for (i = 0; i < seq_len; i++) {
         PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
 #if PY_MAJOR_VERSION >= 3
         long value = PyLong_AsLong(item);
 #else
         long value = PyInt_AsLong(item);
 #endif
-        if (value == -1 && PyErr_Occurred())
+        if ((value == -1) || PyErr_Occurred()) {
+            if (!PyErr_Occurred())
+                PyErr_SetString(PyExc_ValueError, "invalid CPU value");
             goto error;
+        }
         CPU_SET(value, &cpu_set);
     }
 
+
     len = sizeof(cpu_set);
     if (sched_setaffinity(pid, len, &cpu_set)) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     Py_DECREF(py_cpu_seq);
     Py_RETURN_NONE;
@@ -422,146 +442,143 @@ error:
     return NULL;
 }
 
 
 /*
  * Return currently connected users as a list of tuples.
  */
 static PyObject *
-psutil_users(PyObject *self, PyObject *args)
-{
-    PyObject *ret_list = PyList_New(0);
-    PyObject *tuple = NULL;
-    PyObject *user_proc = NULL;
+psutil_users(PyObject *self, PyObject *args) {
     struct utmp *ut;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_username = NULL;
+    PyObject *py_tty = NULL;
+    PyObject *py_hostname = NULL;
+    PyObject *py_user_proc = NULL;
 
-    if (ret_list == NULL)
+    if (py_retlist == NULL)
         return NULL;
     setutent();
     while (NULL != (ut = getutent())) {
-        tuple = NULL;
-        user_proc = NULL;
+        py_tuple = NULL;
+        py_user_proc = NULL;
         if (ut->ut_type == USER_PROCESS)
-            user_proc = Py_True;
+            py_user_proc = Py_True;
         else
-            user_proc = Py_False;
-        tuple = Py_BuildValue(
-            "(sssfO)",
-            ut->ut_user,              // username
-            ut->ut_line,              // tty
-            ut->ut_host,              // hostname
+            py_user_proc = Py_False;
+        py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfOi)",
+            py_username,              // username
+            py_tty,                   // tty
+            py_hostname,              // hostname
             (float)ut->ut_tv.tv_sec,  // tstamp
-            user_proc                 // (bool) user process
+            py_user_proc,             // (bool) user process
+            ut->ut_pid                // process id
         );
-    if (! tuple)
+        if (! py_tuple)
             goto error;
-        if (PyList_Append(ret_list, tuple))
+        if (PyList_Append(py_retlist, py_tuple))
             goto error;
-        Py_DECREF(tuple);
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
     }
     endutent();
-    return ret_list;
+    return py_retlist;
 
 error:
-    Py_XDECREF(tuple);
-    Py_XDECREF(user_proc);
-    Py_DECREF(ret_list);
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tty);
+    Py_XDECREF(py_hostname);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
     endutent();
     return NULL;
 }
 
 
 /*
  * Return stats about a particular network
  * interface.  References:
  * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
  * http://www.i-scream.org/libstatgrab/
  */
 static PyObject*
-psutil_net_if_stats(PyObject* self, PyObject* args)
-{
+psutil_net_if_duplex_speed(PyObject* self, PyObject* args) {
     char *nic_name;
     int sock = 0;
     int ret;
     int duplex;
     int speed;
-    int mtu;
     struct ifreq ifr;
     struct ethtool_cmd ethcmd;
-    PyObject *py_is_up = NULL;
-    PyObject *py_ret = NULL;
+    PyObject *py_retlist = NULL;
 
     if (! PyArg_ParseTuple(args, "s", &nic_name))
         return NULL;
 
     sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock == -1)
         goto error;
     strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
 
-    // is up?
-    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
-    if (ret == -1)
-        goto error;
-    if ((ifr.ifr_flags & IFF_UP) != 0)
-        py_is_up = Py_True;
-    else
-        py_is_up = Py_False;
-    Py_INCREF(py_is_up);
-
-    // MTU
-    ret = ioctl(sock, SIOCGIFMTU, &ifr);
-    if (ret == -1)
-        goto error;
-    mtu = ifr.ifr_mtu;
-
     // duplex and speed
     memset(&ethcmd, 0, sizeof ethcmd);
     ethcmd.cmd = ETHTOOL_GSET;
-    ifr.ifr_data = (caddr_t)&ethcmd;
+    ifr.ifr_data = (void *)&ethcmd;
     ret = ioctl(sock, SIOCETHTOOL, &ifr);
 
     if (ret != -1) {
         duplex = ethcmd.duplex;
         speed = ethcmd.speed;
     }
     else {
-        if (errno == EOPNOTSUPP) {
-            // we typically get here in case of wi-fi cards
+        if ((errno == EOPNOTSUPP) || (errno == EINVAL)) {
+            // EOPNOTSUPP may occur in case of wi-fi cards.
+            // For EINVAL see:
+            // https://github.com/giampaolo/psutil/issues/797
+            //     #issuecomment-202999532
             duplex = DUPLEX_UNKNOWN;
             speed = 0;
         }
         else {
             goto error;
         }
     }
 
     close(sock);
-    py_ret = Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu);
-    if (!py_ret)
+    py_retlist = Py_BuildValue("[ii]", duplex, speed);
+    if (!py_retlist)
         goto error;
-    Py_DECREF(py_is_up);
-    return py_ret;
+    return py_retlist;
 
 error:
-    Py_XDECREF(py_is_up);
-    if (sock != 0)
+    if (sock != -1)
         close(sock);
-    PyErr_SetFromErrno(PyExc_OSError);
-    return NULL;
+    return PyErr_SetFromErrno(PyExc_OSError);
 }
 
 
 /*
  * Define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     // --- per-process functions
 
 #if PSUTIL_HAVE_IOPRIO
     {"proc_ioprio_get", psutil_proc_ioprio_get, METH_VARARGS,
      "Get process I/O priority"},
     {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS,
      "Set process I/O priority"},
 #endif
@@ -572,28 +589,31 @@ PsutilMethods[] =
 
     // --- system related functions
 
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return disk mounted partitions as a list of tuples including "
      "device, mount point and filesystem type"},
     {"users", psutil_users, METH_VARARGS,
      "Return currently connected users as a list of tuples"},
-    {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
-     "Return NIC stats (isup, duplex, speed, mtu)"},
+    {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
+     "Return duplex and speed info about a NIC"},
 
     // --- linux specific
 
     {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS,
      "A wrapper around sysinfo(), return system memory usage statistics"},
 #if PSUTIL_HAVE_PRLIMIT
     {"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"},
 
     {NULL, NULL, 0, NULL}
 };
 
 struct module_state {
     PyObject *error;
 };
 
@@ -635,37 +655,49 @@ static struct PyModuleDef
 PyMODINIT_FUNC PyInit__psutil_linux(void)
 
 #else
 #define INITERROR return
 
 void init_psutil_linux(void)
 #endif
 {
+    PyObject *v;
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
     PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods);
 #endif
 
-
     PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
 #if PSUTIL_HAVE_PRLIMIT
-    PyModule_AddIntConstant(module, "RLIM_INFINITY", RLIM_INFINITY);
     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 defined(HAVE_LONG_LONG)
+    if (sizeof(RLIM_INFINITY) > sizeof(long)) {
+        v = PyLong_FromLongLong((PY_LONG_LONG) RLIM_INFINITY);
+    } else
+#endif
+    {
+        v = PyLong_FromLong((long) RLIM_INFINITY);
+    }
+    if (v) {
+        PyModule_AddObject(module, "RLIM_INFINITY", v);
+    }
+
 #ifdef RLIMIT_MSGQUEUE
     PyModule_AddIntConstant(module, "RLIMIT_MSGQUEUE", RLIMIT_MSGQUEUE);
 #endif
 #ifdef RLIMIT_NICE
     PyModule_AddIntConstant(module, "RLIMIT_NICE", RLIMIT_NICE);
 #endif
 #ifdef RLIMIT_RTPRIO
     PyModule_AddIntConstant(module, "RLIMIT_RTPRIO", RLIMIT_RTPRIO);
@@ -676,14 +708,16 @@ void init_psutil_linux(void)
 #ifdef RLIMIT_SIGPENDING
     PyModule_AddIntConstant(module, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING);
 #endif
 #endif
     PyModule_AddIntConstant(module, "DUPLEX_HALF", DUPLEX_HALF);
     PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL);
     PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN);
 
+    psutil_setup();
+
     if (module == NULL)
         INITERROR;
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
deleted file mode 100644
--- a/third_party/python/psutil/psutil/_psutil_linux.h
+++ /dev/null
@@ -1,21 +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.
- */
-
-#include <Python.h>
-
-// process
-
-static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args);
-
-// system
-
-static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
-static PyObject* psutil_linux_sysinfo(PyObject* self, PyObject* args);
-static PyObject* psutil_users(PyObject* self, PyObject* args);
-static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
--- a/third_party/python/psutil/psutil/_psutil_osx.c
+++ b/third_party/python/psutil/psutil/_psutil_osx.c
@@ -33,318 +33,390 @@
 
 #include <mach-o/loader.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/storage/IOBlockStorageDriver.h>
 #include <IOKit/storage/IOMedia.h>
 #include <IOKit/IOBSD.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
 
-#include "_psutil_osx.h"
 #include "_psutil_common.h"
+#include "_psutil_posix.h"
 #include "arch/osx/process_info.h"
 
 
+#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+
 /*
  * A wrapper around host_statistics() invoked with HOST_VM_INFO.
  */
 int
-psutil_sys_vminfo(vm_statistics_data_t *vmstat)
-{
+psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
     kern_return_t ret;
     mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
     mach_port_t mport = mach_host_self();
 
     ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
     if (ret != KERN_SUCCESS) {
-        PyErr_Format(PyExc_RuntimeError,
-                     "host_statistics() failed: %s", mach_error_string(ret));
+        PyErr_Format(
+            PyExc_RuntimeError,
+            "host_statistics(HOST_VM_INFO) syscall failed: %s",
+            mach_error_string(ret));
         return 0;
     }
     mach_port_deallocate(mach_task_self(), mport);
     return 1;
 }
 
 
 /*
  * Return a Python list of all the PIDs running on the system.
  */
 static PyObject *
-psutil_pids(PyObject *self, PyObject *args)
-{
+psutil_pids(PyObject *self, PyObject *args) {
     kinfo_proc *proclist = NULL;
     kinfo_proc *orig_address = NULL;
     size_t num_processes;
     size_t idx;
-    PyObject *pid = NULL;
-    PyObject *retlist = PyList_New(0);
+    PyObject *py_pid = NULL;
+    PyObject *py_retlist = PyList_New(0);
 
-    if (retlist == NULL)
+    if (py_retlist == NULL)
         return NULL;
 
     if (psutil_get_proc_list(&proclist, &num_processes) != 0) {
-        PyErr_SetString(PyExc_RuntimeError,
-                        "failed to retrieve process list.");
+        if (errno != 0) {
+            PyErr_SetFromErrno(PyExc_OSError);
+        }
+        else {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "failed to retrieve process list");
+        }
         goto error;
     }
 
     if (num_processes > 0) {
         // save the address of proclist so we can free it later
         orig_address = proclist;
         for (idx = 0; idx < num_processes; idx++) {
-            pid = Py_BuildValue("i", proclist->kp_proc.p_pid);
-            if (!pid)
+            py_pid = Py_BuildValue("i", proclist->kp_proc.p_pid);
+            if (! py_pid)
                 goto error;
-            if (PyList_Append(retlist, pid))
+            if (PyList_Append(py_retlist, py_pid))
                 goto error;
-            Py_DECREF(pid);
+            Py_DECREF(py_pid);
             proclist++;
         }
         free(orig_address);
     }
-    return retlist;
+    return py_retlist;
 
 error:
-    Py_XDECREF(pid);
-    Py_DECREF(retlist);
+    Py_XDECREF(py_pid);
+    Py_DECREF(py_retlist);
     if (orig_address != NULL)
         free(orig_address);
     return NULL;
 }
 
 
 /*
+ * Return multiple process info as a Python tuple in one shot by
+ * using sysctl() and filling up a kinfo_proc struct.
+ * It should be possible to do this for all processes without
+ * incurring into permission (EPERM) errors.
+ * This will also succeed for zombie processes returning correct
+ * information.
+ */
+static PyObject *
+psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) {
+    long pid;
+    struct kinfo_proc kp;
+    PyObject *py_name;
+    PyObject *py_retlist;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_get_kinfo_proc(pid, &kp) == -1)
+        return NULL;
+
+    py_name = PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
+    if (! py_name) {
+        // Likely a decoding error. We don't want to fail the whole
+        // operation. The python module may retry with proc_name().
+        PyErr_Clear();
+        py_name = Py_None;
+    }
+
+    py_retlist = Py_BuildValue(
+        "lllllllidiO",
+        (long)kp.kp_eproc.e_ppid,                  // (long) 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
+        (long)kp.kp_eproc.e_pcred.p_rgid,          // (long) real gid
+        (long)kp.kp_eproc.e_ucred.cr_groups[0],    // (long) effective gid
+        (long)kp.kp_eproc.e_pcred.p_svgid,         // (long) saved gid
+        kp.kp_eproc.e_tdev,                        // (int) tty nr
+        PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime),  // (double) create time
+        (int)kp.kp_proc.p_stat,                    // (int) status
+        py_name                                    // (pystr) name
+    );
+
+    if (py_retlist != NULL) {
+        // XXX shall we decref() also in case of Py_BuildValue() error?
+        Py_DECREF(py_name);
+    }
+    return py_retlist;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using proc_pidinfo(PROC_PIDTASKINFO) and filling a proc_taskinfo
+ * struct.
+ * Contrarily from proc_kinfo above this function will fail with
+ * EACCES for PIDs owned by another user and with ESRCH for zombie
+ * processes.
+ */
+static PyObject *
+psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) {
+    long pid;
+    struct proc_taskinfo pti;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) <= 0)
+        return NULL;
+
+    return Py_BuildValue(
+        "(ddKKkkkk)",
+        (float)pti.pti_total_user / 1000000000.0,     // (float) cpu user time
+        (float)pti.pti_total_system / 1000000000.0,   // (float) cpu sys time
+        // Note about memory: determining other mem stats on OSX is a mess:
+        // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
+        // I just give up.
+        // struct proc_regioninfo pri;
+        // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, sizeof(pri))
+        pti.pti_resident_size,  // (uns long long) rss
+        pti.pti_virtual_size,   // (uns long long) vms
+        pti.pti_faults,         // (uns long) number of page faults (pages)
+        pti.pti_pageins,        // (uns long) number of actual pageins (pages)
+        pti.pti_threadnum,      // (uns long) num threads
+        // Unvoluntary value seems not to be available;
+        // pti.pti_csw probably refers to the sum of the two;
+        // getrusage() numbers seems to confirm this theory.
+        pti.pti_csw             // (uns long) voluntary ctx switches
+    );
+}
+
+
+/*
  * Return process name from kinfo_proc as a Python string.
  */
 static PyObject *
-psutil_proc_name(PyObject *self, PyObject *args)
-{
+psutil_proc_name(PyObject *self, PyObject *args) {
     long pid;
     struct kinfo_proc kp;
+
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (psutil_get_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    return Py_BuildValue("s", kp.kp_proc.p_comm);
+    return PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
 }
 
 
 /*
  * Return process current working directory.
+ * Raises NSP in case of zombie process.
  */
 static PyObject *
-psutil_proc_cwd(PyObject *self, PyObject *args)
-{
+psutil_proc_cwd(PyObject *self, PyObject *args) {
     long pid;
     struct proc_vnodepathinfo pathinfo;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
-    if (! psutil_proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, &pathinfo,
-                              sizeof(pathinfo)))
+    if (psutil_proc_pidinfo(
+            pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)) <= 0)
     {
         return NULL;
     }
-    return Py_BuildValue("s", pathinfo.pvi_cdir.vip_path);
+
+    return PyUnicode_DecodeFSDefault(pathinfo.pvi_cdir.vip_path);
 }
 
 
 /*
  * Return path of the process executable.
  */
 static PyObject *
-psutil_proc_exe(PyObject *self, PyObject *args)
-{
+psutil_proc_exe(PyObject *self, PyObject *args) {
     long pid;
     char buf[PATH_MAX];
     int ret;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    ret = proc_pidpath(pid, &buf, sizeof(buf));
+    errno = 0;
+    ret = proc_pidpath((pid_t)pid, &buf, sizeof(buf));
     if (ret == 0) {
-        if (! psutil_pid_exists(pid))
-            return NoSuchProcess();
+        if (pid == 0)
+            AccessDenied("");
         else
-            return AccessDenied();
+            psutil_raise_for_pid(pid, "proc_pidpath()");
+        return NULL;
     }
-    return Py_BuildValue("s", buf);
+    return PyUnicode_DecodeFSDefault(buf);
 }
 
 
 /*
  * Return process cmdline as a Python list of cmdline arguments.
  */
 static PyObject *
-psutil_proc_cmdline(PyObject *self, PyObject *args)
-{
+psutil_proc_cmdline(PyObject *self, PyObject *args) {
     long pid;
-    PyObject *arglist = NULL;
+    PyObject *py_retlist = NULL;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
     // get the commandline, defined in arch/osx/process_info.c
-    arglist = psutil_get_arg_list(pid);
-    return arglist;
-}
-
-
-/*
- * Return process parent pid from kinfo_proc as a Python integer.
- */
-static PyObject *
-psutil_proc_ppid(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid);
+    py_retlist = psutil_get_cmdline(pid);
+    return py_retlist;
 }
 
 
 /*
- * Return process real uid from kinfo_proc as a Python integer.
- */
-static PyObject *
-psutil_proc_uids(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("lll",
-                         (long)kp.kp_eproc.e_pcred.p_ruid,
-                         (long)kp.kp_eproc.e_ucred.cr_uid,
-                         (long)kp.kp_eproc.e_pcred.p_svuid);
-}
-
-
-/*
- * Return process real group id from ki_comm as a Python integer.
+ * Return process environment as a Python string.
  */
 static PyObject *
-psutil_proc_gids(PyObject *self, PyObject *args)
-{
+psutil_proc_environ(PyObject *self, PyObject *args) {
     long pid;
-    struct kinfo_proc kp;
+    PyObject *py_retdict = NULL;
+
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("lll",
-                         (long)kp.kp_eproc.e_pcred.p_rgid,
-                         (long)kp.kp_eproc.e_ucred.cr_groups[0],
-                         (long)kp.kp_eproc.e_pcred.p_svgid);
-}
 
-
-/*
- * Return process controlling terminal number as an integer.
- */
-static PyObject *
-psutil_proc_tty_nr(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("i", kp.kp_eproc.e_tdev);
+    // get the environment block, defined in arch/osx/process_info.c
+    py_retdict = psutil_get_environ(pid);
+    return py_retdict;
 }
 
 
 /*
  * Return a list of tuples for every process memory maps.
  * 'procstat' cmdline utility has been used as an example.
  */
 static PyObject *
-psutil_proc_memory_maps(PyObject *self, PyObject *args)
-{
+psutil_proc_memory_maps(PyObject *self, PyObject *args) {
     char buf[PATH_MAX];
     char addr_str[34];
     char perms[8];
     int pagesize = getpagesize();
     long pid;
     kern_return_t err = KERN_SUCCESS;
     mach_port_t task = MACH_PORT_NULL;
     uint32_t depth = 1;
     vm_address_t address = 0;
     vm_size_t size = 0;
 
     PyObject *py_tuple = NULL;
+    PyObject *py_path = NULL;
     PyObject *py_list = PyList_New(0);
 
     if (py_list == NULL)
         return NULL;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
 
-    err = task_for_pid(mach_task_self(), pid, &task);
-
+    err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
     if (err != KERN_SUCCESS) {
-        if (! psutil_pid_exists(pid)) {
-            NoSuchProcess();
+        if ((err == 5) && (errno == ENOENT)) {
+            // See: https://github.com/giampaolo/psutil/issues/1181
+            psutil_debug("task_for_pid(MACH_PORT_NULL) failed; err=%i, "
+                         "errno=%i, msg='%s'\n", err, errno,
+                         mach_error_string(err));
+            AccessDenied("");
         }
         else {
-            // pid exists, so return AccessDenied error since task_for_pid()
-            // failed
-            AccessDenied();
+            psutil_raise_for_pid(pid, "task_for_pid(MACH_PORT_NULL)");
         }
         goto error;
     }
 
     while (1) {
         py_tuple = NULL;
         struct vm_region_submap_info_64 info;
         mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
 
         err = vm_region_recurse_64(task, &address, &size, &depth,
                                    (vm_region_info_64_t)&info, &count);
-        if (err == KERN_INVALID_ADDRESS)
+        if (err == KERN_INVALID_ADDRESS) {
+            // TODO temporary
+            psutil_debug("vm_region_recurse_64 returned KERN_INVALID_ADDRESS");
             break;
+        }
+        if (err != KERN_SUCCESS) {
+            psutil_debug("vm_region_recurse_64 returned !=  KERN_SUCCESS");
+        }
+
         if (info.is_submap) {
             depth++;
         }
         else {
             // Free/Reset the char[]s to avoid weird paths
             memset(buf, 0, sizeof(buf));
             memset(addr_str, 0, sizeof(addr_str));
             memset(perms, 0, sizeof(perms));
 
-            sprintf(addr_str, "%016lx-%016lx", address, address + size);
+            sprintf(addr_str,
+                    "%016lx-%016lx",
+                    (long unsigned int)address,
+                    (long unsigned int)address + size);
             sprintf(perms, "%c%c%c/%c%c%c",
                     (info.protection & VM_PROT_READ) ? 'r' : '-',
                     (info.protection & VM_PROT_WRITE) ? 'w' : '-',
                     (info.protection & VM_PROT_EXECUTE) ? 'x' : '-',
                     (info.max_protection & VM_PROT_READ) ? 'r' : '-',
                     (info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
                     (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-');
 
-            err = proc_regionfilename(pid, address, buf, sizeof(buf));
+            // proc_regionfilename() return value seems meaningless
+            // so we do what we can in order to not continue in case
+            // of error.
+            errno = 0;
+            proc_regionfilename((pid_t)pid, address, buf, sizeof(buf));
+            if ((errno != 0) || ((sizeof(buf)) <= 0)) {
+                // TODO temporary
+                psutil_debug("proc_regionfilename() failed");
+                psutil_raise_for_pid(pid, "proc_regionfilename()");
+                goto error;
+            }
 
             if (info.share_mode == SM_COW && info.ref_count == 1) {
                 // Treat single reference SM_COW as SM_PRIVATE
                 info.share_mode = SM_PRIVATE;
             }
 
             if (strlen(buf) == 0) {
                 switch (info.share_mode) {
+// #ifdef SM_LARGE_PAGE
                     // case SM_LARGE_PAGE:
                         // Treat SM_LARGE_PAGE the same as SM_PRIVATE
                         // since they are not shareable and are wired.
+// #endif
                     case SM_COW:
                         strcpy(buf, "[cow]");
                         break;
                     case SM_PRIVATE:
                         strcpy(buf, "[prv]");
                         break;
                     case SM_EMPTY:
                         strcpy(buf, "[nul]");
@@ -359,252 +431,286 @@ psutil_proc_memory_maps(PyObject *self, 
                     case SM_SHARED_ALIASED:
                         strcpy(buf, "[s/a]");
                         break;
                     default:
                         strcpy(buf, "[???]");
                 }
             }
 
+            py_path = PyUnicode_DecodeFSDefault(buf);
+            if (! py_path)
+                goto error;
             py_tuple = Py_BuildValue(
-                "sssIIIIIH",
+                "ssOIIIIIH",
                 addr_str,                                 // "start-end"address
                 perms,                                    // "rwx" permissions
-                buf,                                      // path
+                py_path,                                  // path
                 info.pages_resident * pagesize,           // rss
                 info.pages_shared_now_private * pagesize, // private
                 info.pages_swapped_out * pagesize,        // swapped
                 info.pages_dirtied * pagesize,            // dirtied
                 info.ref_count,                           // ref count
                 info.shadow_depth                         // shadow depth
             );
             if (!py_tuple)
                 goto error;
             if (PyList_Append(py_list, py_tuple))
                 goto error;
             Py_DECREF(py_tuple);
+            Py_DECREF(py_path);
         }
 
         // increment address for the next map/file
         address += size;
     }
 
     if (task != MACH_PORT_NULL)
         mach_port_deallocate(mach_task_self(), task);
 
     return py_list;
 
 error:
     if (task != MACH_PORT_NULL)
         mach_port_deallocate(mach_task_self(), task);
     Py_XDECREF(py_tuple);
+    Py_XDECREF(py_path);
     Py_DECREF(py_list);
     return NULL;
 }
 
 
 /*
  * Return the number of logical CPUs in the system.
  * XXX this could be shared with BSD.
  */
 static PyObject *
-psutil_cpu_count_logical(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
+    /*
     int mib[2];
     int ncpu;
     size_t len;
     mib[0] = CTL_HW;
     mib[1] = HW_NCPU;
     len = sizeof(ncpu);
 
     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
         Py_RETURN_NONE;  // mimic os.cpu_count()
     else
         return Py_BuildValue("i", ncpu);
+    */
+    int num;
+    size_t size = sizeof(int);
+
+    if (sysctlbyname("hw.logicalcpu", &num, &size, NULL, 2))
+        Py_RETURN_NONE;  // mimic os.cpu_count()
+    else
+        return Py_BuildValue("i", num);
 }
 
 
 /*
  * Return the number of physical CPUs in the system.
  */
 static PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
     int num;
     size_t size = sizeof(int);
 
     if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0))
         Py_RETURN_NONE;  // mimic os.cpu_count()
     else
         return Py_BuildValue("i", num);
 }
 
 
-#define TV2DOUBLE(t)    ((t).tv_sec + (t).tv_usec / 1000000.0)
-
 /*
- * Return a Python tuple (user_time, kernel_time)
+ * Indicates if the given virtual address on the given architecture is in the
+ * shared VM region.
  */
-static PyObject *
-psutil_proc_cpu_times(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct proc_taskinfo pti;
+bool
+psutil_in_shared_region(mach_vm_address_t addr, cpu_type_t type) {
+    mach_vm_address_t base;
+    mach_vm_address_t size;
 
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
-        return NULL;
-    return Py_BuildValue("(dd)",
-                         (float)pti.pti_total_user / 1000000000.0,
-                         (float)pti.pti_total_system / 1000000000.0);
-}
-
+    switch (type) {
+        case CPU_TYPE_ARM:
+            base = SHARED_REGION_BASE_ARM;
+            size = SHARED_REGION_SIZE_ARM;
+            break;
+        case CPU_TYPE_I386:
+            base = SHARED_REGION_BASE_I386;
+            size = SHARED_REGION_SIZE_I386;
+            break;
+        case CPU_TYPE_X86_64:
+            base = SHARED_REGION_BASE_X86_64;
+            size = SHARED_REGION_SIZE_X86_64;
+            break;
+        default:
+            return false;
+    }
 
-/*
- * 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;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime));
+    return base <= addr && addr < (base + size);
 }
 
 
 /*
- * Return extended memory info about a process.
+ * Returns the USS (unique set size) of the process. Reference:
+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
+ *     nsMemoryReporterManager.cpp
  */
 static PyObject *
-psutil_proc_memory_info(PyObject *self, PyObject *args)
-{
+psutil_proc_memory_uss(PyObject *self, PyObject *args) {
     long pid;
-    struct proc_taskinfo pti;
+    int err;
+    size_t len;
+    cpu_type_t cpu_type;
+    size_t private_pages = 0;
+    mach_vm_size_t size = 0;
+    mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
+    kern_return_t kr;
+    vm_size_t page_size;
+    mach_vm_address_t addr = MACH_VM_MIN_ADDRESS;
+    mach_port_t task = MACH_PORT_NULL;
+    vm_region_top_info_data_t info;
+    mach_port_t object_name;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
+
+    err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
+    if (err != KERN_SUCCESS) {
+        if (psutil_pid_exists(pid) == 0)
+            NoSuchProcess("");
+        else
+            AccessDenied("");
         return NULL;
-    // Note: determining other memory stats on OSX is a mess:
-    // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
-    // I just give up...
-    // struct proc_regioninfo pri;
-    // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, &pri, sizeof(pri))
-    return Py_BuildValue(
-        "(KKkk)",
-        pti.pti_resident_size,  // resident memory size (rss)
-        pti.pti_virtual_size,   // virtual memory size (vms)
-        pti.pti_faults,         // number of page faults (pages)
-        pti.pti_pageins         // number of actual pageins (pages)
-    );
+    }
+
+    len = sizeof(cpu_type);
+    if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    // Roughly based on libtop_update_vm_regions in
+    // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
+    for (addr = 0; ; addr += size) {
+        kr = mach_vm_region(
+            task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info,
+            &info_count, &object_name);
+        if (kr == KERN_INVALID_ADDRESS) {
+            // Done iterating VM regions.
+            break;
+        }
+        else if (kr != KERN_SUCCESS) {
+            PyErr_Format(
+                PyExc_RuntimeError,
+                "mach_vm_region(VM_REGION_TOP_INFO) syscall failed");
+            return NULL;
+        }
+
+        if (psutil_in_shared_region(addr, cpu_type) &&
+                info.share_mode != SM_PRIVATE) {
+            continue;
+        }
+
+        switch (info.share_mode) {
+#ifdef SM_LARGE_PAGE
+            case SM_LARGE_PAGE:
+                // NB: Large pages are not shareable and always resident.
+#endif
+            case SM_PRIVATE:
+                private_pages += info.private_pages_resident;
+                private_pages += info.shared_pages_resident;
+                break;
+            case SM_COW:
+                private_pages += info.private_pages_resident;
+                if (info.ref_count == 1) {
+                    // Treat copy-on-write pages as private if they only
+                    // have one reference.
+                    private_pages += info.shared_pages_resident;
+                }
+                break;
+            case SM_SHARED:
+            default:
+                break;
+        }
+    }
+
+    mach_port_deallocate(mach_task_self(), task);
+
+    if (host_page_size(mach_host_self(), &page_size) != KERN_SUCCESS)
+        page_size = PAGE_SIZE;
+
+    return Py_BuildValue("K", private_pages * page_size);
 }
 
 
 /*
- * Return number of threads used by process as a Python integer.
- */
-static PyObject *
-psutil_proc_num_threads(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct proc_taskinfo pti;
-
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
-        return NULL;
-    return Py_BuildValue("k", pti.pti_threadnum);
-}
-
-
-/*
- * Return the number of context switches performed by process.
+ * Return system virtual memory stats.
+ * See:
+ * http://opensource.apple.com/source/system_cmds/system_cmds-498.2/
+ *     vm_stat.tproj/vm_stat.c
  */
 static PyObject *
-psutil_proc_num_ctx_switches(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct proc_taskinfo pti;
-
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
-        return NULL;
-    // unvoluntary value seems not to be available;
-    // pti.pti_csw probably refers to the sum of the two (getrusage()
-    // numbers seems to confirm this theory).
-    return Py_BuildValue("ki", pti.pti_csw, 0);
-}
-
-
-/*
- * Return system virtual memory stats
- */
-static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args)
-{
-
+psutil_virtual_mem(PyObject *self, PyObject *args) {
     int      mib[2];
     uint64_t total;
     size_t   len = sizeof(total);
     vm_statistics_data_t vm;
     int pagesize = getpagesize();
     // physical mem
     mib[0] = CTL_HW;
     mib[1] = HW_MEMSIZE;
 
+    // This is also available as sysctlbyname("hw.memsize").
     if (sysctl(mib, 2, &total, &len, NULL, 0)) {
         if (errno != 0)
             PyErr_SetFromErrno(PyExc_OSError);
         else
-            PyErr_Format(PyExc_RuntimeError, "sysctl(HW_MEMSIZE) failed");
+            PyErr_Format(
+                PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
         return NULL;
     }
 
     // vm
     if (!psutil_sys_vminfo(&vm))
         return NULL;
 
     return Py_BuildValue(
         "KKKKK",
         total,
         (unsigned long long) vm.active_count * pagesize,
         (unsigned long long) vm.inactive_count * pagesize,
         (unsigned long long) vm.wire_count * pagesize,
-        (unsigned long long) vm.free_count * pagesize
+        // this is how vm_stat cmd does it
+        (unsigned long long) (vm.free_count - vm.speculative_count) * pagesize
     );
 }
 
 
 /*
  * Return stats about swap memory.
  */
 static PyObject *
-psutil_swap_mem(PyObject *self, PyObject *args)
-{
+psutil_swap_mem(PyObject *self, PyObject *args) {
     int mib[2];
     size_t size;
     struct xsw_usage totals;
     vm_statistics_data_t vmstat;
     int pagesize = getpagesize();
 
     mib[0] = CTL_VM;
     mib[1] = VM_SWAPUSAGE;
     size = sizeof(totals);
     if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
         if (errno != 0)
             PyErr_SetFromErrno(PyExc_OSError);
         else
-            PyErr_Format(PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) failed");
+            PyErr_Format(
+                PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
         return NULL;
     }
     if (!psutil_sys_vminfo(&vmstat))
         return NULL;
 
     return Py_BuildValue(
         "LLLKK",
         totals.xsu_total,
@@ -614,65 +720,68 @@ psutil_swap_mem(PyObject *self, PyObject
         (unsigned long long)vmstat.pageouts * pagesize);
 }
 
 
 /*
  * Return a Python tuple representing user, kernel and idle CPU times
  */
 static PyObject *
-psutil_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_cpu_times(PyObject *self, PyObject *args) {
     mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
     kern_return_t error;
     host_cpu_load_info_data_t r_load;
 
     mach_port_t host_port = mach_host_self();
     error = host_statistics(host_port, HOST_CPU_LOAD_INFO,
                             (host_info_t)&r_load, &count);
-    if (error != KERN_SUCCESS)
-        return PyErr_Format(PyExc_RuntimeError,
-                            "Error in host_statistics(): %s",
-                            mach_error_string(error));
+    if (error != KERN_SUCCESS) {
+        return PyErr_Format(
+            PyExc_RuntimeError,
+            "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: %s",
+            mach_error_string(error));
+    }
     mach_port_deallocate(mach_task_self(), host_port);
 
     return Py_BuildValue(
         "(dddd)",
         (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK,
         (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
         (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
         (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
     );
 }
 
 
 /*
  * Return a Python list of tuple representing per-cpu times
  */
 static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
     natural_t cpu_count;
+    natural_t i;
     processor_info_array_t info_array;
     mach_msg_type_number_t info_count;
     kern_return_t error;
     processor_cpu_load_info_data_t *cpu_load_info = NULL;
-    int i, ret;
+    int ret;
     PyObject *py_retlist = PyList_New(0);
     PyObject *py_cputime = NULL;
 
     if (py_retlist == NULL)
         return NULL;
 
     mach_port_t host_port = mach_host_self();
     error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
                                 &cpu_count, &info_array, &info_count);
     if (error != KERN_SUCCESS) {
-        PyErr_Format(PyExc_RuntimeError, "Error in host_processor_info(): %s",
-                     mach_error_string(error));
+        PyErr_Format(
+            PyExc_RuntimeError,
+            "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
+             mach_error_string(error));
         goto error;
     }
     mach_port_deallocate(mach_task_self(), host_port);
 
     cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
 
     for (i = 0; i < cpu_count; i++) {
         py_cputime = Py_BuildValue(
@@ -704,52 +813,75 @@ error:
         if (ret != KERN_SUCCESS)
             PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
     }
     return NULL;
 }
 
 
 /*
+ * Retrieve CPU frequency.
+ */
+static PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+    int64_t curr;
+    int64_t min;
+    int64_t max;
+    size_t size = sizeof(int64_t);
+
+    if (sysctlbyname("hw.cpufrequency", &curr, &size, NULL, 0))
+        return PyErr_SetFromErrno(PyExc_OSError);
+    if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0))
+        return PyErr_SetFromErrno(PyExc_OSError);
+    if (sysctlbyname("hw.cpufrequency_max", &max, &size, NULL, 0))
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    return Py_BuildValue(
+        "KKK",
+        curr / 1000 / 1000,
+        min / 1000 / 1000,
+        max / 1000 / 1000);
+}
+
+
+/*
  * Return a Python float indicating the system boot time expressed in
  * seconds since the epoch.
  */
 static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args)
-{
+psutil_boot_time(PyObject *self, PyObject *args) {
     // fetch sysctl "kern.boottime"
     static int request[2] = { CTL_KERN, KERN_BOOTTIME };
     struct timeval result;
     size_t result_len = sizeof result;
     time_t boot_time = 0;
 
-    if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
-    }
+    if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1)
+        return PyErr_SetFromErrno(PyExc_OSError);
     boot_time = result.tv_sec;
     return Py_BuildValue("f", (float)boot_time);
 }
 
 
 /*
  * Return a list of tuples including device, mount point and fs type
  * for all partitions mounted on the system.
  */
 static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args)
-{
+psutil_disk_partitions(PyObject *self, PyObject *args) {
     int num;
     int i;
-    long len;
+    int len;
     uint64_t flags;
     char opts[400];
     struct statfs *fs = NULL;
+    PyObject *py_dev = NULL;
+    PyObject *py_mountp = NULL;
+    PyObject *py_tuple = NULL;
     PyObject *py_retlist = PyList_New(0);
-    PyObject *py_tuple = NULL;
 
     if (py_retlist == NULL)
         return NULL;
 
     // get the number of mount points
     Py_BEGIN_ALLOW_THREADS
     num = getfsstat(NULL, 0, MNT_NOWAIT);
     Py_END_ALLOW_THREADS
@@ -824,154 +956,148 @@ psutil_disk_partitions(PyObject *self, P
             strlcat(opts, ",update", sizeof(opts));
         if (flags & MNT_RELOAD)
             strlcat(opts, ",reload", sizeof(opts));
         if (flags & MNT_FORCE)
             strlcat(opts, ",force", sizeof(opts));
         if (flags & MNT_CMDFLAGS)
             strlcat(opts, ",cmdflags", sizeof(opts));
 
+        py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
+        if (! py_dev)
+            goto error;
+        py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
+        if (! py_mountp)
+            goto error;
         py_tuple = Py_BuildValue(
-            "(ssss)", fs[i].f_mntfromname,  // device
-            fs[i].f_mntonname,    // mount point
+            "(OOss)",
+            py_dev,               // device
+            py_mountp,            // mount point
             fs[i].f_fstypename,   // fs type
             opts);                // options
         if (!py_tuple)
             goto error;
         if (PyList_Append(py_retlist, py_tuple))
             goto error;
+        Py_DECREF(py_dev);
+        Py_DECREF(py_mountp);
         Py_DECREF(py_tuple);
     }
 
     free(fs);
     return py_retlist;
 
 error:
+    Py_XDECREF(py_dev);
+    Py_XDECREF(py_mountp);
     Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     if (fs != NULL)
         free(fs);
     return NULL;
 }
 
 
 /*
- * Return process status as a Python integer.
- */
-static PyObject *
-psutil_proc_status(PyObject *self, PyObject *args)
-{
-    long pid;
-    struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid))
-        return NULL;
-    if (psutil_get_kinfo_proc(pid, &kp) == -1)
-        return NULL;
-    return Py_BuildValue("i", (int)kp.kp_proc.p_stat);
-}
-
-
-/*
  * Return process threads
  */
 static PyObject *
-psutil_proc_threads(PyObject *self, PyObject *args)
-{
+psutil_proc_threads(PyObject *self, PyObject *args) {
     long pid;
-    int err, j, ret;
+    int err, ret;
     kern_return_t kr;
     unsigned int info_count = TASK_BASIC_INFO_COUNT;
     mach_port_t task = MACH_PORT_NULL;
     struct task_basic_info tasks_info;
     thread_act_port_array_t thread_list = NULL;
     thread_info_data_t thinfo_basic;
     thread_basic_info_t basic_info_th;
-    mach_msg_type_number_t thread_count, thread_info_count;
+    mach_msg_type_number_t thread_count, thread_info_count, j;
 
-    PyObject *retList = PyList_New(0);
-    PyObject *pyTuple = NULL;
+    PyObject *py_tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
 
-    if (retList == NULL)
+    if (py_retlist == NULL)
         return NULL;
 
     // the argument passed should be a process id
     if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
 
-    // task_for_pid() requires special privileges
-    err = task_for_pid(mach_task_self(), pid, &task);
+    // task_for_pid() requires root privileges
+    err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
     if (err != KERN_SUCCESS) {
-        if (! psutil_pid_exists(pid))
-            NoSuchProcess();
+        if (psutil_pid_exists(pid) == 0)
+            NoSuchProcess("");
         else
-            AccessDenied();
+            AccessDenied("");
         goto error;
     }
 
     info_count = TASK_BASIC_INFO_COUNT;
     err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info,
                     &info_count);
     if (err != KERN_SUCCESS) {
         // errcode 4 is "invalid argument" (access denied)
         if (err == 4) {
-            AccessDenied();
+            AccessDenied("");
         }
         else {
             // otherwise throw a runtime error with appropriate error code
             PyErr_Format(PyExc_RuntimeError,
-                         "task_info(TASK_BASIC_INFO) failed");
+                         "task_info(TASK_BASIC_INFO) syscall failed");
         }
         goto error;
     }
 
     err = task_threads(task, &thread_list, &thread_count);
     if (err != KERN_SUCCESS) {
-        PyErr_Format(PyExc_RuntimeError, "task_threads() failed");
+        PyErr_Format(PyExc_RuntimeError, "task_threads() syscall failed");
         goto error;
     }
 
     for (j = 0; j < thread_count; j++) {
-        pyTuple = NULL;
+        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);
         if (kr != KERN_SUCCESS) {
             PyErr_Format(PyExc_RuntimeError,
-                         "thread_info() with flag THREAD_BASIC_INFO failed");
+                         "thread_info(THREAD_BASIC_INFO) syscall failed");
             goto error;
         }
 
         basic_info_th = (thread_basic_info_t)thinfo_basic;
-        pyTuple = Py_BuildValue(
+        py_tuple = Py_BuildValue(
             "Iff",
             j + 1,
             (float)basic_info_th->user_time.microseconds / 1000000.0,
             (float)basic_info_th->system_time.microseconds / 1000000.0
         );
-        if (!pyTuple)
+        if (!py_tuple)
             goto error;
-        if (PyList_Append(retList, pyTuple))
+        if (PyList_Append(py_retlist, py_tuple))
             goto error;
-        Py_DECREF(pyTuple);
+        Py_DECREF(py_tuple);
     }
 
     ret = vm_deallocate(task, (vm_address_t)thread_list,
                         thread_count * sizeof(int));
     if (ret != KERN_SUCCESS)
         PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
 
     mach_port_deallocate(mach_task_self(), task);
 
-    return retList;
+    return py_retlist;
 
 error:
     if (task != MACH_PORT_NULL)
         mach_port_deallocate(mach_task_self(), task);
-    Py_XDECREF(pyTuple);
-    Py_DECREF(retList);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
     if (thread_list != NULL) {
         ret = vm_deallocate(task, (vm_address_t)thread_list,
                             thread_count * sizeof(int));
         if (ret != KERN_SUCCESS)
             PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
     }
     return NULL;
 }
@@ -979,235 +1105,212 @@ error:
 
 /*
  * Return process open files as a Python tuple.
  * References:
  * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd
  * - /usr/include/sys/proc_info.h
  */
 static PyObject *
-psutil_proc_open_files(PyObject *self, PyObject *args)
-{
+psutil_proc_open_files(PyObject *self, PyObject *args) {
     long pid;
     int pidinfo_result;
     int iterations;
     int i;
-    int nb;
+    unsigned long nb;
 
     struct proc_fdinfo *fds_pointer = NULL;
     struct proc_fdinfo *fdp_pointer;
     struct vnode_fdinfowithpath vi;
 
-    PyObject *retList = PyList_New(0);
-    PyObject *tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_path = NULL;
 
-    if (retList == NULL)
+    if (py_retlist == NULL)
         return NULL;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
 
-    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
-    if (pidinfo_result <= 0) {
-        // may be be ignored later if errno != 0
-        PyErr_Format(PyExc_RuntimeError,
-                     "proc_pidinfo(PROC_PIDLISTFDS) failed");
-        goto error;
-    }
-
-    fds_pointer = malloc(pidinfo_result);
-    if (fds_pointer == NULL) {
-        PyErr_NoMemory();
-        goto error;
-    }
-    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
-                                  pidinfo_result);
-    if (pidinfo_result <= 0) {
-        // may be be ignored later if errno != 0
-        PyErr_Format(PyExc_RuntimeError,
-                     "proc_pidinfo(PROC_PIDLISTFDS) failed");
-        goto error;
-    }
-
-    iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
-
-    for (i = 0; i < iterations; i++) {
-        tuple = NULL;
-        fdp_pointer = &fds_pointer[i];
-
-        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE)
-        {
-            nb = proc_pidfdinfo(pid,
-                                fdp_pointer->proc_fd,
-                                PROC_PIDFDVNODEPATHINFO,
-                                &vi,
-                                sizeof(vi));
-
-            // --- errors checking
-            if (nb <= 0) {
-                if ((errno == ENOENT) || (errno == EBADF)) {
-                    // no such file or directory or bad file descriptor;
-                    // let's assume the file has been closed or removed
-                    continue;
-                }
-                // may be be ignored later if errno != 0
-                PyErr_Format(PyExc_RuntimeError,
-                             "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
-                goto error;
-            }
-            if (nb < sizeof(vi)) {
-                PyErr_Format(PyExc_RuntimeError,
-                             "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed "
-                             "(buffer mismatch)");
-                goto error;
-            }
-            // --- /errors checking
-
-            // --- construct python list
-            tuple = Py_BuildValue("(si)",
-                                  vi.pvip.vip_path,
-                                  (int)fdp_pointer->proc_fd);
-            if (!tuple)
-                goto error;
-            if (PyList_Append(retList, tuple))
-                goto error;
-            Py_DECREF(tuple);
-            // --- /construct python list
-        }
-    }
-
-    free(fds_pointer);
-    return retList;
-
-error:
-    Py_XDECREF(tuple);
-    Py_DECREF(retList);
-    if (fds_pointer != NULL)
-        free(fds_pointer);
-    if (errno != 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    else if (! psutil_pid_exists(pid))
-        return NoSuchProcess();
-    else
-        return NULL;  // exception has already been set earlier
-}
-
-
-// a signaler for connections without an actual status
-static int PSUTIL_CONN_NONE = 128;
-
-/*
- * Return process TCP and UDP connections as a list of tuples.
- * References:
- * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
- * - /usr/include/sys/proc_info.h
- */
-static PyObject *
-psutil_proc_connections(PyObject *self, PyObject *args)
-{
-    long pid;
-    int pidinfo_result;
-    int iterations;
-    int i;
-    int nb;
-
-    struct proc_fdinfo *fds_pointer = NULL;
-    struct proc_fdinfo *fdp_pointer;
-    struct socket_fdinfo si;
-
-    PyObject *retList = PyList_New(0);
-    PyObject *tuple = NULL;
-    PyObject *laddr = NULL;
-    PyObject *raddr = NULL;
-    PyObject *af_filter = NULL;
-    PyObject *type_filter = NULL;
-
-    if (retList == NULL)
-        return NULL;
-
-    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter))
-        goto error;
-
-    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
-        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
-        goto error;
-    }
-
-    if (pid == 0)
-        return retList;
-    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+    pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
     if (pidinfo_result <= 0)
         goto error;
 
     fds_pointer = malloc(pidinfo_result);
     if (fds_pointer == NULL) {
         PyErr_NoMemory();
         goto error;
     }
-    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
-                                  pidinfo_result);
-
+    pidinfo_result = psutil_proc_pidinfo(
+        pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
     if (pidinfo_result <= 0)
         goto error;
+
     iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
 
     for (i = 0; i < iterations; i++) {
-        tuple = NULL;
-        laddr = NULL;
-        raddr = NULL;
-        errno = 0;
         fdp_pointer = &fds_pointer[i];
 
-        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET)
-        {
-            nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd,
+        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
+            errno = 0;
+            nb = proc_pidfdinfo((pid_t)pid,
+                                fdp_pointer->proc_fd,
+                                PROC_PIDFDVNODEPATHINFO,
+                                &vi,
+                                sizeof(vi));
+
+            // --- errors checking
+            if ((nb <= 0) || nb < sizeof(vi)) {
+                if ((errno == ENOENT) || (errno == EBADF)) {
+                    // no such file or directory or bad file descriptor;
+                    // let's assume the file has been closed or removed
+                    continue;
+                }
+                else {
+                    psutil_raise_for_pid(
+                        pid, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO)");
+                    goto error;
+                }
+            }
+            // --- /errors checking
+
+            // --- construct python list
+            py_path = PyUnicode_DecodeFSDefault(vi.pvip.vip_path);
+            if (! py_path)
+                goto error;
+            py_tuple = Py_BuildValue(
+                "(Oi)",
+                py_path,
+                (int)fdp_pointer->proc_fd);
+            if (!py_tuple)
+                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;
+            // --- /construct python list
+        }
+    }
+
+    free(fds_pointer);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_path);
+    Py_DECREF(py_retlist);
+    if (fds_pointer != NULL)
+        free(fds_pointer);
+    return NULL;  // exception has already been set earlier
+}
+
+
+/*
+ * Return process TCP and UDP connections as a list of tuples.
+ * Raises NSP in case of zombie process.
+ * References:
+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
+ * - /usr/include/sys/proc_info.h
+ */
+static PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+    long pid;
+    int pidinfo_result;
+    int iterations;
+    int i;
+    unsigned long nb;
+
+    struct proc_fdinfo *fds_pointer = NULL;
+    struct proc_fdinfo *fdp_pointer;
+    struct socket_fdinfo si;
+
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_laddr = NULL;
+    PyObject *py_raddr = NULL;
+    PyObject *py_af_filter = NULL;
+    PyObject *py_type_filter = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    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)) {
+        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+        goto error;
+    }
+
+    if (pid == 0)
+        return py_retlist;
+    pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+    if (pidinfo_result <= 0)
+        goto error;
+
+    fds_pointer = malloc(pidinfo_result);
+    if (fds_pointer == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    pidinfo_result = psutil_proc_pidinfo(
+        pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
+    if (pidinfo_result <= 0)
+        goto error;
+
+    iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
+    for (i = 0; i < iterations; i++) {
+        py_tuple = NULL;
+        py_laddr = NULL;
+        py_raddr = NULL;
+        fdp_pointer = &fds_pointer[i];
+
+        if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {
+            errno = 0;
+            nb = proc_pidfdinfo((pid_t)pid, fdp_pointer->proc_fd,
                                 PROC_PIDFDSOCKETINFO, &si, sizeof(si));
 
             // --- errors checking
-            if (nb <= 0) {
+            if ((nb <= 0) || (nb < sizeof(si))) {
                 if (errno == EBADF) {
                     // let's assume socket has been closed
                     continue;
                 }
-                if (errno != 0)
-                    PyErr_SetFromErrno(PyExc_OSError);
-                else
-                    PyErr_Format(
-                        PyExc_RuntimeError,
-                        "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
-                goto error;
-            }
-            if (nb < sizeof(si)) {
-                PyErr_Format(PyExc_RuntimeError,
-                             "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed "
-                             "(buffer mismatch)");
-                goto error;
+                else {
+                    psutil_raise_for_pid(
+                        pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)");
+                    goto error;
+                }
             }
             // --- /errors checking
 
             //
             int fd, family, type, lport, rport, state;
             char lip[200], rip[200];
             int inseq;
-            PyObject *_family;
-            PyObject *_type;
+            PyObject *py_family;
+            PyObject *py_type;
 
             fd = (int)fdp_pointer->proc_fd;
             family = si.psi.soi_family;
             type = si.psi.soi_type;
 
             // apply filters
-            _family = PyLong_FromLong((long)family);
-            inseq = PySequence_Contains(af_filter, _family);
-            Py_DECREF(_family);
+            py_family = PyLong_FromLong((long)family);
+            inseq = PySequence_Contains(py_af_filter, py_family);
+            Py_DECREF(py_family);
             if (inseq == 0)
                 continue;
-            _type = PyLong_FromLong((long)type);
-            inseq = PySequence_Contains(type_filter, _type);
-            Py_DECREF(_type);
+            py_type = PyLong_FromLong((long)type);
+            inseq = PySequence_Contains(py_type_filter, py_type);
+            Py_DECREF(py_type);
             if (inseq == 0)
                 continue;
 
             if (errno != 0) {
                 PyErr_SetFromErrno(PyExc_OSError);
                 goto error;
             }
 
@@ -1243,113 +1346,115 @@ psutil_proc_connections(PyObject *self, 
 
                 lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);
                 rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
                 if (type == SOCK_STREAM)
                     state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state;
                 else
                     state = PSUTIL_CONN_NONE;
 
-                laddr = Py_BuildValue("(si)", lip, lport);
-                if (!laddr)
+                py_laddr = Py_BuildValue("(si)", lip, lport);
+                if (!py_laddr)
                     goto error;
                 if (rport != 0)
-                    raddr = Py_BuildValue("(si)", rip, rport);
+                    py_raddr = Py_BuildValue("(si)", rip, rport);
                 else
-                    raddr = Py_BuildValue("()");
-                if (!raddr)
+                    py_raddr = Py_BuildValue("()");
+                if (!py_raddr)
                     goto error;
 
                 // construct the python list
-                tuple = Py_BuildValue("(iiiNNi)", fd, family, type, laddr,
-                                      raddr, state);
-                if (!tuple)
+                py_tuple = Py_BuildValue(
+                    "(iiiNNi)", fd, family, type, py_laddr, py_raddr, state);
+                if (!py_tuple)
                     goto error;
-                if (PyList_Append(retList, tuple))
+                if (PyList_Append(py_retlist, py_tuple))
                     goto error;
-                Py_DECREF(tuple);
+                Py_DECREF(py_tuple);
             }
             else if (family == AF_UNIX) {
+                py_laddr = PyUnicode_DecodeFSDefault(
+                    si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path);
+                if (!py_laddr)
+                    goto error;
+                py_raddr = PyUnicode_DecodeFSDefault(
+                    si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path);
+                if (!py_raddr)
+                    goto error;
                 // construct the python list
-                tuple = Py_BuildValue(
-                    "(iiissi)",
+                py_tuple = Py_BuildValue(
+                    "(iiiOOi)",
                     fd, family, type,
-                    si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path,
-                    si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path,
+                    py_laddr,
+                    py_raddr,
                     PSUTIL_CONN_NONE);
-                if (!tuple)
+                if (!py_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_tuple))
                     goto error;
-                if (PyList_Append(retList, tuple))
-                    goto error;
-                Py_DECREF(tuple);
+                Py_DECREF(py_tuple);
+                Py_DECREF(py_laddr);
+                Py_DECREF(py_raddr);
             }
         }
     }
 
     free(fds_pointer);
-    return retList;
+    return py_retlist;
 
 error:
-    Py_XDECREF(tuple);
-    Py_XDECREF(laddr);
-    Py_XDECREF(raddr);
-    Py_DECREF(retList);
-
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_laddr);
+    Py_XDECREF(py_raddr);
+    Py_DECREF(py_retlist);
     if (fds_pointer != NULL)
         free(fds_pointer);
-    if (errno != 0)
-        return PyErr_SetFromErrno(PyExc_OSError);
-    else if (! psutil_pid_exists(pid))
-        return NoSuchProcess();
-    else
-        return PyErr_Format(PyExc_RuntimeError,
-                            "proc_pidinfo(PROC_PIDLISTFDS) failed");
+    return NULL;
 }
 
 
 /*
  * Return number of file descriptors opened by process.
+ * Raises NSP in case of zombie process.
  */
 static PyObject *
-psutil_proc_num_fds(PyObject *self, PyObject *args)
-{
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
     long pid;
     int pidinfo_result;
     int num;
     struct proc_fdinfo *fds_pointer;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
-    pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+    pidinfo_result = proc_pidinfo((pid_t)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, PROC_PIDLISTFDS, 0, fds_pointer,
+    pidinfo_result = proc_pidinfo((pid_t)pid, PROC_PIDLISTFDS, 0, fds_pointer,
                                   pidinfo_result);
     if (pidinfo_result <= 0) {
         free(fds_pointer);
         return PyErr_SetFromErrno(PyExc_OSError);
     }
 
     num = (pidinfo_result / PROC_PIDLISTFD_SIZE);
     free(fds_pointer);
     return Py_BuildValue("i", num);
 }
 
 
 /*
  * Return a Python list of named tuples with overall network I/O information
  */
 static PyObject *
-psutil_net_io_counters(PyObject *self, PyObject *args)
-{
+psutil_net_io_counters(PyObject *self, PyObject *args) {
     char *buf = NULL, *lim, *next;
     struct if_msghdr *ifm;
     int mib[6];
     size_t len;
     PyObject *py_retdict = PyDict_New();
     PyObject *py_ifc_info = NULL;
 
     if (py_retdict == NULL)
@@ -1426,36 +1531,35 @@ error:
     return NULL;
 }
 
 
 /*
  * Return a Python dict of tuples for disk I/O information
  */
 static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args)
-{
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
     CFDictionaryRef parent_dict;
     CFDictionaryRef props_dict;
     CFDictionaryRef stats_dict;
     io_registry_entry_t parent;
     io_registry_entry_t disk;
     io_iterator_t disk_list;
     PyObject *py_retdict = PyDict_New();
     PyObject *py_disk_info = NULL;
 
     if (py_retdict == NULL)
         return NULL;
 
     // Get list of disks
     if (IOServiceGetMatchingServices(kIOMasterPortDefault,
                                      IOServiceMatching(kIOMediaClass),
                                      &disk_list) != kIOReturnSuccess) {
-        PyErr_SetString(PyExc_RuntimeError,
-                        "unable to get the list of disks.");
+        PyErr_SetString(
+            PyExc_RuntimeError, "unable to get the list of disks.");
         goto error;
     }
 
     // Iterate over disks
     while ((disk = IOIteratorNext(disk_list)) != 0) {
         py_disk_info = NULL;
         parent_dict = NULL;
         props_dict = NULL;
@@ -1601,100 +1705,222 @@ error:
     return NULL;
 }
 
 
 /*
  * Return currently connected users as a list of tuples.
  */
 static PyObject *
-psutil_users(PyObject *self, PyObject *args)
-{
+psutil_users(PyObject *self, PyObject *args) {
     struct utmpx *utx;
-    PyObject *ret_list = PyList_New(0);
-    PyObject *tuple = NULL;
+    PyObject *py_username = NULL;
+    PyObject *py_tty = NULL;
+    PyObject *py_hostname = NULL;
+    PyObject *py_tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
 
-    if (ret_list == NULL)
+    if (py_retlist == NULL)
         return NULL;
     while ((utx = getutxent()) != NULL) {
         if (utx->ut_type != USER_PROCESS)
             continue;
-        tuple = Py_BuildValue(
-            "(sssf)",
-            utx->ut_user,             // username
-            utx->ut_line,             // tty
-            utx->ut_host,             // hostname
-            (float)utx->ut_tv.tv_sec  // start time
+        py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfi)",
+            py_username,              // username
+            py_tty,                   // tty
+            py_hostname,              // hostname
+            (float)utx->ut_tv.tv_sec, // start time
+            utx->ut_pid               // process id
         );
-        if (!tuple) {
+        if (!py_tuple) {
+            endutxent();
+            goto error;
+        }
+        if (PyList_Append(py_retlist, py_tuple)) {
             endutxent();
             goto error;
         }
-        if (PyList_Append(ret_list, tuple)) {
-            endutxent();
-            goto error;
-        }
-        Py_DECREF(tuple);
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
     }
 
     endutxent();
-    return ret_list;
+    return py_retlist;
 
 error:
-    Py_XDECREF(tuple);
-    Py_DECREF(ret_list);
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tty);
+    Py_XDECREF(py_hostname);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    struct vmmeter vmstat;
+    kern_return_t ret;
+    mach_msg_type_number_t count = sizeof(vmstat) / sizeof(integer_t);
+    mach_port_t mport = mach_host_self();
+
+    ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);
+    if (ret != KERN_SUCCESS) {
+        PyErr_Format(
+            PyExc_RuntimeError,
+            "host_statistics(HOST_VM_INFO) failed: %s",
+            mach_error_string(ret));
+        return NULL;
+    }
+    mach_port_deallocate(mach_task_self(), mport);
+
+    return Py_BuildValue(
+        "IIIII",
+        vmstat.v_swtch,  // ctx switches
+        vmstat.v_intr,  // interrupts
+        vmstat.v_soft,  // software interrupts
+        vmstat.v_syscall,  // syscalls
+        vmstat.v_trap  // traps
+    );
+}
+
+
+/*
+ * Return battery information.
+ */
+static PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+    PyObject *py_tuple = NULL;
+    CFTypeRef power_info = NULL;
+    CFArrayRef power_sources_list = NULL;
+    CFDictionaryRef power_sources_information = NULL;
+    CFNumberRef capacity_ref = NULL;
+    CFNumberRef time_to_empty_ref = NULL;
+    CFStringRef ps_state_ref = NULL;
+    uint32_t capacity;     /* units are percent */
+    int time_to_empty;     /* units are minutes */
+    int is_power_plugged;
+
+    power_info = IOPSCopyPowerSourcesInfo();
+
+    if (!power_info) {
+        PyErr_SetString(PyExc_RuntimeError,
+            "IOPSCopyPowerSourcesInfo() syscall failed");
+        goto error;
+    }
+
+    power_sources_list = IOPSCopyPowerSourcesList(power_info);
+    if (!power_sources_list) {
+        PyErr_SetString(PyExc_RuntimeError,
+            "IOPSCopyPowerSourcesList() syscall failed");
+        goto error;
+    }
+
+    /* Should only get one source. But in practice, check for > 0 sources */
+    if (!CFArrayGetCount(power_sources_list)) {
+        PyErr_SetString(PyExc_NotImplementedError, "no battery");
+        goto error;
+    }
+
+    power_sources_information = IOPSGetPowerSourceDescription(
+        power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
+
+    capacity_ref = (CFNumberRef)  CFDictionaryGetValue(
+        power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
+    if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
+        PyErr_SetString(PyExc_RuntimeError,
+            "No battery capacity infomration in power sources info");
+        goto error;
+    }
+
+    ps_state_ref = (CFStringRef) CFDictionaryGetValue(
+        power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
+    is_power_plugged = CFStringCompare(
+        ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
+        == kCFCompareEqualTo;
+
+    time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
+        power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
+    if (!CFNumberGetValue(time_to_empty_ref,
+                          kCFNumberIntType, &time_to_empty)) {
+        /* This value is recommended for non-Apple power sources, so it's not
+         * an error if it doesn't exist. We'll return -1 for "unknown" */
+        /* A value of -1 indicates "Still Calculating the Time" also for
+         * apple power source */
+        time_to_empty = -1;
+    }
+
+    py_tuple = Py_BuildValue("Iii",
+        capacity, time_to_empty, is_power_plugged);
+    if (!py_tuple) {
+        goto error;
+    }
+
+    CFRelease(power_info);
+    CFRelease(power_sources_list);
+    /* Caller should NOT release power_sources_information */
+
+    return py_tuple;
+
+error:
+    if (power_info)
+        CFRelease(power_info);
+    if (power_sources_list)
+        CFRelease(power_sources_list);
+    Py_XDECREF(py_tuple);
     return NULL;
 }
 
 
 /*
  * define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     // --- per-process functions
 
+    {"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS,
+     "Return multiple process info."},
+    {"proc_pidtaskinfo_oneshot", psutil_proc_pidtaskinfo_oneshot, METH_VARARGS,
+     "Return multiple process info."},
     {"proc_name", psutil_proc_name, METH_VARARGS,
      "Return process name"},
     {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
      "Return process cmdline as a list of cmdline arguments"},
+    {"proc_environ", psutil_proc_environ, METH_VARARGS,
+     "Return process environment data"},
     {"proc_exe", psutil_proc_exe, METH_VARARGS,
      "Return path of the process executable"},
     {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
      "Return process current working directory."},
-    {"proc_ppid", psutil_proc_ppid, METH_VARARGS,
-     "Return process ppid as an integer"},
-    {"proc_uids", psutil_proc_uids, METH_VARARGS,
-     "Return process real user id as an integer"},
-    {"proc_gids", psutil_proc_gids, METH_VARARGS,
-     "Return process real group id as an integer"},
-    {"proc_cpu_times", psutil_proc_cpu_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 memory information about a process"},
-    {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS,
-     "Return number of threads used by process"},
-    {"proc_status", psutil_proc_status, METH_VARARGS,
-     "Return process status as an integer"},
+    {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
+     "Return process USS memory"},
     {"proc_threads", psutil_proc_threads, METH_VARARGS,
      "Return process threads as a list of tuples"},
     {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
      "Return files opened by process as a list of tuples"},
     {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
      "Return the number of fds opened by process."},
-    {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
-     "Return the number of context switches performed by process"},
     {"proc_connections", psutil_proc_connections, METH_VARARGS,
      "Get process TCP and UDP connections as a list of tuples"},
-    {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS,
-     "Return process tty number as an integer"},
     {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
      "Return a list of tuples for every process's memory map"},
 
     // --- system-related functions
 
     {"pids", psutil_pids, METH_VARARGS,
      "Returns a list of PIDs currently running on the system"},
     {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
@@ -1704,27 +1930,37 @@ PsutilMethods[] =
     {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
      "Return system virtual memory stats"},
     {"swap_mem", psutil_swap_mem, METH_VARARGS,
      "Return stats about swap memory, in bytes"},
     {"cpu_times", psutil_cpu_times, METH_VARARGS,
      "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
     {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
      "Return system per-cpu times as a list of tuples"},
+    {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
+     "Return cpu current frequency"},
     {"boot_time", psutil_boot_time, METH_VARARGS,
      "Return the system boot time expressed in seconds since the epoch."},
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return a list of tuples including device, mount point and "
      "fs type for all partitions mounted on the system."},
     {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
      "Return dict of tuples of networks I/O information."},
     {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
      "Return dict of tuples of disks I/O information."},
     {"users", psutil_users, METH_VARARGS,
      "Return currently connected users as a list of tuples"},
+    {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+     "Return CPU statistics"},
+    {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
+     "Return battery information."},
+
+    // --- others
+    {"set_testing", psutil_set_testing, METH_NOARGS,
+     "Set psutil in testing mode"},
 
     {NULL, NULL, 0, NULL}
 };
 
 
 struct module_state {
     PyObject *error;
 };
@@ -1795,14 +2031,16 @@ init_psutil_osx(void)
     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);
 
+    psutil_setup();
+
     if (module == NULL)
         INITERROR;
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
deleted file mode 100644
--- a/third_party/python/psutil/psutil/_psutil_osx.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 <Python.h>
-
-// --- per-process functions
-static PyObject* psutil_proc_cmdline(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_create_time(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_gids(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_name(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_ppid(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_status(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_tty_nr(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_uids(PyObject* self, PyObject* args);
-
-// --- system-related functions
-static PyObject* psutil_boot_time(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_logical(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
-static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_pids(PyObject* self, PyObject* args);
-static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
-static PyObject* psutil_users(PyObject* self, PyObject* args);
-static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
--- a/third_party/python/psutil/psutil/_psutil_posix.c
+++ b/third_party/python/psutil/psutil/_psutil_posix.c
@@ -6,86 +6,192 @@
  * Functions specific to all POSIX compliant platforms.
  */
 
 #include <Python.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/resource.h>
 #include <sys/types.h>
+#include <signal.h>
 #include <sys/socket.h>
-#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#ifdef PSUTIL_SUNOS10
+    #include "arch/solaris/v10/ifaddrs.h"
+#elif PSUTIL_AIX
+    #include "arch/aix/ifaddrs.h"
+#else
+    #include <ifaddrs.h>
+#endif
 
-#ifdef __linux
-#include <netdb.h>
-#include <linux/if_packet.h>
-#endif  // end linux
-
-#if defined(__FreeBSD__) || defined(__APPLE__)
-#include <netdb.h>
-#include <netinet/in.h>
-#include <net/if_dl.h>
+#if defined(PSUTIL_LINUX)
+    #include <netdb.h>
+    #include <linux/types.h>
+    #include <linux/if_packet.h>
+#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+    #include <netdb.h>
+    #include <netinet/in.h>
+    #include <net/if_dl.h>
+    #include <sys/sockio.h>
+    #include <net/if_media.h>
+    #include <net/if.h>
+#elif defined(PSUTIL_SUNOS)
+    #include <netdb.h>
+    #include <sys/sockio.h>
+#elif defined(PSUTIL_AIX)
+    #include <netdb.h>
 #endif
 
-#if defined(__sun)
-#include <netdb.h>
+#include "_psutil_common.h"
+
+/*
+ * Check if PID exists. Return values:
+ * 1: exists
+ * 0: does not exist
+ * -1: error (Python exception is set)
+ */
+int
+psutil_pid_exists(long pid) {
+    int ret;
+
+    // No negative PID exists, plus -1 is an alias for sending signal
+    // too all processes except system ones. Not what we want.
+    if (pid < 0)
+        return 0;
+
+    // As per "man 2 kill" PID 0 is an alias for sending the signal to
+    // every process in the process group of the calling process.
+    // Not what we want. Some platforms have PID 0, some do not.
+    // We decide that at runtime.
+    if (pid == 0) {
+#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+        return 0;
+#else
+        return 1;
+#endif
+    }
+
+#if defined(PSUTIL_OSX)
+    ret = kill((pid_t)pid , 0);
+#else
+    ret = kill(pid , 0);
 #endif
 
-#include "_psutil_posix.h"
+    if (ret == 0)
+        return 1;
+    else {
+        if (errno == ESRCH) {
+            // ESRCH == No such process
+            return 0;
+        }
+        else if (errno == EPERM) {
+            // EPERM clearly indicates there's a process to deny
+            // access to.
+            return 1;
+        }
+        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.
+            PyErr_SetFromErrno(PyExc_OSError);
+            return -1;
+        }
+    }
+}
+
+
+/*
+ * Utility used for those syscalls which do not return a meaningful
+ * error that we can translate into an exception which makes sense.
+ * As such, we'll have to guess.
+ * On UNIX, if errno is set, we return that one (OSError).
+ * Else, if PID does not exist we assume the syscall failed because
+ * of that so we raise NoSuchProcess.
+ * 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;
+}
 
 
 /*
  * Given a PID return process priority as a Python integer.
  */
 static PyObject *
-psutil_posix_getpriority(PyObject *self, PyObject *args)
-{
+psutil_posix_getpriority(PyObject *self, PyObject *args) {
     long pid;
     int priority;
     errno = 0;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
+
+#ifdef PSUTIL_OSX
+    priority = getpriority(PRIO_PROCESS, (id_t)pid);
+#else
     priority = getpriority(PRIO_PROCESS, pid);
+#endif
     if (errno != 0)
         return PyErr_SetFromErrno(PyExc_OSError);
     return Py_BuildValue("i", priority);
 }
 
 
 /*
  * Given a PID and a value change process priority.
  */
 static PyObject *
-psutil_posix_setpriority(PyObject *self, PyObject *args)
-{
+psutil_posix_setpriority(PyObject *self, PyObject *args) {
     long pid;
     int priority;
     int retval;
 
     if (! PyArg_ParseTuple(args, "li", &pid, &priority))
         return NULL;
+
+#ifdef PSUTIL_OSX
+    retval = setpriority(PRIO_PROCESS, (id_t)pid, priority);
+#else
     retval = setpriority(PRIO_PROCESS, pid, priority);
+#endif
     if (retval == -1)
         return PyErr_SetFromErrno(PyExc_OSError);
     Py_RETURN_NONE;
 }
 
 
 /*
  * Translate a sockaddr struct into a Python string.
  * Return None if address family is not AF_INET* or AF_PACKET.
  */
 static PyObject *
-psutil_convert_ipaddr(struct sockaddr *addr, int family)
-{
+psutil_convert_ipaddr(struct sockaddr *addr, int family) {
     char buf[NI_MAXHOST];
     int err;
     int addrlen;
-    int n;
+    size_t n;
     size_t len;
     const char *data;
     char *ptr;
 
     if (addr == NULL) {
         Py_INCREF(Py_None);
         return Py_None;
     }
@@ -104,24 +210,23 @@ psutil_convert_ipaddr(struct sockaddr *a
             //return NULL;
             Py_INCREF(Py_None);
             return Py_None;
         }
         else {
             return Py_BuildValue("s", buf);
         }
     }
-#ifdef __linux
+#ifdef PSUTIL_LINUX
     else if (family == AF_PACKET) {
         struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;
         len = lladdr->sll_halen;
         data = (const char *)lladdr->sll_addr;
     }
-#endif
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
     else if (addr->sa_family == AF_LINK) {
         // Note: prior to Python 3.4 socket module does not expose
         // AF_LINK so we'll do.
         struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;
         len = dladdr->sdl_alen;
         data = LLADDR(dladdr);
     }
 #endif
@@ -148,26 +253,26 @@ psutil_convert_ipaddr(struct sockaddr *a
 }
 
 
 /*
  * Return NICs information a-la ifconfig as a list of tuples.
  * TODO: on Solaris we won't get any MAC address.
  */
 static PyObject*
-psutil_net_if_addrs(PyObject* self, PyObject* args)
-{
+psutil_net_if_addrs(PyObject* self, PyObject* args) {
     struct ifaddrs *ifaddr, *ifa;
     int family;
 
     PyObject *py_retlist = PyList_New(0);
     PyObject *py_tuple = NULL;
     PyObject *py_address = NULL;
     PyObject *py_netmask = NULL;
     PyObject *py_broadcast = NULL;
+    PyObject *py_ptp = NULL;
 
     if (py_retlist == NULL)
         return NULL;
     if (getifaddrs(&ifaddr) == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
@@ -180,66 +285,160 @@ psutil_net_if_addrs(PyObject* self, PyOb
         // I've never seen this happen on Linux but I did on FreeBSD.
         if (py_address == Py_None)
             continue;
         if (py_address == NULL)
             goto error;
         py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family);
         if (py_netmask == NULL)
             goto error;
-#ifdef __linux
-        py_broadcast = psutil_convert_ipaddr(ifa->ifa_ifu.ifu_broadaddr, family);
-#else
-        py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family);
-#endif
-        if (py_broadcast == NULL)
+
+        if (ifa->ifa_flags & IFF_BROADCAST) {
+            py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family);
+            Py_INCREF(Py_None);
+            py_ptp = Py_None;
+        }
+        else if (ifa->ifa_flags & IFF_POINTOPOINT) {
+            py_ptp = psutil_convert_ipaddr(ifa->ifa_dstaddr, family);
+            Py_INCREF(Py_None);
+            py_broadcast = Py_None;
+        }
+        else {
+            Py_INCREF(Py_None);
+            Py_INCREF(Py_None);
+            py_broadcast = Py_None;
+            py_ptp = Py_None;
+        }
+
+        if ((py_broadcast == NULL) || (py_ptp == NULL))
             goto error;
         py_tuple = Py_BuildValue(
-            "(siOOO)",
+            "(siOOOO)",
             ifa->ifa_name,
             family,
             py_address,
             py_netmask,
-            py_broadcast
+            py_broadcast,
+            py_ptp
         );
 
         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);
         Py_DECREF(py_broadcast);
+        Py_DECREF(py_ptp);
     }
 
     freeifaddrs(ifaddr);
     return py_retlist;
 
 error:
     if (ifaddr != NULL)
         freeifaddrs(ifaddr);
     Py_DECREF(py_retlist);
     Py_XDECREF(py_tuple);
     Py_XDECREF(py_address);
     Py_XDECREF(py_netmask);
     Py_XDECREF(py_broadcast);
+    Py_XDECREF(py_ptp);
     return NULL;
 }
 
 
 /*
- * net_if_stats() implementation. This is here because it is common
- * to both OSX and FreeBSD and I didn't know where else to put it.
+ * Return NIC MTU. References:
+ * http://www.i-scream.org/libstatgrab/
  */
-#if defined(__FreeBSD__) || defined(__APPLE__)
+static PyObject *
+psutil_net_if_mtu(PyObject *self, PyObject *args) {
+    char *nic_name;
+    int sock = 0;
+    int ret;
+#ifdef PSUTIL_SUNOS10
+    struct lifreq lifr;
+#else
+    struct ifreq ifr;
+#endif
+
+    if (! PyArg_ParseTuple(args, "s", &nic_name))
+        return NULL;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1)
+        goto error;
+
+#ifdef PSUTIL_SUNOS10
+    strncpy(lifr.lifr_name, nic_name, sizeof(lifr.lifr_name));
+    ret = ioctl(sock, SIOCGIFMTU, &lifr);
+#else
+    strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+    ret = ioctl(sock, SIOCGIFMTU, &ifr);
+#endif
+    if (ret == -1)
+        goto error;
+    close(sock);
+
+#ifdef PSUTIL_SUNOS10
+    return Py_BuildValue("i", lifr.lifr_mtu);
+#else
+    return Py_BuildValue("i", ifr.ifr_mtu);
+#endif
+
+error:
+    if (sock != 0)
+        close(sock);
+    return PyErr_SetFromErrno(PyExc_OSError);
+}
+
 
-#include <sys/sockio.h>
-#include <net/if_media.h>
-#include <net/if.h>
+/*
+ * Inspect NIC flags, returns a bool indicating whether the NIC is
+ * running. References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_flags(PyObject *self, PyObject *args) {
+    char *nic_name;
+    int sock = 0;
+    int ret;
+    struct ifreq ifr;
+
+    if (! PyArg_ParseTuple(args, "s", &nic_name))
+        return NULL;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1)
+        goto error;
+
+    strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+    if (ret == -1)
+        goto error;
+
+    close(sock);
+    if ((ifr.ifr_flags & IFF_UP) != 0)
+        return Py_BuildValue("O", Py_True);
+    else
+        return Py_BuildValue("O", Py_False);
+
+error:
+    if (sock != 0)
+        close(sock);
+    return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+/*
+ * net_if_stats() OSX/BSD implementation.
+ */
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
 
 int psutil_get_nic_speed(int ifm_active) {
     // Determine NIC speed. Taken from:
     // http://www.i-scream.org/libstatgrab/
     // Assuming only ETHER devices
     switch(IFM_TYPE(ifm_active)) {
         case IFM_ETHER:
             switch(IFM_SUBTYPE(ifm_active)) {
@@ -262,18 +461,18 @@ int psutil_get_nic_speed(int ifm_active)
                 case(IFM_100_T4):  // 100BaseT4 - 4 pair cat 3
                 case(IFM_100_VG):  // 100VG-AnyLAN
                 case(IFM_100_T2):  // 100BaseT2
                     return 100;
                 // 1000 Mbit
                 case(IFM_1000_SX):  // 1000BaseSX - multi-mode fiber
                 case(IFM_1000_LX):  // 1000baseLX - single-mode fiber
                 case(IFM_1000_CX):  // 1000baseCX - 150ohm STP
-#if defined(IFM_1000_TX) && !defined(OPENBSD)
-                // FreeBSD 4 and others (but NOT OpenBSD)?
+#if defined(IFM_1000_TX) && !defined(PSUTIL_OPENBSD)
+                // FreeBSD 4 and others (but NOT OpenBSD) -> #define IFM_1000_T in net/if_media.h
                 case(IFM_1000_TX):
 #endif
 #ifdef IFM_1000_FX
                 case(IFM_1000_FX):
 #endif
 #ifdef IFM_1000_T
                 case(IFM_1000_T):
 #endif
@@ -373,53 +572,33 @@ int psutil_get_nic_speed(int ifm_active)
 
 
 /*
  * Return stats about a particular network interface.
  * References:
  * http://www.i-scream.org/libstatgrab/
  */
 static PyObject *
-psutil_net_if_stats(PyObject *self, PyObject *args)
-{
+psutil_net_if_duplex_speed(PyObject *self, PyObject *args) {
     char *nic_name;
     int sock = 0;
     int ret;
     int duplex;
     int speed;
-    int mtu;
     struct ifreq ifr;
     struct ifmediareq ifmed;
 
-    PyObject *py_is_up = NULL;
-
     if (! PyArg_ParseTuple(args, "s", &nic_name))
         return NULL;
 
     sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock == -1)
         goto error;
     strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
 
-    // is up?
-    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
-    if (ret == -1)
-        goto error;
-    if ((ifr.ifr_flags & IFF_UP) != 0)
-        py_is_up = Py_True;
-    else
-        py_is_up = Py_False;
-    Py_INCREF(py_is_up);
-
-    // MTU
-    ret = ioctl(sock, SIOCGIFMTU, &ifr);
-    if (ret == -1)
-        goto error;
-    mtu = ifr.ifr_mtu;
-
     // speed / duplex
     memset(&ifmed, 0, sizeof(struct ifmediareq));
     strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name));
     ret = ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed);
     if (ret == -1) {
         speed = 0;
         duplex = 0;
     }
@@ -429,44 +608,43 @@ psutil_net_if_stats(PyObject *self, PyOb
             duplex = 2;
         else if ((ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active)
             duplex = 1;
         else
             duplex = 0;
     }
 
     close(sock);
-    Py_DECREF(py_is_up);
-
-    return Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu);
+    return Py_BuildValue("[ii]", duplex, speed);
 
 error:
-    Py_XDECREF(py_is_up);
     if (sock != 0)
         close(sock);
-    PyErr_SetFromErrno(PyExc_OSError);
-    return NULL;
+    return PyErr_SetFromErrno(PyExc_OSError);
 }
-#endif  // net_if_stats() implementation
+#endif  // net_if_stats() OSX/BSD implementation
 
 
 /*
  * define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     {"getpriority", psutil_posix_getpriority, METH_VARARGS,
      "Return process priority"},
     {"setpriority", psutil_posix_setpriority, METH_VARARGS,
      "Set process priority"},
     {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
      "Retrieve NICs information"},
-#if defined(__FreeBSD__) || defined(__APPLE__)
-    {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+    {"net_if_mtu", psutil_net_if_mtu, METH_VARARGS,
+     "Retrieve NIC MTU"},
+    {"net_if_flags", psutil_net_if_flags, METH_VARARGS,
+     "Retrieve NIC flags"},
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+    {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
      "Return NIC stats."},
 #endif
     {NULL, NULL, 0, NULL}
 };
 
 struct module_state {
     PyObject *error;
 };
@@ -480,16 +658,17 @@ struct module_state {
 #if PY_MAJOR_VERSION >= 3
 
 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,
@@ -514,17 +693,17 @@ void init_psutil_posix(void)
 #endif
 {
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
     PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
 #endif
 
-#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__sun)
+#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 PY_MAJOR_VERSION >= 3
     return module;
 #endif
--- a/third_party/python/psutil/psutil/_psutil_posix.h
+++ b/third_party/python/psutil/psutil/_psutil_posix.h
@@ -1,15 +1,8 @@
 /*
  * 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 <Python.h>
-
-static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args);
-static PyObject* psutil_posix_getpriority(PyObject* self, PyObject* args);
-static PyObject* psutil_posix_setpriority(PyObject* self, PyObject* args);
-
-#if defined(__FreeBSD__) || defined(__APPLE__)
-static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
-#endif
+int psutil_pid_exists(long pid);
+void psutil_raise_for_pid(long pid, char *msg);
--- a/third_party/python/psutil/psutil/_psutil_sunos.c
+++ b/third_party/python/psutil/psutil/_psutil_sunos.c
@@ -4,28 +4,33 @@
  * found in the LICENSE file.
  *
  * Functions specific to Sun OS Solaris platforms.
  *
  * Thanks to Justin Venus who originally wrote a consistent part of
  * this in Cython which I later on translated in C.
  */
 
+/* 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 <Python.h>
 
-// fix for "Cannot use procfs in the large file compilation environment"
-// error, see:
-// http://sourceware.org/ml/gdb-patches/2010-11/msg00336.html
-#undef _FILE_OFFSET_BITS
-#define _STRUCTURED_PROC 1
-
-// fix compilation issue on SunOS 5.10, see:
-// https://github.com/giampaolo/psutil/issues/421
-#define NEW_MIB_COMPLIANT
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#  undef _FILE_OFFSET_BITS
+#  undef _LARGEFILE64_SOURCE
+#endif
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/proc.h>
 #include <sys/swap.h>
 #include <sys/sysinfo.h>
 #include <sys/mntent.h>  // for MNTTAB
 #include <sys/mnttab.h>
@@ -37,225 +42,411 @@
 #include <kstat.h>
 #include <sys/ioctl.h>
 #include <sys/tihdr.h>
 #include <stropts.h>
 #include <inet/tcp.h>
 #include <arpa/inet.h>
 #include <net/if.h>
 
-#include "_psutil_sunos.h"
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
 
+#include "arch/solaris/environ.h"
 
-#define TV2DOUBLE(t)   (((t).tv_nsec * 0.000000001) + (t).tv_sec)
+#define PSUTIL_TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)
+
 
 /*
  * Read a file content and fills a C structure with it.
  */
 int
-psutil_file_to_struct(char *path, void *fstruct, size_t size)
-{
+psutil_file_to_struct(char *path, void *fstruct, size_t size) {
     int fd;
     size_t nbytes;
     fd = open(path, O_RDONLY);
     if (fd == -1) {
         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
         return 0;
     }
     nbytes = read(fd, fstruct, size);
-    if (nbytes <= 0) {
+    if (nbytes == -1) {
         close(fd);
         PyErr_SetFromErrno(PyExc_OSError);
         return 0;
     }
     if (nbytes != size) {
         close(fd);
-        PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
+        PyErr_SetString(
+            PyExc_RuntimeError, "read() file structure size mismatch");
         return 0;
     }
     close(fd);
     return nbytes;
 }
 
 
 /*
  * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
  * as a Python tuple.
  */
 static PyObject *
-psutil_proc_basic_info(PyObject *self, PyObject *args)
-{
+psutil_proc_basic_info(PyObject *self, PyObject *args) {
     int pid;
-    char path[100];
+    char path[1000];
     psinfo_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/psinfo", pid);
+
+    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
-    return Py_BuildValue("ikkdiiik",
-                         info.pr_ppid,              // parent pid
-                         info.pr_rssize,            // rss
-                         info.pr_size,              // vms
-                         TV2DOUBLE(info.pr_start),  // create time
-                         info.pr_lwp.pr_nice,       // nice
-                         info.pr_nlwp,              // no. of threads
-                         info.pr_lwp.pr_state,      // status code
-                         info.pr_ttydev             // tty nr
-                        );
+    return Py_BuildValue(
+        "ikkdiiik",
+        info.pr_ppid,              // parent pid
+        info.pr_rssize,            // rss
+        info.pr_size,              // vms
+        PSUTIL_TV2DOUBLE(info.pr_start),  // create time
+        // XXX - niceness is wrong (20 instead of 0), see:
+        // https://github.com/giampaolo/psutil/issues/1082
+        info.pr_lwp.pr_nice,       // nice
+        info.pr_nlwp,              // no. of threads
+        info.pr_lwp.pr_state,      // status code
+        info.pr_ttydev             // tty nr
+        );
 }
 
 
 /*
  * Return process name and args as a Python tuple.
  */
 static PyObject *
-psutil_proc_name_and_args(PyObject *self, PyObject *args)
-{
+psutil_proc_name_and_args(PyObject *self, PyObject *args) {
     int pid;
-    char path[100];
+    char path[1000];
     psinfo_t info;
+    const char *procfs_path;
+    PyObject *py_name = NULL;
+    PyObject *py_args = NULL;
+    PyObject *py_retlist = NULL;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/psinfo", pid);
+    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
-    return Py_BuildValue("ss", info.pr_fname, info.pr_psargs);
+
+    py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
+    if (!py_name)
+        goto error;
+    py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
+    if (!py_args)
+        goto error;
+    py_retlist = Py_BuildValue("OO", py_name, py_args);
+    if (!py_retlist)
+        goto error;
+    Py_DECREF(py_name);
+    Py_DECREF(py_args);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_name);
+    Py_XDECREF(py_args);
+    Py_XDECREF(py_retlist);
+    return NULL;
+}
+
+
+/*
+ * Return process environ block.
+ */
+static PyObject *
+psutil_proc_environ(PyObject *self, PyObject *args) {
+    int pid;
+    char path[1000];
+    psinfo_t info;
+    const char *procfs_path;
+    char **env = NULL;
+    ssize_t env_count = -1;
+    char *dm;
+    int i = 0;
+    PyObject *py_envname = NULL;
+    PyObject *py_envval = NULL;
+    PyObject *py_retdict = PyDict_New();
+
+    if (! py_retdict)
+        return PyErr_NoMemory();
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+
+    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+        goto error;
+
+    if (! info.pr_envp) {
+        AccessDenied("");
+        goto error;
+    }
+
+    env = psutil_read_raw_env(info, procfs_path, &env_count);
+    if (! env && env_count != 0)
+        goto error;
+
+    for (i=0; i<env_count; i++) {
+        if (! env[i])
+            break;
+
+        dm = strchr(env[i], '=');
+        if (! dm)
+            continue;
+
+        *dm = '\0';
+
+        py_envname = PyUnicode_DecodeFSDefault(env[i]);
+        if (! py_envname)
+            goto error;
+
+        py_envval = PyUnicode_DecodeFSDefault(dm+1);
+        if (! py_envname)
+            goto error;
+
+        if (PyDict_SetItem(py_retdict, py_envname, py_envval) < 0)
+            goto error;
+
+        Py_DECREF(py_envname);
+        Py_DECREF(py_envval);
+    }
+
+    psutil_free_cstrings_array(env, env_count);
+    return py_retdict;
+
+ error:
+    if (env && env_count >= 0)
+        psutil_free_cstrings_array(env, env_count);
+
+    Py_XDECREF(py_envname);
+    Py_XDECREF(py_envval);
+    Py_XDECREF(py_retdict);
+    return NULL;
 }
 
 
 /*
  * Return process user and system CPU times as a Python tuple.
  */
 static PyObject *
-psutil_proc_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
     int pid;
-    char path[100];
+    char path[1000];
     pstatus_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/status", pid);
+    sprintf(path, "%s/%i/status", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
     // results are more precise than os.times()
-    return Py_BuildValue("dd",
-                         TV2DOUBLE(info.pr_utime),
-                         TV2DOUBLE(info.pr_stime));
+    return Py_BuildValue(
+        "(dddd)",
+         PSUTIL_TV2DOUBLE(info.pr_utime),
+         PSUTIL_TV2DOUBLE(info.pr_stime),
+         PSUTIL_TV2DOUBLE(info.pr_cutime),
+         PSUTIL_TV2DOUBLE(info.pr_cstime)
+    );
+}
+
+
+/*
+ * Return what CPU the process is running on.
+ */
+static PyObject *
+psutil_proc_cpu_num(PyObject *self, PyObject *args) {
+    int fd = NULL;
+    int pid;
+    char path[1000];
+    struct prheader header;
+    struct lwpsinfo *lwp;
+    char *lpsinfo = NULL;
+    char *ptr = NULL;
+    int nent;
+    int size;
+    int proc_num;
+    size_t nbytes;
+    const char *procfs_path;
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+
+    sprintf(path, "%s/%i/lpsinfo", procfs_path, pid);
+    fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+        return NULL;
+    }
+
+    // read header
+    nbytes = pread(fd, &header, sizeof(header), 0);
+    if (nbytes == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (nbytes != sizeof(header)) {
+        PyErr_SetString(
+            PyExc_RuntimeError, "read() file structure size mismatch");
+        goto error;
+    }
+
+    // malloc
+    nent = header.pr_nent;
+    size = header.pr_entsize * nent;
+    ptr = lpsinfo = malloc(size);
+    if (lpsinfo == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    // read the rest
+    nbytes = pread(fd, lpsinfo, size, sizeof(header));
+    if (nbytes == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (nbytes != size) {
+        PyErr_SetString(
+            PyExc_RuntimeError, "read() file structure size mismatch");
+        goto error;
+    }
+
+    // done
+    lwp = (lwpsinfo_t *)ptr;
+    proc_num = lwp->pr_onpro;
+    close(fd);
+    free(ptr);
+    free(lpsinfo);
+    return Py_BuildValue("i", proc_num);
+
+error:
+    if (fd != -1)
+        close(fd);
+    if (ptr != NULL)
+        free(ptr);
+    if (lpsinfo != NULL)
+        free(lpsinfo);
+    return NULL;
 }
 
 
 /*
  * Return process uids/gids as a Python tuple.
  */
 static PyObject *
-psutil_proc_cred(PyObject *self, PyObject *args)
-{
+psutil_proc_cred(PyObject *self, PyObject *args) {
     int pid;
-    char path[100];
+    char path[1000];
     prcred_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/cred", pid);
+    sprintf(path, "%s/%i/cred", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
     return Py_BuildValue("iiiiii",
                          info.pr_ruid, info.pr_euid, info.pr_suid,
                          info.pr_rgid, info.pr_egid, info.pr_sgid);
 }
 
 
 /*
- * Return process uids/gids as a Python tuple.
+ * Return process voluntary and involuntary context switches as a Python tuple.
  */
 static PyObject *
-psutil_proc_num_ctx_switches(PyObject *self, PyObject *args)
-{
+psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
     int pid;
-    char path[100];
+    char path[1000];
     prusage_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/usage", pid);
+    sprintf(path, "%s/%i/usage", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
     return Py_BuildValue("kk", info.pr_vctx, info.pr_ictx);
 }
 
 
 /*
  * Process IO counters.
  *
  * Commented out and left here as a reminder.  Apparently we cannot
  * retrieve process IO stats because:
  * - 'pr_ioch' is a sum of chars read and written, with no distinction
  * - 'pr_inblk' and 'pr_oublk', which should be the number of bytes
  *    read and written, hardly increase and according to:
- *    http://www.brendangregg.com/Perf/paper_diskubyp1.pdf
+ *    http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf
  *    ...they should be meaningless anyway.
  *
 static PyObject*
-proc_io_counters(PyObject* self, PyObject* args)
-{
+proc_io_counters(PyObject* self, PyObject* args) {
     int pid;
-    char path[100];
+    char path[1000];
     prusage_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/usage", pid);
+    sprintf(path, "%s/%i/usage", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
 
     // On Solaris we only have 'pr_ioch' which accounts for bytes read
     // *and* written.
     // 'pr_inblk' and 'pr_oublk' should be expressed in blocks of
     // 8KB according to:
-    // http://www.brendangregg.com/Perf/paper_diskubyp1.pdf  (pag. 8)
+    // http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf (pag. 8)
     return Py_BuildValue("kkkk",
                          info.pr_ioch,
                          info.pr_ioch,
                          info.pr_inblk,
                          info.pr_oublk);
 }
  */
 
 
 /*
  * Return information about a given process thread.
  */
 static PyObject *
-psutil_proc_query_thread(PyObject *self, PyObject *args)
-{
+psutil_proc_query_thread(PyObject *self, PyObject *args) {
     int pid, tid;
-    char path[100];
+    char path[1000];
     lwpstatus_t info;
+    const char *procfs_path;
 
-    if (! PyArg_ParseTuple(args, "ii", &pid, &tid))
+    if (! PyArg_ParseTuple(args, "iis", &pid, &tid, &procfs_path))
         return NULL;
-    sprintf(path, "/proc/%i/lwp/%i/lwpstatus", pid, tid);
+    sprintf(path, "%s/%i/lwp/%i/lwpstatus", procfs_path, pid, tid);
     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
         return NULL;
     return Py_BuildValue("dd",
-                         TV2DOUBLE(info.pr_utime),
-                         TV2DOUBLE(info.pr_stime));
+                         PSUTIL_TV2DOUBLE(info.pr_utime),
+                         PSUTIL_TV2DOUBLE(info.pr_stime));
 }
 
 
 /*
  * Return information about system virtual memory.
  */
 static PyObject *
-psutil_swap_mem(PyObject *self, PyObject *args)
-{
+psutil_swap_mem(PyObject *self, PyObject *args) {
 // XXX (arghhh!)
 // total/free swap mem: commented out as for some reason I can't
 // manage to get the same results shown by "swap -l", despite the
 // code below is exactly the same as:
 // http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/
 //    cmd/swap/swap.c
 // We're going to parse "swap -l" output from Python (sigh!)
 
@@ -340,110 +531,138 @@ psutil_swap_mem(PyObject *self, PyObject
     return Py_BuildValue("(II)", sin, sout);
 }
 
 
 /*
  * Return users currently connected on the system.
  */
 static PyObject *
-psutil_users(PyObject *self, PyObject *args)
-{
+psutil_users(PyObject *self, PyObject *args) {
     struct utmpx *ut;
-    PyObject *ret_list = PyList_New(0);
-    PyObject *tuple = NULL;
-    PyObject *user_proc = NULL;
+    PyObject *py_tuple = NULL;
+    PyObject *py_username = NULL;
+    PyObject *py_tty = NULL;
+    PyObject *py_hostname = NULL;
+    PyObject *py_user_proc = NULL;
+    PyObject *py_retlist = PyList_New(0);
 
-    if (ret_list == NULL)
+    if (py_retlist == NULL)
         return NULL;
 
+    setutxent();
     while (NULL != (ut = getutxent())) {
         if (ut->ut_type == USER_PROCESS)
-            user_proc = Py_True;
+            py_user_proc = Py_True;
         else
-            user_proc = Py_False;
-        tuple = Py_BuildValue(
-            "(sssfO)",
-            ut->ut_user,              // username
-            ut->ut_line,              // tty
-            ut->ut_host,              // hostname
+            py_user_proc = Py_False;
+        py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfOi)",
+            py_username,              // username
+            py_tty,                   // tty
+            py_hostname,              // hostname
             (float)ut->ut_tv.tv_sec,  // tstamp
-            user_proc);               // (bool) user process
-        if (tuple == NULL)
+            py_user_proc,             // (bool) user process
+            ut->ut_pid                // process id
+        );
+        if (py_tuple == NULL)
             goto error;
-        if (PyList_Append(ret_list, tuple))
+        if (PyList_Append(py_retlist, py_tuple))
             goto error;
-        Py_DECREF(tuple);
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
     }
-    endutent();
+    endutxent();
 
-    return ret_list;
+    return py_retlist;
 
 error:
-    Py_XDECREF(tuple);
-    Py_DECREF(ret_list);
-    if (ut != NULL)
-        endutent();
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tty);
+    Py_XDECREF(py_hostname);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    endutxent();
     return NULL;
 }
 
 
 /*
  * Return disk mounted partitions as a list of tuples including device,
  * mount point and filesystem type.
  */
 static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args)
-{
+psutil_disk_partitions(PyObject *self, PyObject *args) {
     FILE *file;
     struct mnttab mt;
+    PyObject *py_dev = NULL;
+    PyObject *py_mountp = NULL;
+    PyObject *py_tuple = NULL;
     PyObject *py_retlist = PyList_New(0);
-    PyObject *py_tuple = NULL;
 
     if (py_retlist == NULL)
         return NULL;
 
     file = fopen(MNTTAB, "rb");
     if (file == NULL) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     while (getmntent(file, &mt) == 0) {
+        py_dev = PyUnicode_DecodeFSDefault(mt.mnt_special);
+        if (! py_dev)
+            goto error;
+        py_mountp = PyUnicode_DecodeFSDefault(mt.mnt_mountp);
+        if (! py_mountp)
+            goto error;
         py_tuple = Py_BuildValue(
-            "(ssss)",
-            mt.mnt_special,   // device
-            mt.mnt_mountp,    // mount point
+            "(OOss)",
+            py_dev,           // device
+            py_mountp,        // mount point
             mt.mnt_fstype,    // fs type
             mt.mnt_mntopts);  // options
         if (py_tuple == NULL)
             goto error;
         if (PyList_Append(py_retlist, py_tuple))
             goto error;
+        Py_DECREF(py_dev);
+        Py_DECREF(py_mountp);
         Py_DECREF(py_tuple);
-
     }
     fclose(file);
     return py_retlist;
 
 error:
+    Py_XDECREF(py_dev);
+    Py_XDECREF(py_mountp);
     Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     if (file != NULL)
         fclose(file);
     return NULL;
 }
 
 
 /*
  * Return system-wide CPU times.
  */
 static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
     kstat_ctl_t *kc;
     kstat_t *ksp;
     cpu_stat_t cs;
     PyObject *py_retlist = PyList_New(0);
     PyObject *py_cputime = NULL;
 
     if (py_retlist == NULL)
         return NULL;
@@ -485,18 +704,17 @@ error:
     return NULL;
 }
 
 
 /*
  * Return disk IO statistics.
  */
 static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args)
-{
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
     kstat_ctl_t *kc;
     kstat_t *ksp;
     kstat_io_t kio;
     PyObject *py_retdict = PyDict_New();
     PyObject *py_disk_info = NULL;
 
     if (py_retdict == NULL)
         return NULL;
@@ -544,46 +762,47 @@ error:
     return NULL;
 }
 
 
 /*
  * Return process memory mappings.
  */
 static PyObject *
-psutil_proc_memory_maps(PyObject *self, PyObject *args)
-{
+psutil_proc_memory_maps(PyObject *self, PyObject *args) {
     int pid;
     int fd = -1;
-    char path[100];
+    char path[1000];
     char perms[10];
     char *name;
     struct stat st;
     pstatus_t status;
 
     prxmap_t *xmap = NULL, *p;
     off_t size;
     size_t nread;
     int nmap;
     uintptr_t pr_addr_sz;
     uintptr_t stk_base_sz, brk_base_sz;
+    const char *procfs_path;
 
-    PyObject *pytuple = NULL;
+    PyObject *py_tuple = NULL;
+    PyObject *py_path = NULL;
     PyObject *py_retlist = PyList_New(0);
 
     if (py_retlist == NULL)
         return NULL;
-    if (! PyArg_ParseTuple(args, "i", &pid))
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
         goto error;
 
-    sprintf(path, "/proc/%i/status", pid);
+    sprintf(path, "%s/%i/status", procfs_path, pid);
     if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
         goto error;
 
-    sprintf(path, "/proc/%i/xmap", pid);
+    sprintf(path, "%s/%i/xmap", procfs_path, pid);
     if (stat(path, &st) == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     size = st.st_size;
 
     fd = open(path, O_RDONLY);
@@ -608,22 +827,20 @@ psutil_proc_memory_maps(PyObject *self, 
             p += 1;
             continue;
         }
 
         perms[0] = '\0';
         pr_addr_sz = p->pr_vaddr + p->pr_size;
 
         // perms
-        sprintf(perms, "%c%c%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
+        sprintf(perms, "%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
                 p->pr_mflags & MA_WRITE ? 'w' : '-',
                 p->pr_mflags & MA_EXEC ? 'x' : '-',
-                p->pr_mflags & MA_SHARED ? 's' : '-',
-                p->pr_mflags & MA_NORESERVE ? 'R' : '-',
-                p->pr_mflags & MA_RESERVED1 ? '*' : ' ');
+                p->pr_mflags & MA_SHARED ? 's' : '-');
 
         // name
         if (strlen(p->pr_mapname) > 0) {
             name = p->pr_mapname;
         }
         else {
             if ((p->pr_mflags & MA_ISM) || (p->pr_mflags & MA_SHM)) {
                 name = "[shmid]";
@@ -642,248 +859,260 @@ psutil_proc_memory_maps(PyObject *self, 
                     name = "[heap]";
                 }
                 else {
                     name = "[anon]";
                 }
             }
         }
 
-        pytuple = Py_BuildValue("iisslll",
-                                p->pr_vaddr,
-                                pr_addr_sz,
-                                perms,
-                                name,
-                                (long)p->pr_rss * p->pr_pagesize,
-                                (long)p->pr_anon * p->pr_pagesize,
-                                (long)p->pr_locked * p->pr_pagesize);
-        if (!pytuple)
+        py_path = PyUnicode_DecodeFSDefault(name);
+        if (! py_path)
             goto error;
-        if (PyList_Append(py_retlist, pytuple))
+        py_tuple = Py_BuildValue(
+            "kksOkkk",
+            (unsigned long)p->pr_vaddr,
+            (unsigned long)pr_addr_sz,
+            perms,
+            py_path,
+            (unsigned long)p->pr_rss * p->pr_pagesize,
+            (unsigned long)p->pr_anon * p->pr_pagesize,
+            (unsigned long)p->pr_locked * p->pr_pagesize);
+        if (!py_tuple)
             goto error;
-        Py_DECREF(pytuple);
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_path);
+        Py_DECREF(py_tuple);
 
         // increment pointer
         p += 1;
     }
 
     close(fd);
     free(xmap);
     return py_retlist;
 
 error:
     if (fd != -1)
         close(fd);
-    Py_XDECREF(pytuple);
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_path);
     Py_DECREF(py_retlist);
     if (xmap != NULL)
         free(xmap);
     return NULL;
 }
 
 
 /*
  * Return a list of tuples for network I/O statistics.
  */
 static PyObject *
-psutil_net_io_counters(PyObject *self, PyObject *args)
-{
+psutil_net_io_counters(PyObject *self, PyObject *args) {
     kstat_ctl_t    *kc = NULL;
     kstat_t *ksp;
     kstat_named_t *rbytes, *wbytes, *rpkts, *wpkts, *ierrs, *oerrs;
+    int ret;
+    int sock = -1;
+    struct lifreq ifr;
 
     PyObject *py_retdict = PyDict_New();
     PyObject *py_ifc_info = NULL;
 
     if (py_retdict == NULL)
         return NULL;
     kc = kstat_open();
     if (kc == NULL)
         goto error;
 
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
     ksp = kc->kc_chain;
     while (ksp != NULL) {
         if (ksp->ks_type != KSTAT_TYPE_NAMED)
             goto next;
         if (strcmp(ksp->ks_class, "net") != 0)
             goto next;
-        /*
-        // XXX "lo" (localhost) interface makes kstat_data_lookup() fail
-        // (maybe because "ifconfig -a" says it's a virtual interface?).
-        if ((strcmp(ksp->ks_module, "link") != 0) &&
-            (strcmp(ksp->ks_module, "lo") != 0)) {
-            goto skip;
-        */
-        if ((strcmp(ksp->ks_module, "link") != 0))
+        // skip 'lo' (localhost) because it doesn't have the statistics we need
+        // and it makes kstat_data_lookup() fail
+        if (strcmp(ksp->ks_module, "lo") == 0)
+            goto next;
+
+        // check if this is a network interface by sending a ioctl
+        strncpy(ifr.lifr_name, ksp->ks_name, sizeof(ifr.lifr_name));
+        ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);
+        if (ret == -1)
             goto next;
 
         if (kstat_read(kc, ksp, NULL) == -1) {
             errno = 0;
-            continue;
+            goto next;
         }
 
         rbytes = (kstat_named_t *)kstat_data_lookup(ksp, "rbytes");
         wbytes = (kstat_named_t *)kstat_data_lookup(ksp, "obytes");
         rpkts = (kstat_named_t *)kstat_data_lookup(ksp, "ipackets");
         wpkts = (kstat_named_t *)kstat_data_lookup(ksp, "opackets");
         ierrs = (kstat_named_t *)kstat_data_lookup(ksp, "ierrors");
         oerrs = (kstat_named_t *)kstat_data_lookup(ksp, "oerrors");
 
         if ((rbytes == NULL) || (wbytes == NULL) || (rpkts == NULL) ||
                 (wpkts == NULL) || (ierrs == NULL) || (oerrs == NULL))
         {
             PyErr_SetString(PyExc_RuntimeError, "kstat_data_lookup() failed");
             goto error;
         }
 
-#if defined(_INT64_TYPE)
-        py_ifc_info = Py_BuildValue("(KKKKkkii)",
-                                    wbytes->value.ui64,
-                                    rbytes->value.ui64,
-                                    wpkts->value.ui64,
-                                    rpkts->value.ui64,
-                                    ierrs->value.ui32,
-                                    oerrs->value.ui32,
-#else
-        py_ifc_info = Py_BuildValue("(kkkkkkii)",
-                                    wbytes->value.ui32,
-                                    rbytes->value.ui32,
-                                    wpkts->value.ui32,
-                                    rpkts->value.ui32,
-                                    ierrs->value.ui32,
-                                    oerrs->value.ui32,
-#endif
-                                    0,  // dropin not supported
-                                    0   // dropout not supported
-                                   );
+        if (rbytes->data_type == KSTAT_DATA_UINT64)
+        {
+            py_ifc_info = Py_BuildValue("(KKKKIIii)",
+                                        wbytes->value.ui64,
+                                        rbytes->value.ui64,
+                                        wpkts->value.ui64,
+                                        rpkts->value.ui64,
+                                        ierrs->value.ui32,
+                                        oerrs->value.ui32,
+                                        0,  // dropin not supported
+                                        0   // dropout not supported
+                                       );
+        }
+        else
+        {
+            py_ifc_info = Py_BuildValue("(IIIIIIii)",
+                                        wbytes->value.ui32,
+                                        rbytes->value.ui32,
+                                        wpkts->value.ui32,
+                                        rpkts->value.ui32,
+                                        ierrs->value.ui32,
+                                        oerrs->value.ui32,
+                                        0,  // dropin not supported
+                                        0   // dropout not supported
+                                       );
+        }
         if (!py_ifc_info)
             goto error;
         if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))
             goto error;
         Py_DECREF(py_ifc_info);
         goto next;
 
 next:
         ksp = ksp->ks_next;
     }
 
     kstat_close(kc);
+    close(sock);
     return py_retdict;
 
 error:
     Py_XDECREF(py_ifc_info);
     Py_DECREF(py_retdict);
     if (kc != NULL)
         kstat_close(kc);
+    if (sock != -1) {
+        close(sock);
+    }
     return NULL;
 }
 
 
-#ifndef EXPER_IP_AND_ALL_IRES
-#define EXPER_IP_AND_ALL_IRES   (1024+4)
-#endif
-
-// a signaler for connections without an actual status
-static int PSUTIL_CONN_NONE = 128;
-
 /*
  * Return TCP and UDP connections opened by process.
  * UNIX sockets are excluded.
  *
  * Thanks to:
  * https://github.com/DavidGriffith/finx/blob/master/
  *     nxsensor-3.5.0-1/src/sysdeps/solaris.c
  * ...and:
  * https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/cmd/
  *     cmd-inet/usr.bin/netstat/netstat.c
  */
 static PyObject *
-psutil_net_connections(PyObject *self, PyObject *args)
-{
+psutil_net_connections(PyObject *self, PyObject *args) {
     long pid;
     int sd = 0;
     mib2_tcpConnEntry_t *tp = NULL;
     mib2_udpEntry_t     *ude;
 #if defined(AF_INET6)
     mib2_tcp6ConnEntry_t *tp6;
     mib2_udp6Entry_t     *ude6;
 #endif
     char buf[512];
     int i, flags, getcode, num_ent, state;
-    char lip[200], rip[200];
+    char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];
     int lport, rport;
     int processed_pid;
     int databuf_init = 0;
     struct strbuf ctlbuf, databuf;
     struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
     struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
     struct T_error_ack   *tea = (struct T_error_ack *)buf;
     struct opthdr        *mibhdr;
 
     PyObject *py_retlist = PyList_New(0);
     PyObject *py_tuple = NULL;
     PyObject *py_laddr = NULL;
     PyObject *py_raddr = NULL;
-    PyObject *af_filter = NULL;
-    PyObject *type_filter = NULL;
 
     if (py_retlist == NULL)
         return NULL;
-    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter))
+    if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
-    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
-        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
-        goto error;
-    }
 
     sd = open("/dev/arp", O_RDWR);
     if (sd == -1) {
         PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/dev/arp");
         goto error;
     }
 
-    /*
-    XXX - These 2 are used in ifconfig.c but they seem unnecessary
-    ret = ioctl(sd, I_PUSH, "tcp");
+    int ret = ioctl(sd, I_PUSH, "tcp");
     if (ret == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
     ret = ioctl(sd, I_PUSH, "udp");
     if (ret == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
-    */
-
+    //
     // OK, this mess is basically copied and pasted from nxsensor project
     // which copied and pasted it from netstat source code, mibget()
     // function.  Also see:
     // http://stackoverflow.com/questions/8723598/
     tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
     tor->OPT_offset = sizeof (struct T_optmgmt_req);
     tor->OPT_length = sizeof (struct opthdr);
     tor->MGMT_flags = T_CURRENT;
     mibhdr = (struct opthdr *)&tor[1];
-    mibhdr->level = EXPER_IP_AND_ALL_IRES;
+    mibhdr->level = MIB2_IP;
     mibhdr->name  = 0;
+
+#ifdef NEW_MIB_COMPLIANT
+    mibhdr->len   = 1;
+#else
     mibhdr->len   = 0;
+#endif
 
     ctlbuf.buf = buf;
     ctlbuf.len = tor->OPT_offset + tor->OPT_length;
     flags = 0;  // request to be sent in non-priority
 
     if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     mibhdr = (struct opthdr *)&toa[1];
     ctlbuf.maxlen = sizeof (buf);
-
     for (;;) {
         flags = 0;
         getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);
 
         if (getcode != MOREDATA ||
                 ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
                 toa->PRIM_type != T_OPTMGMT_ACK ||
                 toa->MGMT_flags != T_SUCCESS)
@@ -921,17 +1150,21 @@ psutil_net_connections(PyObject *self, P
             goto error;
         }
 
         // TCPv4
         if (mibhdr->level == MIB2_TCP && mibhdr->name == MIB2_TCP_13) {
             tp = (mib2_tcpConnEntry_t *)databuf.buf;
             num_ent = mibhdr->len / sizeof(mib2_tcpConnEntry_t);
             for (i = 0; i < num_ent; i++, tp++) {
+#ifdef NEW_MIB_COMPLIANT
                 processed_pid = tp->tcpConnCreationProcess;
+#else
+                processed_pid = 0;
+#endif
                 if (pid != -1 && processed_pid != pid)
                     continue;
                 // construct local/remote addresses
                 inet_ntop(AF_INET, &tp->tcpConnLocalAddress, lip, sizeof(lip));
                 inet_ntop(AF_INET, &tp->tcpConnRemAddress, rip, sizeof(rip));
                 lport = tp->tcpConnLocalPort;
                 rport = tp->tcpConnRemPort;
 
@@ -962,17 +1195,21 @@ psutil_net_connections(PyObject *self, P
 #if defined(AF_INET6)
         // TCPv6
         else if (mibhdr->level == MIB2_TCP6 && mibhdr->name == MIB2_TCP6_CONN)
         {
             tp6 = (mib2_tcp6ConnEntry_t *)databuf.buf;
             num_ent = mibhdr->len / sizeof(mib2_tcp6ConnEntry_t);
 
             for (i = 0; i < num_ent; i++, tp6++) {
+#ifdef NEW_MIB_COMPLIANT
                 processed_pid = tp6->tcp6ConnCreationProcess;
+#else
+        		processed_pid = 0;
+#endif
                 if (pid != -1 && processed_pid != pid)
                     continue;
                 // construct local/remote addresses
                 inet_ntop(AF_INET6, &tp6->tcp6ConnLocalAddress, lip, sizeof(lip));
                 inet_ntop(AF_INET6, &tp6->tcp6ConnRemAddress, rip, sizeof(rip));
                 lport = tp6->tcp6ConnLocalPort;
                 rport = tp6->tcp6ConnRemPort;
 
@@ -998,18 +1235,23 @@ psutil_net_connections(PyObject *self, P
                 Py_DECREF(py_tuple);
             }
         }
 #endif
         // UDPv4
         else if (mibhdr->level == MIB2_UDP || mibhdr->level == MIB2_UDP_ENTRY) {
             ude = (mib2_udpEntry_t *)databuf.buf;
             num_ent = mibhdr->len / sizeof(mib2_udpEntry_t);
+	    assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr->len);
             for (i = 0; i < num_ent; i++, ude++) {
+#ifdef NEW_MIB_COMPLIANT
                 processed_pid = ude->udpCreationProcess;
+#else
+                processed_pid = 0;
+#endif
                 if (pid != -1 && processed_pid != pid)
                     continue;
                 // XXX Very ugly hack! It seems we get here only the first
                 // time we bump into a UDPv4 socket.  PID is a very high
                 // number (clearly impossible) and the address does not
                 // belong to any valid interface.  Not sure what else
                 // to do other than skipping.
                 if (processed_pid > 131072)
@@ -1035,17 +1277,21 @@ psutil_net_connections(PyObject *self, P
 #if defined(AF_INET6)
         // UDPv6
         else if (mibhdr->level == MIB2_UDP6 ||
                     mibhdr->level == MIB2_UDP6_ENTRY)
             {
             ude6 = (mib2_udp6Entry_t *)databuf.buf;
             num_ent = mibhdr->len / sizeof(mib2_udp6Entry_t);
             for (i = 0; i < num_ent; i++, ude6++) {
+#ifdef NEW_MIB_COMPLIANT
                 processed_pid = ude6->udp6CreationProcess;
+#else
+                processed_pid = 0;
+#endif
                 if (pid != -1 && processed_pid != pid)
                     continue;
                 inet_ntop(AF_INET6, &ude6->udp6LocalAddress, lip, sizeof(lip));
                 lport = ude6->udp6LocalPort;
                 py_laddr = Py_BuildValue("(si)", lip, lport);
                 if (!py_laddr)
                     goto error;
                 py_raddr = Py_BuildValue("()");
@@ -1077,44 +1323,42 @@ error:
         free(databuf.buf);
     if (sd != 0)
         close(sd);
     return NULL;
 }
 
 
 static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args)
-{
+psutil_boot_time(PyObject *self, PyObject *args) {
     float boot_time = 0.0;
     struct utmpx *ut;
 
+    setutxent();
     while (NULL != (ut = getutxent())) {
         if (ut->ut_type == BOOT_TIME) {
             boot_time = (float)ut->ut_tv.tv_sec;
             break;
         }
     }
-    endutent();
-    if (boot_time != 0.0) {
-        return Py_BuildValue("f", boot_time);
-    }
-    else {
+    endutxent();
+    if (boot_time == 0.0) {
+        /* could not find BOOT_TIME in getutxent loop */
         PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
         return NULL;
     }
+    return Py_BuildValue("f", boot_time);
 }
 
 
 /*
  * Return the number of physical CPU cores on the system.
  */
 static PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
     kstat_ctl_t *kc;
     kstat_t *ksp;
     int ncpus = 0;
 
     kc = kstat_open();
     if (kc == NULL)
         goto error;
     ksp = kstat_lookup(kc, "cpu_info", -1, NULL);
@@ -1145,56 +1389,57 @@ error:
 
 /*
  * Return stats about a particular network
  * interface.  References:
  * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
  * http://www.i-scream.org/libstatgrab/
  */
 static PyObject*
-psutil_net_if_stats(PyObject* self, PyObject* args)
-{
+psutil_net_if_stats(PyObject* self, PyObject* args) {
     kstat_ctl_t *kc = NULL;
     kstat_t *ksp;
     kstat_named_t *knp;
     int ret;
-    int sock = 0;
+    int sock = -1;
     int duplex;
     int speed;
 
     PyObject *py_retdict = PyDict_New();
     PyObject *py_ifc_info = NULL;
     PyObject *py_is_up = NULL;
 
     if (py_retdict == NULL)
         return NULL;
     kc = kstat_open();
     if (kc == NULL)
         goto error;
     sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock == -1)
+    if (sock == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
         goto error;
+    }
 
     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
         if (strcmp(ksp->ks_class, "net") == 0) {
-            struct ifreq ifr;
+            struct lifreq ifr;
 
             kstat_read(kc, ksp, NULL);
             if (ksp->ks_type != KSTAT_TYPE_NAMED)
                 continue;
             if (strcmp(ksp->ks_class, "net") != 0)
                 continue;
 
-            strncpy(ifr.ifr_name, ksp->ks_name, sizeof(ifr.ifr_name));
-            ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+            strncpy(ifr.lifr_name, ksp->ks_name, sizeof(ifr.lifr_name));
+            ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);
             if (ret == -1)
                 continue;  // not a network interface
 
             // is up?
-            if ((ifr.ifr_flags & IFF_UP) != 0) {
+            if ((ifr.lifr_flags & IFF_UP) != 0) {
                 if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) {
                     if (knp->value.ui32 != 0u)
                         py_is_up = Py_True;
                     else
                         py_is_up = Py_False;
                 }
                 else {
                     py_is_up = Py_True;
@@ -1217,68 +1462,115 @@ psutil_net_if_stats(PyObject* self, PyOb
             // speed
             if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL)
                 // expressed in bits per sec, we want mega bits per sec
                 speed = (int)knp->value.ui64 / 1000000;
             else
                 speed = 0;
 
             // mtu
-            ret = ioctl(sock, SIOCGIFMTU, &ifr);
+            ret = ioctl(sock, SIOCGLIFMTU, &ifr);
             if (ret == -1)
                 goto error;
 
             py_ifc_info = Py_BuildValue("(Oiii)", py_is_up, duplex, speed,
-                                        ifr.ifr_mtu);
+                                        ifr.lifr_mtu);
             if (!py_ifc_info)
                 goto error;
             if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))
                 goto error;
             Py_DECREF(py_ifc_info);
         }
     }
 
     close(sock);
     kstat_close(kc);
     return py_retdict;
 
 error:
     Py_XDECREF(py_is_up);
     Py_XDECREF(py_ifc_info);
     Py_DECREF(py_retdict);
-    if (sock != 0)
+    if (sock != -1)
         close(sock);
     if (kc != NULL)
         kstat_close(kc);
     PyErr_SetFromErrno(PyExc_OSError);
     return NULL;
 }
 
 
 /*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    kstat_ctl_t *kc;
+    kstat_t *ksp;
+    cpu_stat_t cs;
+    unsigned int ctx_switches = 0;
+    unsigned int interrupts = 0;
+    unsigned int traps = 0;
+    unsigned int syscalls = 0;
+
+    kc = kstat_open();
+    if (kc == NULL) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
+        if (strcmp(ksp->ks_module, "cpu_stat") == 0) {
+            if (kstat_read(kc, ksp, &cs) == -1) {
+                PyErr_SetFromErrno(PyExc_OSError);
+                goto error;
+            }
+            // voluntary + involuntary
+            ctx_switches += cs.cpu_sysinfo.pswitch + cs.cpu_sysinfo.inv_swtch;
+            interrupts += cs.cpu_sysinfo.intr;
+            traps += cs.cpu_sysinfo.trap;
+            syscalls += cs.cpu_sysinfo.syscall;
+        }
+    }
+
+    kstat_close(kc);
+    return Py_BuildValue(
+        "IIII", ctx_switches, interrupts, syscalls, traps);
+
+error:
+    if (kc != NULL)
+        kstat_close(kc);
+    return NULL;
+}
+
+
+/*
  * define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     // --- process-related functions
     {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
      "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
     {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS,
      "Return process name and args."},
+    {"proc_environ", psutil_proc_environ, METH_VARARGS,
+      "Return process environment."},
     {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
      "Return process user and system CPU times."},
     {"proc_cred", psutil_proc_cred, METH_VARARGS,
      "Return process uids/gids."},
     {"query_process_thread", psutil_proc_query_thread, METH_VARARGS,
      "Return info about a process thread"},
     {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
      "Return process memory mappings"},
     {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
      "Return the number of context switches performed by process"},
+    {"proc_cpu_num", psutil_proc_cpu_num, METH_VARARGS,
+     "Return what CPU the process is on"},
 
     // --- system-related functions
     {"swap_mem", psutil_swap_mem, METH_VARARGS,
      "Return information about system swap memory."},
     {"users", psutil_users, METH_VARARGS,
      "Return currently connected users."},
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return disk partitions."},
@@ -1291,18 +1583,24 @@ PsutilMethods[] =
     {"boot_time", psutil_boot_time, METH_VARARGS,
      "Return system boot time in seconds since the EPOCH."},
     {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
      "Return the number of physical CPUs on the system."},
     {"net_connections", psutil_net_connections, METH_VARARGS,
      "Return TCP and UDP syste-wide open connections."},
     {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
      "Return NIC stats (isup, duplex, speed, mtu)"},
+    {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+     "Return CPU statistics"},
 
-{NULL, NULL, 0, NULL}
+    // --- others
+    {"set_testing", psutil_set_testing, METH_NOARGS,
+     "Set psutil in testing mode"},
+
+    {NULL, NULL, 0, NULL}
 };
 
 
 struct module_state {
     PyObject *error;
 };
 
 #if PY_MAJOR_VERSION >= 3
@@ -1376,14 +1674,16 @@ void init_psutil_sunos(void)
     PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
     PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
     // sunos specific
     PyModule_AddIntConstant(module, "TCPS_IDLE", TCPS_IDLE);
     // sunos specific
     PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND);
     PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
 
+    psutil_setup();
+
     if (module == NULL)
         INITERROR;
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
deleted file mode 100644
--- a/third_party/python/psutil/psutil/_psutil_sunos.h
+++ /dev/null
@@ -1,28 +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.
- */
-
-#include <Python.h>
-
-// processes
-static PyObject* psutil_proc_basic_info(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cred(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_name_and_args(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_ctx_switches(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_query_thread(PyObject* self, PyObject* args);
-
-// system
-static PyObject* psutil_boot_time(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
-static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
-static PyObject* psutil_users(PyObject* self, PyObject* args);
-static PyObject* psutil_net_connections(PyObject* self, PyObject* args);
-static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
--- a/third_party/python/psutil/psutil/_psutil_windows.c
+++ b/third_party/python/psutil/psutil/_psutil_windows.c
@@ -13,45 +13,47 @@
 #include <windows.h>
 #include <Psapi.h>
 #include <time.h>
 #include <lm.h>
 #include <WinIoCtl.h>
 #include <tchar.h>
 #include <tlhelp32.h>
 #include <winsock2.h>
+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above
+#include <ws2tcpip.h>
+#endif
 #include <iphlpapi.h>
 #include <wtsapi32.h>
-#include <ws2tcpip.h>
+#include <Winsvc.h>
+#include <PowrProf.h>
+#include <signal.h>
 
 // Link with Iphlpapi.lib
 #pragma comment(lib, "IPHLPAPI.lib")
 
-#include "_psutil_windows.h"
 #include "_psutil_common.h"
 #include "arch/windows/security.h"
 #include "arch/windows/process_info.h"
 #include "arch/windows/process_handles.h"
 #include "arch/windows/ntextapi.h"
 #include "arch/windows/inet_ntop.h"
+#include "arch/windows/services.h"
 
 #ifdef __MINGW32__
 #include "arch/windows/glpi.h"
 #endif
 
 
 /*
  * ============================================================================
  * Utilities
  * ============================================================================
  */
 
- // a flag for connections without an actual status
-static int PSUTIL_CONN_NONE = 128;
-
 #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
 #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
 #define LO_T ((float)1e-7)
 #define HI_T (LO_T*4294967296.0)
 #define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff)
 #ifndef AF_INET6
 #define AF_INET6 23
 #endif
@@ -59,18 +61,21 @@ static int PSUTIL_CONN_NONE = 128;
     Py_DECREF(_AF_INET); \
     Py_DECREF(_AF_INET6);\
     Py_DECREF(_SOCK_STREAM);\
     Py_DECREF(_SOCK_DGRAM);
 
 typedef BOOL (WINAPI *LPFN_GLPI)
     (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,  PDWORD);
 
-// fix for mingw32, see
+// Fix for mingw32, see:
 // https://github.com/giampaolo/psutil/issues/351#c2
+// This is actually a DISK_PERFORMANCE struct:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/
+//     aa363991(v=vs.85).aspx
 typedef struct _DISK_PERFORMANCE_WIN_2008 {
     LARGE_INTEGER BytesRead;
     LARGE_INTEGER BytesWritten;
     LARGE_INTEGER ReadTime;
     LARGE_INTEGER WriteTime;
     LARGE_INTEGER IdleTime;
     DWORD         ReadCount;
     DWORD         WriteCount;
@@ -78,32 +83,34 @@ typedef struct _DISK_PERFORMANCE_WIN_200
     DWORD         SplitCount;
     LARGE_INTEGER QueryTime;
     DWORD         StorageDeviceNumber;
     WCHAR         StorageManagerName[8];
 } DISK_PERFORMANCE_WIN_2008;
 
 // --- network connections mingw32 support
 #ifndef _IPRTRMIB_H
+#if (_WIN32_WINNT < 0x0600) // Windows XP
 typedef struct _MIB_TCP6ROW_OWNER_PID {
     UCHAR ucLocalAddr[16];
     DWORD dwLocalScopeId;
     DWORD dwLocalPort;
     UCHAR ucRemoteAddr[16];
     DWORD dwRemoteScopeId;
     DWORD dwRemotePort;
     DWORD dwState;
     DWORD dwOwningPid;
 } MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID;
 
 typedef struct _MIB_TCP6TABLE_OWNER_PID {
     DWORD dwNumEntries;
     MIB_TCP6ROW_OWNER_PID table[ANY_SIZE];
 } MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID;
 #endif
+#endif
 
 #ifndef __IPHLPAPI_H__
 typedef struct in6_addr {
     union {
         UCHAR Byte[16];
         USHORT Word[8];
     } u;
 } IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR;
@@ -121,27 +128,38 @@ typedef struct _MIB_UDPROW_OWNER_PID {
 } MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID;
 
 typedef struct _MIB_UDPTABLE_OWNER_PID {
     DWORD dwNumEntries;
     MIB_UDPROW_OWNER_PID table[ANY_SIZE];
 } MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID;
 #endif
 
+#if (_WIN32_WINNT < 0x0600) // Windows XP
 typedef struct _MIB_UDP6ROW_OWNER_PID {
     UCHAR ucLocalAddr[16];
     DWORD dwLocalScopeId;
     DWORD dwLocalPort;
     DWORD dwOwningPid;
 } MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID;
 
 typedef struct _MIB_UDP6TABLE_OWNER_PID {
     DWORD dwNumEntries;
     MIB_UDP6ROW_OWNER_PID table[ANY_SIZE];
 } MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID;
+#endif
+
+typedef struct _PROCESSOR_POWER_INFORMATION {
+   ULONG Number;
+   ULONG MaxMhz;
+   ULONG CurrentMhz;
+   ULONG MhzLimit;
+   ULONG MaxIdleState;
+   ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
 
 
 PIP_ADAPTER_ADDRESSES
 psutil_get_nic_addresses() {
     // allocate a 15 KB buffer to start with
     int outBufLen = 15000;
     DWORD dwRetVal = 0;
     ULONG attempts = 0;
@@ -163,75 +181,101 @@ psutil_get_nic_addresses() {
         else {
             break;
         }
 
         attempts++;
     } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3));
 
     if (dwRetVal != NO_ERROR) {
-        PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed.");
+        PyErr_SetString(
+            PyExc_RuntimeError, "GetAdaptersAddresses() syscall failed.");
         return NULL;
     }
 
     return pAddresses;
 }
 
 
 /*
  * ============================================================================
  * Public Python API
  * ============================================================================
  */
 
 
+static ULONGLONG (*psutil_GetTickCount64)(void) = NULL;
+
 /*
  * Return a Python float representing the system uptime expressed in seconds
  * since the epoch.
  */
 static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args)
-{
-    double  uptime;
+psutil_boot_time(PyObject *self, PyObject *args) {
+#if (_WIN32_WINNT >= 0x0600)  // Windows Vista
+    ULONGLONG uptime;
+#else
+    double uptime;
+#endif
     time_t pt;
     FILETIME fileTime;
     long long ll;
+    HINSTANCE hKernel32;
+    psutil_GetTickCount64 = NULL;
 
     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 betwee 01-01-1970
     and 01-01-1601, from time_t the divide by 1e+7 to get to the same
     base granularity.
     */
-    ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) \
-        + fileTime.dwLowDateTime;
+#if (_WIN32_WINNT >= 0x0600)  // Windows Vista
+    ll = (((ULONGLONG)
+#else
+    ll = (((LONGLONG)
+#endif
+        (fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime;
     pt = (time_t)((ll - 116444736000000000ull) / 10000000ull);
 
-    // XXX - By using GetTickCount() time will wrap around to zero if the
-    // system is run continuously for 49.7 days.
-    uptime = GetTickCount() / 1000.00f;
-    return Py_BuildValue("d", (double)pt - uptime);
+    // GetTickCount64() is Windows Vista+ only. Dinamically load
+    // GetTickCount64() at runtime. We may have used
+    // "#if (_WIN32_WINNT >= 0x0600)" pre-processor but that way
+    // the produced exe/wheels cannot be used on Windows XP, see:
+    // https://github.com/giampaolo/psutil/issues/811#issuecomment-230639178
+    hKernel32 = GetModuleHandleW(L"KERNEL32");
+    psutil_GetTickCount64 = (void*)GetProcAddress(hKernel32, "GetTickCount64");
+    if (psutil_GetTickCount64 != NULL) {
+        // Windows >= Vista
+        uptime = psutil_GetTickCount64() / (ULONGLONG)1000.00f;
+        return Py_BuildValue("K", pt - uptime);
+    }
+    else {
+        // Windows XP.
+        // GetTickCount() time will wrap around to zero if the
+        // system is run continuously for 49.7 days.
+        uptime = GetTickCount() / (LONGLONG)1000.00f;
+        return Py_BuildValue("L", pt - uptime);
+    }
 }
 
 
 /*
  * Return 1 if PID exists in the current process list, else 0.
  */
 static PyObject *
-psutil_pid_exists(PyObject *self, PyObject *args)
-{
+psutil_pid_exists(PyObject *self, PyObject *args) {
     long pid;
     int status;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
     status = psutil_pid_is_running(pid);
     if (-1 == status)
@@ -239,176 +283,176 @@ psutil_pid_exists(PyObject *self, PyObje
     return PyBool_FromLong(status);
 }
 
 
 /*
  * Return a Python list of all the PIDs running on the system.
  */
 static PyObject *
-psutil_pids(PyObject *self, PyObject *args)
-{
+psutil_pids(PyObject *self, PyObject *args) {
     DWORD *proclist = NULL;
     DWORD numberOfReturnedPIDs;
     DWORD i;
-    PyObject *pid = NULL;
-    PyObject *retlist = PyList_New(0);
-
-    if (retlist == NULL)
+    PyObject *py_pid = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
         return NULL;
     proclist = psutil_get_pids(&numberOfReturnedPIDs);
     if (proclist == NULL)
         goto error;
 
     for (i = 0; i < numberOfReturnedPIDs; i++) {
-        pid = Py_BuildValue("I", proclist[i]);
-        if (!pid)
+        py_pid = Py_BuildValue("I", proclist[i]);
+        if (!py_pid)
             goto error;
-        if (PyList_Append(retlist, pid))
+        if (PyList_Append(py_retlist, py_pid))
             goto error;
-        Py_DECREF(pid);
+        Py_DECREF(py_pid);
     }
 
     // free C array allocated for PIDs
     free(proclist);
-    return retlist;
+    return py_retlist;
 
 error:
-    Py_XDECREF(pid);
-    Py_DECREF(retlist);
+    Py_XDECREF(py_pid);
+    Py_DECREF(py_retlist);
     if (proclist != NULL)
         free(proclist);
     return NULL;
 }
 
 
 /*
  * Kill a process given its PID.
  */
 static PyObject *
-psutil_proc_kill(PyObject *self, PyObject *args)
-{
+psutil_proc_kill(PyObject *self, PyObject *args) {
     HANDLE hProcess;
+    DWORD err;
     long pid;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (pid == 0)
-        return AccessDenied();
+        return AccessDenied("");
 
     hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
     if (hProcess == NULL) {
         if (GetLastError() == ERROR_INVALID_PARAMETER) {
             // see https://github.com/giampaolo/psutil/issues/24
-            NoSuchProcess();
+            psutil_debug("OpenProcess -> ERROR_INVALID_PARAMETER turned "
+                         "into NoSuchProcess");
+            NoSuchProcess("");
         }
         else {
             PyErr_SetFromWindowsErr(0);
         }
         return NULL;
     }
 
     // kill the process
-    if (! TerminateProcess(hProcess, 0)) {
-        PyErr_SetFromWindowsErr(0);
-        CloseHandle(hProcess);
-        return NULL;
+    if (! TerminateProcess(hProcess, SIGTERM)) {
+        err = GetLastError();
+        // See: https://github.com/giampaolo/psutil/issues/1099
+        if (err != ERROR_ACCESS_DENIED) {
+            CloseHandle(hProcess);
+            PyErr_SetFromWindowsErr(err);
+            return NULL;
+        }
     }
 
     CloseHandle(hProcess);
     Py_RETURN_NONE;
 }
 
 
 /*
  * Wait for process to terminate and return its exit code.
  */
 static PyObject *
-psutil_proc_wait(PyObject *self, PyObject *args)
-{
+psutil_proc_wait(PyObject *self, PyObject *args) {
     HANDLE hProcess;
     DWORD ExitCode;
     DWORD retVal;
     long pid;
     long timeout;
 
     if (! PyArg_ParseTuple(args, "ll", &pid, &timeout))
         return NULL;
     if (pid == 0)
-        return AccessDenied();
+        return AccessDenied("");
 
     hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
                            FALSE, pid);
     if (hProcess == NULL) {
         if (GetLastError() == ERROR_INVALID_PARAMETER) {
             // no such process; we do not want to raise NSP but
             // return None instead.
             Py_RETURN_NONE;
         }
-        else {
-            PyErr_SetFromWindowsErr(0);
-            return NULL;
-        }
+        else
+            return PyErr_SetFromWindowsErr(0);
     }
 
     // wait until the process has terminated
     Py_BEGIN_ALLOW_THREADS
     retVal = WaitForSingleObject(hProcess, timeout);
     Py_END_ALLOW_THREADS
 
     if (retVal == WAIT_FAILED) {
         CloseHandle(hProcess);
-        return PyErr_SetFromWindowsErr(GetLastError());
+        return PyErr_SetFromWindowsErr(0);
     }
     if (retVal == WAIT_TIMEOUT) {
         CloseHandle(hProcess);
         return Py_BuildValue("l", WAIT_TIMEOUT);
     }
 
-    // get the exit code; note: subprocess module (erroneously?) uses
-    // what returned by WaitForSingleObject
     if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
         CloseHandle(hProcess);
         return PyErr_SetFromWindowsErr(GetLastError());
     }
+
     CloseHandle(hProcess);
+
 #if PY_MAJOR_VERSION >= 3
     return PyLong_FromLong((long) ExitCode);
 #else
     return PyInt_FromLong((long) ExitCode);
 #endif
 }
 
 
 /*
  * Return a Python tuple (user_time, kernel_time)
  */
 static PyObject *
-psutil_proc_cpu_times(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
     long        pid;
     HANDLE      hProcess;
     FILETIME    ftCreate, ftExit, ftKernel, ftUser;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
     hProcess = psutil_handle_from_pid(pid);
     if (hProcess == NULL)
         return NULL;
     if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
         CloseHandle(hProcess);
         if (GetLastError() == ERROR_ACCESS_DENIED) {
             // usually means the process has died so we throw a NoSuchProcess
             // here
-            return NoSuchProcess();
+            return NoSuchProcess("");
         }
         else {
-            PyErr_SetFromWindowsErr(0);
-            return NULL;
+            return PyErr_SetFromWindowsErr(0);
         }
     }
 
     CloseHandle(hProcess);
 
     /*
      * User and kernel times are represented as a FILETIME structure
      * wich contains a 64-bit value representing the number of
@@ -428,23 +472,20 @@ psutil_proc_cpu_times(PyObject *self, Py
 }
 
 
 /*
  * 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)
-{
+psutil_proc_create_time(PyObject *self, PyObject *args) {
     long        pid;
     long long   unix_time;
-    DWORD       exitCode;
     HANDLE      hProcess;
-    BOOL        ret;
     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);
@@ -452,80 +493,77 @@ psutil_proc_create_time(PyObject *self, 
     hProcess = psutil_handle_from_pid(pid);
     if (hProcess == NULL)
         return NULL;
     if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
         CloseHandle(hProcess);
         if (GetLastError() == ERROR_ACCESS_DENIED) {
             // usually means the process has died so we throw a
             // NoSuchProcess here
-            return NoSuchProcess();
+            return NoSuchProcess("");
         }
         else {
-            PyErr_SetFromWindowsErr(0);
-            return NULL;
+            return PyErr_SetFromWindowsErr(0);
         }
     }
 
+    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();
+            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) {
-            PyErr_SetFromWindowsErr(0);
-            return NULL;
-        }
+        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.
     */
+
+    // 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 logical CPUs.
  */
 static PyObject *
-psutil_cpu_count_logical(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
     SYSTEM_INFO system_info;
     system_info.dwNumberOfProcessors = 0;
 
     GetSystemInfo(&system_info);
     if (system_info.dwNumberOfProcessors == 0)
         Py_RETURN_NONE;  // mimic os.cpu_count()
     else
         return Py_BuildValue("I", system_info.dwNumberOfProcessors);
 }
 
 
 /*
  * Return the number of physical CPU cores.
  */
 static PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args)
-{
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
     LPFN_GLPI glpi;
     DWORD rc;
     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
     DWORD length = 0;
     DWORD offset = 0;
     int ncpus = 0;
 
@@ -580,116 +618,122 @@ return_none:
 
 /*
  * Return process cmdline as a Python list of cmdline arguments.
  */
 static PyObject *
 psutil_proc_cmdline(PyObject *self, PyObject *args) {
     long pid;
     int pid_return;
-    PyObject *arglist;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if ((pid == 0) || (pid == 4))
         return Py_BuildValue("[]");
 
     pid_return = psutil_pid_is_running(pid);
     if (pid_return == 0)
-        return NoSuchProcess();
+        return NoSuchProcess("");
     if (pid_return == -1)
         return NULL;
 
-    // XXX the assumptio below probably needs to go away
-
-    // May fail any of several ReadProcessMemory calls etc. and
-    // not indicate a real problem so we ignore any errors and
-    // just live without commandline.
-    arglist = psutil_get_arg_list(pid);
-    if ( NULL == arglist ) {
-        // carry on anyway, clear any exceptions too
-        PyErr_Clear();
-        return Py_BuildValue("[]");
-    }
-
-    return arglist;
+    return psutil_get_cmdline(pid);
+}
+
+
+/*
+ * Return process cmdline as a Python list of cmdline arguments.
+ */
+static PyObject *
+psutil_proc_environ(PyObject *self, PyObject *args) {
+    long pid;
+    int pid_return;
+
+    if (! PyArg_ParseTuple(args, "l", &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("");
+    if (pid_return == -1)
+        return NULL;
+
+    return psutil_get_environ(pid);
 }
 
 
 /*
  * Return process executable path.
  */
 static PyObject *
 psutil_proc_exe(PyObject *self, PyObject *args) {
     long pid;
     HANDLE hProcess;
     wchar_t exe[MAX_PATH];
+
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-
     hProcess = psutil_handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION);
     if (NULL == hProcess)
         return NULL;
     if (GetProcessImageFileNameW(hProcess, exe, MAX_PATH) == 0) {
         CloseHandle(hProcess);
-        PyErr_SetFromWindowsErr(0);
-        return NULL;
+        return PyErr_SetFromWindowsErr(0);
     }
     CloseHandle(hProcess);
-    return Py_BuildValue("u", exe);
+    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;
-    PROCESSENTRY32 pentry;
+    PROCESSENTRY32W pentry;
     HANDLE hSnapShot;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid);
-    if (hSnapShot == INVALID_HANDLE_VALUE) {
-        PyErr_SetFromWindowsErr(0);
-        return NULL;
-    }
-    pentry.dwSize = sizeof(PROCESSENTRY32);
-    ok = Process32First(hSnapShot, &pentry);
+    if (hSnapShot == INVALID_HANDLE_VALUE)
+        return PyErr_SetFromWindowsErr(0);
+    pentry.dwSize = sizeof(PROCESSENTRY32W);
+    ok = Process32FirstW(hSnapShot, &pentry);
     if (! ok) {
         CloseHandle(hSnapShot);
-        PyErr_SetFromWindowsErr(0);
-        return NULL;
+        return PyErr_SetFromWindowsErr(0);
     }
     while (ok) {
         if (pentry.th32ProcessID == pid) {
             CloseHandle(hSnapShot);
-            return Py_BuildValue("s", pentry.szExeFile);
+            return PyUnicode_FromWideChar(
+                pentry.szExeFile, wcslen(pentry.szExeFile));
         }
-        ok = Process32Next(hSnapShot, &pentry);
+        ok = Process32NextW(hSnapShot, &pentry);
     }
 
     CloseHandle(hSnapShot);
-    NoSuchProcess();
+    NoSuchProcess("");
     return NULL;
 }
 
 
 /*
  * Return process memory information as a Python tuple.
  */
 static PyObject *
-psutil_proc_memory_info(PyObject *self, PyObject *args)
-{
+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;
@@ -742,77 +786,110 @@ psutil_proc_memory_info(PyObject *self, 
         (unsigned int)cnt.QuotaNonPagedPoolUsage,
         (unsigned int)cnt.PagefileUsage,
         (unsigned int)cnt.PeakPagefileUsage,
         (unsigned int)private);
 #endif
 }
 
 
-/*
- * Alternative implementation of the one above but bypasses ACCESS DENIED.
+
+/**
+ * Returns the USS of the process.
+ * Reference:
+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
+ *     nsMemoryReporterManager.cpp
  */
 static PyObject *
-psutil_proc_memory_info_2(PyObject *self, PyObject *args)
+psutil_proc_memory_uss(PyObject *self, PyObject *args)
 {
     DWORD pid;
-    PSYSTEM_PROCESS_INFORMATION process;
-    PVOID buffer;
-    SIZE_T private;
-    unsigned long pfault_count;
-
-#if defined(_WIN64)
-    unsigned long long m1, m2, m3, m4, m5, m6, m7, m8;
-#else
-    unsigned int m1, m2, m3, m4, m5, m6, m7, m8;
-#endif
+    HANDLE proc;
+    PSAPI_WORKING_SET_INFORMATION tmp;
+    DWORD tmp_size = sizeof(tmp);
+    size_t entries;
+    size_t private_pages;
+    size_t i;
+    DWORD info_array_size;
+    PSAPI_WORKING_SET_INFORMATION* info_array;
+    SYSTEM_INFO system_info;
+    PyObject* py_result = NULL;
+    unsigned long long total = 0;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    if (! psutil_get_proc_info(pid, &process, &buffer))
+
+    proc = psutil_handle_from_pid(pid);
+    if (proc == NULL)
         return NULL;
 
-#if (_WIN32_WINNT >= 0x0501)  // Windows XP with SP2
-    private = process->PrivatePageCount;
-#else
-    private = 0;
-#endif
-    pfault_count = process->PageFaultCount;
-
-    m1 = process->PeakWorkingSetSize;
-    m2 = process->WorkingSetSize;
-    m3 = process->QuotaPeakPagedPoolUsage;
-    m4 = process->QuotaPagedPoolUsage;
-    m5 = process->QuotaPeakNonPagedPoolUsage;
-    m6 = process->QuotaNonPagedPoolUsage;
-    m7 = process->PagefileUsage;
-    m8 = process->PeakPagefileUsage;
-
-    free(buffer);
-
-    // SYSTEM_PROCESS_INFORMATION values are defined as SIZE_T which on 64
-    // bits is an (unsigned long long) and on 32bits is an (unsigned int).
-    // "_WIN64" is defined if we're running a 64bit Python interpreter not
-    // exclusively if the *system* is 64bit.
-#if defined(_WIN64)
-    return Py_BuildValue("(kKKKKKKKKK)",
-#else
-    return Py_BuildValue("(kIIIIIIIII)",
-#endif
-        pfault_count, m1, m2, m3, m4, m5, m6, m7, m8, private);
+    // Determine how many entries we need.
+    memset(&tmp, 0, tmp_size);
+    if (!QueryWorkingSet(proc, &tmp, tmp_size)) {
+        // NB: QueryWorkingSet is expected to fail here due to the
+        // buffer being too small.
+        if (tmp.NumberOfEntries == 0) {
+            PyErr_SetFromWindowsErr(0);
+            goto done;
+        }
+    }
+
+    // Fudge the size in case new entries are added between calls.
+    entries = tmp.NumberOfEntries * 2;
+
+    if (!entries) {
+        goto done;
+    }
+
+    info_array_size = tmp_size + (entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+    info_array = (PSAPI_WORKING_SET_INFORMATION*)malloc(info_array_size);
+    if (!info_array) {
+        PyErr_NoMemory();
+        goto done;
+    }
+
+    if (!QueryWorkingSet(proc, info_array, info_array_size)) {
+        PyErr_SetFromWindowsErr(0);
+        goto done;
+    }
+
+    entries = (size_t)info_array->NumberOfEntries;
+    private_pages = 0;
+    for (i = 0; i < entries; i++) {
+        // Count shared pages that only one process is using as private.
+        if (!info_array->WorkingSetInfo[i].Shared ||
+                info_array->WorkingSetInfo[i].ShareCount <= 1) {
+            private_pages++;
+        }
+    }
+
+    // GetSystemInfo has no return value.
+    GetSystemInfo(&system_info);
+    total = private_pages * system_info.dwPageSize;
+    py_result = Py_BuildValue("K", total);
+
+done:
+    if (proc) {
+        CloseHandle(proc);
+    }
+
+    if (info_array) {
+        free(info_array);
+    }
+
+    return py_result;
 }
 
 
 /*
  * Return a Python integer indicating the total amount of physical memory
  * in bytes.
  */
 static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args)
-{
+psutil_virtual_mem(PyObject *self, PyObject *args) {
     MEMORYSTATUSEX memInfo;
     memInfo.dwLength = sizeof(MEMORYSTATUSEX);
 
     if (! GlobalMemoryStatusEx(&memInfo))
         return PyErr_SetFromWindowsErr(0);
     return Py_BuildValue("(LLLLLL)",
                          memInfo.ullTotalPhys,      // total
                          memInfo.ullAvailPhys,      // avail
@@ -824,18 +901,17 @@ psutil_virtual_mem(PyObject *self, PyObj
 
 
 /*
  * 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)
-{
+psutil_cpu_times(PyObject *self, PyObject *args) {
     float idle, kernel, user, system;
     FILETIME idle_time, kernel_time, user_time;
 
     if (!GetSystemTimes(&idle_time, &kernel_time, &user_time))
         return PyErr_SetFromWindowsErr(0);
 
     idle = (float)((HI_T * idle_time.dwHighDateTime) + \
                    (LO_T * idle_time.dwLowDateTime));
@@ -851,251 +927,154 @@ psutil_cpu_times(PyObject *self, PyObjec
     return Py_BuildValue("(fff)", user, system, idle);
 }
 
 
 /*
  * Same as above but for all system CPUs.
  */
 static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args)
-{
-    float idle, kernel, user;
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+    // NtQuerySystemInformation stuff
     typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG);
     NTQSI_PROC NtQuerySystemInformation;
     HINSTANCE hNtDll;
-    SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
+
+    float idle, kernel, systemt, user, interrupt, dpc;
+    NTSTATUS status;
+    _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
     SYSTEM_INFO si;
     UINT i;
-    PyObject *arg = NULL;
-    PyObject *retlist = PyList_New(0);
-
-    if (retlist == NULL)
+    PyObject *py_tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
         return NULL;
 
-    // dynamic linking is mandatory to use NtQuerySystemInformation
+    // obtain NtQuerySystemInformation
     hNtDll = LoadLibrary(TEXT("ntdll.dll"));
-    if (hNtDll != NULL) {
-        // gets NtQuerySystemInformation address
-        NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(
-                                       hNtDll, "NtQuerySystemInformation");
-
-        if (NtQuerySystemInformation != NULL)
-        {
-            // retrives number of processors
-            GetSystemInfo(&si);
-
-            // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
-            // structures, one per processor
-            sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
-                   malloc(si.dwNumberOfProcessors * \
-                          sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
-            if (sppi != NULL)
-            {
-                // gets cpu time informations
-                if (0 == NtQuerySystemInformation(
-                            SystemProcessorPerformanceInformation,
-                            sppi,
-                            si.dwNumberOfProcessors * sizeof
-                            (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
-                            NULL)
-                   )
-                {
-                    // computes system global times summing each
-                    // processor value
-                    idle = user = kernel = 0;
-                    for (i = 0; i < si.dwNumberOfProcessors; i++) {
-                        arg = NULL;
-                        user = (float)((HI_T * sppi[i].UserTime.HighPart) +
-                                       (LO_T * sppi[i].UserTime.LowPart));
-                        idle = (float)((HI_T * sppi[i].IdleTime.HighPart) +
-                                       (LO_T * sppi[i].IdleTime.LowPart));
-                        kernel = (float)((HI_T * sppi[i].KernelTime.HighPart) +
-                                         (LO_T * sppi[i].KernelTime.LowPart));
-                        // kernel time includes idle time on windows
-                        // we return only busy kernel time subtracting
-                        // idle time from kernel time
-                        arg = Py_BuildValue("(ddd)",
-                                            user,
-                                            kernel - idle,
-                                            idle);
-                        if (!arg)
-                            goto error;
-                        if (PyList_Append(retlist, arg))
-                            goto error;
-                        Py_DECREF(arg);
-                    }
-                    free(sppi);
-                    FreeLibrary(hNtDll);
-                    return retlist;
-
-                }  // END NtQuerySystemInformation
-            }  // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
-        }  // END GetProcAddress
-    }  // END LoadLibrary
-    goto error;
+    if (hNtDll == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+    NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(
+        hNtDll, "NtQuerySystemInformation");
+    if (NtQuerySystemInformation == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // retrives number of processors
+    GetSystemInfo(&si);
+
+    // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
+    // structures, one per processor
+    sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
+           malloc(si.dwNumberOfProcessors * \
+                  sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
+    if (sppi == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    // gets cpu time informations
+    status = NtQuerySystemInformation(
+        SystemProcessorPerformanceInformation,
+        sppi,
+        si.dwNumberOfProcessors * sizeof
+            (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
+        NULL);
+    if (status != 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // computes system global times summing each
+    // processor value
+    idle = user = kernel = interrupt = dpc = 0;
+    for (i = 0; i < si.dwNumberOfProcessors; i++) {
+        py_tuple = NULL;
+        user = (float)((HI_T * sppi[i].UserTime.HighPart) +
+                       (LO_T * sppi[i].UserTime.LowPart));
+        idle = (float)((HI_T * sppi[i].IdleTime.HighPart) +
+                       (LO_T * sppi[i].IdleTime.LowPart));
+        kernel = (float)((HI_T * sppi[i].KernelTime.HighPart) +
+                         (LO_T * sppi[i].KernelTime.LowPart));
+        interrupt = (float)((HI_T * sppi[i].InterruptTime.HighPart) +
+                            (LO_T * sppi[i].InterruptTime.LowPart));
+        dpc = (float)((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);
+    FreeLibrary(hNtDll);
+    return py_retlist;
 
 error:
-    Py_XDECREF(arg);
-    Py_DECREF(retlist);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
     if (sppi)
         free(sppi);
     if (hNtDll)
         FreeLibrary(hNtDll);
-    PyErr_SetFromWindowsErr(0);
     return NULL;
 }
 
 
 /*
  * Return process current working directory as a Python string.
  */
-
 static PyObject *
-psutil_proc_cwd(PyObject *self, PyObject *args)
-{
+psutil_proc_cwd(PyObject *self, PyObject *args) {
     long pid;
-    HANDLE processHandle = NULL;
-    PVOID pebAddress;
-    PVOID rtlUserProcParamsAddress;
-    UNICODE_STRING currentDirectory;
-    WCHAR *currentDirectoryContent = NULL;
-    PyObject *returnPyObj = NULL;
-    PyObject *cwd_from_wchar = NULL;
-    PyObject *cwd = NULL;
+    int pid_return;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
-    processHandle = psutil_handle_from_pid(pid);
-    if (processHandle == NULL)
+    pid_return = psutil_pid_is_running(pid);
+    if (pid_return == 0)
+        return NoSuchProcess("");
+    if (pid_return == -1)
         return NULL;
 
-    pebAddress = psutil_get_peb_address(processHandle);
-
-    // get the address of ProcessParameters
-#ifdef _WIN64
-    if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 32,
-                           &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
-#else
-    if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 0x10,
-                           &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
-#endif
-    {
-        CloseHandle(processHandle);
-        if (GetLastError() == ERROR_PARTIAL_COPY) {
-            // this occurs quite often with system processes
-            return AccessDenied();
-        }
-        else {
-            return PyErr_SetFromWindowsErr(0);
-        }
-    }
-
-    // Read the currentDirectory UNICODE_STRING structure.
-    // 0x24 refers to "CurrentDirectoryPath" of RTL_USER_PROCESS_PARAMETERS
-    // structure, see:
-    // http://wj32.wordpress.com/2009/01/24/
-    //     howto-get-the-command-line-of-processes/
-#ifdef _WIN64
-    if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 56,
-                           &currentDirectory, sizeof(currentDirectory), NULL))
-#else
-    if (!ReadProcessMemory(processHandle,
-                           (PCHAR)rtlUserProcParamsAddress + 0x24,
-                           &currentDirectory, sizeof(currentDirectory), NULL))
-#endif
-    {
-        CloseHandle(processHandle);
-        if (GetLastError() == ERROR_PARTIAL_COPY) {
-            // this occurs quite often with system processes
-            return AccessDenied();
-        }
-        else {
-            return PyErr_SetFromWindowsErr(0);
-        }
-    }
-
-    // allocate memory to hold cwd
-    currentDirectoryContent = (WCHAR *)malloc(currentDirectory.Length + 1);
-    if (currentDirectoryContent == NULL) {
-        PyErr_NoMemory();
-        goto error;
-    }
-
-    // read cwd
-    if (!ReadProcessMemory(processHandle, currentDirectory.Buffer,
-                           currentDirectoryContent, currentDirectory.Length,
-                           NULL))
-    {
-        if (GetLastError() == ERROR_PARTIAL_COPY) {
-            // this occurs quite often with system processes
-            AccessDenied();
-        }
-        else {
-            PyErr_SetFromWindowsErr(0);
-        }
-        goto error;
-    }
-
-    // null-terminate the string to prevent wcslen from returning
-    // incorrect length the length specifier is in characters, but
-    // currentDirectory.Length is in bytes
-    currentDirectoryContent[(currentDirectory.Length / sizeof(WCHAR))] = '\0';
-
-    // convert wchar array to a Python unicode string, and then to UTF8
-    cwd_from_wchar = PyUnicode_FromWideChar(currentDirectoryContent,
-                                            wcslen(currentDirectoryContent));
-    if (cwd_from_wchar == NULL)
-        goto error;
-
-#if PY_MAJOR_VERSION >= 3
-    cwd = PyUnicode_FromObject(cwd_from_wchar);
-#else
-    cwd = PyUnicode_AsUTF8String(cwd_from_wchar);
-#endif
-    if (cwd == NULL)
-        goto error;
-
-    // decrement the reference count on our temp unicode str to avoid
-    // mem leak
-    returnPyObj = Py_BuildValue("N", cwd);
-    if (!returnPyObj)
-        goto error;
-
-    Py_DECREF(cwd_from_wchar);
-
-    CloseHandle(processHandle);
-    free(currentDirectoryContent);
-    return returnPyObj;
-
-error:
-    Py_XDECREF(cwd_from_wchar);
-    Py_XDECREF(cwd);
-    Py_XDECREF(returnPyObj);
-    if (currentDirectoryContent != NULL)
-        free(currentDirectoryContent);
-    if (processHandle != NULL)
-        CloseHandle(processHandle);
-    return NULL;
+    return psutil_get_cwd(pid);
 }
 
 
 /*
  * Resume or suspends a process
  */
 int
-psutil_proc_suspend_or_resume(DWORD pid, int suspend)
-{
+psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
     // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx
     HANDLE hThreadSnap = NULL;
+    HANDLE hThread;
     THREADENTRY32  te32 = {0};
 
     if (pid == 0) {
-        AccessDenied();
+        AccessDenied("");
         return FALSE;
     }
 
     hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
     if (hThreadSnap == INVALID_HANDLE_VALUE) {
         PyErr_SetFromWindowsErr(0);
         return FALSE;
     }
@@ -1107,39 +1086,35 @@ psutil_proc_suspend_or_resume(DWORD pid,
         PyErr_SetFromWindowsErr(0);
         CloseHandle(hThreadSnap);
         return FALSE;
     }
 
     // Walk the thread snapshot to find all threads of the process.
     // If the thread belongs to the process, add its information
     // to the display list.
-    do
-    {
-        if (te32.th32OwnerProcessID == pid)
-        {
-            HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
-                                        te32.th32ThreadID);
+    do {
+        if (te32.th32OwnerProcessID == pid) {
+            hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
+                                 te32.th32ThreadID);
             if (hThread == NULL) {
                 PyErr_SetFromWindowsErr(0);
                 CloseHandle(hThread);
                 CloseHandle(hThreadSnap);
                 return FALSE;
             }
-            if (suspend == 1)
-            {
+            if (suspend == 1) {
                 if (SuspendThread(hThread) == (DWORD) - 1) {
                     PyErr_SetFromWindowsErr(0);
                     CloseHandle(hThread);
                     CloseHandle(hThreadSnap);
                     return FALSE;
                 }
             }
-            else
-            {
+            else {
                 if (ResumeThread(hThread) == (DWORD) - 1) {
                     PyErr_SetFromWindowsErr(0);
                     CloseHandle(hThread);
                     CloseHandle(hThreadSnap);
                     return FALSE;
                 }
             }
             CloseHandle(hThread);
@@ -1147,70 +1122,67 @@ psutil_proc_suspend_or_resume(DWORD pid,
     } while (Thread32Next(hThreadSnap, &te32));
 
     CloseHandle(hThreadSnap);
     return TRUE;
 }
 
 
 static PyObject *
-psutil_proc_suspend(PyObject *self, PyObject *args)
-{
+psutil_proc_suspend(PyObject *self, PyObject *args) {
     long pid;
     int suspend = 1;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (! psutil_proc_suspend_or_resume(pid, suspend))
         return NULL;
     Py_RETURN_NONE;
 }
 
 
 static PyObject *
-psutil_proc_resume(PyObject *self, PyObject *args)
-{
+psutil_proc_resume(PyObject *self, PyObject *args) {
     long pid;
     int suspend = 0;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (! psutil_proc_suspend_or_resume(pid, suspend))
         return NULL;
     Py_RETURN_NONE;
 }
 
 
 static PyObject *
-psutil_proc_threads(PyObject *self, PyObject *args)
-{
+psutil_proc_threads(PyObject *self, PyObject *args) {
     HANDLE hThread;
     THREADENTRY32 te32 = {0};
     long pid;
     int pid_return;
     int rc;
     FILETIME ftDummy, ftKernel, ftUser;
-    PyObject *retList = PyList_New(0);
-    PyObject *pyTuple = NULL;
     HANDLE hThreadSnap = NULL;
-
-    if (retList == NULL)
+    PyObject *py_tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
         return NULL;
     if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
     if (pid == 0) {
         // raise AD instead of returning 0 as procexp is able to
         // retrieve useful information somehow
-        AccessDenied();
+        AccessDenied("");
         goto error;
     }
 
     pid_return = psutil_pid_is_running(pid);
     if (pid_return == 0) {
-        NoSuchProcess();
+        NoSuchProcess("");
         goto error;
     }
     if (pid_return == -1)
         goto error;
 
     hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
     if (hThreadSnap == INVALID_HANDLE_VALUE) {
         PyErr_SetFromWindowsErr(0);
@@ -1224,17 +1196,17 @@ psutil_proc_threads(PyObject *self, PyOb
         PyErr_SetFromWindowsErr(0);
         goto error;
     }
 
     // Walk the thread snapshot to find all threads of the process.
     // If the thread belongs to the process, increase the counter.
     do {
         if (te32.th32OwnerProcessID == pid) {
-            pyTuple = NULL;
+            py_tuple = NULL;
             hThread = NULL;
             hThread = OpenThread(THREAD_QUERY_INFORMATION,
                                  FALSE, te32.th32ThreadID);
             if (hThread == NULL) {
                 // thread has disappeared on us
                 continue;
             }
 
@@ -1249,77 +1221,75 @@ psutil_proc_threads(PyObject *self, PyOb
              * User and kernel times are represented as a FILETIME structure
              * wich contains a 64-bit value representing the number of
              * 100-nanosecond intervals since January 1, 1601 (UTC):
              * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
              * To convert it into a float representing the seconds that the
              * process has executed in user/kernel mode I borrowed the code
              * below from Python's Modules/posixmodule.c
              */
-            pyTuple = Py_BuildValue(
+            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));
-            if (!pyTuple)
+            if (!py_tuple)
                 goto error;
-            if (PyList_Append(retList, pyTuple))
+            if (PyList_Append(py_retlist, py_tuple))
                 goto error;
-            Py_DECREF(pyTuple);
+            Py_DECREF(py_tuple);
 
             CloseHandle(hThread);
         }
     } while (Thread32Next(hThreadSnap, &te32));
 
     CloseHandle(hThreadSnap);
-    return retList;
+    return py_retlist;
 
 error:
-    Py_XDECREF(pyTuple);
-    Py_DECREF(retList);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
     if (hThread != NULL)
         CloseHandle(hThread);
     if (hThreadSnap != NULL)
         CloseHandle(hThreadSnap);
     return NULL;
 }
 
 
 static PyObject *
-psutil_proc_open_files(PyObject *self, PyObject *args)
-{
+psutil_proc_open_files(PyObject *self, PyObject *args) {
     long       pid;
     HANDLE     processHandle;
     DWORD      access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
-    PyObject  *filesList;
+    PyObject  *py_retlist;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
     processHandle = psutil_handle_from_pid_waccess(pid, access);
     if (processHandle == NULL)
         return NULL;
-    filesList = psutil_get_open_files(pid, processHandle);
+    py_retlist = psutil_get_open_files(pid, processHandle);
     CloseHandle(processHandle);
-    if (filesList == NULL)
+    if (py_retlist == NULL)
         return PyErr_SetFromWindowsErr(0);
-    return filesList;
+    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)
-{
+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')) {
@@ -1336,190 +1306,278 @@ psutil_win32_QueryDosDevice(PyObject *se
     return Py_BuildValue("s", "");
 }
 
 
 /*
  * Return process username as a "DOMAIN//USERNAME" string.
  */
 static PyObject *
-psutil_proc_username(PyObject *self, PyObject *args)
-{
+psutil_proc_username(PyObject *self, PyObject *args) {
     long pid;
-    HANDLE processHandle;
-    HANDLE tokenHandle;
-    PTOKEN_USER user;
+    HANDLE processHandle = NULL;
+    HANDLE tokenHandle = NULL;
+    PTOKEN_USER user = NULL;
     ULONG bufferSize;
-    PTSTR name;
+    WCHAR *name = NULL;
+    WCHAR *domainName = NULL;
     ULONG nameSize;
-    PTSTR domainName;
     ULONG domainNameSize;
     SID_NAME_USE nameUse;
-    PTSTR fullName;
-    PyObject *returnObject;
+    PyObject *py_username;
+    PyObject *py_domain;
+    PyObject *py_tuple;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
 
     processHandle = psutil_handle_from_pid_waccess(
         pid, PROCESS_QUERY_INFORMATION);
     if (processHandle == NULL)
         return NULL;
 
     if (!OpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)) {
-        CloseHandle(processHandle);
-        return PyErr_SetFromWindowsErr(0);
+        PyErr_SetFromWindowsErr(0);
+        goto error;
     }
 
     CloseHandle(processHandle);
+    processHandle = NULL;
 
     // Get the user SID.
-
     bufferSize = 0x100;
-    user = malloc(bufferSize);
-    if (user == NULL)
-        return PyErr_NoMemory();
-
-    if (!GetTokenInformation(tokenHandle, TokenUser, user, bufferSize,
-                             &bufferSize))
-    {
-        free(user);
+    while (1) {
         user = malloc(bufferSize);
         if (user == NULL) {
-            CloseHandle(tokenHandle);
-            return PyErr_NoMemory();
+            PyErr_NoMemory();
+            goto error;
         }
         if (!GetTokenInformation(tokenHandle, TokenUser, user, bufferSize,
                                  &bufferSize))
         {
-            free(user);
-            CloseHandle(tokenHandle);
-            return PyErr_SetFromWindowsErr(0);
+            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+                free(user);
+                continue;
+            }
+            else {
+                PyErr_SetFromWindowsErr(0);
+                goto error;
+            }
         }
+        break;
     }
 
     CloseHandle(tokenHandle);
+    tokenHandle = NULL;
 
     // resolve the SID to a name
     nameSize = 0x100;
     domainNameSize = 0x100;
-
-    name = malloc(nameSize * sizeof(TCHAR));
-    if (name == NULL)
-        return PyErr_NoMemory();
-    domainName = malloc(domainNameSize * sizeof(TCHAR));
-    if (domainName == NULL)
-        return PyErr_NoMemory();
-
-    if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName,
-                          &domainNameSize, &nameUse))
-    {
-        free(name);
-        free(domainName);
-        name = malloc(nameSize * sizeof(TCHAR));
-        if (name == NULL)
-            return PyErr_NoMemory();
-        domainName = malloc(domainNameSize * sizeof(TCHAR));
-        if (domainName == NULL)
-            return PyErr_NoMemory();
-        if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize,
-                              domainName, &domainNameSize, &nameUse))
+    while (1) {
+        name = malloc(nameSize * sizeof(WCHAR));
+        if (name == NULL) {
+            PyErr_NoMemory();
+            goto error;
+        }
+        domainName = malloc(domainNameSize * sizeof(WCHAR));
+        if (domainName == NULL) {
+            PyErr_NoMemory();
+            goto error;
+        }
+        if (!LookupAccountSidW(NULL, user->User.Sid, name, &nameSize,
+                               domainName, &domainNameSize, &nameUse))
         {
-            free(name);
-            free(domainName);
-            free(user);
-
-            return PyErr_SetFromWindowsErr(0);
+            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+                free(name);
+                free(domainName);
+                continue;
+            }
+            else {
+                PyErr_SetFromWindowsErr(0);
+                goto error;
+            }
         }
+        break;
     }
 
-    nameSize = _tcslen(name);
-    domainNameSize = _tcslen(domainName);
-
-    // build the full username string
-    fullName = malloc((domainNameSize + 1 + nameSize + 1) * sizeof(TCHAR));
-    if (fullName == NULL) {
-        free(name);
-        free(domainName);
-        free(user);
-        return PyErr_NoMemory();
-    }
-    memcpy(fullName, domainName, domainNameSize);
-    fullName[domainNameSize] = '\\';
-    memcpy(&fullName[domainNameSize + 1], name, nameSize);
-    fullName[domainNameSize + 1 + nameSize] = '\0';
-
-    returnObject = PyUnicode_Decode(
-        fullName, _tcslen(fullName), Py_FileSystemDefaultEncoding, "replace");
-
-    free(fullName);
+    py_domain = PyUnicode_FromWideChar(domainName, wcslen(domainName));
+    if (! py_domain)
+        goto error;
+    py_username = PyUnicode_FromWideChar(name, wcslen(name));
+    if (! py_username)
+        goto error;
+    py_tuple = Py_BuildValue("OO", py_domain, py_username);
+    if (! py_tuple)
+        goto error;
+    Py_DECREF(py_domain);
+    Py_DECREF(py_username);
+
     free(name);
     free(domainName);
     free(user);
 
-    return returnObject;
+    return py_tuple;
+
+error:
+    if (processHandle != NULL)
+        CloseHandle(processHandle);
+    if (tokenHandle != NULL)
+        CloseHandle(tokenHandle);
+    if (name != NULL)
+        free(name);
+    if (domainName != NULL)
+        free(domainName);
+    if (user != NULL)
+        free(user);
+    Py_XDECREF(py_domain);
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tuple);
+    return NULL;
+}
+
+
+typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG,
+                                              TCP_TABLE_CLASS, ULONG);
+
+
+// https://msdn.microsoft.com/library/aa365928.aspx
+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.
+    //
+    // Also, since we may loop a theoretically unbounded number of times here,
+    // release the GIL while we're doing this.
+    DWORD error = ERROR_INSUFFICIENT_BUFFER;
+    *size = 0;
+    *data = NULL;
+    Py_BEGIN_ALLOW_THREADS;
+    error = call(NULL, size, FALSE, address_family,
+                 TCP_TABLE_OWNER_PID_ALL, 0);
+    while (error == ERROR_INSUFFICIENT_BUFFER)
+    {
+        *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;
+        }
+    }
+    Py_END_ALLOW_THREADS;
+    return error;
+}
+
+
+typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG,
+                                              UDP_TABLE_CLASS, ULONG);
+
+
+// https://msdn.microsoft.com/library/aa365930.aspx
+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.
+    //
+    // Also, since we may loop a theoretically unbounded number of times here,
+    // release the GIL while we're doing this.
+    DWORD error = ERROR_INSUFFICIENT_BUFFER;
+    *size = 0;
+    *data = NULL;
+    Py_BEGIN_ALLOW_THREADS;
+    error = call(NULL, size, FALSE, address_family,
+                 UDP_TABLE_OWNER_PID, 0);
+    while (error == ERROR_INSUFFICIENT_BUFFER)
+    {
+        *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;
+        }
+    }
+    Py_END_ALLOW_THREADS;
+    return error;
 }
 
 
 /*
  * Return a list of network connections opened by a process
  */
 static PyObject *
-psutil_net_connections(PyObject *self, PyObject *args)
-{
+psutil_net_connections(PyObject *self, PyObject *args) {
     static long null_address[4] = { 0, 0, 0, 0 };
-
     unsigned long pid;
-    PyObject *connectionsList;
-    PyObject *connectionTuple = NULL;
-    PyObject *af_filter = NULL;
-    PyObject *type_filter = 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);
-
+    int pid_return;
     typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)(struct in_addr *, PSTR);
     _RtlIpv4AddressToStringA rtlIpv4AddressToStringA;
     typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR);
     _RtlIpv6AddressToStringA rtlIpv6AddressToStringA;
-    typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG,
-                                                  TCP_TABLE_CLASS, ULONG);
     _GetExtendedTcpTable getExtendedTcpTable;
-    typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG,
-                                                  UDP_TABLE_CLASS, ULONG);
     _GetExtendedUdpTable getExtendedUdpTable;
     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];
-    PyObject *addressTupleLocal = NULL;
     CHAR addressBufferRemote[65];
-    PyObject *addressTupleRemote = NULL;
-
-    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) {
+
+    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);
+
+    if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter))
+    {
         _psutil_conn_decref_objs();
         return NULL;
     }
 
-    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
+    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) {
-        if (psutil_pid_is_running(pid) == 0) {
+        pid_return = psutil_pid_is_running(pid);
+        if (pid_return == 0) {
             _psutil_conn_decref_objs();
-            return NoSuchProcess();
+            return NoSuchProcess("");
+        }
+        else if (pid_return == -1) {
+            _psutil_conn_decref_objs();
+            return NULL;
         }
     }
 
     // Import some functions.
     {
         HMODULE ntdll;
         HMODULE iphlpapi;
 
@@ -1541,43 +1599,41 @@ psutil_net_connections(PyObject *self, P
 
     if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) {
         PyErr_SetString(PyExc_NotImplementedError,
                         "feature not supported on this Windows version");
         _psutil_conn_decref_objs();
         return NULL;
     }
 
-    connectionsList = PyList_New(0);
-    if (connectionsList == NULL) {
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL) {
         _psutil_conn_decref_objs();
         return NULL;
     }
 
     // TCP IPv4
 
-    if ((PySequence_Contains(af_filter, _AF_INET) == 1) &&
-            (PySequence_Contains(type_filter, _SOCK_STREAM) == 1))
+    if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) &&
+            (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1))
     {
         table = NULL;
-        connectionTuple = NULL;
-        addressTupleLocal = NULL;
-        addressTupleRemote = NULL;
+        py_conn_tuple = NULL;
+        py_addr_tuple_local = NULL;
+        py_addr_tuple_remote = NULL;
         tableSize = 0;
-        getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET,
-                            TCP_TABLE_OWNER_PID_ALL, 0);
-
-        table = malloc(tableSize);
-        if (table == NULL) {
+
+        error = __GetExtendedTcpTable(getExtendedTcpTable,
+                                      AF_INET, &table, &tableSize);
+        if (error == ERROR_NOT_ENOUGH_MEMORY) {
             PyErr_NoMemory();
             goto error;
         }
 
-        if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET,
-                                TCP_TABLE_OWNER_PID_ALL, 0) == 0)
+        if (error == NO_ERROR)
         {
             tcp4Table = table;
 
             for (i = 0; i < tcp4Table->dwNumEntries; i++)
             {
                 if (pid != -1) {
                     if (tcp4Table->table[i].dwOwningPid != pid) {
                         continue;
@@ -1586,92 +1642,95 @@ psutil_net_connections(PyObject *self, P
 
                 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);
-                    addressTupleLocal = Py_BuildValue(
+                    py_addr_tuple_local = Py_BuildValue(
                         "(si)",
                         addressBufferLocal,
                         BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort));
                 }
                 else {
-                    addressTupleLocal = PyTuple_New(0);
+                    py_addr_tuple_local = PyTuple_New(0);
                 }
 
-                if (addressTupleLocal == NULL)
+                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);
-                    addressTupleRemote = Py_BuildValue(
+                    py_addr_tuple_remote = Py_BuildValue(
                         "(si)",
                         addressBufferRemote,
                         BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort));
                 }
                 else
                 {
-                    addressTupleRemote = PyTuple_New(0);
+                    py_addr_tuple_remote = PyTuple_New(0);
                 }
 
-                if (addressTupleRemote == NULL)
+                if (py_addr_tuple_remote == NULL)
                     goto error;
 
-                connectionTuple = Py_BuildValue(
+                py_conn_tuple = Py_BuildValue(
                     "(iiiNNiI)",
                     -1,
                     AF_INET,
                     SOCK_STREAM,
-                    addressTupleLocal,
-                    addressTupleRemote,
+                    py_addr_tuple_local,
+                    py_addr_tuple_remote,
                     tcp4Table->table[i].dwState,
                     tcp4Table->table[i].dwOwningPid);
-                if (!connectionTuple)
+                if (!py_conn_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_conn_tuple))
                     goto error;
-                if (PyList_Append(connectionsList, connectionTuple))
-                    goto error;
-                Py_DECREF(connectionTuple);
+                Py_DECREF(py_conn_tuple);
             }
         }
+        else {
+            PyErr_SetFromWindowsErr(error);
+            goto error;
+        }
 
         free(table);
+        table = NULL;
+        tableSize = 0;
     }
 
     // TCP IPv6
-
-    if ((PySequence_Contains(af_filter, _AF_INET6) == 1) &&
-            (PySequence_Contains(type_filter, _SOCK_STREAM) == 1))
+    if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) &&
+            (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1))
     {
         table = NULL;
-        connectionTuple = NULL;
-        addressTupleLocal = NULL;
-        addressTupleRemote = NULL;
+        py_conn_tuple = NULL;
+        py_addr_tuple_local = NULL;
+        py_addr_tuple_remote = NULL;
         tableSize = 0;
-        getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6,
-                            TCP_TABLE_OWNER_PID_ALL, 0);
-
-        table = malloc(tableSize);
-        if (table == NULL) {
+
+        error = __GetExtendedTcpTable(getExtendedTcpTable,
+                                      AF_INET6, &table, &tableSize);
+        if (error == ERROR_NOT_ENOUGH_MEMORY) {
             PyErr_NoMemory();
             goto error;
         }
 
-        if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET6,
-                                TCP_TABLE_OWNER_PID_ALL, 0) == 0)
+        if (error == NO_ERROR)
         {
             tcp6Table = table;
 
             for (i = 0; i < tcp6Table->dwNumEntries; i++)
             {
                 if (pid != -1) {
                     if (tcp6Table->table[i].dwOwningPid != pid) {
                         continue;
@@ -1680,94 +1739,95 @@ psutil_net_connections(PyObject *self, P
 
                 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);
-                    addressTupleLocal = Py_BuildValue(
+                    py_addr_tuple_local = Py_BuildValue(
                         "(si)",
                         addressBufferLocal,
                         BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort));
                 }
-                else
-                {
-                    addressTupleLocal = PyTuple_New(0);
+                else {
+                    py_addr_tuple_local = PyTuple_New(0);
                 }
 
-                if (addressTupleLocal == NULL)
+                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);
-                    addressTupleRemote = Py_BuildValue(
+                    py_addr_tuple_remote = Py_BuildValue(
                         "(si)",
                         addressBufferRemote,
                         BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort));
                 }
-                else
-                {
-                    addressTupleRemote = PyTuple_New(0);
+                else {
+                    py_addr_tuple_remote = PyTuple_New(0);
                 }
 
-                if (addressTupleRemote == NULL)
+                if (py_addr_tuple_remote == NULL)
                     goto error;
 
-                connectionTuple = Py_BuildValue(
+                py_conn_tuple = Py_BuildValue(
                     "(iiiNNiI)",
                     -1,
                     AF_INET6,
                     SOCK_STREAM,
-                    addressTupleLocal,
-                    addressTupleRemote,
+                    py_addr_tuple_local,
+                    py_addr_tuple_remote,
                     tcp6Table->table[i].dwState,
                     tcp6Table->table[i].dwOwningPid);
-                if (!connectionTuple)
+                if (!py_conn_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_conn_tuple))
                     goto error;
-                if (PyList_Append(connectionsList, connectionTuple))
-                    goto error;
-                Py_DECREF(connectionTuple);
+                Py_DECREF(py_conn_tuple);
             }
         }
+        else {
+            PyErr_SetFromWindowsErr(error);
+            goto error;
+        }
 
         free(table);
+        table = NULL;
+        tableSize = 0;
     }
 
     // UDP IPv4
 
-    if ((PySequence_Contains(af_filter, _AF_INET) == 1) &&
-            (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1))
+    if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) &&
+            (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1))
     {
         table = NULL;
-        connectionTuple = NULL;
-        addressTupleLocal = NULL;
-        addressTupleRemote = NULL;
+        py_conn_tuple = NULL;
+        py_addr_tuple_local = NULL;
+        py_addr_tuple_remote = NULL;
         tableSize = 0;
-        getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET,
-                            UDP_TABLE_OWNER_PID, 0);
-
-        table = malloc(tableSize);
-        if (table == NULL) {
+        error = __GetExtendedUdpTable(getExtendedUdpTable,
+                                      AF_INET, &table, &tableSize);
+        if (error == ERROR_NOT_ENOUGH_MEMORY) {
             PyErr_NoMemory();
             goto error;
         }
 
-        if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET,
-                                UDP_TABLE_OWNER_PID, 0) == 0)
+        if (error == NO_ERROR)
         {
             udp4Table = table;
 
             for (i = 0; i < udp4Table->dwNumEntries; i++)
             {
                 if (pid != -1) {
                     if (udp4Table->table[i].dwOwningPid != pid) {
                         continue;
@@ -1776,213 +1836,207 @@ psutil_net_connections(PyObject *self, P
 
                 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);
-                    addressTupleLocal = Py_BuildValue(
+                    py_addr_tuple_local = Py_BuildValue(
                         "(si)",
                         addressBufferLocal,
                         BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort));
                 }
                 else {
-                    addressTupleLocal = PyTuple_New(0);
+                    py_addr_tuple_local = PyTuple_New(0);
                 }
 
-                if (addressTupleLocal == NULL)
+                if (py_addr_tuple_local == NULL)
                     goto error;
 
-                connectionTuple = Py_BuildValue(
+                py_conn_tuple = Py_BuildValue(
                     "(iiiNNiI)",
                     -1,
                     AF_INET,
                     SOCK_DGRAM,
-                    addressTupleLocal,
+                    py_addr_tuple_local,
                     PyTuple_New(0),
                     PSUTIL_CONN_NONE,
                     udp4Table->table[i].dwOwningPid);
-                if (!connectionTuple)
+                if (!py_conn_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_conn_tuple))
                     goto error;
-                if (PyList_Append(connectionsList, connectionTuple))
-                    goto error;
-                Py_DECREF(connectionTuple);
+                Py_DECREF(py_conn_tuple);
             }
         }
+        else {
+            PyErr_SetFromWindowsErr(error);
+            goto error;
+        }
 
         free(table);
+        table = NULL;
+        tableSize = 0;
     }
 
     // UDP IPv6
 
-    if ((PySequence_Contains(af_filter, _AF_INET6) == 1) &&
-            (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1))
+    if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) &&
+            (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1))
     {
         table = NULL;
-        connectionTuple = NULL;
-        addressTupleLocal = NULL;
-        addressTupleRemote = NULL;
+        py_conn_tuple = NULL;
+        py_addr_tuple_local = NULL;
+        py_addr_tuple_remote = NULL;
         tableSize = 0;
-        getExtendedUdpTable(NULL, &tableSize, FALSE,
-                            AF_INET6, UDP_TABLE_OWNER_PID, 0);
-
-        table = malloc(tableSize);
-        if (table == NULL) {
+        error = __GetExtendedUdpTable(getExtendedUdpTable,
+                                      AF_INET6, &table, &tableSize);
+        if (error == ERROR_NOT_ENOUGH_MEMORY) {
             PyErr_NoMemory();
             goto error;
         }
 
-        if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET6,
-                                UDP_TABLE_OWNER_PID, 0) == 0)
+        if (error == NO_ERROR)
         {
             udp6Table = table;
 
-            for (i = 0; i < udp6Table->dwNumEntries; i++)
-            {
+            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);
-                    addressTupleLocal = Py_BuildValue(
+                    py_addr_tuple_local = Py_BuildValue(
                         "(si)",
                         addressBufferLocal,
                         BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort));
                 }
                 else {
-                    addressTupleLocal = PyTuple_New(0);
+                    py_addr_tuple_local = PyTuple_New(0);
                 }
 
-                if (addressTupleLocal == NULL)
+                if (py_addr_tuple_local == NULL)
                     goto error;
 
-                connectionTuple = Py_BuildValue(
+                py_conn_tuple = Py_BuildValue(
                     "(iiiNNiI)",
                     -1,
                     AF_INET6,
                     SOCK_DGRAM,
-                    addressTupleLocal,
+                    py_addr_tuple_local,
                     PyTuple_New(0),
                     PSUTIL_CONN_NONE,
                     udp6Table->table[i].dwOwningPid);
-                if (!connectionTuple)
+                if (!py_conn_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_conn_tuple))
                     goto error;
-                if (PyList_Append(connectionsList, connectionTuple))
-                    goto error;
-                Py_DECREF(connectionTuple);
+                Py_DECREF(py_conn_tuple);
             }
         }
+        else {
+            PyErr_SetFromWindowsErr(error);
+            goto error;
+        }
 
         free(table);
+        table = NULL;
+        tableSize = 0;
     }
 
     _psutil_conn_decref_objs();
-    return connectionsList;
+    return py_retlist;
 
 error:
     _psutil_conn_decref_objs();
-    Py_XDECREF(connectionTuple);
-    Py_XDECREF(addressTupleLocal);
-    Py_XDECREF(addressTupleRemote);
-    Py_DECREF(connectionsList);
+    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)
-{
+psutil_proc_priority_get(PyObject *self, PyObject *args) {
     long pid;
     DWORD priority;
     HANDLE hProcess;
+
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-
     hProcess = psutil_handle_from_pid(pid);
-    if (hProcess == NULL) {
+    if (hProcess == NULL)
         return NULL;
-    }
-
     priority = GetPriorityClass(hProcess);
     CloseHandle(hProcess);
-    if (priority == 0) {
-        PyErr_SetFromWindowsErr(0);
-        return NULL;
-    }
+    if (priority == 0)
+        return PyErr_SetFromWindowsErr(0);
     return Py_BuildValue("i", priority);
 }
 
 
 /*
  * Set process priority.
  */
 static PyObject *
-psutil_proc_priority_set(PyObject *self, PyObject *args)
-{
+psutil_proc_priority_set(PyObject *self, PyObject *args) {
     long pid;
     int priority;
     int retval;
     HANDLE hProcess;
-    DWORD dwDesiredAccess = \
-        PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
-    if (! PyArg_ParseTuple(args, "li", &pid, &priority)) {
+    DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
+
+    if (! PyArg_ParseTuple(args, "li", &pid, &priority))
         return NULL;
-    }
-
-    hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess);
-    if (hProcess == NULL) {
+    hProcess = psutil_handle_from_pid_waccess(pid, access);
+    if (hProcess == NULL)
         return NULL;
-    }
-
     retval = SetPriorityClass(hProcess, priority);
     CloseHandle(hProcess);
-    if (retval == 0) {
-        PyErr_SetFromWindowsErr(0);
-        return NULL;
-    }
+    if (retval == 0)
+        return PyErr_SetFromWindowsErr(0);
     Py_RETURN_NONE;
 }
 
 
 #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)
-{
+psutil_proc_io_priority_get(PyObject *self, PyObject *args) {
     long pid;
     HANDLE hProcess;
     PULONG IoPriority;
 
     _NtQueryInformationProcess NtQueryInformationProcess =
         (_NtQueryInformationProcess)GetProcAddress(
             GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     hProcess = psutil_handle_from_pid(pid);
-    if (hProcess == NULL) {
+    if (hProcess == NULL)
         return NULL;
-    }
 
     NtQueryInformationProcess(
         hProcess,
         ProcessIoPriority,
         &IoPriority,
         sizeof(ULONG),
         NULL
     );
@@ -1990,39 +2044,36 @@ psutil_proc_io_priority_get(PyObject *se
     return Py_BuildValue("i", IoPriority);
 }
 
 
 /*
  * Set process IO priority.
  */
 static PyObject *
-psutil_proc_io_priority_set(PyObject *self, PyObject *args)
-{
+psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
     long pid;
     int prio;
     HANDLE hProcess;
 
     _NtSetInformationProcess NtSetInformationProcess =
         (_NtSetInformationProcess)GetProcAddress(
             GetModuleHandleA("ntdll.dll"), "NtSetInformationProcess");
 
     if (NtSetInformationProcess == NULL) {
         PyErr_SetString(PyExc_RuntimeError,
-                        "couldn't get NtSetInformationProcess");
+                        "couldn't get NtSetInformationProcess syscall");
         return NULL;
     }
 
-    if (! PyArg_ParseTuple(args, "li", &pid, &prio)) {
+    if (! PyArg_ParseTuple(args, "li", &pid, &prio))
         return NULL;
-    }
     hProcess = psutil_handle_from_pid_waccess(pid, PROCESS_ALL_ACCESS);
-    if (hProcess == NULL) {
+    if (hProcess == NULL)
         return NULL;
-    }
 
     NtSetInformationProcess(
         hProcess,
         ProcessIoPriority,
         (PVOID)&prio,
         sizeof((PVOID)prio)
     );
 
@@ -2031,47 +2082,46 @@ psutil_proc_io_priority_set(PyObject *se
 }
 #endif
 
 
 /*
  * Return a Python tuple referencing process I/O counters.
  */
 static PyObject *
-psutil_proc_io_counters(PyObject *self, PyObject *args)
-{
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
     DWORD pid;
     HANDLE hProcess;
     IO_COUNTERS IoCounters;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     hProcess = psutil_handle_from_pid(pid);
-    if (NULL == hProcess) {
+    if (NULL == hProcess)
         return NULL;
-    }
     if (! GetProcessIoCounters(hProcess, &IoCounters)) {
         CloseHandle(hProcess);
         return PyErr_SetFromWindowsErr(0);
     }
     CloseHandle(hProcess);
-    return Py_BuildValue("(KKKK)",
+    return Py_BuildValue("(KKKKKK)",
                          IoCounters.ReadOperationCount,
                          IoCounters.WriteOperationCount,
                          IoCounters.ReadTransferCount,
-                         IoCounters.WriteTransferCount);
+                         IoCounters.WriteTransferCount,
+                         IoCounters.OtherOperationCount,
+                         IoCounters.OtherTransferCount);
 }
 
 
 /*
  * Return process CPU affinity as a bitmask
  */
 static PyObject *
-psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
     DWORD pid;
     HANDLE hProcess;
     DWORD_PTR proc_mask;
     DWORD_PTR system_mask;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     hProcess = psutil_handle_from_pid(pid);
@@ -2091,54 +2141,51 @@ psutil_proc_cpu_affinity_get(PyObject *s
 #endif
 }
 
 
 /*
  * Set process CPU affinity
  */
 static PyObject *
-psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args)
-{
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
     DWORD pid;
     HANDLE hProcess;
     DWORD dwDesiredAccess = \
         PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
     DWORD_PTR mask;
 
 #ifdef _WIN64
     if (! PyArg_ParseTuple(args, "lK", &pid, &mask))
 #else
     if (! PyArg_ParseTuple(args, "lk", &pid, &mask))
 #endif
     {
         return NULL;
     }
     hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess);
-    if (hProcess == NULL) {
+    if (hProcess == NULL)
         return NULL;
-    }
 
     if (SetProcessAffinityMask(hProcess, mask) == 0) {
         CloseHandle(hProcess);
         return PyErr_SetFromWindowsErr(0);
     }
 
     CloseHandle(hProcess);
     Py_RETURN_NONE;
 }
 
 
 /*
  * Return True if one of the process threads is in a waiting or
  * suspended status.
  */
 static PyObject *
-psutil_proc_is_suspended(PyObject *self, PyObject *args)
-{
+psutil_proc_is_suspended(PyObject *self, PyObject *args) {
     DWORD pid;
     ULONG i;
     PSYSTEM_PROCESS_INFORMATION process;
     PVOID buffer;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     if (! psutil_get_proc_info(pid, &process, &buffer)) {
@@ -2156,18 +2203,17 @@ psutil_proc_is_suspended(PyObject *self,
     Py_RETURN_TRUE;
 }
 
 
 /*
  * Return path's disk total and free as a Python tuple.
  */
 static PyObject *
-psutil_disk_usage(PyObject *self, PyObject *args)
-{
+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
@@ -2195,67 +2241,96 @@ return_:
         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)
-{
-    char ifname[MAX_PATH];
+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() failed.");
+            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->dwInUcastPkts,
+                                    (pIfRow->dwOutUcastPkts + pIfRow->dwOutNUcastPkts),
+                                    (pIfRow->dwInUcastPkts + pIfRow->dwInNUcastPkts),
                                     pIfRow->dwInErrors,
                                     pIfRow->dwOutErrors,
                                     pIfRow->dwInDiscards,
                                     pIfRow->dwOutDiscards);
+#endif
+
         if (!py_nic_info)
             goto error;
 
-        sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName);
-        py_nic_name = PyUnicode_Decode(
-            ifname, _tcslen(ifname), Py_FileSystemDefaultEncoding, "replace");
+        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);
 
@@ -2277,88 +2352,123 @@ error:
     return NULL;
 }
 
 
 /*
  * Return a Python dict of tuples for disk I/O information
  */
 static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args)
-{
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
     DISK_PERFORMANCE_WIN_2008 diskPerformance;
     DWORD dwSize;
     HANDLE hDevice = NULL;
     char szDevice[MAX_PATH];
     char szDeviceDisplay[MAX_PATH];
     int devNum;
+    int i;
+    size_t ioctrlSize;
+    BOOL WINAPI ret;
     PyObject *py_retdict = PyDict_New();
-    PyObject *py_disk_info = NULL;
-    if (py_retdict == NULL) {
+    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_disk_info = NULL;
+        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) {
+        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;
         }
-        if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
-                            &diskPerformance, sizeof(diskPerformance),
-                            &dwSize, NULL))
-        {
-            sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum);
-            py_disk_info = Py_BuildValue(
-                "(IILLKK)",
-                diskPerformance.ReadCount,
-                diskPerformance.WriteCount,
-                diskPerformance.BytesRead,
-                diskPerformance.BytesWritten,
-                (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000,
-                (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000);
-            if (!py_disk_info)
-                goto error;
-            if (PyDict_SetItemString(py_retdict, szDeviceDisplay,
-                                     py_disk_info))
-            {
-                goto error;
-            }
-            Py_XDECREF(py_disk_info);
-        }
-        else {
-            // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when
-            // compiling with mingw32; not sure what to do.
-            // return PyErr_SetFromWindowsErr(0);
-            ;;
-        }
-
+
+        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_disk_info);
+    Py_XDECREF(py_tuple);
     Py_DECREF(py_retdict);
     if (hDevice != NULL)
         CloseHandle(hDevice);
     return NULL;
 }
 
 
-static char *psutil_get_drive_type(int type)
-{
+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:
@@ -2374,47 +2484,51 @@ static char *psutil_get_drive_type(int t
     }
 }
 
 
 #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)
-{
+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
-    SetErrorMode(SEM_FAILCRITICALERRORS);
-
-    if (! PyArg_ParseTuple(args, "O", &py_all)) {
+    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);
@@ -2453,30 +2567,60 @@ psutil_disk_partitions(PyObject *self, P
             // 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) {
+            if (pflags & FILE_READ_ONLY_VOLUME)
                 strcat_s(opts, _countof(opts), "ro");
-            }
-            else {
+            else
                 strcat_s(opts, _countof(opts), "rw");
-            }
-            if (pflags & FILE_VOLUME_IS_COMPRESSED) {
+            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) {
+        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);
@@ -2486,34 +2630,33 @@ psutil_disk_partitions(PyObject *self, P
             goto error;
         Py_DECREF(py_tuple);
         goto next;
 
 next:
         drive_letter = strchr(drive_letter, 0) + 1;
     }
 
-    SetErrorMode(0);
+    SetErrorMode(old_mode);
     return py_retlist;
 
 error:
-    SetErrorMode(0);
+    SetErrorMode(old_mode);
     Py_XDECREF(py_tuple);
     Py_DECREF(py_retlist);
     return NULL;
 }
 
 /*
  * Return a Python dict of tuples for disk I/O information
  */
 static PyObject *
-psutil_users(PyObject *self, PyObject *args)
-{
-    HANDLE hServer = NULL;
-    LPTSTR buffer_user = NULL;
+psutil_users(PyObject *self, PyObject *args) {
+    HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
+    WCHAR *buffer_user = NULL;
     LPTSTR buffer_addr = NULL;
     PWTS_SESSION_INFO sessions = NULL;
     DWORD count;
     DWORD i;
     DWORD sessionId;
     DWORD bytes;
     PWTS_CLIENT_ADDRESS address;
     char address_str[50];
@@ -2522,61 +2665,51 @@ psutil_users(PyObject *self, PyObject *a
     PWINSTATIONQUERYINFORMATIONW WinStationQueryInformationW;
     WINSTATION_INFO station_info;
     HINSTANCE hInstWinSta = NULL;
     ULONG returnLen;
 
     PyObject *py_retlist = PyList_New(0);
     PyObject *py_tuple = NULL;
     PyObject *py_address = NULL;
-    PyObject *py_buffer_user_encoded = NULL;
-
-    if (py_retlist == NULL) {
+    PyObject *py_username = NULL;
+
+    if (py_retlist == NULL)
         return NULL;
-    }
 
     hInstWinSta = LoadLibraryA("winsta.dll");
     WinStationQueryInformationW = (PWINSTATIONQUERYINFORMATIONW) \
         GetProcAddress(hInstWinSta, "WinStationQueryInformationW");
 
-    hServer = WTSOpenServer('\0');
-    if (hServer == NULL) {
-        PyErr_SetFromWindowsErr(0);
-        goto error;
-    }
-
     if (WTSEnumerateSessions(hServer, 0, 1, &sessions, &count) == 0) {
         PyErr_SetFromWindowsErr(0);
         goto error;
     }
 
     for (i = 0; i < count; i++) {
         py_address = NULL;
         py_tuple = NULL;
         sessionId = sessions[i].SessionId;
-        if (buffer_user != NULL) {
+        if (buffer_user != NULL)
             WTSFreeMemory(buffer_user);
-        }
-        if (buffer_addr != NULL) {
+        if (buffer_addr != NULL)
             WTSFreeMemory(buffer_addr);
-        }
 
         buffer_user = NULL;
         buffer_addr = NULL;
 
         // username
         bytes = 0;
-        if (WTSQuerySessionInformation(hServer, sessionId, WTSUserName,
-                                       &buffer_user, &bytes) == 0) {
+        if (WTSQuerySessionInformationW(hServer, sessionId, WTSUserName,
+                                        &buffer_user, &bytes) == 0) {
             PyErr_SetFromWindowsErr(0);
             goto error;
         }
-        if (bytes == 1) {
+        if (bytes <= 2)
             continue;
-        }
 
         // address
         bytes = 0;
         if (WTSQuerySessionInformation(hServer, sessionId, WTSClientAddress,
                                        &buffer_addr, &bytes) == 0) {
             PyErr_SetFromWindowsErr(0);
             goto error;
         }
@@ -2609,122 +2742,111 @@ psutil_users(PyObject *self, PyObject *a
             goto error;
         }
 
         unix_time = ((LONGLONG)station_info.ConnectTime.dwHighDateTime) << 32;
         unix_time += \
             station_info.ConnectTime.dwLowDateTime - 116444736000000000LL;
         unix_time /= 10000000;
 
-        py_buffer_user_encoded = PyUnicode_Decode(
-            buffer_user, _tcslen(buffer_user), Py_FileSystemDefaultEncoding,
-            "replace");
-        py_tuple = Py_BuildValue("OOd", py_buffer_user_encoded, py_address,
+        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);
         if (!py_tuple)
             goto error;
         if (PyList_Append(py_retlist, py_tuple))
             goto error;
-        Py_XDECREF(py_buffer_user_encoded);
+        Py_XDECREF(py_username);
         Py_XDECREF(py_address);
         Py_XDECREF(py_tuple);
     }
 
-    WTSCloseServer(hServer);
     WTSFreeMemory(sessions);
     WTSFreeMemory(buffer_user);
     WTSFreeMemory(buffer_addr);
     FreeLibrary(hInstWinSta);
     return py_retlist;
 
 error:
-    Py_XDECREF(py_buffer_user_encoded);
+    Py_XDECREF(py_username);
     Py_XDECREF(py_tuple);
     Py_XDECREF(py_address);
     Py_DECREF(py_retlist);
 
-    if (hInstWinSta != NULL) {
+    if (hInstWinSta != NULL)
         FreeLibrary(hInstWinSta);
-    }
-    if (hServer != NULL) {
-        WTSCloseServer(hServer);
-    }
-    if (sessions != NULL) {
+    if (sessions != NULL)
         WTSFreeMemory(sessions);
-    }
-    if (buffer_user != NULL) {
+    if (buffer_user != NULL)
         WTSFreeMemory(buffer_user);
-    }
-    if (buffer_addr != NULL) {
+    if (buffer_addr != NULL)
         WTSFreeMemory(buffer_addr);
-    }
     return NULL;
 }
 
 
 /*
  * Return the number of handles opened by process.
  */
 static PyObject *
-psutil_proc_num_handles(PyObject *self, PyObject *args)
-{
+psutil_proc_num_handles(PyObject *self, PyObject *args) {
     DWORD pid;
     HANDLE hProcess;
     DWORD handleCount;
 
     if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
     hProcess = psutil_handle_from_pid(pid);
-    if (NULL == hProcess) {
+    if (NULL == hProcess)
         return NULL;
-    }
     if (! GetProcessHandleCount(hProcess, &handleCount)) {
         CloseHandle(hProcess);
         return PyErr_SetFromWindowsErr(0);
     }
     CloseHandle(hProcess);
     return Py_BuildValue("k", handleCount);
 }
 
 
 /*
  * 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)
- * - user/kernel times (fallback)
- * - create time (fallback)
- * - io counters (fallback)
+ * - 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)
-{
+psutil_proc_info(PyObject *self, PyObject *args) {
     DWORD pid;
     PSYSTEM_PROCESS_INFORMATION process;
     PVOID buffer;
-    ULONG num_handles;
     ULONG i;
     ULONG ctx_switches = 0;
     double user_time;
     double kernel_time;
     long long create_time;
-    int num_threads;
-    LONGLONG io_rcount, io_wcount, io_rbytes, io_wbytes;
-
+    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;
 
-    num_handles = process->HandleCount;
     for (i = 0; i < process->NumberOfThreads; i++)
         ctx_switches += process->Threads[i].ContextSwitches;
     user_time = (double)process->UserTime.HighPart * 429.4967296 + \
                 (double)process->UserTime.LowPart * 1e-7;
     kernel_time = (double)process->KernelTime.HighPart * 429.4967296 + \
                   (double)process->KernelTime.LowPart * 1e-7;
     // Convert the LARGE_INTEGER union to a Unix time.
     // It's the best I could find by googling and borrowing code here
@@ -2733,41 +2855,61 @@ psutil_proc_info(PyObject *self, PyObjec
         // 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;
     }
-    num_threads = (int)process->NumberOfThreads;
-    io_rcount = process->ReadOperationCount.QuadPart;
-    io_wcount = process->WriteOperationCount.QuadPart;
-    io_rbytes = process->ReadTransferCount.QuadPart;
-    io_wbytes = process->WriteTransferCount.QuadPart;
+
+#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_BuildValue(
-        "kkdddiKKKK",
-        num_handles,
-        ctx_switches,
-        user_time,
-        kernel_time,
-        (double)create_time,
-        num_threads,
-        io_rcount,
-        io_wcount,
-        io_rbytes,
-        io_wbytes
-    );
+    return py_retlist;
 }
 
 
-static char *get_region_protection_string(ULONG protection)
-{
+static char *get_region_protection_string(ULONG protection) {
     switch (protection & 0xff) {
         case PAGE_NOACCESS:
             return "";
         case PAGE_READONLY:
             return "r";
         case PAGE_READWRITE:
             return "rw";
         case PAGE_WRITECOPY:
@@ -2785,301 +2927,352 @@ static char *get_region_protection_strin
     }
 }
 
 
 /*
  * Return a list of process's memory mappings.
  */
 static PyObject *
-psutil_proc_memory_maps(PyObject *self, PyObject *args)
-{
+psutil_proc_memory_maps(PyObject *self, PyObject *args) {
+#ifdef _WIN64
+    MEMORY_BASIC_INFORMATION64 basicInfo;
+#else
+    MEMORY_BASIC_INFORMATION basicInfo;
+#endif
     DWORD pid;
     HANDLE hProcess = NULL;
-    MEMORY_BASIC_INFORMATION basicInfo;
     PVOID baseAddress;
     PVOID previousAllocationBase;
-    CHAR mappedFileName[MAX_PATH];
+    WCHAR mappedFileName[MAX_PATH];
     SYSTEM_INFO system_info;
     LPVOID maxAddr;
-    PyObject *py_list = PyList_New(0);
+    PyObject *py_retlist = PyList_New(0);
     PyObject *py_tuple = NULL;
-
-    if (py_list == NULL) {
+    PyObject *py_str = NULL;
+
+    if (py_retlist == NULL)
         return NULL;
-    }
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
-    }
     hProcess = psutil_handle_from_pid(pid);
-    if (NULL == hProcess) {
+    if (NULL == hProcess)
         goto error;
-    }
 
     GetSystemInfo(&system_info);
     maxAddr = system_info.lpMaximumApplicationAddress;
     baseAddress = NULL;
     previousAllocationBase = NULL;
 
     while (VirtualQueryEx(hProcess, baseAddress, &basicInfo,
                           sizeof(MEMORY_BASIC_INFORMATION)))
     {
         py_tuple = NULL;
-        if (baseAddress > maxAddr) {
+        if (baseAddress > maxAddr)
             break;
-        }
-        if (GetMappedFileNameA(hProcess, baseAddress, mappedFileName,
+        if (GetMappedFileNameW(hProcess, baseAddress, mappedFileName,
                                sizeof(mappedFileName)))
         {
-            py_tuple = Py_BuildValue(
-                "(kssI)",
-                (unsigned long)baseAddress,
-                get_region_protection_string(basicInfo.Protect),
-                mappedFileName,
-                basicInfo.RegionSize);
+            py_str = PyUnicode_FromWideChar(mappedFileName,
+                                            wcslen(mappedFileName));
+            if (py_str == NULL)
+                goto error;
+#ifdef _WIN64
+           py_tuple = Py_BuildValue(
+              "(KsOI)",
+              (unsigned long long)baseAddress,
+#else
+           py_tuple = Py_BuildValue(
+              "(ksOI)",
+              (unsigned long)baseAddress,
+#endif
+              get_region_protection_string(basicInfo.Protect),
+              py_str,
+              basicInfo.RegionSize);
+
             if (!py_tuple)
                 goto error;
-            if (PyList_Append(py_list, py_tuple))
+            if (PyList_Append(py_retlist, py_tuple))
                 goto error;
             Py_DECREF(py_tuple);
+            Py_DECREF(py_str);
         }
         previousAllocationBase = basicInfo.AllocationBase;
         baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize;
     }
 
     CloseHandle(hProcess);
-    return py_list;
+    return py_retlist;
 
 error:
     Py_XDECREF(py_tuple);
-    Py_DECREF(py_list);
+    Py_XDECREF(py_str);
+    Py_DECREF(py_retlist);
     if (hProcess != NULL)
         CloseHandle(hProcess);
     return NULL;
 }
 
 
 /*
  * Return a {pid:ppid, ...} dict for all running processes.
  */
 static PyObject *
-psutil_ppid_map(PyObject *self, PyObject *args)
-{
-    PyObject *pid = NULL;
-    PyObject *ppid = NULL;
+psutil_ppid_map(PyObject *self, PyObject *args) {
+    PyObject *py_pid = NULL;
+    PyObject *py_ppid = NULL;
     PyObject *py_retdict = PyDict_New();
     HANDLE handle = NULL;
     PROCESSENTRY32 pe = {0};
     pe.dwSize = sizeof(PROCESSENTRY32);
 
     if (py_retdict == NULL)
         return NULL;
     handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     if (handle == INVALID_HANDLE_VALUE) {
         PyErr_SetFromWindowsErr(0);
         Py_DECREF(py_retdict);
         return NULL;
     }
 
     if (Process32First(handle, &pe)) {
         do {
-            pid = Py_BuildValue("I", pe.th32ProcessID);
-            if (pid == NULL)
+            py_pid = Py_BuildValue("I", pe.th32ProcessID);
+            if (py_pid == NULL)
                 goto error;
-            ppid = Py_BuildValue("I", pe.th32ParentProcessID);
-            if (ppid == NULL)
+            py_ppid = Py_BuildValue("I", pe.th32ParentProcessID);
+            if (py_ppid == NULL)
                 goto error;
-            if (PyDict_SetItem(py_retdict, pid, ppid))
+            if (PyDict_SetItem(py_retdict, py_pid, py_ppid))
                 goto error;
-            Py_DECREF(pid);
-            Py_DECREF(ppid);
+            Py_DECREF(py_pid);
+            Py_DECREF(py_ppid);
         } while (Process32Next(handle, &pe));
     }
 
     CloseHandle(handle);
     return py_retdict;
 
 error:
-    Py_XDECREF(pid);
-    Py_XDECREF(ppid);
+    Py_XDECREF(py_pid);
+    Py_XDECREF(py_ppid);
     Py_DECREF(py_retdict);
     CloseHandle(handle);
     return NULL;
 }
 
 
 /*
  * Return NICs addresses.
  */
 
 static PyObject *
-psutil_net_if_addrs(PyObject *self, PyObject *args)
-{
+psutil_net_if_addrs(PyObject *self, PyObject *args) {
     unsigned int i = 0;
     ULONG family;
     PCTSTR intRet;
+    PCTSTR netmaskIntRet;
     char *ptr;
-    char buff[100];
-    char ifname[MAX_PATH];
-    DWORD bufflen = 100;
+    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;
-        sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName);
+
+        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;
+            ptr = buff_macaddr;
             *ptr = '\0';
             for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) {
                 if (i == (pCurrAddresses->PhysicalAddressLength - 1)) {
-                    sprintf_s(ptr, _countof(buff), "%.2X\n",
+                    sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n",
                             (int)pCurrAddresses->PhysicalAddress[i]);
                 }
                 else {
-                    sprintf_s(ptr, _countof(buff), "%.2X-",
+                    sprintf_s(ptr, _countof(buff_macaddr), "%.2X-",
                             (int)pCurrAddresses->PhysicalAddress[i]);
                 }
                 ptr += 3;
             }
             *--ptr = '\0';
 
-#if PY_MAJOR_VERSION >= 3
-            py_mac_address = PyUnicode_FromString(buff);
-#else
-            py_mac_address = PyString_FromString(buff);
-#endif
+            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(
-                "(siOOO)",
-                ifname,
+                "(OiOOOO)",
+                py_nic_name,
                 -1,  // this will be converted later to AF_LINK
                 py_mac_address,
-                Py_None,
-                Py_None
+                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,
-                                       bufflen);
+                    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, bufflen);
+                                       buff_addr, sizeof(buff_addr));
+                    if (!intRet)
+                        goto error;
                 }
                 else {
                     // we should never get here
                     pUnicast = pUnicast->Next;
                     continue;
                 }
 
-                if (intRet == NULL) {
-                    PyErr_SetFromWindowsErr(GetLastError());
-                    goto error;
-                }
 #if PY_MAJOR_VERSION >= 3
-                py_address = PyUnicode_FromString(buff);
+                py_address = PyUnicode_FromString(buff_addr);
 #else
-                py_address = PyString_FromString(buff);
+                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(
-                    "(siOOO)",
-                    ifname,
+                    "(OiOOOO)",
+                    py_nic_name,
                     family,
                     py_address,
-                    Py_None,
-                    Py_None
+                    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)
-{
+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 friendly_name[MAX_PATH];
     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();
@@ -3098,110 +3291,317 @@ psutil_net_if_stats(PyObject *self, PyOb
         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() failed");
+        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) {
-                sprintf_s(friendly_name, MAX_PATH, "%wS", pCurrAddresses->FriendlyName);
+                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 ||
+        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_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_SetItemString(py_retdict, friendly_name, py_ifc_info))
+        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) {
+    // NtQuerySystemInformation stuff
+    typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG);
+    NTQSI_PROC NtQuerySystemInformation;
+    HINSTANCE hNtDll;
+
+    NTSTATUS status;
+    _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL;
+    _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
+    _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL;
+    SYSTEM_INFO si;
+    UINT i;
+    ULONG64 dpcs = 0;
+    ULONG interrupts = 0;
+
+    // obtain NtQuerySystemInformation
+    hNtDll = LoadLibrary(TEXT("ntdll.dll"));
+    if (hNtDll == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+    NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(
+        hNtDll, "NtQuerySystemInformation");
+    if (NtQuerySystemInformation == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // retrives number of processors
+    GetSystemInfo(&si);
+
+    // get syscalls / ctx switches
+    spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \
+           malloc(si.dwNumberOfProcessors * \
+                  sizeof(_SYSTEM_PERFORMANCE_INFORMATION));
+    if (spi == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    status = NtQuerySystemInformation(
+        SystemPerformanceInformation,
+        spi,
+        si.dwNumberOfProcessors * sizeof(_SYSTEM_PERFORMANCE_INFORMATION),
+        NULL);
+    if (status != 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // get DPCs
+    InterruptInformation = \
+        malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) *
+               si.dwNumberOfProcessors);
+    if (InterruptInformation == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    status = NtQuerySystemInformation(
+        SystemInterruptInformation,
+        InterruptInformation,
+        si.dwNumberOfProcessors * sizeof(SYSTEM_INTERRUPT_INFORMATION),
+        NULL);
+    if (status != 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+    for (i = 0; i < si.dwNumberOfProcessors; i++) {
+        dpcs += InterruptInformation[i].DpcCount;
+    }
+
+    // get interrupts
+    sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
+        malloc(si.dwNumberOfProcessors * \
+               sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
+    if (sppi == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    status = NtQuerySystemInformation(
+        SystemProcessorPerformanceInformation,
+        sppi,
+        si.dwNumberOfProcessors * sizeof
+            (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
+        NULL);
+    if (status != 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    for (i = 0; i < si.dwNumberOfProcessors; i++) {
+        interrupts += sppi[i].InterruptCount;
+    }
+
+    // done
+    free(spi);
+    free(InterruptInformation);
+    free(sppi);
+    FreeLibrary(hNtDll);
+    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);
+    if (hNtDll)
+        FreeLibrary(hNtDll);
+    return NULL;
+}
+
+
+/*
+ * Return CPU frequency.
+ */
+static PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+    PROCESSOR_POWER_INFORMATION *ppi;
+    NTSTATUS ret;
+    size_t size;
+    LPBYTE pBuffer = NULL;
+    ULONG current;
+    ULONG max;
+    unsigned int num_cpus;
+    SYSTEM_INFO system_info;
+    system_info.dwNumberOfProcessors = 0;
+
+    // Get the number of CPUs.
+    GetSystemInfo(&system_info);
+    if (system_info.dwNumberOfProcessors == 0)
+        num_cpus = 1;
+    else
+        num_cpus = system_info.dwNumberOfProcessors;
+
+    // Allocate size.
+    size = num_cpus * sizeof(PROCESSOR_POWER_INFORMATION);
+    pBuffer = (BYTE*)LocalAlloc(LPTR, size);
+    if (! pBuffer)
+        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.
+ */
+static PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+    SYSTEM_POWER_STATUS sps;
+
+    if (GetSystemPowerStatus(&sps) == 0)
+        return PyErr_SetFromWindowsErr(0);
+    return Py_BuildValue(
+        "iiiI",
+        sps.ACLineStatus,  // whether AC is connected: 0=no, 1=yes, 255=unknown
+        // status flag:
+        // 1, 2, 4 = high, low, critical
+        // 8 = charging
+        // 128 = no battery
+        sps.BatteryFlag,
+        sps.BatteryLifePercent,  // percent
+        sps.BatteryLifeTime  // remaining secs
+    );
+}
+
+
 // ------------------------ Python init ---------------------------
 
 static PyMethodDef
-PsutilMethods[] =
-{
+PsutilMethods[] = {
     // --- per-process functions
 
     {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
      "Return process cmdline as a list of cmdline arguments"},
+    {"proc_environ", psutil_proc_environ, METH_VARARGS,
+     "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,
      "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_info_2", psutil_proc_memory_info_2, METH_VARARGS,
-     "Alternate implementation"},
+    {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
+     "Return the USS of the process"},
     {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
      "Return process current working directory"},
     {"proc_suspend", psutil_proc_suspend, METH_VARARGS,
      "Suspend a process"},
     {"proc_resume", psutil_proc_resume, METH_VARARGS,
      "Resume a process"},
     {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
      "Return files opened by process"},
@@ -3268,21 +3668,45 @@ PsutilMethods[] =
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return disk partitions."},
     {"net_connections", psutil_net_connections, METH_VARARGS,
      "Return system-wide connections"},
     {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
      "Return NICs addresses."},
     {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
      "Return NICs stats."},
+    {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+     "Return NICs stats."},
+    {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
+     "Return CPU frequency."},
+    {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
+     "Return battery metrics usage."},
+
+    // --- windows services
+    {"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS,
+     "List all services"},
+    {"winservice_query_config", psutil_winservice_query_config, METH_VARARGS,
+     "Return service config"},
+    {"winservice_query_status", psutil_winservice_query_status, METH_VARARGS,
+     "Return service config"},
+    {"winservice_query_descr", psutil_winservice_query_descr, METH_VARARGS,
+     "Return the description of a service"},
+    {"winservice_start", psutil_winservice_start, METH_VARARGS,
+     "Start a service"},
+    {"winservice_stop", psutil_winservice_stop, METH_VARARGS,
+     "Stop a service"},
 
     // --- windows API bindings
     {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
      "QueryDosDevice binding"},
 
+    // --- others
+    {"set_testing", psutil_set_testing, METH_NOARGS,
+     "Set psutil in testing mode"},
+
     {NULL, NULL, 0, NULL}
 };
 
 
 struct module_state {
     PyObject *error;
 };
 
@@ -3355,16 +3779,17 @@ void init_psutil_windows(void)
     PyModule_AddIntConstant(
         module, "HIGH_PRIORITY_CLASS", HIGH_PRIORITY_CLASS);
     PyModule_AddIntConstant(
         module, "IDLE_PRIORITY_CLASS", IDLE_PRIORITY_CLASS);
     PyModule_AddIntConstant(
         module, "NORMAL_PRIORITY_CLASS", NORMAL_PRIORITY_CLASS);
     PyModule_AddIntConstant(
         module, "REALTIME_PRIORITY_CLASS", REALTIME_PRIORITY_CLASS);
+
     // connection status constants
     // http://msdn.microsoft.com/en-us/library/cc669305.aspx
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_CLOSED", MIB_TCP_STATE_CLOSED);
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_CLOSING", MIB_TCP_STATE_CLOSING);
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_CLOSE_WAIT", MIB_TCP_STATE_CLOSE_WAIT);
@@ -3385,21 +3810,45 @@ void init_psutil_windows(void)
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT);
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT);
     PyModule_AddIntConstant(
         module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB);
     PyModule_AddIntConstant(
         module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+
+    // service status constants
+    /*
+    PyModule_AddIntConstant(
+        module, "SERVICE_CONTINUE_PENDING", SERVICE_CONTINUE_PENDING);
+    PyModule_AddIntConstant(
+        module, "SERVICE_PAUSE_PENDING", SERVICE_PAUSE_PENDING);
+    PyModule_AddIntConstant(
+        module, "SERVICE_PAUSED", SERVICE_PAUSED);
+    PyModule_AddIntConstant(
+        module, "SERVICE_RUNNING", SERVICE_RUNNING);
+    PyModule_AddIntConstant(
+        module, "SERVICE_START_PENDING", SERVICE_START_PENDING);
+    PyModule_AddIntConstant(
+        module, "SERVICE_STOP_PENDING", SERVICE_STOP_PENDING);
+    PyModule_AddIntConstant(
+        module, "SERVICE_STOPPED", SERVICE_STOPPED);
+    */
+
     // ...for internal use in _psutil_windows.py
     PyModule_AddIntConstant(
         module, "INFINITE", INFINITE);
     PyModule_AddIntConstant(
         module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED);
+    PyModule_AddIntConstant(
+        module, "ERROR_INVALID_NAME", ERROR_INVALID_NAME);
+    PyModule_AddIntConstant(
+        module, "ERROR_SERVICE_DOES_NOT_EXIST", ERROR_SERVICE_DOES_NOT_EXIST);
 
     // set SeDebug for the current process
     psutil_set_se_debug();
+    psutil_setup();
 
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
deleted file mode 100644
--- a/third_party/python/psutil/psutil/_psutil_windows.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 <Python.h>
-#include <windows.h>
-
-// --- per-process functions
-
-static PyObject* psutil_proc_cmdline(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_create_time(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_info(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_is_suspended(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_kill(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_info_2(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_name(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_handles(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_priority_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_priority_set(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_resume(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_suspend(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_username(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_wait(PyObject* self, PyObject* args);
-
-#if (PSUTIL_WINVER >= 0x0600)  // Windows Vista
-static PyObject* psutil_proc_io_priority_get(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_io_priority_set(PyObject* self, PyObject* args);
-#endif
-
-// --- system-related functions
-
-static PyObject* psutil_boot_time(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_logical(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
-static PyObject* psutil_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_usage(PyObject* self, PyObject* args);
-static PyObject* psutil_net_connections(PyObject* self, PyObject* args);
-static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
-static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_pid_exists(PyObject* self, PyObject* args);
-static PyObject* psutil_pids(PyObject* self, PyObject* args);
-static PyObject* psutil_ppid_map(PyObject* self, PyObject* args);
-static PyObject* psutil_users(PyObject* self, PyObject* args);
-static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
-static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args);
-static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
-
-// --- windows API bindings
-
-static PyObject* psutil_win32_QueryDosDevice(PyObject* self, PyObject* args);
-
-// --- internal
-
-int psutil_proc_suspend_or_resume(DWORD pid, int suspend);
--- a/third_party/python/psutil/psutil/_pswindows.py
+++ b/third_party/python/psutil/psutil/_pswindows.py
@@ -1,54 +1,95 @@
-#!/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.
 
 """Windows platform implementation."""
 
+import contextlib
 import errno
 import functools
 import os
 import sys
 from collections import namedtuple
 
 from . import _common
-from . import _psutil_windows as cext
-from ._common import conn_tmap, usage_percent, isfile_strict
-from ._common import sockfam_to_enum, socktype_to_enum
-from ._compat import PY3, xrange, lru_cache, long
-from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,
-                              BELOW_NORMAL_PRIORITY_CLASS,
-                              HIGH_PRIORITY_CLASS,
-                              IDLE_PRIORITY_CLASS,
-                              NORMAL_PRIORITY_CLASS,
-                              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
+        # 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"
+        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:
     enum = None
 
 # process priority constants, import from __init__.py:
 # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
-__extra__all__ = ["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",
-                  ]
+__extra__all__ = [
+    "win_service_iter", "win_service_get",
+    "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",
+]
 
-# --- module level constants (gets pushed up to psutil module)
+
+# =====================================================================
+# --- globals
+# =====================================================================
 
 CONN_DELETE_TCB = "DELETE_TCB"
 WAIT_TIMEOUT = 0x00000102  # 258 in decimal
-ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES,
-                               cext.ERROR_ACCESS_DENIED])
+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])
+
+
 if enum is None:
     AF_LINK = -1
 else:
     AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})
     AF_LINK = AddressFamily.AF_LINK
 
 TCP_STATUSES = {
     cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
@@ -72,53 +113,104 @@ if enum is not None:
         BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS
         HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS
         IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS
         NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS
         REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS
 
     globals().update(Priority.__members__)
 
-scputimes = namedtuple('scputimes', ['user', 'system', 'idle'])
+pinfo_map = dict(
+    num_handles=0,
+    ctx_switches=1,
+    user_time=2,
+    kernel_time=3,
+    create_time=4,
+    num_threads=5,
+    io_rcount=6,
+    io_wcount=7,
+    io_rbytes=8,
+    io_wbytes=9,
+    io_count_others=10,
+    io_bytes_others=11,
+    num_page_faults=12,
+    peak_wset=13,
+    wset=14,
+    peak_paged_pool=15,
+    paged_pool=16,
+    peak_non_paged_pool=17,
+    non_paged_pool=18,
+    pagefile=19,
+    peak_pagefile=20,
+    mem_private=21,
+)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.cpu_times()
+scputimes = namedtuple('scputimes',
+                       ['user', 'system', 'idle', 'interrupt', 'dpc'])
+# psutil.virtual_memory()
 svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
-pextmem = namedtuple(
-    'pextmem', ['num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
-                'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
-                'pagefile', 'peak_pagefile', 'private'])
+# psutil.Process.memory_info()
+pmem = namedtuple(
+    'pmem', ['rss', 'vms',
+             'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
+             'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
+             'pagefile', 'peak_pagefile', 'private'])
+# psutil.Process.memory_full_info()
+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
+# psutil.Process.memory_maps(grouped=True)
 pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss'])
+# psutil.Process.memory_maps(grouped=False)
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
-ntpinfo = namedtuple(
-    'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time',
-                'create_time', 'num_threads', 'io_rcount', 'io_wcount',
-                'io_rbytes', 'io_wbytes'])
+# psutil.Process.io_counters()
+pio = namedtuple('pio', ['read_count', 'write_count',
+                         'read_bytes', 'write_bytes',
+                         'other_count', 'other_bytes'])
 
-# set later from __init__.py
-NoSuchProcess = None
-AccessDenied = None
-TimeoutExpired = None
+
+# =====================================================================
+# --- utils
+# =====================================================================
 
 
 @lru_cache(maxsize=512)
-def _win32_QueryDosDevice(s):
-    return cext.win32_QueryDosDevice(s)
-
-
-def _convert_raw_path(s):
-    # convert paths using native DOS format like:
-    # "\Device\HarddiskVolume1\Windows\systemew\file.txt"
-    # into: "C:\Windows\systemew\file.txt"
-    if PY3 and not isinstance(s, str):
-        s = s.decode('utf8')
+def convert_dos_path(s):
+    r"""Convert paths using native DOS format like:
+        "\Device\HarddiskVolume1\Windows\systemew\file.txt"
+    into:
+        "C:\Windows\systemew\file.txt"
+    """
     rawdrive = '\\'.join(s.split('\\')[:3])
-    driveletter = _win32_QueryDosDevice(rawdrive)
+    driveletter = cext.win32_QueryDosDevice(rawdrive)
     return os.path.join(driveletter, s[len(rawdrive):])
 
 
-# --- public functions
+def py2_strencode(s):
+    """Encode a unicode string to a byte string by using the default fs
+    encoding + "replace" error handler.
+    """
+    if PY3:
+        return s
+    else:
+        if isinstance(s, str):
+            return s
+        else:
+            return s.encode(ENCODING, errors=ENCODING_ERRS)
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
 
 
 def virtual_memory():
     """System virtual memory as a namedtuple."""
     mem = cext.virtual_mem()
     totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
     #
     total = totphys
@@ -134,132 +226,420 @@ def swap_memory():
     mem = cext.virtual_mem()
     total = mem[2]
     free = mem[3]
     used = total - free
     percent = usage_percent(used, total, _round=1)
     return _common.sswap(total, used, free, percent, 0, 0)
 
 
+# =====================================================================
+# --- disk
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+
+
 def disk_usage(path):
     """Return disk usage associated with path."""
-    try:
-        total, free = cext.disk_usage(path)
-    except WindowsError:
-        if not os.path.exists(path):
-            msg = "No such file or directory: '%s'" % path
-            raise OSError(errno.ENOENT, msg)
-        raise
+    if PY3 and isinstance(path, bytes):
+        # XXX: do we want to use "strict"? Probably yes, in order
+        # to fail immediately. After all we are accepting input here...
+        path = path.decode(ENCODING, errors="strict")
+    total, free = cext.disk_usage(path)
     used = total - free
     percent = usage_percent(used, total, _round=1)
     return _common.sdiskusage(total, used, free, percent)
 
 
 def disk_partitions(all):
     """Return disk partitions."""
     rawlist = cext.disk_partitions(all)
     return [_common.sdiskpart(*x) for x in rawlist]
 
 
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
 def cpu_times():
     """Return system CPU times as a named tuple."""
     user, system, idle = cext.cpu_times()
-    return scputimes(user, system, idle)
+    # Internally, GetSystemTimes() is used, and it doesn't return
+    # interrupt and dpc times. cext.per_cpu_times() does, so we
+    # rely on it to get those only.
+    percpu_summed = scputimes(*[sum(n) for n in zip(*cext.per_cpu_times())])
+    return scputimes(user, system, idle,
+                     percpu_summed.interrupt, percpu_summed.dpc)
 
 
 def per_cpu_times():
     """Return system per-CPU times as a list of named tuples."""
     ret = []
-    for cpu_t in cext.per_cpu_times():
-        user, system, idle = cpu_t
-        item = scputimes(user, system, idle)
+    for user, system, idle, interrupt, dpc in cext.per_cpu_times():
+        item = scputimes(user, system, idle, interrupt, dpc)
         ret.append(item)
     return ret
 
 
 def cpu_count_logical():
     """Return the number of logical CPUs in the system."""
     return cext.cpu_count_logical()
 
 
 def cpu_count_physical():
     """Return the number of physical CPUs in the system."""
     return cext.cpu_count_phys()
 
 
-def boot_time():
-    """The system boot time expressed in seconds since the epoch."""
-    return cext.boot_time()
+def cpu_stats():
+    """Return CPU statistics."""
+    ctx_switches, interrupts, dpcs, syscalls = cext.cpu_stats()
+    soft_interrupts = 0
+    return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
+                             syscalls)
+
+
+def cpu_freq():
+    """Return CPU frequency.
+    On Windows per-cpu frequency is not supported.
+    """
+    curr, max_ = cext.cpu_freq()
+    min_ = 0.0
+    return [_common.scpufreq(float(curr), min_, float(max_))]
+
+
+# =====================================================================
+# --- network
+# =====================================================================
 
 
 def net_connections(kind, _pid=-1):
     """Return socket connections.  If pid == -1 return system-wide
     connections (as opposed to connections opened by one process only).
     """
     if kind not in conn_tmap:
         raise ValueError("invalid %r kind argument; choose between %s"
                          % (kind, ', '.join([repr(x) for x in conn_tmap])))
     families, types = conn_tmap[kind]
     rawlist = cext.net_connections(_pid, families, types)
     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)
         ret.add(nt)
     return list(ret)
 
 
 def net_if_stats():
-    ret = cext.net_if_stats()
-    for name, items in ret.items():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    ret = {}
+    rawdict = cext.net_if_stats()
+    for name, items in rawdict.items():
+        if not PY3:
+            assert isinstance(name, unicode), type(name)
+            name = py2_strencode(name)
         isup, duplex, speed, mtu = items
         if hasattr(_common, 'NicDuplex'):
             duplex = _common.NicDuplex(duplex)
         ret[name] = _common.snicstats(isup, duplex, speed, mtu)
     return ret
 
 
+def net_io_counters():
+    """Return network I/O statistics for every network interface
+    installed on the system as a dict of raw tuples.
+    """
+    ret = cext.net_io_counters()
+    return dict([(py2_strencode(k), v) for k, v in ret.items()])
+
+
+def net_if_addrs():
+    """Return the addresses associated to each NIC."""
+    ret = []
+    for items in cext.net_if_addrs():
+        items = list(items)
+        items[0] = py2_strencode(items[0])
+        ret.append(items)
+    return ret
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_battery():
+    """Return battery information."""
+    # For constants meaning see:
+    # https://msdn.microsoft.com/en-us/library/windows/desktop/
+    #     aa373232(v=vs.85).aspx
+    acline_status, flags, percent, secsleft = cext.sensors_battery()
+    power_plugged = acline_status == 1
+    no_battery = bool(flags & 128)
+    charging = bool(flags & 8)
+
+    if no_battery:
+        return None
+    if power_plugged or charging:
+        secsleft = _common.POWER_TIME_UNLIMITED
+    elif secsleft == -1:
+        secsleft = _common.POWER_TIME_UNKNOWN
+
+    return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+_last_btime = 0
+
+
+def boot_time():
+    """The system boot time expressed in seconds since the epoch."""
+    # This dirty hack is to adjust the precision of the returned
+    # value which may have a 1 second fluctuation, see:
+    # https://github.com/giampaolo/psutil/issues/1007
+    global _last_btime
+    ret = float(cext.boot_time())
+    if abs(ret - _last_btime) <= 1:
+        return _last_btime
+    else:
+        _last_btime = ret
+        return ret
+
+
 def users():
     """Return currently connected users as a list of namedtuples."""
     retlist = []
     rawlist = cext.users()
     for item in rawlist:
         user, hostname, tstamp = item
-        nt = _common.suser(user, None, hostname, tstamp)
+        user = py2_strencode(user)
+        nt = _common.suser(user, None, hostname, tstamp, None)
         retlist.append(nt)
     return retlist
 
 
+# =====================================================================
+# --- Windows services
+# =====================================================================
+
+
+def win_service_iter():
+    """Yields a list of WindowsService instances."""
+    for name, display_name in cext.winservice_enumerate():
+        yield WindowsService(py2_strencode(name), py2_strencode(display_name))
+
+
+def win_service_get(name):
+    """Open a Windows service and return it as a WindowsService instance."""
+    service = WindowsService(name, None)
+    service._display_name = service._query_config()['display_name']
+    return service
+
+
+class WindowsService(object):
+    """Represents an installed Windows service."""
+
+    def __init__(self, name, display_name):
+        self._name = name
+        self._display_name = display_name
+
+    def __str__(self):
+        details = "(name=%r, display_name=%r)" % (
+            self._name, self._display_name)
+        return "%s%s" % (self.__class__.__name__, details)
+
+    def __repr__(self):
+        return "<%s at %s>" % (self.__str__(), id(self))
+
+    def __eq__(self, other):
+        # Test for equality with another WindosService object based
+        # on name.
+        if not isinstance(other, WindowsService):
+            return NotImplemented
+        return self._name == other._name
+
+    def __ne__(self, other):
+        return not self == other
+
+    def _query_config(self):
+        with self._wrap_exceptions():
+            display_name, binpath, username, start_type = \
+                cext.winservice_query_config(self._name)
+        # XXX - update _self.display_name?
+        return dict(
+            display_name=py2_strencode(display_name),
+            binpath=py2_strencode(binpath),
+            username=py2_strencode(username),
+            start_type=py2_strencode(start_type))
+
+    def _query_status(self):
+        with self._wrap_exceptions():
+            status, pid = cext.winservice_query_status(self._name)
+        if pid == 0:
+            pid = None
+        return dict(status=status, pid=pid)
+
+    @contextlib.contextmanager
+    def _wrap_exceptions(self):
+        """Ctx manager which translates bare OSError and WindowsError
+        exceptions into NoSuchProcess and AccessDenied.
+        """
+        try:
+            yield
+        except WindowsError as err:
+            if err.errno in ACCESS_DENIED_ERRSET:
+                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:
+                raise NoSuchProcess(
+                    pid=None, name=self._name,
+                    msg="service %r does not exist)" % self._name)
+            else:
+                raise
+
+    # config query
+
+    def name(self):
+        """The service name. This string is how a service is referenced
+        and can be passed to win_service_get() to get a new
+        WindowsService instance.
+        """
+        return self._name
+
+    def display_name(self):
+        """The service display name. The value is cached when this class
+        is instantiated.
+        """
+        return self._display_name
+
+    def binpath(self):
+        """The fully qualified path to the service binary/exe file as
+        a string, including command line arguments.
+        """
+        return self._query_config()['binpath']
+
+    def username(self):
+        """The name of the user that owns this service."""
+        return self._query_config()['username']
+
+    def start_type(self):
+        """A string which can either be "automatic", "manual" or
+        "disabled".
+        """
+        return self._query_config()['start_type']
+
+    # status query
+
+    def pid(self):
+        """The process PID, if any, else None. This can be passed
+        to Process class to control the service's process.
+        """
+        return self._query_status()['pid']
+
+    def status(self):
+        """Service status as a string."""
+        return self._query_status()['status']
+
+    def description(self):
+        """Service long description."""
+        return py2_strencode(cext.winservice_query_descr(self.name()))
+
+    # utils
+
+    def as_dict(self):
+        """Utility method retrieving all the information above as a
+        dictionary.
+        """
+        d = self._query_config()
+        d.update(self._query_status())
+        d['name'] = self.name()
+        d['display_name'] = self.display_name()
+        d['description'] = self.description()
+        return d
+
+    # actions
+    # XXX: the necessary C bindings for start() and stop() are
+    # implemented but for now I prefer not to expose them.
+    # I may change my mind in the future. Reasons:
+    # - they require Administrator privileges
+    # - can't implement a timeout for stop() (unless by using a thread,
+    #   which sucks)
+    # - would require adding ServiceAlreadyStarted and
+    #   ServiceAlreadyStopped exceptions, adding two new APIs.
+    # - we might also want to have modify(), which would basically mean
+    #   rewriting win32serviceutil.ChangeServiceConfig, which involves a
+    #   lot of stuff (and API constants which would pollute the API), see:
+    #   http://pyxr.sourceforge.net/PyXR/c/python24/lib/site-packages/
+    #       win32/lib/win32serviceutil.py.html#0175
+    # - psutil is typically about "read only" monitoring stuff;
+    #   win_service_* APIs should only be used to retrieve a service and
+    #   check whether it's running
+
+    # def start(self, timeout=None):
+    #     with self._wrap_exceptions():
+    #         cext.winservice_start(self.name())
+    #         if timeout:
+    #             giveup_at = time.time() + timeout
+    #             while True:
+    #                 if self.status() == "running":
+    #                     return
+    #                 else:
+    #                     if time.time() > giveup_at:
+    #                         raise TimeoutExpired(timeout)
+    #                     else:
+    #                         time.sleep(.1)
+
+    # def stop(self):
+    #     # Note: timeout is not implemented because it's just not
+    #     # possible, see:
+    #     # http://stackoverflow.com/questions/11973228/
+    #     with self._wrap_exceptions():
+    #         return cext.winservice_stop(self.name())
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
 pids = cext.pids
 pid_exists = cext.pid_exists
-net_io_counters = cext.net_io_counters
-disk_io_counters = cext.disk_io_counters
-ppid_map = cext.ppid_map  # not meant to be public
-net_if_addrs = cext.net_if_addrs
+ppid_map = cext.ppid_map  # used internally by Process.children()
 
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError and WindowsError
     exceptions into NoSuchProcess and AccessDenied.
     """
     @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
         except OSError as err:
-            # support for private module import
-            if NoSuchProcess is None or AccessDenied is None:
-                raise
-            if err.errno in ACCESS_DENIED_SET:
+            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
     return wrapper
 
 
 class Process(object):
@@ -267,192 +647,250 @@ class Process(object):
 
     __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
         self._ppid = None
 
+    # --- oneshot() stuff
+
+    def oneshot_enter(self):
+        self.oneshot_info.cache_activate()
+
+    def oneshot_exit(self):
+        self.oneshot_info.cache_deactivate()
+
+    @memoize_when_activated
+    def oneshot_info(self):
+        """Return multiple information about this process as a
+        raw tuple.
+        """
+        ret = cext.proc_info(self.pid)
+        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.
         """
         # This is how PIDs 0 and 4 are always represented in taskmgr
         # and process-hacker.
         if self.pid == 0:
             return "System Idle Process"
         elif 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 os.path.basename(self.exe())
+                return py2_strencode(os.path.basename(self.exe()))
             except AccessDenied:
-                return cext.proc_name(self.pid)
+                return py2_strencode(cext.proc_name(self.pid))
 
     @wrap_exceptions
     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 _convert_raw_path(cext.proc_exe(self.pid))
+        return py2_strencode(convert_dos_path(cext.proc_exe(self.pid)))
 
     @wrap_exceptions
     def cmdline(self):
-        return cext.proc_cmdline(self.pid)
+        ret = cext.proc_cmdline(self.pid)
+        if PY3:
+            return ret
+        else:
+            return [py2_strencode(s) for s in ret]
+
+    @wrap_exceptions
+    def environ(self):
+        ustr = cext.proc_environ(self.pid)
+        if ustr and not PY3:
+            assert isinstance(ustr, unicode), type(ustr)
+        return parse_environ_block(py2_strencode(ustr))
 
     def ppid(self):
         try:
             return ppid_map()[self.pid]
         except KeyError:
             raise NoSuchProcess(self.pid, self._name)
 
     def _get_raw_meminfo(self):
         try:
             return cext.proc_memory_info(self.pid)
         except OSError as err:
-            if err.errno in ACCESS_DENIED_SET:
+            if err.errno in ACCESS_DENIED_ERRSET:
                 # TODO: the C ext can probably be refactored in order
                 # to get this from cext.proc_info()
-                return cext.proc_memory_info_2(self.pid)
+                info = self.oneshot_info()
+                return (
+                    info[pinfo_map['num_page_faults']],
+                    info[pinfo_map['peak_wset']],
+                    info[pinfo_map['wset']],
+                    info[pinfo_map['peak_paged_pool']],
+                    info[pinfo_map['paged_pool']],
+                    info[pinfo_map['peak_non_paged_pool']],
+                    info[pinfo_map['non_paged_pool']],
+                    info[pinfo_map['pagefile']],
+                    info[pinfo_map['peak_pagefile']],
+                    info[pinfo_map['mem_private']],
+                )
             raise
 
     @wrap_exceptions
     def memory_info(self):
-        # on Windows RSS == WorkingSetSize and VSM == PagefileUsage
-        # fields of PROCESS_MEMORY_COUNTERS struct:
-        # http://msdn.microsoft.com/en-us/library/windows/desktop/
-        #     ms684877(v=vs.85).aspx
+        # on Windows RSS == WorkingSetSize and VSM == PagefileUsage.
+        # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS
+        # struct.
         t = self._get_raw_meminfo()
-        return _common.pmem(t[2], t[7])
+        rss = t[2]  # wset
+        vms = t[7]  # pagefile
+        return pmem(*(rss, vms, ) + t)
 
     @wrap_exceptions
-    def memory_info_ex(self):
-        return pextmem(*self._get_raw_meminfo())
+    def memory_full_info(self):
+        basic_mem = self.memory_info()
+        uss = cext.proc_memory_uss(self.pid)
+        return pfullmem(*basic_mem + (uss, ))
 
     def memory_maps(self):
         try:
             raw = cext.proc_memory_maps(self.pid)
         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_SET:
+            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
         else:
             for addr, perm, path, rss in raw:
-                path = _convert_raw_path(path)
+                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)
 
     @wrap_exceptions
     def kill(self):
         return cext.proc_kill(self.pid)
 
     @wrap_exceptions
+    def send_signal(self, sig):
+        os.kill(self.pid, sig)
+
+    @wrap_exceptions
     def wait(self, timeout=None):
         if timeout is None:
-            timeout = cext.INFINITE
+            cext_timeout = cext.INFINITE
         else:
             # WaitForSingleObject() expects time in milliseconds
-            timeout = int(timeout * 1000)
-        ret = cext.proc_wait(self.pid, timeout)
-        if ret == WAIT_TIMEOUT:
-            # support for private module import
-            if TimeoutExpired is None:
-                raise RuntimeError("timeout expired")
-            raise TimeoutExpired(timeout, self.pid, self._name)
-        return ret
+            cext_timeout = int(timeout * 1000)
+        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
 
     @wrap_exceptions
     def username(self):
         if self.pid in (0, 4):
             return 'NT AUTHORITY\\SYSTEM'
-        return cext.proc_username(self.pid)
+        domain, user = cext.proc_username(self.pid)
+        return py2_strencode(domain) + '\\' + py2_strencode(user)
 
     @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()
         try:
             return cext.proc_create_time(self.pid)
         except OSError as err:
-            if err.errno in ACCESS_DENIED_SET:
-                return ntpinfo(*cext.proc_info(self.pid)).create_time
+            if err.errno in ACCESS_DENIED_ERRSET:
+                return self.oneshot_info()[pinfo_map['create_time']]
             raise
 
     @wrap_exceptions
     def num_threads(self):
-        return ntpinfo(*cext.proc_info(self.pid)).num_threads
+        return self.oneshot_info()[pinfo_map['num_threads']]
 
     @wrap_exceptions
     def threads(self):
         rawlist = cext.proc_threads(self.pid)
         retlist = []
         for thread_id, utime, stime in rawlist:
             ntuple = _common.pthread(thread_id, utime, stime)
             retlist.append(ntuple)
         return retlist
 
     @wrap_exceptions
     def cpu_times(self):
         try:
-            ret = cext.proc_cpu_times(self.pid)
+            user, system = cext.proc_cpu_times(self.pid)
         except OSError as err:
-            if err.errno in ACCESS_DENIED_SET:
-                nt = ntpinfo(*cext.proc_info(self.pid))
-                ret = (nt.user_time, nt.kernel_time)
+            if err.errno in ACCESS_DENIED_ERRSET:
+                info = self.oneshot_info()
+                user = info[pinfo_map['user_time']]
+                system = info[pinfo_map['kernel_time']]
             else:
                 raise
-        return _common.pcputimes(*ret)
+        # 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)
 
     @wrap_exceptions
     def resume(self):
         return cext.proc_resume(self.pid)
 
     @wrap_exceptions
     def cwd(self):
         if self.pid in (0, 4):
             raise AccessDenied(self.pid, self._name)
         # return a normalized pathname since the native C function appends
         # "\\" at the and of the path
         path = cext.proc_cwd(self.pid)
-        return os.path.normpath(path)
+        return py2_strencode(os.path.normpath(path))
 
     @wrap_exceptions
     def open_files(self):
         if self.pid in (0, 4):
             return []
-        retlist = []
+        ret = set()
         # Filenames come in in native format like:
         # "\Device\HarddiskVolume1\Windows\systemew\file.txt"
         # Convert the first part in the corresponding drive letter
         # (e.g. "C:\") by using Windows's QueryDosDevice()
         raw_file_names = cext.proc_open_files(self.pid)
         for _file in raw_file_names:
-            _file = _convert_raw_path(_file)
-            if isfile_strict(_file) and _file not in retlist:
+            _file = convert_dos_path(_file)
+            if isfile_strict(_file):
+                if not PY3:
+                    _file = py2_strencode(_file)
                 ntuple = _common.popenfile(_file, -1)
-                retlist.append(ntuple)
-        return retlist
+                ret.add(ntuple)
+        return list(ret)
 
     @wrap_exceptions
     def connections(self, kind='inet'):
         return net_connections(kind, _pid=self.pid)
 
     @wrap_exceptions
     def nice_get(self):
         value = cext.proc_priority_get(self.pid)
@@ -480,22 +918,29 @@ class Process(object):
                                  "(very low); got %r" % value)
             return cext.proc_io_priority_set(self.pid, value)
 
     @wrap_exceptions
     def io_counters(self):
         try:
             ret = cext.proc_io_counters(self.pid)
         except OSError as err:
-            if err.errno in ACCESS_DENIED_SET:
-                nt = ntpinfo(*cext.proc_info(self.pid))
-                ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes)
+            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:
                 raise
-        return _common.pio(*ret)
+        return pio(*ret)
 
     @wrap_exceptions
     def status(self):
         suspended = cext.proc_is_suspended(self.pid)
         if suspended:
             return _common.STATUS_STOPPED
         else:
             return _common.STATUS_RUNNING
@@ -532,17 +977,17 @@ class Process(object):
         bitmask = to_bitmask(value)
         cext.proc_cpu_affinity_set(self.pid, bitmask)
 
     @wrap_exceptions
     def num_handles(self):
         try:
             return cext.proc_num_handles(self.pid)
         except OSError as err:
-            if err.errno in ACCESS_DENIED_SET:
-                return ntpinfo(*cext.proc_info(self.pid)).num_handles
+            if err.errno in ACCESS_DENIED_ERRSET:
+                return self.oneshot_info()[pinfo_map['num_handles']]
             raise
 
     @wrap_exceptions
     def num_ctx_switches(self):
-        ctx_switches = ntpinfo(*cext.proc_info(self.pid)).ctx_switches
+        ctx_switches = self.oneshot_info()[pinfo_map['ctx_switches']]
         # only voluntary ctx switches are supported
         return _common.pctxsw(ctx_switches, 0)
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/common.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <sys/core.h>
+#include <stdlib.h>
+#include "common.h"
+
+/* psutil_kread() - read from kernel memory */
+int
+psutil_kread(
+    int Kd,             /* kernel memory file descriptor */
+    KA_T addr,          /* kernel memory address */
+    char *buf,          /* buffer to receive data */
+    size_t len) {       /* length to read */
+    int br;
+
+    if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return 1;
+    }
+    br = read(Kd, buf, len);
+    if (br == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return 1;
+    }
+    if (br != len) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "size mismatch when reading kernel memory fd");
+        return 1;
+    }
+    return 0;
+}
+
+struct procentry64 *
+psutil_read_process_table(int * num) {
+    size_t msz;
+    pid32_t pid = 0;
+    struct procentry64 *processes = (struct procentry64 *)NULL;
+    struct procentry64 *p;
+    int Np = 0;          /* number of processes allocated in 'processes' */
+    int np = 0;          /* number of processes read into 'processes' */
+    int i;               /* number of processes read in current iteration */
+
+    msz = (size_t)(PROCSIZE * PROCINFO_INCR);
+    processes = (struct procentry64 *)malloc(msz);
+    if (!processes) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    Np = PROCINFO_INCR;
+    p = processes;
+    while ((i = getprocs64(p, PROCSIZE, (struct fdsinfo64 *)NULL, 0, &pid,
+                 PROCINFO_INCR))
+    == PROCINFO_INCR) {
+        np += PROCINFO_INCR;
+        if (np >= Np) {
+            msz = (size_t)(PROCSIZE * (Np + PROCINFO_INCR));
+            processes = (struct procentry64 *)realloc((char *)processes, msz);
+            if (!processes) {
+                PyErr_NoMemory();
+                return NULL;
+            }
+            Np += PROCINFO_INCR;
+        }
+        p = (struct procentry64 *)((char *)processes + (np * PROCSIZE));
+    }
+
+    /* add the number of processes read in the last iteration */
+    if (i > 0)
+        np += i;
+
+    *num = np;
+    return processes;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/common.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __PSUTIL_AIX_COMMON_H__
+#define __PSUTIL_AIX_COMMON_H__
+
+#include <sys/core.h>
+
+#define PROCINFO_INCR   (256)
+#define PROCSIZE        (sizeof(struct procentry64))
+#define FDSINFOSIZE     (sizeof(struct fdsinfo64))
+#define KMEM            "/dev/kmem"
+
+typedef u_longlong_t    KA_T;
+
+/* psutil_kread() - read from kernel memory */
+int psutil_kread(int Kd,     /* kernel memory file descriptor */
+    KA_T addr,               /* kernel memory address */
+    char *buf,               /* buffer to receive data */
+    size_t len);             /* length to read */
+
+struct procentry64 *
+psutil_read_process_table(
+    int * num               /* out - number of processes read */
+);
+
+#endif  /* __PSUTIL_AIX_COMMON_H__ */
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/ifaddrs.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*! Based on code from
+    https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+!*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "ifaddrs.h"
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#define SIZE(p) MAX((p).sa_len,sizeof(p))
+
+
+static struct sockaddr *
+sa_dup(struct sockaddr *sa1)
+{
+    struct sockaddr *sa2;
+    size_t sz = sa1->sa_len;
+    sa2 = (struct sockaddr *) calloc(1, sz);
+    if (sa2 == NULL)
+        return NULL;
+    memcpy(sa2, sa1, sz);
+    return sa2;
+}
+
+
+void freeifaddrs(struct ifaddrs *ifp)
+{
+    if (NULL == ifp) return;
+    free(ifp->ifa_name);
+    free(ifp->ifa_addr);
+    free(ifp->ifa_netmask);
+    free(ifp->ifa_dstaddr);
+    freeifaddrs(ifp->ifa_next);
+    free(ifp);
+}
+
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+    int sd, ifsize;
+    char *ccp, *ecp;
+    struct ifconf ifc;
+    struct ifreq *ifr;
+    struct ifaddrs *cifa = NULL; /* current */
+    struct ifaddrs *pifa = NULL; /* previous */
+    const size_t IFREQSZ = sizeof(struct ifreq);
+    int fam;
+
+    *ifap = NULL;
+
+    sd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sd == -1)
+        goto error;
+
+    /* find how much memory to allocate for the SIOCGIFCONF call */
+    if (ioctl(sd, SIOCGSIZIFCONF, (caddr_t)&ifsize) < 0)
+        goto error;
+
+    ifc.ifc_req = (struct ifreq *) calloc(1, ifsize);
+    if (ifc.ifc_req == NULL)
+        goto error;
+    ifc.ifc_len = ifsize;
+
+    if (ioctl(sd, SIOCGIFCONF, &ifc) < 0)
+        goto error;
+
+    ccp = (char *)ifc.ifc_req;
+    ecp = ccp + ifsize;
+
+    while (ccp < ecp) {
+
+        ifr = (struct ifreq *) ccp;
+        ifsize = sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr);
+        fam = ifr->ifr_addr.sa_family;
+
+        if (fam == AF_INET || fam == AF_INET6) {
+            cifa = (struct ifaddrs *) calloc(1, sizeof(struct ifaddrs));
+            if (cifa == NULL)
+                goto error;
+            cifa->ifa_next = NULL;
+
+            if (pifa == NULL) *ifap = cifa; /* first one */
+            else pifa->ifa_next = cifa;
+
+            cifa->ifa_name = strdup(ifr->ifr_name);
+            if (cifa->ifa_name == NULL)
+                goto error;
+            cifa->ifa_flags = 0;
+            cifa->ifa_dstaddr = NULL;
+
+            cifa->ifa_addr = sa_dup(&ifr->ifr_addr);
+            if (cifa->ifa_addr == NULL)
+                goto error;
+
+            if (fam == AF_INET) {
+                if (ioctl(sd, SIOCGIFNETMASK, ifr, IFREQSZ) < 0)
+                    goto error;
+                cifa->ifa_netmask = sa_dup(&ifr->ifr_addr);
+                if (cifa->ifa_netmask == NULL)
+                    goto error;
+            }
+
+            if (0 == ioctl(sd, SIOCGIFFLAGS, ifr)) /* optional */
+                cifa->ifa_flags = ifr->ifr_flags;
+
+            if (fam == AF_INET) {
+                if (ioctl(sd, SIOCGIFDSTADDR, ifr, IFREQSZ) < 0) {
+                    if (0 == ioctl(sd, SIOCGIFBRDADDR, ifr, IFREQSZ)) {
+                        cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+                        if (cifa->ifa_dstaddr == NULL)
+                            goto error;
+                    }
+                }
+                else {
+                    cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+                    if (cifa->ifa_dstaddr == NULL)
+                        goto error;
+                }
+            }
+            pifa = cifa;
+        }
+
+        ccp += ifsize;
+    }
+    free(ifc.ifc_req);
+    close(sd);
+    return 0;
+error:
+    if (ifc.ifc_req != NULL)
+        free(ifc.ifc_req);
+    if (sd != -1)
+        close(sd);
+    freeifaddrs(*ifap);
+    return (-1);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/ifaddrs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*! Based on code from
+    https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+!*/
+
+
+#ifndef GENERIC_AIX_IFADDRS_H
+#define GENERIC_AIX_IFADDRS_H
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#undef  ifa_dstaddr
+#undef  ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr
+
+struct ifaddrs {
+    struct ifaddrs  *ifa_next;
+    char            *ifa_name;
+    unsigned int     ifa_flags;
+    struct sockaddr *ifa_addr;
+    struct sockaddr *ifa_netmask;
+    struct sockaddr *ifa_dstaddr;
+};
+
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+
+#endif
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/net_connections.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Baded on code from lsof:
+ * http://www.ibm.com/developerworks/aix/library/au-lsof.html
+ * - dialects/aix/dproc.c:gather_proc_info
+ * - lib/prfp.c:process_file
+ * - dialects/aix/dsock.c:process_socket
+ * - dialects/aix/dproc.c:get_kernel_access
+*/
+
+#include <Python.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#define _KERNEL
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/types.h>
+#include <sys/core.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <netinet/in_pcb.h>
+#include <arpa/inet.h>
+
+#include "../../_psutil_common.h"
+#include "net_kernel_structs.h"
+#include "net_connections.h"
+#include "common.h"
+
+#define NO_SOCKET       (PyObject *)(-1)
+
+static int
+read_unp_addr(
+    int Kd,
+    KA_T unp_addr,
+    char *buf,
+    size_t buflen
+) {
+    struct sockaddr_un *ua = (struct sockaddr_un *)NULL;
+    struct sockaddr_un un;
+    struct mbuf64 mb;
+    int uo;
+
+    if (psutil_kread(Kd, unp_addr, (char *)&mb, sizeof(mb))) {
+        return 1;
+    }
+
+    uo = (int)(mb.m_hdr.mh_data - unp_addr);
+    if ((uo + sizeof(struct sockaddr)) <= sizeof(mb))
+        ua = (struct sockaddr_un *)((char *)&mb + uo);
+    else {
+        if (psutil_kread(Kd, (KA_T)mb.m_hdr.mh_data,
+                         (char *)&un, sizeof(un))) {
+            return 1;
+        }
+        ua = &un;
+    }
+    if (ua && ua->sun_path[0]) {
+        if (mb.m_len > sizeof(struct sockaddr_un))
+            mb.m_len = sizeof(struct sockaddr_un);
+        *((char *)ua + mb.m_len - 1) = '\0';
+        snprintf(buf, buflen, "%s", ua->sun_path);
+    }
+    return 0;
+}
+
+static PyObject *
+process_file(int Kd, pid32_t pid, int fd, KA_T fp) {
+    struct file64 f;
+    struct socket64 s;
+    struct protosw64 p;
+    struct domain d;
+    struct inpcb64 inp;
+    int fam;
+    struct tcpcb64 t;
+    int state = PSUTIL_CONN_NONE;
+    unsigned char *laddr = (unsigned char *)NULL;
+    unsigned char *raddr = (unsigned char *)NULL;
+    int rport, lport;
+    char laddr_str[INET6_ADDRSTRLEN];
+    char raddr_str[INET6_ADDRSTRLEN];
+    struct unpcb64 unp;
+    char unix_laddr_str[PATH_MAX] = { 0 };
+    char unix_raddr_str[PATH_MAX] = { 0 };
+
+    /* Read file structure */
+    if (psutil_kread(Kd, fp, (char *)&f, sizeof(f))) {
+        return NULL;
+    }
+    if (!f.f_count || f.f_type != DTYPE_SOCKET) {
+        return NO_SOCKET;
+    }
+
+    if (psutil_kread(Kd, (KA_T) f.f_data, (char *) &s, sizeof(s))) {
+        return NULL;
+    }
+
+    if (!s.so_type) {
+        return NO_SOCKET;
+    }
+
+    if (!s.so_proto) {
+        PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol handle");
+        return NULL;
+    }
+    if (psutil_kread(Kd, (KA_T)s.so_proto, (char *)&p, sizeof(p))) {
+        return NULL;
+    }
+
+    if (!p.pr_domain) {
+        PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol domain");
+        return NULL;
+    }
+    if (psutil_kread(Kd, (KA_T)p.pr_domain, (char *)&d, sizeof(d))) {
+        return NULL;
+    }
+
+    fam = d.dom_family;
+    if (fam == AF_INET || fam == AF_INET6) {
+        /* Read protocol control block */
+        if (!s.so_pcb) {
+            PyErr_SetString(PyExc_RuntimeError, "invalid socket PCB");
+            return NULL;
+        }
+        if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *) &inp, sizeof(inp))) {
+            return NULL;
+        }
+
+        if (p.pr_protocol == IPPROTO_TCP) {
+            /* If this is a TCP socket, read its control block */
+            if (inp.inp_ppcb
+                &&  !psutil_kread(Kd, (KA_T)inp.inp_ppcb,
+                                  (char *)&t, sizeof(t)))
+                state = t.t_state;
+        }
+
+        if (fam == AF_INET6) {
+            laddr = (unsigned char *)&inp.inp_laddr6;
+            if (!IN6_IS_ADDR_UNSPECIFIED(&inp.inp_faddr6)) {
+                raddr = (unsigned char *)&inp.inp_faddr6;
+                rport = (int)ntohs(inp.inp_fport);
+            }
+        }
+        if (fam == AF_INET) {
+            laddr = (unsigned char *)&inp.inp_laddr;
+            if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport != 0) {
+                raddr =  (unsigned char *)&inp.inp_faddr;
+                rport = (int)ntohs(inp.inp_fport);
+            }
+        }
+        lport = (int)ntohs(inp.inp_lport);
+
+        inet_ntop(fam, laddr, laddr_str, sizeof(laddr_str));
+
+        if (raddr != NULL) {
+            inet_ntop(fam, raddr, raddr_str, sizeof(raddr_str));
+            return Py_BuildValue("(iii(si)(si)ii)", fd, fam,
+                                 s.so_type, laddr_str, lport, raddr_str,
+                                 rport, state, pid);
+        }
+        else {
+            return Py_BuildValue("(iii(si)()ii)", fd, fam,
+                                 s.so_type, laddr_str, lport, state,
+                                 pid);
+        }
+    }
+
+
+    if (fam == AF_UNIX) {
+        if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *)&unp, sizeof(unp))) {
+            return NULL;
+        }
+        if ((KA_T) f.f_data != (KA_T) unp.unp_socket) {
+            PyErr_SetString(PyExc_RuntimeError, "unp_socket mismatch");
+            return NULL;
+        }
+
+        if (unp.unp_addr) {
+            if (read_unp_addr(Kd, unp.unp_addr, unix_laddr_str,
+                              sizeof(unix_laddr_str))) {
+                return NULL;
+            }
+        }
+
+        if (unp.unp_conn) {
+            if (psutil_kread(Kd, (KA_T) unp.unp_conn, (char *)&unp,
+                sizeof(unp))) {
+                return NULL;
+            }
+            if (read_unp_addr(Kd, unp.unp_addr, unix_raddr_str,
+                              sizeof(unix_raddr_str))) {
+                return NULL;
+            }
+        }
+
+        return Py_BuildValue("(iiissii)", fd, d.dom_family,
+                 s.so_type, unix_laddr_str, unix_raddr_str, PSUTIL_CONN_NONE,
+                 pid);
+    }
+    return NO_SOCKET;
+}
+
+PyObject *
+psutil_net_connections(PyObject *self, PyObject *args) {
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    KA_T fp;
+    int Kd = -1;
+    int i, np;
+    struct procentry64 *p;
+    struct fdsinfo64 *fds = (struct fdsinfo64 *)NULL;
+    pid32_t requested_pid;
+    pid32_t pid;
+    struct procentry64 *processes = (struct procentry64 *)NULL;
+                    /* the process table */
+
+    if (py_retlist == NULL)
+        goto error;
+    if (! PyArg_ParseTuple(args, "i", &requested_pid))
+        goto error;
+
+    Kd = open(KMEM, O_RDONLY, 0);
+    if (Kd < 0) {
+        PyErr_SetFromErrnoWithFilename(PyExc_OSError, KMEM);
+        goto error;
+    }
+
+    processes = psutil_read_process_table(&np);
+    if (!processes)
+        goto error;
+
+    /* Loop through processes */
+    for (p = processes; np > 0; np--, p++) {
+        pid = p->pi_pid;
+        if (requested_pid != -1 && requested_pid != pid)
+            continue;
+        if (p->pi_state == 0 || p->pi_state == SZOMB)
+            continue;
+
+        if (!fds) {
+            fds = (struct fdsinfo64 *)malloc((size_t)FDSINFOSIZE);
+            if (!fds) {
+                PyErr_NoMemory();
+                goto error;
+            }
+        }
+        if (getprocs64((struct procentry64 *)NULL, PROCSIZE, fds, FDSINFOSIZE,
+              &pid, 1)
+        != 1)
+            continue;
+
+        /* loop over file descriptors */
+        for (i = 0; i < p->pi_maxofile; i++) {
+            fp = (KA_T)fds->pi_ufd[i].fp;
+            if (fp) {
+                py_tuple = process_file(Kd, p->pi_pid, i, fp);
+                if (py_tuple == NULL)
+                    goto error;
+                if (py_tuple != NO_SOCKET) {
+                    if (PyList_Append(py_retlist, py_tuple))
+                        goto error;
+                    Py_DECREF(py_tuple);
+                }
+            }
+        }
+    }
+    close(Kd);
+    free(processes);
+    if (fds != NULL)
+        free(fds);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (Kd > 0)
+        close(Kd);
+    if (processes != NULL)
+        free(processes);
+    if (fds != NULL)
+        free(fds);
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/net_connections.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __NET_CONNECTIONS_H__
+#define __NET_CONNECTIONS_H__
+
+#include <Python.h>
+
+PyObject* psutil_net_connections(PyObject *self, PyObject *args);
+
+#endif  /* __NET_CONNECTIONS_H__ */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/aix/net_kernel_structs.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* The kernel is always 64 bit but Python is usually compiled as a 32 bit
+ * process. We're reading the kernel memory to get the network connections,
+ * so we need the structs we read to be defined with 64 bit "pointers".
+ * Here are the partial definitions of the structs we use, taken from the
+ * header files, with data type sizes converted to their 64 bit counterparts,
+ * and unused data truncated. */
+
+#ifdef __64BIT__
+/* In case we're in a 64 bit process after all */
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/unpcb.h>
+#include <sys/mbuf_base.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#define file64 file
+#define socket64 socket
+#define protosw64 protosw
+#define inpcb64 inpcb
+#define tcpcb64 tcpcb
+#define unpcb64 unpcb
+#define mbuf64 mbuf
+#else
+  struct file64 {
+    int f_flag;
+    int f_count;
+    int f_options;
+    int f_type;
+    u_longlong_t f_data;
+ };
+
+ struct socket64 {
+    short   so_type;                 /* generic type, see socket.h */
+    short   so_options;              /* from socket call, see socket.h */
+    ushort  so_linger;               /* time to linger while closing */
+    short   so_state;                /* internal state flags SS_*, below */
+    u_longlong_t so_pcb;             /* protocol control block */
+    u_longlong_t so_proto;           /* protocol handle */
+ };
+
+ struct protosw64 {
+    short   pr_type;                 /* socket type used for */
+    u_longlong_t pr_domain;          /* domain protocol a member of */
+    short   pr_protocol;             /* protocol number */
+    short   pr_flags;                /* see below */
+ };
+
+ struct inpcb64 {
+    u_longlong_t inp_next,inp_prev;
+                                     /* pointers to other pcb's */
+    u_longlong_t inp_head;           /* pointer back to chain of inpcb's
+                                       for this protocol */
+    u_int32_t inp_iflowinfo;         /* input flow label */
+    u_short inp_fport;               /* foreign port */
+    u_int16_t inp_fatype;            /* foreign address type */
+    union   in_addr_6 inp_faddr_6;   /* foreign host table entry */
+    u_int32_t inp_oflowinfo;         /* output flow label */
+    u_short inp_lport;               /* local port */
+    u_int16_t inp_latype;            /* local address type */
+    union   in_addr_6 inp_laddr_6;   /* local host table entry */
+    u_longlong_t inp_socket;         /* back pointer to socket */
+    u_longlong_t inp_ppcb;           /* pointer to per-protocol pcb */
+    u_longlong_t space_rt;
+    struct  sockaddr_in6 spare_dst;
+    u_longlong_t inp_ifa;            /* interface address to use */
+    int     inp_flags;               /* generic IP/datagram flags */
+};
+
+struct tcpcb64 {
+    u_longlong_t seg__next;
+    u_longlong_t seg__prev;
+    short   t_state;                 /* state of this connection */
+};
+
+struct unpcb64 {
+        u_longlong_t unp_socket;     /* pointer back to socket */
+        u_longlong_t unp_vnode;      /* if associated with file */
+        ino_t   unp_vno;             /* fake vnode number */
+        u_longlong_t unp_conn;       /* control block of connected socket */
+        u_longlong_t unp_refs;       /* referencing socket linked list */
+        u_longlong_t unp_nextref;    /* link in unp_refs list */
+        u_longlong_t unp_addr;       /* bound address of socket */
+};
+
+struct m_hdr64
+{
+    u_longlong_t mh_next;            /* next buffer in chain */
+    u_longlong_t mh_nextpkt;         /* next chain in queue/record */
+    long mh_len;                     /* amount of data in this mbuf */
+    u_longlong_t mh_data;            /* location of data */
+};
+
+struct mbuf64
+{
+    struct m_hdr64 m_hdr;
+};
+
+#define m_len               m_hdr.mh_len
+
+#endif
\ No newline at end of file
deleted file mode 100644
--- a/third_party/python/psutil/psutil/arch/bsd/process_info.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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 functions related to fetching process information.
- * Used by _psutil_bsd module methods.
- */
-
-
-#include <Python.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/param.h>
-#include <sys/user.h>
-#include <sys/proc.h>
-#include <signal.h>
-
-#include "process_info.h"
-
-
-/*
- * 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.
- */
-int
-psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount)
-{
-    int err;
-    struct kinfo_proc *result;
-    int done;
-    static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
-    // Declaring name as const requires us to cast it when passing it to
-    // sysctl because the prototype doesn't include the const modifier.
-    size_t              length;
-
-    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;
-    }
-
-    *procList = result;
-    *procCount = length / sizeof(struct kinfo_proc);
-
-    assert((err == 0) == (*procList != NULL));
-    return err;
-}
-
-
-char
-*psutil_get_cmd_path(long pid, size_t *pathsize)
-{
-    int mib[4];
-    char *path;
-    size_t size = 0;
-
-    /*
-     * Make a sysctl() call to get the raw argument space of the process.
-     */
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_PROC;
-    mib[2] = KERN_PROC_PATHNAME;
-    mib[3] = pid;
-
-    // call with a null buffer first to determine if we need a buffer
-    if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1)
-        return NULL;
-
-    path = malloc(size);
-    if (path == NULL) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-
-    *pathsize = size;
-    if (sysctl(mib, 4, path, &size, NULL, 0) == -1) {
-        free(path);
-        return NULL;       // Insufficient privileges
-    }
-
-    return path;
-}
-
-
-/*
- * XXX no longer used; it probably makese sense to remove it.
- * Borrowed from psi Python System Information project
- *
- * Get command arguments and environment variables.
- *
- * Based on code from ps.
- *
- * Returns:
- *      0 for success;
- *      -1 for failure (Exception raised);
- *      1 for insufficient privileges.
- */
-char
-*psutil_get_cmd_args(long pid, size_t *argsize)
-{
-    int mib[4], argmax;
-    size_t size = sizeof(argmax);
-    char *procargs = NULL;
-
-    // Get the maximum process arguments size.
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_ARGMAX;
-
-    size = sizeof(argmax);
-    if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
-        return NULL;
-
-    // Allocate space for the arguments.
-    procargs = (char *)malloc(argmax);
-    if (procargs == NULL) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-
-    /*
-     * Make a sysctl() call to get the raw argument space of the process.
-     */
-    mib[0] = CTL_KERN;
-    mib[1] = KERN_PROC;
-    mib[2] = KERN_PROC_ARGS;
-    mib[3] = pid;
-
-    size = argmax;
-    if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
-        free(procargs);
-        return NULL;       // Insufficient privileges
-    }
-
-    // return string and set the length of arguments
-    *argsize = size;
-    return procargs;
-}
-
-
-// returns the command line as a python list object
-PyObject *
-psutil_get_arg_list(long pid)
-{
-    char *argstr = NULL;
-    int pos = 0;
-    size_t argsize = 0;
-    PyObject *retlist = Py_BuildValue("[]");
-    PyObject *item = NULL;
-
-    if (pid < 0)
-        return retlist;
-    argstr = psutil_get_cmd_args(pid, &argsize);
-    if (argstr == NULL)
-        goto error;
-
-    // args are returned as a flattened string with \0 separators between
-    // arguments add each string to the list then step forward to the next
-    // separator
-    if (argsize > 0) {
-        while (pos < argsize) {
-            item = Py_BuildValue("s", &argstr[pos]);
-            if (!item)
-                goto error;
-            if (PyList_Append(retlist, item))
-                goto error;
-            Py_DECREF(item);
-            pos = pos + strlen(&argstr[pos]) + 1;
-        }
-    }
-
-    free(argstr);
-    return retlist;
-
-error:
-    Py_XDECREF(item);
-    Py_DECREF(retlist);
-    if (argstr != NULL)
-        free(argstr);
-    return NULL;
-}
-
-
-/*
- * Return 1 if PID exists in the current process list, else 0.
- */
-int
-psutil_pid_exists(long pid)
-{
-    int kill_ret;
-
-    if (pid < 0)
-        return 0;
-    // if kill returns success of permission denied we know it's a valid PID
-    kill_ret = kill(pid , 0);
-    if ((0 == kill_ret) || (EPERM == errno))
-        return 1;
-    // otherwise return 0 for PID not found
-    return 0;
-}
-
deleted file mode 100644
--- a/third_party/python/psutil/psutil/arch/bsd/process_info.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * 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 <Python.h>
-
-typedef struct kinfo_proc kinfo_proc;
-
-char *psutil_get_cmd_args(long pid, size_t *argsize);
-char *psutil_get_cmd_path(long pid, size_t *pathsize);
-int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
-int psutil_pid_exists(long pid);
-PyObject* psutil_get_arg_list(long pid);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/proc_socks.c
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ *
+ * Retrieves per-process open socket connections.
+ */
+
+#include <Python.h>
+#include <sys/user.h>
+#include <sys/socketvar.h>    // for struct xsocket
+#include <sys/un.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>   // for xinpcb struct
+#include <netinet/in_pcb.h>
+#include <netinet/tcp_var.h>   // for struct xtcpcb
+#include <arpa/inet.h>         // for inet_ntop()
+#include <libutil.h>
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+
+// The tcplist fetching and walking is borrowed from netstat/inet.c.
+static char *
+psutil_fetch_tcplist(void) {
+    char *buf;
+    size_t len;
+
+    for (;;) {
+        if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) {
+            PyErr_SetFromErrno(PyExc_OSError);
+            return NULL;
+        }
+        buf = malloc(len);
+        if (buf == NULL) {
+            PyErr_NoMemory();
+            return NULL;
+        }
+        if (sysctlbyname("net.inet.tcp.pcblist", buf, &len, NULL, 0) < 0) {
+            free(buf);
+            PyErr_SetFromErrno(PyExc_OSError);
+            return NULL;
+        }
+        return buf;
+    }
+}
+
+
+static int
+psutil_sockaddr_port(int family, struct sockaddr_storage *ss) {
+    struct sockaddr_in6 *sin6;
+    struct sockaddr_in *sin;
+
+    if (family == AF_INET) {
+        sin = (struct sockaddr_in *)ss;
+        return (sin->sin_port);
+    }
+    else {
+        sin6 = (struct sockaddr_in6 *)ss;
+        return (sin6->sin6_port);
+    }
+}
+
+
+static void *
+psutil_sockaddr_addr(int family, struct sockaddr_storage *ss) {
+    struct sockaddr_in6 *sin6;
+    struct sockaddr_in *sin;
+
+    if (family == AF_INET) {
+        sin = (struct sockaddr_in *)ss;
+        return (&sin->sin_addr);
+    }
+    else {
+        sin6 = (struct sockaddr_in6 *)ss;
+        return (&sin6->sin6_addr);
+    }
+}
+
+
+static socklen_t
+psutil_sockaddr_addrlen(int family) {
+    if (family == AF_INET)
+        return (sizeof(struct in_addr));
+    else
+        return (sizeof(struct in6_addr));
+}
+
+
+static int
+psutil_sockaddr_matches(int family, int port, void *pcb_addr,
+                        struct sockaddr_storage *ss) {
+    if (psutil_sockaddr_port(family, ss) != port)
+        return (0);
+    return (memcmp(psutil_sockaddr_addr(family, ss), pcb_addr,
+                   psutil_sockaddr_addrlen(family)) == 0);
+}
+
+
+#if __FreeBSD_version >= 1200026
+static struct xtcpcb *
+psutil_search_tcplist(char *buf, struct kinfo_file *kif) {
+    struct xtcpcb *tp;
+    struct xinpcb *inp;
+#else
+static struct tcpcb *
+psutil_search_tcplist(char *buf, struct kinfo_file *kif) {
+    struct tcpcb *tp;
+    struct inpcb *inp;
+#endif
+    struct xinpgen *xig, *oxig;
+    struct xsocket *so;
+
+    oxig = xig = (struct xinpgen *)buf;
+    for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
+            xig->xig_len > sizeof(struct xinpgen);
+            xig = (struct xinpgen *)((char *)xig + xig->xig_len)) {
+
+#if __FreeBSD_version >= 1200026
+        tp = (struct xtcpcb *)xig;
+        inp = &tp->xt_inp;
+        so = &inp->xi_socket;
+#else
+        tp = &((struct xtcpcb *)xig)->xt_tp;
+        inp = &((struct xtcpcb *)xig)->xt_inp;
+        so = &((struct xtcpcb *)xig)->xt_socket;
+#endif
+
+        if (so->so_type != kif->kf_sock_type ||
+                so->xso_family != kif->kf_sock_domain ||
+                so->xso_protocol != kif->kf_sock_protocol)
+            continue;
+
+        if (kif->kf_sock_domain == AF_INET) {
+            if (!psutil_sockaddr_matches(
+                    AF_INET, inp->inp_lport, &inp->inp_laddr,
+#if __FreeBSD_version < 1200031
+                    &kif->kf_sa_local))
+#else
+                    &kif->kf_un.kf_sock.kf_sa_local))
+#endif
+                continue;
+            if (!psutil_sockaddr_matches(
+                    AF_INET, inp->inp_fport, &inp->inp_faddr,
+#if __FreeBSD_version < 1200031
+                    &kif->kf_sa_peer))
+#else
+                    &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
+                continue;
+        } else {
+            if (!psutil_sockaddr_matches(
+                    AF_INET6, inp->inp_lport, &inp->in6p_laddr,
+#if __FreeBSD_version < 1200031
+                    &kif->kf_sa_local))
+#else
+                    &kif->kf_un.kf_sock.kf_sa_local))
+#endif
+                continue;
+            if (!psutil_sockaddr_matches(
+                    AF_INET6, inp->inp_fport, &inp->in6p_faddr,
+#if __FreeBSD_version < 1200031
+                    &kif->kf_sa_peer))
+#else
+                    &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
+                continue;
+        }
+
+        return (tp);
+    }
+    return NULL;
+}
+
+
+
+PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+    // Return connections opened by process.
+    long pid;
+    int i;
+    int cnt;
+    struct kinfo_file *freep = NULL;
+    struct kinfo_file *kif;
+    char *tcplist = NULL;
+#if __FreeBSD_version >= 1200026
+    struct xtcpcb *tcp;
+#else
+    struct tcpcb *tcp;
+#endif
+
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_laddr = NULL;
+    PyObject *py_raddr = NULL;
+    PyObject *py_af_filter = NULL;
+    PyObject *py_type_filter = NULL;
+    PyObject *py_family = NULL;
+    PyObject *py_type = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+    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)) {
+        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;
+    }
+
+    tcplist = psutil_fetch_tcplist();
+    if (tcplist == NULL) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        int lport, rport, state;
+        char lip[200], rip[200];
+        char path[PATH_MAX];
+        int inseq;
+        py_tuple = NULL;
+        py_laddr = NULL;
+        py_raddr = NULL;
+
+        kif = &freep[i];
+        if (kif->kf_type == KF_TYPE_SOCKET) {
+            // apply filters
+            py_family = PyLong_FromLong((long)kif->kf_sock_domain);
+            inseq = PySequence_Contains(py_af_filter, py_family);
+            Py_DECREF(py_family);
+            if (inseq == 0)
+                continue;
+            py_type = PyLong_FromLong((long)kif->kf_sock_type);
+            inseq = PySequence_Contains(py_type_filter, py_type);
+            Py_DECREF(py_type);
+            if (inseq == 0)
+                continue;
+            // IPv4 / IPv6 socket
+            if ((kif->kf_sock_domain == AF_INET) ||
+                    (kif->kf_sock_domain == AF_INET6)) {
+                // fill status
+                state = PSUTIL_CONN_NONE;
+                if (kif->kf_sock_type == SOCK_STREAM) {
+                    tcp = psutil_search_tcplist(tcplist, kif);
+                    if (tcp != NULL)
+                        state = (int)tcp->t_state;
+                }
+
+                // build addr and port
+                inet_ntop(
+                    kif->kf_sock_domain,
+                    psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
+                                         &kif->kf_sa_local),
+#else
+                                         &kif->kf_un.kf_sock.kf_sa_local),
+#endif
+                    lip,
+                    sizeof(lip));
+                inet_ntop(
+                    kif->kf_sock_domain,
+                    psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
+                                         &kif->kf_sa_peer),
+#else
+                                         &kif->kf_un.kf_sock.kf_sa_peer),
+#endif
+                    rip,
+                    sizeof(rip));
+                lport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
+                                                   &kif->kf_sa_local));
+#else
+                                                   &kif->kf_un.kf_sock.kf_sa_local));
+#endif
+                rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
+                                                   &kif->kf_sa_peer));
+#else
+                                                   &kif->kf_un.kf_sock.kf_sa_peer));
+#endif
+
+                // construct python tuple/list
+                py_laddr = Py_BuildValue("(si)", lip, lport);
+                if (!py_laddr)
+                    goto error;
+                if (rport != 0)
+                    py_raddr = Py_BuildValue("(si)", rip, rport);
+                else
+                    py_raddr = Py_BuildValue("()");
+                if (!py_raddr)
+                    goto error;
+                py_tuple = Py_BuildValue(
+                    "(iiiNNi)",
+                    kif->kf_fd,
+                    kif->kf_sock_domain,
+                    kif->kf_sock_type,
+                    py_laddr,
+                    py_raddr,
+                    state
+                );
+                if (!py_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_tuple))
+                    goto error;
+                Py_DECREF(py_tuple);
+            }
+            // UNIX socket.
+            // Note: remote path cannot be determined.
+            else if (kif->kf_sock_domain == AF_UNIX) {
+                struct sockaddr_un *sun;
+
+#if __FreeBSD_version < 1200031
+                sun = (struct sockaddr_un *)&kif->kf_sa_local;
+#else
+                sun = (struct sockaddr_un *)&kif->kf_un.kf_sock.kf_sa_local;
+#endif
+                snprintf(
+                    path, sizeof(path), "%.*s",
+                    (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+                    sun->sun_path);
+
+                py_laddr = PyUnicode_DecodeFSDefault(path);
+                if (! py_laddr)
+                    goto error;
+
+                py_tuple = Py_BuildValue(
+                    "(iiiOsi)",
+                    kif->kf_fd,
+                    kif->kf_sock_domain,
+                    kif->kf_sock_type,
+                    py_laddr,
+                    "",  // raddr can't be determined
+                    PSUTIL_CONN_NONE
+                );
+                if (!py_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_tuple))
+                    goto error;
+                Py_DECREF(py_tuple);
+                Py_DECREF(py_laddr);
+            }
+        }
+    }
+    free(freep);
+    free(tcplist);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_laddr);
+    Py_XDECREF(py_raddr);
+    Py_DECREF(py_retlist);
+    if (freep != NULL)
+        free(freep);
+    if (tcplist != NULL)
+        free(tcplist);
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/proc_socks.h
@@ -0,0 +1,9 @@
+/*
+ * 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 <Python.h>
+
+PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/specific.c
@@ -0,0 +1,1009 @@
+/*
+ * 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 functions specific to FreeBSD.
+ * Used by _psutil_bsd module methods.
+ */
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/vmmeter.h>  // needed for vmtotal struct
+#include <devstat.h>  // for swap mem
+#include <libutil.h>  // process open files, shared libs (kinfo_getvmmap), cwd
+#include <sys/cpuset.h>
+
+#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)
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+
+// ============================================================================
+// Utility functions
+// ============================================================================
+
+
+int
+psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) {
+    // Fills a kinfo_proc struct based on process pid.
+    int mib[4];
+    size_t size;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PID;
+    mib[3] = pid;
+
+    size = sizeof(struct kinfo_proc);
+    if (sysctl((int *)mib, 4, proc, &size, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    // sysctl stores 0 in the size if we can't find the process information.
+    if (size == 0) {
+        NoSuchProcess("");
+        return -1;
+    }
+    return 0;
+}
+
+
+// remove spaces from string
+static void psutil_remove_spaces(char *str) {
+    char *p1 = str;
+    char *p2 = str;
+    do
+        while (*p2 == ' ')
+            p2++;
+    while ((*p1++ = *p2++));
+}
+
+
+// ============================================================================
+// APIS
+// ============================================================================
+
+int
+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.
+    int err;
+    struct kinfo_proc *result;
+    int done;
+    int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
+    size_t length;
+
+    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;
+    }
+
+    *procList = result;
+    *procCount = length / sizeof(struct kinfo_proc);
+
+    assert((err == 0) == (*procList != NULL));
+    return err;
+}
+
+
+/*
+ * XXX no longer used; it probably makese sense to remove it.
+ * Borrowed from psi Python System Information project
+ *
+ * Get command arguments and environment variables.
+ *
+ * Based on code from ps.
+ *
+ * Returns:
+ *      0 for success;
+ *      -1 for failure (Exception raised);
+ *      1 for insufficient privileges.
+ */
+static char
+*psutil_get_cmd_args(long pid, size_t *argsize) {
+    int mib[4];
+    int argmax;
+    size_t size = sizeof(argmax);
+    char *procargs = NULL;
+
+    // Get the maximum process arguments size.
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_ARGMAX;
+
+    size = sizeof(argmax);
+    if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
+        return NULL;
+
+    // Allocate space for the arguments.
+    procargs = (char *)malloc(argmax);
+    if (procargs == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    // Make a sysctl() call to get the raw argument space of the process.
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_ARGS;
+    mib[3] = pid;
+
+    size = argmax;
+    if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
+        free(procargs);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    // return string and set the length of arguments
+    *argsize = size;
+    return procargs;
+}
+
+
+// returns the command line as a python list object
+PyObject *
+psutil_get_cmdline(long pid) {
+    char *argstr = NULL;
+    size_t pos = 0;
+    size_t argsize = 0;
+    PyObject *py_retlist = Py_BuildValue("[]");
+    PyObject *py_arg = NULL;
+
+    if (pid < 0)
+        return py_retlist;
+    argstr = psutil_get_cmd_args(pid, &argsize);
+    if (argstr == NULL)
+        goto error;
+
+    // args are returned as a flattened string with \0 separators between
+    // arguments add each string to the list then step forward to the next
+    // separator
+    if (argsize > 0) {
+        while (pos < argsize) {
+            py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]);
+            if (!py_arg)
+                goto error;
+            if (PyList_Append(py_retlist, py_arg))
+                goto error;
+            Py_DECREF(py_arg);
+            pos = pos + strlen(&argstr[pos]) + 1;
+        }
+    }
+
+    free(argstr);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_arg);
+    Py_DECREF(py_retlist);
+    if (argstr != NULL)
+        free(argstr);
+    return NULL;
+}
+
+
+/*
+ * Return process pathname executable.
+ * Thanks to Robert N. M. Watson:
+ * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
+ */
+PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+    long pid;
+    char pathname[PATH_MAX];
+    int error;
+    int mib[4];
+    int ret;
+    size_t size;
+    const char *encoding_errs;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PATHNAME;
+    mib[3] = pid;
+
+    size = sizeof(pathname);
+    error = sysctl(mib, 4, pathname, &size, NULL, 0);
+    if (error == -1) {
+        // see: https://github.com/giampaolo/psutil/issues/907
+        if (errno == ENOENT)
+            return PyUnicode_DecodeFSDefault("");
+        else
+            return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    if (size == 0 || strlen(pathname) == 0) {
+        ret = psutil_pid_exists(pid);
+        if (ret == -1)
+            return NULL;
+        else if (ret == 0)
+            return NoSuchProcess("");
+        else
+            strcpy(pathname, "");
+    }
+
+    return PyUnicode_DecodeFSDefault(pathname);
+}
+
+
+PyObject *
+psutil_proc_num_threads(PyObject *self, PyObject *args) {
+    // Return number of threads used by process as a Python integer.
+    long pid;
+    struct kinfo_proc kp;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_kinfo_proc(pid, &kp) == -1)
+        return NULL;
+    return Py_BuildValue("l", (long)kp.ki_numthreads);
+}
+
+
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+    // Retrieves all threads used by process returning a list of tuples
+    // including thread id, user time and system time.
+    // Thanks to Robert N. M. Watson:
+    // http://code.metager.de/source/xref/freebsd/usr.bin/procstat/
+    //     procstat_threads.c
+    long pid;
+    int mib[4];
+    struct kinfo_proc *kip = NULL;
+    struct kinfo_proc *kipp = NULL;
+    int error;
+    unsigned int i;
+    size_t size;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+
+    // we need to re-query for thread information, so don't use *kipp
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;
+    mib[3] = pid;
+
+    size = 0;
+    error = sysctl(mib, 4, NULL, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess("");
+        goto error;
+    }
+
+    kip = malloc(size);
+    if (kip == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    error = sysctl(mib, 4, kip, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess("");
+        goto error;
+    }
+
+    for (i = 0; i < size / sizeof(*kipp); i++) {
+        kipp = &kip[i];
+        py_tuple = Py_BuildValue("Idd",
+                                 kipp->ki_tid,
+                                 PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_utime),
+                                 PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_stime));
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+    free(kip);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (kip != NULL)
+        free(kip);
+    return NULL;
+}
+
+
+PyObject *
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
+    // Return an XML string from which we'll determine the number of
+    // physical CPU cores in the system.
+    void *topology = NULL;
+    size_t size = 0;
+    PyObject *py_str;
+
+    if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0))
+        goto error;
+
+    topology = malloc(size);
+    if (!topology) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0))
+        goto error;
+
+    py_str = Py_BuildValue("s", topology);
+    free(topology);
+    return py_str;
+
+error:
+    if (topology != NULL)
+        free(topology);
+    Py_RETURN_NONE;
+}
+
+
+/*
+ * Return virtual memory usage statistics.
+ */
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+    unsigned long  total;
+    unsigned int   active, inactive, wired, cached, free;
+    size_t         size = sizeof(total);
+    struct vmtotal vm;
+    int            mib[] = {CTL_VM, VM_METER};
+    long           pagesize = getpagesize();
+#if __FreeBSD_version > 702101
+    long buffers;
+#else
+    int buffers;
+#endif
+    size_t buffers_size = sizeof(buffers);
+
+    if (sysctlbyname("hw.physmem", &total, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_inactive_count",
+                     &inactive, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0))
+        goto error;
+    // https://github.com/giampaolo/psutil/issues/997
+    if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0))
+        cached = 0;
+    if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0))
+        goto error;
+
+    size = sizeof(vm);
+    if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0)
+        goto error;
+
+    return Py_BuildValue("KKKKKKKK",
+        (unsigned long long) total,
+        (unsigned long long) free     * pagesize,
+        (unsigned long long) active   * pagesize,
+        (unsigned long long) inactive * pagesize,
+        (unsigned long long) wired    * pagesize,
+        (unsigned long long) cached   * pagesize,
+        (unsigned long long) buffers,
+        (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize  // shared
+    );
+
+error:
+    return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+    // Return swap memory stats (see 'swapinfo' cmdline tool)
+    kvm_t *kd;
+    struct kvm_swap kvmsw[1];
+    unsigned int swapin, swapout, nodein, nodeout;
+    size_t size = sizeof(unsigned int);
+
+    kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
+    if (kd == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "kvm_open() syscall failed");
+        return NULL;
+    }
+
+    if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
+        kvm_close(kd);
+        PyErr_SetString(PyExc_RuntimeError,
+                        "kvm_getswapinfo() syscall failed");
+        return NULL;
+    }
+
+    kvm_close(kd);
+
+    if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1)
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1)
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1)
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1)
+        goto error;
+
+    return Py_BuildValue("(iiiII)",
+                         kvmsw[0].ksw_total,                     // total
+                         kvmsw[0].ksw_used,                      // used
+                         kvmsw[0].ksw_total - kvmsw[0].ksw_used, // free
+                         swapin + swapout,                       // swap in
+                         nodein + nodeout);                      // swap out
+
+error:
+    return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+    long pid;
+    struct kinfo_file *freep = NULL;
+    struct kinfo_file *kif;
+    struct kinfo_proc kipp;
+    const char *encoding_errs;
+    PyObject *py_path = NULL;
+
+    int i, cnt;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+    if (psutil_kinfo_proc(pid, &kipp) == -1)
+        goto error;
+
+    errno = 0;
+    freep = kinfo_getfile(pid, &cnt);
+    if (freep == NULL) {
+        psutil_raise_for_pid(pid, "kinfo_getfile()");
+        goto error;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        kif = &freep[i];
+        if (kif->kf_fd == KF_FD_TYPE_CWD) {
+            py_path = PyUnicode_DecodeFSDefault(kif->kf_path);
+            if (!py_path)
+                goto error;
+            break;
+        }
+    }
+    /*
+     * For lower pids it seems we can't retrieve any information
+     * (lsof can't do that it either).  Since this happens even
+     * as root we return an empty string instead of AccessDenied.
+     */
+    if (py_path == NULL)
+        py_path = PyUnicode_DecodeFSDefault("");
+    free(freep);
+    return py_path;
+
+error:
+    Py_XDECREF(py_path);
+    if (freep != NULL)
+        free(freep);
+    return NULL;
+}
+#endif
+
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+    long pid;
+    int cnt;
+
+    struct kinfo_file *freep;
+    struct kinfo_proc kipp;
+
+    if (! PyArg_ParseTuple(args, "l", &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()");
+        return NULL;
+    }
+    free(freep);
+
+    return Py_BuildValue("i", cnt);
+}
+#endif
+
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+    static int maxcpus;
+    int mib[2];
+    int ncpu;
+    size_t len;
+    size_t size;
+    int i;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_cputime = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    // retrieve maxcpus value
+    size = sizeof(maxcpus);
+    if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+        Py_DECREF(py_retlist);
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    long cpu_time[maxcpus][CPUSTATES];
+
+    // retrieve the number of cpus
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+    len = sizeof(ncpu);
+    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    // per-cpu info
+    size = sizeof(cpu_time);
+    if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < ncpu; i++) {
+        py_cputime = Py_BuildValue(
+            "(ddddd)",
+            (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
+            (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
+            (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
+            (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
+            (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC);
+        if (!py_cputime)
+            goto error;
+        if (PyList_Append(py_retlist, py_cputime))
+            goto error;
+        Py_DECREF(py_cputime);
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cputime);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+    int i;
+    struct statinfo stats;
+
+    PyObject *py_retdict = PyDict_New();
+    PyObject *py_disk_info = NULL;
+
+    if (py_retdict == NULL)
+        return NULL;
+    if (devstat_checkversion(NULL) < 0) {
+        PyErr_Format(PyExc_RuntimeError,
+                     "devstat_checkversion() syscall failed");
+        goto error;
+    }
+
+    stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+    if (stats.dinfo == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    bzero(stats.dinfo, sizeof(struct devinfo));
+
+    if (devstat_getdevs(NULL, &stats) == -1) {
+        PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() syscall failed");
+        goto error;
+    }
+
+    for (i = 0; i < stats.dinfo->numdevs; i++) {
+        py_disk_info = NULL;
+        struct devstat current;
+        char disk_name[128];
+        current = stats.dinfo->devices[i];
+        snprintf(disk_name, sizeof(disk_name), "%s%d",
+                 current.device_name,
+                 current.unit_number);
+
+        py_disk_info = Py_BuildValue(
+            "(KKKKLLL)",
+            current.operations[DEVSTAT_READ],   // no reads
+            current.operations[DEVSTAT_WRITE],  // no writes
+            current.bytes[DEVSTAT_READ],        // bytes read
+            current.bytes[DEVSTAT_WRITE],       // bytes written
+            (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_READ]),  // r time
+            (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_WRITE]),  // w time
+            (long long) PSUTIL_BT2MSEC(current.busy_time)  // busy time
+        );      // finished transactions
+        if (!py_disk_info)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
+            goto error;
+        Py_DECREF(py_disk_info);
+    }
+
+    if (stats.dinfo->mem_ptr)
+        free(stats.dinfo->mem_ptr);
+    free(stats.dinfo);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_disk_info);
+    Py_DECREF(py_retdict);
+    if (stats.dinfo != NULL)
+        free(stats.dinfo);
+    return NULL;
+}
+
+
+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;
+    int ptrwidth;
+    int i, cnt;
+    char addr[1000];
+    char perms[4];
+    char *path;
+    struct kinfo_proc kp;
+    struct kinfo_vmentry *freep = NULL;
+    struct kinfo_vmentry *kve;
+    ptrwidth = 2 * sizeof(void *);
+    PyObject *py_tuple = NULL;
+    PyObject *py_path = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+    if (psutil_kinfo_proc(pid, &kp) == -1)
+        goto error;
+
+    errno = 0;
+    freep = kinfo_getvmmap(pid, &cnt);
+    if (freep == NULL) {
+        psutil_raise_for_pid(pid, "kinfo_getvmmap()");
+        goto error;
+    }
+    for (i = 0; i < cnt; i++) {
+        py_tuple = NULL;
+        kve = &freep[i];
+        addr[0] = '\0';
+        perms[0] = '\0';
+        sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
+                ptrwidth, (uintmax_t)kve->kve_end);
+        psutil_remove_spaces(addr);
+        strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
+                sizeof(perms));
+        strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
+                sizeof(perms));
+        strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
+                sizeof(perms));
+
+        if (strlen(kve->kve_path) == 0) {
+            switch (kve->kve_type) {
+                case KVME_TYPE_NONE:
+                    path = "[none]";
+                    break;
+                case KVME_TYPE_DEFAULT:
+                    path = "[default]";
+                    break;
+                case KVME_TYPE_VNODE:
+                    path = "[vnode]";
+                    break;
+                case KVME_TYPE_SWAP:
+                    path = "[swap]";
+                    break;
+                case KVME_TYPE_DEVICE:
+                    path = "[device]";
+                    break;
+                case KVME_TYPE_PHYS:
+                    path = "[phys]";
+                    break;
+                case KVME_TYPE_DEAD:
+                    path = "[dead]";
+                    break;
+                case KVME_TYPE_SG:
+                    path = "[sg]";
+                    break;
+                case KVME_TYPE_UNKNOWN:
+                    path = "[unknown]";
+                    break;
+                default:
+                    path = "[?]";
+                    break;
+            }
+        }
+        else {
+            path = kve->kve_path;
+        }
+
+        py_path = PyUnicode_DecodeFSDefault(path);
+        if (! py_path)
+            goto error;
+        py_tuple = Py_BuildValue("ssOiiii",
+            addr,                       // "start-end" address
+            perms,                      // "rwx" permissions
+            py_path,                    // path
+            kve->kve_resident,          // rss
+            kve->kve_private_resident,  // private
+            kve->kve_ref_count,         // ref count
+            kve->kve_shadow_count);     // shadow count
+        if (!py_tuple)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_path);
+        Py_DECREF(py_tuple);
+    }
+    free(freep);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_path);
+    Py_DECREF(py_retlist);
+    if (freep != NULL)
+        free(freep);
+    return NULL;
+}
+
+
+PyObject*
+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;
+    int ret;
+    int i;
+    cpuset_t mask;
+    PyObject* py_retlist;
+    PyObject* py_cpu_num;
+
+    if (!PyArg_ParseTuple(args, "i", &pid))
+        return NULL;
+    ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+                             sizeof(mask), &mask);
+    if (ret != 0)
+        return PyErr_SetFromErrno(PyExc_OSError);
+
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL)
+        return NULL;
+
+    for (i = 0; i < CPU_SETSIZE; i++) {
+        if (CPU_ISSET(i, &mask)) {
+            py_cpu_num = Py_BuildValue("i", i);
+            if (py_cpu_num == NULL)
+                goto error;
+            if (PyList_Append(py_retlist, py_cpu_num))
+                goto error;
+        }
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cpu_num);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+PyObject *
+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;
+    int i;
+    int seq_len;
+    int ret;
+    cpuset_t cpu_set;
+    PyObject *py_cpu_set;
+    PyObject *py_cpu_seq = NULL;
+
+    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
+        return NULL;
+
+    py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
+    if (!py_cpu_seq)
+        return NULL;
+    seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
+
+    // calculate the mask
+    CPU_ZERO(&cpu_set);
+    for (i = 0; i < seq_len; i++) {
+        PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
+#if PY_MAJOR_VERSION >= 3
+        long value = PyLong_AsLong(item);
+#else
+        long value = PyInt_AsLong(item);
+#endif
+        if (value == -1 || PyErr_Occurred())
+            goto error;
+        CPU_SET(value, &cpu_set);
+    }
+
+    // set affinity
+    ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+                             sizeof(cpu_set), &cpu_set);
+    if (ret != 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    Py_DECREF(py_cpu_seq);
+    Py_RETURN_NONE;
+
+error:
+    if (py_cpu_seq != NULL)
+        Py_DECREF(py_cpu_seq);
+    return NULL;
+}
+
+
+PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    unsigned int v_soft;
+    unsigned int v_intr;
+    unsigned int v_syscall;
+    unsigned int v_trap;
+    unsigned int v_swtch;
+    size_t size = sizeof(v_soft);
+
+    if (sysctlbyname("vm.stats.sys.v_soft", &v_soft, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.sys.v_intr", &v_intr, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.sys.v_syscall", &v_syscall, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.sys.v_trap", &v_trap, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.sys.v_swtch", &v_swtch, &size, NULL, 0))
+        goto error;
+
+    return Py_BuildValue(
+        "IIIII",
+        v_swtch,  // ctx switches
+        v_intr,  // interrupts
+        v_soft,  // software interrupts
+        v_syscall,  // syscalls
+        v_trap  // traps
+    );
+
+error:
+    return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+/*
+ * Return battery information.
+ */
+PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+    int percent;
+    int minsleft;
+    int power_plugged;
+    size_t size = sizeof(percent);
+
+    if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("hw.acpi.battery.time", &minsleft, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("hw.acpi.acline", &power_plugged, &size, NULL, 0))
+        goto error;
+    return Py_BuildValue("iii", percent, minsleft, power_plugged);
+
+error:
+    // see: https://github.com/giampaolo/psutil/issues/1074
+    if (errno == ENOENT)
+        PyErr_SetString(PyExc_NotImplementedError, "no battery");
+    else
+        PyErr_SetFromErrno(PyExc_OSError);
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/specific.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <Python.h>
+
+typedef struct kinfo_proc kinfo_proc;
+
+int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
+int psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc);
+
+//
+PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
+PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
+PyObject* psutil_get_cmdline(long pid);
+PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
+PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
+PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
+PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
+PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
+PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
+PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
+PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
+PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
+PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
+PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
+PyObject* psutil_cpu_stats(PyObject* self, PyObject* args);
+#if defined(PSUTIL_FREEBSD)
+PyObject* psutil_sensors_battery(PyObject* self, PyObject* args);
+#endif
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/sys_socks.c
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ *
+ * Retrieves system-wide open socket connections. This is based off of
+ * sockstat utility source code:
+ * https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c
+ */
+
+#include <Python.h>
+#include <sys/user.h>
+#include <sys/file.h>
+#include <sys/socketvar.h>    // for struct xsocket
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>   // for xinpcb struct
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp_var.h>   // for struct xtcpcb
+#include <arpa/inet.h>         // for inet_ntop()
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+static struct xfile *psutil_xfiles;
+static int psutil_nxfiles;
+
+
+int
+psutil_populate_xfiles() {
+    size_t len;
+
+    if ((psutil_xfiles = malloc(len = sizeof *psutil_xfiles)) == NULL) {
+        PyErr_NoMemory();
+        return 0;
+    }
+    while (sysctlbyname("kern.file", psutil_xfiles, &len, 0, 0) == -1) {
+        if (errno != ENOMEM) {
+            PyErr_SetFromErrno(0);
+            return 0;
+        }
+        len *= 2;
+        if ((psutil_xfiles = realloc(psutil_xfiles, len)) == NULL) {
+            PyErr_NoMemory();
+            return 0;
+        }
+    }
+    if (len > 0 && psutil_xfiles->xf_size != sizeof *psutil_xfiles) {
+        PyErr_Format(PyExc_RuntimeError, "struct xfile size mismatch");
+        return 0;
+    }
+    psutil_nxfiles = len / sizeof *psutil_xfiles;
+    return 1;
+}
+
+
+struct xfile *
+psutil_get_file_from_sock(void *sock) {
+    struct xfile *xf;
+    int n;
+
+    for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {
+        if (xf->xf_data == sock)
+            return xf;
+    }
+    return NULL;
+}
+
+
+// Reference:
+// https://github.com/freebsd/freebsd/blob/master/usr.bin/sockstat/sockstat.c
+int psutil_gather_inet(int proto, PyObject *py_retlist) {
+    struct xinpgen *xig, *exig;
+    struct xinpcb *xip;
+    struct xtcpcb *xtp;
+#if __FreeBSD_version >= 1200026
+    struct xinpcb *inp;
+#else
+    struct inpcb *inp;
+#endif
+    struct xsocket *so;
+    const char *varname = NULL;
+    size_t len, bufsize;
+    void *buf;
+    int retry;
+    int type;
+
+    PyObject *py_tuple = NULL;
+    PyObject *py_laddr = NULL;
+    PyObject *py_raddr = NULL;
+
+    switch (proto) {
+        case IPPROTO_TCP:
+            varname = "net.inet.tcp.pcblist";
+            type = SOCK_STREAM;
+            break;
+        case IPPROTO_UDP:
+            varname = "net.inet.udp.pcblist";
+            type = SOCK_DGRAM;
+            break;
+    }
+
+    buf = NULL;
+    bufsize = 8192;
+    retry = 5;
+    do {
+        for (;;) {
+            buf = realloc(buf, bufsize);
+            if (buf == NULL)
+                continue;  // XXX
+            len = bufsize;
+            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
+                break;
+            if (errno != ENOMEM) {
+                PyErr_SetFromErrno(0);
+                goto error;
+            }
+            bufsize *= 2;
+        }
+        xig = (struct xinpgen *)buf;
+        exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);
+        if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {
+            PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
+            goto error;
+        }
+    } while (xig->xig_gen != exig->xig_gen && retry--);
+
+    for (;;) {
+	struct xfile *xf;
+        int lport, rport, status, family;
+
+        xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
+        if (xig >= exig)
+            break;
+
+        switch (proto) {
+            case IPPROTO_TCP:
+                xtp = (struct xtcpcb *)xig;
+                if (xtp->xt_len != sizeof *xtp) {
+                    PyErr_Format(PyExc_RuntimeError,
+                                 "struct xtcpcb size mismatch");
+                    goto error;
+                }
+                inp = &xtp->xt_inp;
+#if __FreeBSD_version >= 1200026
+                so = &inp->xi_socket;
+                status = xtp->t_state;
+#else
+                so = &xtp->xt_socket;
+                status = xtp->xt_tp.t_state;
+#endif
+                break;
+            case IPPROTO_UDP:
+                xip = (struct xinpcb *)xig;
+                if (xip->xi_len != sizeof *xip) {
+                    PyErr_Format(PyExc_RuntimeError,
+                                 "struct xinpcb size mismatch");
+                    goto error;
+                }
+#if __FreeBSD_version >= 1200026
+                inp = xip;
+#else
+                inp = &xip->xi_inp;
+#endif
+                so = &xip->xi_socket;
+                status = PSUTIL_CONN_NONE;
+                break;
+            default:
+                PyErr_Format(PyExc_RuntimeError, "invalid proto");
+                goto error;
+        }
+
+        char lip[200], rip[200];
+
+        xf = psutil_get_file_from_sock(so->xso_so);
+        if (xf == NULL)
+            continue;
+        lport = ntohs(inp->inp_lport);
+        rport = ntohs(inp->inp_fport);
+
+        if (inp->inp_vflag & INP_IPV4) {
+            family = AF_INET;
+            inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));
+            inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));
+        }
+        else if (inp->inp_vflag & INP_IPV6) {
+            family = AF_INET6;
+            inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));
+            inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));
+        }
+
+        // construct python tuple/list
+        py_laddr = Py_BuildValue("(si)", lip, lport);
+        if (!py_laddr)
+            goto error;
+        if (rport != 0)
+            py_raddr = Py_BuildValue("(si)", rip, rport);
+        else
+            py_raddr = Py_BuildValue("()");
+        if (!py_raddr)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(iiiNNii)",
+            xf->xf_fd, // fd
+            family,    // family
+            type,      // type
+            py_laddr,  // laddr
+            py_raddr,  // raddr
+            status,    // status
+            xf->xf_pid); // pid
+        if (!py_tuple)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+
+    free(buf);
+    return 1;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_laddr);
+    Py_XDECREF(py_raddr);
+    free(buf);
+    return 0;
+}
+
+
+int psutil_gather_unix(int proto, PyObject *py_retlist) {
+    struct xunpgen *xug, *exug;
+    struct xunpcb *xup;
+    const char *varname = NULL;
+    const char *protoname = NULL;
+    size_t len;
+    size_t bufsize;
+    void *buf;
+    int retry;
+    struct sockaddr_un *sun;
+    char path[PATH_MAX];
+
+    PyObject *py_tuple = NULL;
+    PyObject *py_lpath = NULL;
+
+    switch (proto) {
+        case SOCK_STREAM:
+            varname = "net.local.stream.pcblist";
+            protoname = "stream";
+            break;
+        case SOCK_DGRAM:
+            varname = "net.local.dgram.pcblist";
+            protoname = "dgram";
+            break;
+    }
+
+    buf = NULL;
+    bufsize = 8192;
+    retry = 5;
+
+    do {
+        for (;;) {
+            buf = realloc(buf, bufsize);
+            if (buf == NULL) {
+                PyErr_NoMemory();
+                goto error;
+            }
+            len = bufsize;
+            if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
+                break;
+            if (errno != ENOMEM) {
+                PyErr_SetFromErrno(0);
+                goto error;
+            }
+            bufsize *= 2;
+        }
+        xug = (struct xunpgen *)buf;
+        exug = (struct xunpgen *)(void *)
+            ((char *)buf + len - sizeof *exug);
+        if (xug->xug_len != sizeof *xug || exug->xug_len != sizeof *exug) {
+            PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
+            goto error;
+        }
+    } while (xug->xug_gen != exug->xug_gen && retry--);
+
+    for (;;) {
+	struct xfile *xf;
+
+        xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
+        if (xug >= exug)
+            break;
+        xup = (struct xunpcb *)xug;
+        if (xup->xu_len != sizeof *xup)
+            goto error;
+
+        xf = psutil_get_file_from_sock(xup->xu_socket.xso_so);
+        if (xf == NULL)
+            continue;
+
+        sun = (struct sockaddr_un *)&xup->xu_addr;
+        snprintf(path, sizeof(path), "%.*s",
+                 (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+                 sun->sun_path);
+        py_lpath = PyUnicode_DecodeFSDefault(path);
+        if (! py_lpath)
+            goto error;
+
+        py_tuple = Py_BuildValue("(iiiOsii)",
+            xf->xf_fd,         // fd
+            AF_UNIX,           // family
+            proto,             // type
+            py_lpath,          // lpath
+            "",                // rath
+            PSUTIL_CONN_NONE,  // status
+            xf->xf_pid);       // pid
+        if (!py_tuple)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_lpath);
+        Py_DECREF(py_tuple);
+    }
+
+    free(buf);
+    return 1;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_lpath);
+    free(buf);
+    return 0;
+}
+
+
+PyObject*
+psutil_net_connections(PyObject* self, PyObject* args) {
+    // Return system-wide open connections.
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (psutil_populate_xfiles() != 1)
+        goto error;
+    if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0)
+        goto error;
+    if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0)
+        goto error;
+    if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0)
+       goto error;
+    if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0)
+        goto error;
+
+    free(psutil_xfiles);
+    return py_retlist;
+
+error:
+    Py_DECREF(py_retlist);
+    free(psutil_xfiles);
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/freebsd/sys_socks.h
@@ -0,0 +1,10 @@
+/*
+ * 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 <Python.h>
+
+PyObject* psutil_net_connections(PyObject* self, PyObject* args);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/netbsd/socks.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'.
+ * Copyright (c) 2015, Ryo ONODERA.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <sys/file.h>
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+
+// address family filter
+enum af_filter {
+    INET,
+    INET4,
+    INET6,
+    TCP,
+    TCP4,
+    TCP6,
+    UDP,
+    UDP4,
+    UDP6,
+    UNIX,
+    ALL,
+};
+
+// kinfo_file results
+struct kif {
+    SLIST_ENTRY(kif) kifs;
+    struct kinfo_file *kif;
+};
+
+// kinfo_file results list
+SLIST_HEAD(kifhead, kif) kihead = SLIST_HEAD_INITIALIZER(kihead);
+
+
+// kinfo_pcb results
+struct kpcb {
+    SLIST_ENTRY(kpcb) kpcbs;
+    struct kinfo_pcb *kpcb;
+};
+
+// kinfo_pcb results list
+SLIST_HEAD(kpcbhead, kpcb) kpcbhead = SLIST_HEAD_INITIALIZER(kpcbhead);
+
+static void psutil_kiflist_init(void);
+static void psutil_kiflist_clear(void);
+static void psutil_kpcblist_init(void);
+static void psutil_kpcblist_clear(void);
+static int psutil_get_files(void);
+static int psutil_get_sockets(const char *name);
+static int psutil_get_info(int aff);
+
+
+// Initialize kinfo_file results list.
+static void
+psutil_kiflist_init(void) {
+    SLIST_INIT(&kihead);
+    return;
+}
+
+
+// Clear kinfo_file results list.
+static void
+psutil_kiflist_clear(void) {
+     while (!SLIST_EMPTY(&kihead)) {
+             SLIST_REMOVE_HEAD(&kihead, kifs);
+     }
+
+    return;
+}
+
+
+// Initialize kinof_pcb result list.
+static void
+psutil_kpcblist_init(void) {
+    SLIST_INIT(&kpcbhead);
+    return;
+}
+
+
+// Clear kinof_pcb result list.
+static void
+psutil_kpcblist_clear(void) {
+     while (!SLIST_EMPTY(&kpcbhead)) {
+             SLIST_REMOVE_HEAD(&kpcbhead, kpcbs);
+     }
+
+    return;
+}
+
+
+// Get all open files including socket.
+static int
+psutil_get_files(void) {
+    size_t len;
+    size_t j;
+    int mib[6];
+    char *buf;
+    off_t offset;
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_FILE2;
+    mib[2] = KERN_FILE_BYFILE;
+    mib[3] = 0;
+    mib[4] = sizeof(struct kinfo_file);
+    mib[5] = 0;
+
+    if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    offset = len % sizeof(off_t);
+    mib[5] = len / sizeof(struct kinfo_file);
+
+    if ((buf = malloc(len + offset)) == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
+
+    if (sysctl(mib, 6, buf + offset, &len, NULL, 0) == -1) {
+        free(buf);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    len /= sizeof(struct kinfo_file);
+    struct kinfo_file *ki = (struct kinfo_file *)(buf + offset);
+
+    for (j = 0; j < len; j++) {
+        struct kif *kif = malloc(sizeof(struct kif));
+        kif->kif = &ki[j];
+        SLIST_INSERT_HEAD(&kihead, kif, kifs);
+    }
+
+    /*
+    // debug
+    struct kif *k;
+    SLIST_FOREACH(k, &kihead, kifs) {
+            printf("%d\n", k->kif->ki_pid);
+    }
+    */
+
+    return 0;
+}
+
+
+// Get open sockets.
+static int
+psutil_get_sockets(const char *name) {
+    size_t namelen;
+    int mib[8];
+    struct kinfo_pcb *pcb;
+    size_t len;
+    size_t j;
+
+    memset(mib, 0, sizeof(mib));
+
+    if (sysctlnametomib(name, mib, &namelen) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    if ((pcb = malloc(len)) == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    memset(pcb, 0, len);
+
+    mib[6] = sizeof(*pcb);
+    mib[7] = len / sizeof(*pcb);
+
+    if (sysctl(mib, __arraycount(mib), pcb, &len, NULL, 0) == -1) {
+        free(pcb);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+
+    len /= sizeof(struct kinfo_pcb);
+    struct kinfo_pcb *kp = (struct kinfo_pcb *)pcb;
+
+    for (j = 0; j < len; j++) {
+        struct kpcb *kpcb = malloc(sizeof(struct kpcb));
+        kpcb->kpcb = &kp[j];
+        SLIST_INSERT_HEAD(&kpcbhead, kpcb, kpcbs);
+    }
+
+    /*
+    // debug
+    struct kif *k;
+    struct kpcb *k;
+    SLIST_FOREACH(k, &kpcbhead, kpcbs) {
+            printf("ki_type: %d\n", k->kpcb->ki_type);
+            printf("ki_family: %d\n", k->kpcb->ki_family);
+    }
+    */
+
+    return 0;
+}
+
+
+// Collect open file and connections.
+static int
+psutil_get_info(int aff) {
+    switch (aff) {
+        case INET:
+            if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
+                return -1;
+            break;
+        case INET4:
+            if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
+                return -1;
+            break;
+        case INET6:
+            if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
+                return -1;
+            break;
+        case TCP:
+            if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
+                return -1;
+            break;
+        case TCP4:
+            if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
+                return -1;
+            break;
+        case TCP6:
+            if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
+                return -1;
+            break;
+        case UDP:
+            if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
+                return -1;
+            break;
+        case UDP4:
+            if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
+                return -1;
+            break;
+        case UDP6:
+            if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
+                return -1;
+            break;
+        case UNIX:
+            if (psutil_get_sockets("net.local.stream.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.local.dgram.pcblist") != 0)
+                return -1;
+            break;
+        case ALL:
+            if (psutil_get_sockets("net.inet.tcp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet.udp.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.tcp6.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.inet6.udp6.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.local.stream.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.local.seqpacket.pcblist") != 0)
+                return -1;
+            if (psutil_get_sockets("net.local.dgram.pcblist") != 0)
+                return -1;
+            break;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Return system-wide connections (unless a pid != -1 is passed).
+ */
+PyObject *
+psutil_net_connections(PyObject *self, PyObject *args) {
+    char laddr[PATH_MAX];
+    char raddr[PATH_MAX];
+    int32_t lport;
+    int32_t rport;
+    int32_t status;
+    pid_t pid;
+    PyObject *py_tuple = NULL;
+    PyObject *py_laddr = NULL;
+    PyObject *py_raddr = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+
+    psutil_kiflist_init();
+    psutil_kpcblist_init();
+    if (psutil_get_files() != 0)
+        goto error;
+    if (psutil_get_info(ALL) != 0)
+        goto error;
+
+    struct kif *k;
+    SLIST_FOREACH(k, &kihead, kifs) {
+        struct kpcb *kp;
+        if ((pid != -1) && (k->kif->ki_pid != (unsigned int)pid))
+            continue;
+        SLIST_FOREACH(kp, &kpcbhead, kpcbs) {
+            if (k->kif->ki_fdata != kp->kpcb->ki_sockaddr)
+                continue;
+
+            // IPv4 or IPv6
+            if ((kp->kpcb->ki_family == AF_INET) ||
+                    (kp->kpcb->ki_family == AF_INET6)) {
+
+                if (kp->kpcb->ki_family == AF_INET) {
+                    // IPv4
+                    struct sockaddr_in *sin_src =
+                        (struct sockaddr_in *)&kp->kpcb->ki_src;
+                    struct sockaddr_in *sin_dst =
+                        (struct sockaddr_in *)&kp->kpcb->ki_dst;
+                    // source addr and port
+                    inet_ntop(AF_INET, &sin_src->sin_addr, laddr,
+                              sizeof(laddr));
+                    lport = ntohs(sin_src->sin_port);
+                    // remote addr and port
+                    inet_ntop(AF_INET, &sin_dst->sin_addr, raddr,
+                              sizeof(raddr));
+                    rport = ntohs(sin_dst->sin_port);
+                }
+                else {
+                    // IPv6
+                    struct sockaddr_in6 *sin6_src =
+                        (struct sockaddr_in6 *)&kp->kpcb->ki_src;
+                    struct sockaddr_in6 *sin6_dst =
+                        (struct sockaddr_in6 *)&kp->kpcb->ki_dst;
+                    // local addr and port
+                    inet_ntop(AF_INET6, &sin6_src->sin6_addr, laddr,
+                              sizeof(laddr));
+                    lport = ntohs(sin6_src->sin6_port);
+                    // remote addr and port
+                    inet_ntop(AF_INET6, &sin6_dst->sin6_addr, raddr,
+                              sizeof(raddr));
+                    rport = ntohs(sin6_dst->sin6_port);
+                }
+
+                // status
+                if (kp->kpcb->ki_type == SOCK_STREAM)
+                    status = kp->kpcb->ki_tstate;
+                else
+                    status = PSUTIL_CONN_NONE;
+
+                // build addr tuple
+                py_laddr = Py_BuildValue("(si)", laddr, lport);
+                if (! py_laddr)
+                    goto error;
+                if (rport != 0)
+                    py_raddr = Py_BuildValue("(si)", raddr, rport);
+                else
+                    py_raddr = Py_BuildValue("()");
+                if (! py_raddr)
+                    goto error;
+            }
+            else if (kp->kpcb->ki_family == AF_UNIX) {
+                // UNIX sockets
+                struct sockaddr_un *sun_src =
+                    (struct sockaddr_un *)&kp->kpcb->ki_src;
+                struct sockaddr_un *sun_dst =
+                    (struct sockaddr_un *)&kp->kpcb->ki_dst;
+                strcpy(laddr, sun_src->sun_path);
+                strcpy(raddr, sun_dst->sun_path);
+                status = PSUTIL_CONN_NONE;
+                py_laddr = PyUnicode_DecodeFSDefault(laddr);
+                if (! py_laddr)
+                    goto error;
+                py_raddr = PyUnicode_DecodeFSDefault(raddr);
+                if (! py_raddr)
+                    goto error;
+            }
+            else {
+                continue;
+            }
+
+            // append tuple to list
+            py_tuple = Py_BuildValue(
+                "(iiiOOii)",
+                k->kif->ki_fd,
+                kp->kpcb->ki_family,
+                kp->kpcb->ki_type,
+                py_laddr,
+                py_raddr,
+                status,
+                k->kif->ki_pid);
+            if (! py_tuple)
+                goto error;
+            if (PyList_Append(py_retlist, py_tuple))
+                goto error;
+            Py_DECREF(py_laddr);
+            Py_DECREF(py_raddr);
+            Py_DECREF(py_tuple);
+        }
+    }
+
+    psutil_kiflist_clear();
+    psutil_kpcblist_clear();
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_laddr);
+    Py_XDECREF(py_raddr);
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/netbsd/socks.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'.
+ * Copyright (c) 2015, Ryo ONODERA.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+PyObject *psutil_proc_connections(PyObject *, PyObject *);
+PyObject *psutil_net_connections(PyObject *, PyObject *);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/netbsd/specific.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Platform-specific module methods for NetBSD.
+ */
+
+#if defined(PSUTIL_NETBSD)
+    #define _KMEMUSER
+#endif
+
+#include <Python.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/swap.h>  // for swap_mem
+#include <signal.h>
+#include <kvm.h>
+// connection stuff
+#include <netdb.h>  // for NI_MAXHOST
+#include <sys/socket.h>
+#include <sys/sched.h>  // for CPUSTATES & CP_*
+#define _KERNEL  // for DTYPE_*
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/disk.h>  // struct diskstats
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "specific.h"
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
+#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+
+// ============================================================================
+// Utility functions
+// ============================================================================
+
+
+int
+psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) {
+    // Fills a kinfo_proc struct based on process pid.
+    int ret;
+    int mib[6];
+    size_t size = sizeof(kinfo_proc);
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC2;
+    mib[2] = KERN_PROC_PID;
+    mib[3] = pid;
+    mib[4] = size;
+    mib[5] = 1;
+
+    ret = sysctl((int*)mib, 6, proc, &size, NULL, 0);
+    if (ret == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+    // sysctl stores 0 in the size if we can't find the process information.
+    if (size == 0) {
+        NoSuchProcess("");
+        return -1;
+    }
+    return 0;
+}
+
+
+struct kinfo_file *
+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];
+    size_t len;
+    struct kinfo_file* kf;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_FILE2;
+    mib[2] = KERN_FILE_BYPID;
+    mib[3] = (int) pid;
+    mib[4] = sizeof(struct kinfo_file);
+    mib[5] = 0;
+
+    // get the size of what would be returned
+    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+    if ((kf = malloc(len)) == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    mib[5] = (int)(len / sizeof(struct kinfo_file));
+    if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    *cnt = (int)(len / sizeof(struct kinfo_file));
+    return kf;
+}
+
+
+// XXX: This is no longer used as per
+// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820
+// Current implementation uses /proc instead.
+// Left here just in case.
+/*
+PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+#if __NetBSD_Version__ >= 799000000
+    pid_t pid;
+    char pathname[MAXPATHLEN];
+    int error;
+    int mib[4];
+    int ret;
+    size_t size;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (pid == 0) {
+        // else returns ENOENT
+        return Py_BuildValue("s", "");
+    }
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC_ARGS;
+    mib[2] = pid;
+    mib[3] = KERN_PROC_PATHNAME;
+
+    size = sizeof(pathname);
+    error = sysctl(mib, 4, NULL, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    error = sysctl(mib, 4, pathname, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+    if (size == 0 || strlen(pathname) == 0) {
+        ret = psutil_pid_exists(pid);
+        if (ret == -1)
+            return NULL;
+        else if (ret == 0)
+            return NoSuchProcess("");
+        else
+            strcpy(pathname, "");
+    }
+
+    return PyUnicode_DecodeFSDefault(pathname);
+#else
+    return Py_BuildValue("s", "");
+#endif
+}
+*/
+
+PyObject *
+psutil_proc_num_threads(PyObject *self, PyObject *args) {
+    // Return number of threads used by process as a Python integer.
+    long pid;
+    kinfo_proc kp;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_kinfo_proc(pid, &kp) == -1)
+        return NULL;
+    return Py_BuildValue("l", (long)kp.p_nlwps);
+}
+
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+    pid_t pid;
+    int mib[5];
+    int i, nlwps;
+    ssize_t st;
+    size_t size;
+    struct kinfo_lwp *kl = NULL;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_LWP;
+    mib[2] = pid;
+    mib[3] = sizeof(struct kinfo_lwp);
+    mib[4] = 0;
+
+    st = sysctl(mib, 5, NULL, &size, NULL, 0);
+    if (st == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess("");
+        goto error;
+    }
+
+    mib[4] = size / sizeof(size_t);
+    kl = malloc(size);
+    if (kl == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    st = sysctl(mib, 5, kl, &size, NULL, 0);
+    if (st == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess("");
+        goto error;
+    }
+
+    nlwps = (int)(size / sizeof(struct kinfo_lwp));
+    for (i = 0; i < nlwps; i++) {
+        py_tuple = Py_BuildValue("idd",
+                                 (&kl[i])->l_lid,
+                                 PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime),
+                                 PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime));
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+    free(kl);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (kl != NULL)
+        free(kl);
+    return NULL;
+}
+
+
+// ============================================================================
+// APIS
+// ============================================================================
+
+int
+psutil_get_proc_list(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.
+    kinfo_proc *result;
+    // Declaring name as const requires us to cast it when passing it to
+    // sysctl because the prototype doesn't include the const modifier.
+    char errbuf[_POSIX2_LINE_MAX];
+    int cnt;
+    kvm_t *kd;
+
+    assert( procList != NULL);
+    assert(*procList == NULL);
+    assert(procCount != NULL);
+
+    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+
+    if (kd == NULL) {
+        PyErr_Format(
+            PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf);
+        return errno;
+    }
+
+    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;
+    }
+
+    *procCount = (size_t)cnt;
+
+    size_t mlen = cnt * sizeof(kinfo_proc);
+
+    if ((*procList = malloc(mlen)) == NULL) {
+        PyErr_NoMemory();
+        kvm_close(kd);
+        return errno;
+    }
+
+    memcpy(*procList, result, mlen);
+    assert(*procList != NULL);
+    kvm_close(kd);
+
+    return 0;
+}
+
+
+char *
+psutil_get_cmd_args(pid_t pid, size_t *argsize) {
+    int mib[4];
+    ssize_t st;
+    size_t argmax;
+    size_t size;
+    char *procargs = NULL;
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_ARGMAX;
+
+    size = sizeof(argmax);
+    st = sysctl(mib, 2, &argmax, &size, NULL, 0);
+    if (st == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    procargs = (char *)malloc(argmax);
+    if (procargs == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC_ARGS;
+    mib[2] = pid;
+    mib[3] = KERN_PROC_ARGV;
+
+    st = sysctl(mib, 4, procargs, &argmax, NULL, 0);
+    if (st == -1) {
+        free(procargs);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    *argsize = argmax;
+    return procargs;
+}
+
+
+// Return the command line as a python list object.
+// XXX - most of the times sysctl() returns a truncated string.
+// Also /proc/pid/cmdline behaves the same so it looks like this
+// is a kernel bug.
+PyObject *
+psutil_get_cmdline(pid_t pid) {
+    char *argstr = NULL;
+    size_t pos = 0;
+    size_t argsize = 0;
+    PyObject *py_arg = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (pid == 0)
+        return py_retlist;
+
+    argstr = psutil_get_cmd_args(pid, &argsize);
+    if (argstr == NULL)
+        goto error;
+
+    // args are returned as a flattened string with \0 separators between
+    // arguments add each string to the list then step forward to the next
+    // separator
+    if (argsize > 0) {
+        while (pos < argsize) {
+            py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]);
+            if (!py_arg)
+                goto error;
+            if (PyList_Append(py_retlist, py_arg))
+                goto error;
+            Py_DECREF(py_arg);
+            pos = pos + strlen(&argstr[pos]) + 1;
+        }
+    }
+
+    free(argstr);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_arg);
+    Py_DECREF(py_retlist);
+    if (argstr != NULL)
+        free(argstr);
+    return NULL;
+}
+
+
+/*
+ * Virtual memory stats, taken from:
+ * https://github.com/satterly/zabbix-stats/blob/master/src/libs/zbxsysinfo/
+ *     netbsd/memory.c
+ */
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+    size_t size;
+    struct uvmexp_sysctl uv;
+    int mib[] = {CTL_VM, VM_UVMEXP2};
+    long pagesize = getpagesize();
+
+    size = sizeof(uv);
+    if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("KKKKKKKK",
+        (unsigned long long) uv.npages << uv.pageshift,  // total
+        (unsigned long long) uv.free << uv.pageshift,  // free
+        (unsigned long long) uv.active << uv.pageshift,  // active
+        (unsigned long long) uv.inactive << uv.pageshift,  // inactive
+        (unsigned long long) uv.wired << uv.pageshift,  // wired
+        (unsigned long long) uv.filepages + uv.execpages * pagesize,  // cached
+        // These are determined from /proc/meminfo in Python.
+        (unsigned long long) 0,  // buffers
+        (unsigned long long) 0  // shared
+    );
+}
+
+
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+    uint64_t swap_total, swap_free;
+    struct swapent *swdev;
+    int nswap, i;
+
+    nswap = swapctl(SWAP_NSWAP, 0, 0);
+    if (nswap == 0) {
+        // This means there's no swap partition.
+        return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0);
+    }
+
+    swdev = calloc(nswap, sizeof(*swdev));
+    if (swdev == NULL) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    // Total things up.
+    swap_total = swap_free = 0;
+    for (i = 0; i < nswap; i++) {
+        if (swdev[i].se_flags & SWF_ENABLE) {
+            swap_total += swdev[i].se_nblks * DEV_BSIZE;
+            swap_free += (swdev[i].se_nblks - swdev[i].se_inuse) * DEV_BSIZE;
+        }
+    }
+    free(swdev);
+
+    // Get swap in/out
+    unsigned int total;
+    size_t size = sizeof(total);
+    struct uvmexp_sysctl uv;
+    int mib[] = {CTL_VM, VM_UVMEXP2};
+    long pagesize = getpagesize();
+    size = sizeof(uv);
+    if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    return Py_BuildValue("(LLLll)",
+                         swap_total,
+                         (swap_total - swap_free),
+                         swap_free,
+                         (long) uv.pgswapin * pagesize,  // swap in
+                         (long) uv.pgswapout * pagesize);  // swap out
+
+error:
+    free(swdev);
+    return NULL;
+}
+
+
+PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+    long pid;
+    int cnt;
+
+    struct kinfo_file *freep;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+
+    errno = 0;
+    freep = kinfo_getfile(pid, &cnt);
+    if (freep == NULL) {
+        psutil_raise_for_pid(pid, "kinfo_getfile()");
+        return NULL;
+    }
+    free(freep);
+
+    return Py_BuildValue("i", cnt);
+}
+
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+    // XXX: why static?
+    int mib[3];
+    int ncpu;
+    size_t len;
+    size_t size;
+    int i;
+    PyObject *py_cputime = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+    // retrieve the number of cpus
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+    len = sizeof(ncpu);
+    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    uint64_t cpu_time[CPUSTATES];
+
+    for (i = 0; i < ncpu; i++) {
+        // per-cpu info
+        mib[0] = CTL_KERN;
+        mib[1] = KERN_CP_TIME;
+        mib[2] = i;
+        size = sizeof(cpu_time);
+        if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) {
+            warn("failed to get kern.cptime2");
+            PyErr_SetFromErrno(PyExc_OSError);
+            return NULL;
+        }
+
+        py_cputime = Py_BuildValue(
+            "(ddddd)",
+            (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC);
+        if (!py_cputime)
+            goto error;
+        if (PyList_Append(py_retlist, py_cputime))
+            goto error;
+        Py_DECREF(py_cputime);
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cputime);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+    int i, dk_ndrive, mib[3];
+    size_t len;
+    struct io_sysctl *stats = NULL;
+    PyObject *py_disk_info = NULL;
+    PyObject *py_retdict = PyDict_New();
+
+    if (py_retdict == NULL)
+        return NULL;
+    mib[0] = CTL_HW;
+    mib[1] = HW_IOSTATS;
+    mib[2] = sizeof(struct io_sysctl);
+    len = 0;
+    if (sysctl(mib, 3, NULL, &len, NULL, 0) < 0) {
+        warn("can't get HW_IOSTATS");
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    dk_ndrive = (int)(len / sizeof(struct io_sysctl));
+
+    stats = malloc(len);
+    if (stats == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    if (sysctl(mib, 3, stats, &len, NULL, 0) < 0 ) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < dk_ndrive; i++) {
+        py_disk_info = Py_BuildValue(
+            "(KKKK)",
+            stats[i].rxfer,
+            stats[i].wxfer,
+            stats[i].rbytes,
+            stats[i].wbytes
+        );
+        if (!py_disk_info)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, stats[i].name, py_disk_info))
+            goto error;
+        Py_DECREF(py_disk_info);
+    }
+
+    free(stats);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_disk_info);
+    Py_DECREF(py_retdict);
+    if (stats != NULL)
+        free(stats);
+    return NULL;
+}
+
+
+PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    size_t size;
+    struct uvmexp_sysctl uv;
+    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};
+
+    size = sizeof(uv);
+    if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue(
+        "IIIIIII",
+        uv.swtch,  // ctx switches
+        uv.intrs,  // interrupts - XXX always 0, will be determined via /proc
+        uv.softs,  // soft interrupts
+        uv.syscalls,  // syscalls - XXX always 0
+        uv.traps,  // traps
+        uv.faults,  // faults
+        uv.forks  // forks
+    );
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/netbsd/specific.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+typedef struct kinfo_proc2 kinfo_proc;
+
+int psutil_kinfo_proc(pid_t pid, kinfo_proc *proc);
+struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt);
+int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
+char *psutil_get_cmd_args(pid_t pid, size_t *argsize);
+
+//
+PyObject *psutil_get_cmdline(pid_t pid);
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
+PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
+PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
+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);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/openbsd/specific.c
@@ -0,0 +1,791 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Platform-specific module methods for OpenBSD.
+ */
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/mount.h>  // for VFS_*
+#include <sys/swap.h>  // for swap_mem
+#include <sys/vmmeter.h>  // for vmtotal struct
+#include <signal.h>
+#include <kvm.h>
+// connection stuff
+#include <netdb.h>  // for NI_MAXHOST
+#include <sys/socket.h>
+#include <sys/sched.h>  // for CPUSTATES & CP_*
+#define _KERNEL  // for DTYPE_*
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/disk.h>  // struct diskstats
+#include <arpa/inet.h> // for inet_ntoa()
+#include <err.h> // for warn() & err()
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
+// #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+
+// ============================================================================
+// Utility functions
+// ============================================================================
+
+int
+psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
+    // Fills a kinfo_proc struct based on process pid.
+    int ret;
+    int mib[6];
+    size_t size = sizeof(struct kinfo_proc);
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PID;
+    mib[3] = pid;
+    mib[4] = size;
+    mib[5] = 1;
+
+    ret = sysctl((int*)mib, 6, proc, &size, NULL, 0);
+    if (ret == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return -1;
+    }
+    // sysctl stores 0 in the size if we can't find the process information.
+    if (size == 0) {
+        NoSuchProcess("");
+        return -1;
+    }
+    return 0;
+}
+
+
+struct kinfo_file *
+kinfo_getfile(long 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];
+    size_t len;
+    struct kinfo_file* kf;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_FILE;
+    mib[2] = KERN_FILE_BYPID;
+    mib[3] = (int) pid;
+    mib[4] = sizeof(struct kinfo_file);
+    mib[5] = 0;
+
+    /* get the size of what would be returned */
+    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+    if ((kf = malloc(len)) == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    mib[5] = (int)(len / sizeof(struct kinfo_file));
+    if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) {
+        free(kf);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    *cnt = (int)(len / sizeof(struct kinfo_file));
+    return kf;
+}
+
+
+// ============================================================================
+// APIS
+// ============================================================================
+
+int
+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.
+    struct kinfo_proc *result;
+    // Declaring name as const requires us to cast it when passing it to
+    // sysctl because the prototype doesn't include the const modifier.
+    char errbuf[_POSIX2_LINE_MAX];
+    int cnt;
+    kvm_t *kd;
+
+    assert(procList != NULL);
+    assert(*procList == NULL);
+    assert(procCount != NULL);
+
+    kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+
+    if (kd == NULL) {
+        return errno;
+    }
+
+    result = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt);
+    if (result == NULL) {
+        kvm_close(kd);
+        err(1, NULL);
+        return errno;
+    }
+
+    *procCount = (size_t)cnt;
+
+    size_t mlen = cnt * sizeof(struct kinfo_proc);
+
+    if ((*procList = malloc(mlen)) == NULL) {
+        kvm_close(kd);
+        err(1, NULL);
+        return errno;
+    }
+
+    memcpy(*procList, result, mlen);
+    assert(*procList != NULL);
+    kvm_close(kd);
+
+    return 0;
+}
+
+
+char **
+_psutil_get_argv(long pid) {
+    static char **argv;
+    int argv_mib[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
+    size_t argv_size = 128;
+    // Loop and reallocate until we have enough space to fit argv.
+    for (;; argv_size *= 2) {
+        if (argv_size >= 8192) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "can't allocate enough space for KERN_PROC_ARGV");
+            return NULL;
+        }
+        if ((argv = realloc(argv, argv_size)) == NULL)
+            continue;
+        if (sysctl(argv_mib, 4, argv, &argv_size, NULL, 0) == 0)
+            return argv;
+        if (errno == ENOMEM)
+            continue;
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+}
+
+
+// returns the command line as a python list object
+PyObject *
+psutil_get_cmdline(long pid) {
+    static char **argv;
+    char **p;
+    PyObject *py_arg = NULL;
+    PyObject *py_retlist = Py_BuildValue("[]");
+
+    if (!py_retlist)
+        return NULL;
+    if (pid < 0)
+        return py_retlist;
+
+    if ((argv = _psutil_get_argv(pid)) == NULL)
+        goto error;
+
+    for (p = argv; *p != NULL; p++) {
+        py_arg = PyUnicode_DecodeFSDefault(*p);
+        if (!py_arg)
+            goto error;
+        if (PyList_Append(py_retlist, py_arg))
+            goto error;
+        Py_DECREF(py_arg);
+    }
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_arg);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+    // OpenBSD reference:
+    // 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;
+    kvm_t *kd = NULL;
+    int nentries, i;
+    char errbuf[4096];
+    struct kinfo_proc *kp;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (! PyArg_ParseTuple(args, "l", &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");
+        goto error;
+    }
+
+    kp = kvm_getprocs(
+        kd, KERN_PROC_PID | KERN_PROC_SHOW_THREADS | KERN_PROC_KTHREAD, pid,
+        sizeof(*kp), &nentries);
+    if (! kp) {
+        if (strstr(errbuf, "Permission denied") != NULL)
+            AccessDenied("");
+        else
+            PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed");
+        goto error;
+    }
+
+    for (i = 0; i < nentries; i++) {
+        if (kp[i].p_tid < 0)
+            continue;
+        if (kp[i].p_pid == pid) {
+            py_tuple = Py_BuildValue(
+                "Idd",
+                kp[i].p_tid,
+                PSUTIL_KPT2DOUBLE(kp[i].p_uutime),
+                PSUTIL_KPT2DOUBLE(kp[i].p_ustime));
+            if (py_tuple == NULL)
+                goto error;
+            if (PyList_Append(py_retlist, py_tuple))
+                goto error;
+            Py_DECREF(py_tuple);
+        }
+    }
+
+    kvm_close(kd);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (kd != NULL)
+        kvm_close(kd);
+    return NULL;
+}
+
+
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+    int64_t total_physmem;
+    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
+    int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
+    int physmem_mib[] = {CTL_HW, HW_PHYSMEM64};
+    int vmmeter_mib[] = {CTL_VM, VM_METER};
+    size_t size;
+    struct uvmexp uvmexp;
+    struct bcachestats bcstats;
+    struct vmtotal vmdata;
+    long pagesize = getpagesize();
+
+    size = sizeof(total_physmem);
+    if (sysctl(physmem_mib, 2, &total_physmem, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    size = sizeof(uvmexp);
+    if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    size = sizeof(bcstats);
+    if (sysctl(bcstats_mib, 3, &bcstats, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    size = sizeof(vmdata);
+    if (sysctl(vmmeter_mib, 2, &vmdata, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("KKKKKKKK",
+        // Note: many programs calculate total memory as
+        // "uvmexp.npages * pagesize" but this is incorrect and does not
+        // match "sysctl | grep hw.physmem".
+        (unsigned long long) total_physmem,
+        (unsigned long long) uvmexp.free * pagesize,
+        (unsigned long long) uvmexp.active * pagesize,
+        (unsigned long long) uvmexp.inactive * pagesize,
+        (unsigned long long) uvmexp.wired * pagesize,
+        // this is how "top" determines it
+        (unsigned long long) bcstats.numbufpages * pagesize,  // cached
+        (unsigned long long) 0,  // buffers
+        (unsigned long long) vmdata.t_vmshr + vmdata.t_rmshr  // shared
+    );
+}
+
+
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+    uint64_t swap_total, swap_free;
+    struct swapent *swdev;
+    int nswap, i;
+
+    if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) == 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    if ((swdev = calloc(nswap, sizeof(*swdev))) == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    // Total things up.
+    swap_total = swap_free = 0;
+    for (i = 0; i < nswap; i++) {
+        if (swdev[i].se_flags & SWF_ENABLE) {
+            swap_free += (swdev[i].se_nblks - swdev[i].se_inuse);
+            swap_total += swdev[i].se_nblks;
+        }
+    }
+
+    free(swdev);
+    return Py_BuildValue("(LLLII)",
+                         swap_total * DEV_BSIZE,
+                         (swap_total - swap_free) * DEV_BSIZE,
+                         swap_free * DEV_BSIZE,
+                         // swap in / swap out is not supported as the
+                         // swapent struct does not provide any info
+                         // about it.
+                         0, 0);
+
+error:
+    free(swdev);
+    return NULL;
+}
+
+
+PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+    long pid;
+    int cnt;
+
+    struct kinfo_file *freep;
+    struct kinfo_proc kipp;
+
+    if (! PyArg_ParseTuple(args, "l", &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()");
+        return NULL;
+    }
+    free(freep);
+
+    return Py_BuildValue("i", cnt);
+}
+
+
+PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+    // Reference:
+    // https://github.com/openbsd/src/blob/
+    //     588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L191
+    long pid;
+    struct kinfo_proc kp;
+    char path[MAXPATHLEN];
+    size_t pathlen = sizeof path;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (psutil_kinfo_proc(pid, &kp) == -1)
+        return NULL;
+
+    int name[] = { CTL_KERN, KERN_PROC_CWD, pid };
+    if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+    return PyUnicode_DecodeFSDefault(path);
+}
+
+
+// see sys/kern/kern_sysctl.c lines 1100 and
+// usr.bin/fstat/fstat.c print_inet_details()
+static char *
+psutil_convert_ipv4(int family, uint32_t addr[4]) {
+    struct in_addr a;
+    memcpy(&a, addr, sizeof(a));
+    return inet_ntoa(a);
+}
+
+
+static char *
+psutil_inet6_addrstr(struct in6_addr *p)
+{
+    struct sockaddr_in6 sin6;
+    static char hbuf[NI_MAXHOST];
+    const int niflags = NI_NUMERICHOST;
+
+    memset(&sin6, 0, sizeof(sin6));
+    sin6.sin6_family = AF_INET6;
+    sin6.sin6_len = sizeof(struct sockaddr_in6);
+    sin6.sin6_addr = *p;
+    if (IN6_IS_ADDR_LINKLOCAL(p) &&
+        *(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) {
+        sin6.sin6_scope_id =
+            ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+        sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0;
+    }
+
+    if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+        hbuf, sizeof(hbuf), NULL, 0, niflags))
+        return "invalid";
+
+    return hbuf;
+}
+
+
+/*
+ * List process connections.
+ * Note: there is no net_connections() on OpenBSD. The Python
+ * implementation will iterate over all processes and use this
+ * function.
+ * Note: local and remote paths cannot be determined for UNIX sockets.
+ */
+PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+    long pid;
+    int i;
+    int cnt;
+    struct kinfo_file *freep = NULL;
+    struct kinfo_file *kif;
+    char *tcplist = NULL;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_laddr = NULL;
+    PyObject *py_raddr = NULL;
+    PyObject *py_af_filter = NULL;
+    PyObject *py_type_filter = NULL;
+    PyObject *py_family = NULL;
+    PyObject *_type = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+    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)) {
+        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;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        int state;
+        int lport;
+        int rport;
+        char addrbuf[NI_MAXHOST + 2];
+        int inseq;
+        struct in6_addr laddr6;
+        py_tuple = NULL;
+        py_laddr = NULL;
+        py_raddr = NULL;
+
+        kif = &freep[i];
+        if (kif->f_type == DTYPE_SOCKET) {
+            // apply filters
+            py_family = PyLong_FromLong((long)kif->so_family);
+            inseq = PySequence_Contains(py_af_filter, py_family);
+            Py_DECREF(py_family);
+            if (inseq == 0)
+                continue;
+            _type = PyLong_FromLong((long)kif->so_type);
+            inseq = PySequence_Contains(py_type_filter, _type);
+            Py_DECREF(_type);
+            if (inseq == 0)
+                continue;
+
+            // IPv4 / IPv6 socket
+            if ((kif->so_family == AF_INET) || (kif->so_family == AF_INET6)) {
+                // fill status
+                if (kif->so_type == SOCK_STREAM)
+                    state = kif->t_state;
+                else
+                    state = PSUTIL_CONN_NONE;
+
+                // ports
+                lport = ntohs(kif->inp_lport);
+                rport = ntohs(kif->inp_fport);
+
+                // local address, IPv4
+                if (kif->so_family == AF_INET) {
+                    py_laddr = Py_BuildValue(
+                        "(si)",
+                        psutil_convert_ipv4(kif->so_family, kif->inp_laddru),
+                        lport);
+                    if (!py_laddr)
+                        goto error;
+                }
+                else {
+                    // local address, IPv6
+                    memcpy(&laddr6, kif->inp_laddru, sizeof(laddr6));
+                    snprintf(addrbuf, sizeof(addrbuf), "%s",
+                             psutil_inet6_addrstr(&laddr6));
+                    py_laddr = Py_BuildValue("(si)", addrbuf, lport);
+                    if (!py_laddr)
+                        goto error;
+                }
+
+                if (rport != 0) {
+                    // remote address, IPv4
+                    if (kif->so_family == AF_INET) {
+                        py_raddr = Py_BuildValue(
+                            "(si)",
+                            psutil_convert_ipv4(
+                                kif->so_family, kif->inp_faddru),
+                            rport);
+                    }
+                    else {
+                        // remote address, IPv6
+                        memcpy(&laddr6, kif->inp_faddru, sizeof(laddr6));
+                        snprintf(addrbuf, sizeof(addrbuf), "%s",
+                                 psutil_inet6_addrstr(&laddr6));
+                        py_raddr = Py_BuildValue("(si)", addrbuf, rport);
+                        if (!py_raddr)
+                            goto error;
+                    }
+                }
+                else {
+                    py_raddr = Py_BuildValue("()");
+                }
+
+                if (!py_raddr)
+                    goto error;
+                py_tuple = Py_BuildValue(
+                    "(iiiNNi)",
+                    kif->fd_fd,
+                    kif->so_family,
+                    kif->so_type,
+                    py_laddr,
+                    py_raddr,
+                    state);
+                if (!py_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_tuple))
+                    goto error;
+                Py_DECREF(py_tuple);
+            }
+            // UNIX socket.
+            // XXX: local addr is supposed to be in "unp_path" but it
+            // always empty; also "fstat" command is not able to show
+            // UNIX socket paths.
+            else if (kif->so_family == AF_UNIX) {
+                py_tuple = Py_BuildValue(
+                    "(iiissi)",
+                    kif->fd_fd,
+                    kif->so_family,
+                    kif->so_type,
+                    "",  // laddr (kif->unp_path is empty)
+                    "",  // raddr
+                    PSUTIL_CONN_NONE);
+                if (!py_tuple)
+                    goto error;
+                if (PyList_Append(py_retlist, py_tuple))
+                    goto error;
+                Py_DECREF(py_tuple);
+                Py_INCREF(Py_None);
+            }
+        }
+    }
+    free(freep);
+    free(tcplist);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_XDECREF(py_laddr);
+    Py_XDECREF(py_raddr);
+    Py_DECREF(py_retlist);
+    if (freep != NULL)
+        free(freep);
+    if (tcplist != NULL)
+        free(tcplist);
+    return NULL;
+}
+
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+    int mib[3];
+    int ncpu;
+    size_t len;
+    size_t size;
+    int i;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_cputime = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+
+    // retrieve the number of cpus
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+    len = sizeof(ncpu);
+    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    uint64_t cpu_time[CPUSTATES];
+
+    for (i = 0; i < ncpu; i++) {
+        // per-cpu info
+        mib[0] = CTL_KERN;
+        mib[1] = KERN_CPTIME2;
+        mib[2] = i;
+        size = sizeof(cpu_time);
+        if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) {
+            warn("failed to get kern.cptime2");
+            PyErr_SetFromErrno(PyExc_OSError);
+            return NULL;
+        }
+
+        py_cputime = Py_BuildValue(
+            "(ddddd)",
+            (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
+            (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC);
+        if (!py_cputime)
+            goto error;
+        if (PyList_Append(py_retlist, py_cputime))
+            goto error;
+        Py_DECREF(py_cputime);
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cputime);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+    int i, dk_ndrive, mib[3];
+    size_t len;
+    struct diskstats *stats = NULL;
+
+    PyObject *py_retdict = PyDict_New();
+    PyObject *py_disk_info = NULL;
+    if (py_retdict == NULL)
+        return NULL;
+
+    mib[0] = CTL_HW;
+    mib[1] = HW_DISKSTATS;
+    len = 0;
+    if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) {
+        warn("can't get hw.diskstats size");
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    dk_ndrive = (int)(len / sizeof(struct diskstats));
+
+    stats = malloc(len);
+    if (stats == NULL) {
+        warn("can't malloc");
+        PyErr_NoMemory();
+        goto error;
+    }
+    if (sysctl(mib, 2, stats, &len, NULL, 0) < 0 ) {
+        warn("could not read hw.diskstats");
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < dk_ndrive; i++) {
+        py_disk_info = Py_BuildValue(
+            "(KKKK)",
+            stats[i].ds_rxfer,  // num reads
+            stats[i].ds_wxfer,  // num writes
+            stats[i].ds_rbytes,  // read bytes
+            stats[i].ds_wbytes  // write bytes
+        );
+        if (!py_disk_info)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, stats[i].ds_name, py_disk_info))
+            goto error;
+        Py_DECREF(py_disk_info);
+    }
+
+    free(stats);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_disk_info);
+    Py_DECREF(py_retdict);
+    if (stats != NULL)
+        free(stats);
+    return NULL;
+}
+
+
+PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    size_t size;
+    struct uvmexp uv;
+    int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
+
+    size = sizeof(uv);
+    if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue(
+        "IIIIIII",
+        uv.swtch,  // ctx switches
+        uv.intrs,  // interrupts - XXX always 0, will be determined via /proc
+        uv.softs,  // soft interrupts
+        uv.syscalls,  // syscalls - XXX always 0
+        uv.traps,  // traps
+        uv.faults,  // faults
+        uv.forks  // forks
+    );
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/openbsd/specific.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+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);
+int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
+char **_psutil_get_argv(long pid);
+PyObject * psutil_get_cmdline(long pid);
+
+//
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cwd(PyObject *self, PyObject *args);
+PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
+PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
+PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
+PyObject* psutil_cpu_stats(PyObject* self, PyObject* args);
--- a/third_party/python/psutil/psutil/arch/osx/process_info.c
+++ b/third_party/python/psutil/psutil/arch/osx/process_info.c
@@ -16,56 +16,33 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <signal.h>
 #include <sys/sysctl.h>
 #include <libproc.h>
 
 #include "process_info.h"
 #include "../../_psutil_common.h"
-
-
-/*
- * Return 1 if PID exists in the current process list, else 0.
- */
-int
-psutil_pid_exists(long pid)
-{
-    int kill_ret;
-
-    // save some time if it's an invalid PID
-    if (pid < 0)
-        return 0;
-    // if kill returns success of permission denied we know it's a valid PID
-    kill_ret = kill(pid , 0);
-    if ( (0 == kill_ret) || (EPERM == errno))
-        return 1;
-
-    // otherwise return 0 for PID not found
-    return 0;
-}
-
+#include "../../_psutil_posix.h"
 
 /*
  * 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.
  */
 int
-psutil_get_proc_list(kinfo_proc **procList, size_t *procCount)
-{
-    // Declaring mib as const requires use of a cast since the
-    // sysctl prototype doesn't include the const modifier.
-    static const int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
-    size_t           size, size2;
-    void            *ptr;
-    int              err, lim = 8;  // some limit
+psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
+    int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
+    size_t size, size2;
+    void *ptr;
+    int err;
+    int lim = 8;  // some limit
 
     assert( procList != NULL);
     assert(*procList == NULL);
     assert(procCount != NULL);
 
     *procCount = 0;
 
     /*
@@ -111,42 +88,41 @@ psutil_get_proc_list(kinfo_proc **procLi
         }
     }
     return ENOMEM;
 }
 
 
 // Read the maximum argument size for processes
 int
-psutil_get_argmax()
-{
+psutil_get_argmax() {
     int argmax;
     int mib[] = { CTL_KERN, KERN_ARGMAX };
     size_t size = sizeof(argmax);
 
     if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0)
         return argmax;
     return 0;
 }
 
 
 // return process args as a python list
 PyObject *
-psutil_get_arg_list(long pid)
-{
+psutil_get_cmdline(long pid) {
     int mib[3];
     int nargs;
-    int len;
+    size_t len;
     char *procargs = NULL;
     char *arg_ptr;
     char *arg_end;
     char *curr_arg;
     size_t argmax;
-    PyObject *arg = NULL;
-    PyObject *arglist = NULL;
+
+    PyObject *py_arg = NULL;
+    PyObject *py_retlist = NULL;
 
     // special case for PID 0 (kernel_task) where cmdline cannot be fetched
     if (pid == 0)
         return Py_BuildValue("[]");
 
     // read argmax and allocate memory for argument space.
     argmax = psutil_get_argmax();
     if (! argmax) {
@@ -158,25 +134,24 @@ psutil_get_arg_list(long pid)
     if (NULL == procargs) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     // read argument space
     mib[0] = CTL_KERN;
     mib[1] = KERN_PROCARGS2;
-    mib[2] = pid;
+    mib[2] = (pid_t)pid;
     if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
-        if (EINVAL == errno) {
-            // EINVAL == access denied OR nonexistent PID
-            if (psutil_pid_exists(pid))
-                AccessDenied();
-            else
-                NoSuchProcess();
-        }
+        // 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("");
+        else
+            PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     arg_end = &procargs[argmax];
     // copy the number of arguments to nargs
     memcpy(&nargs, procargs, sizeof(nargs));
 
     arg_ptr = procargs + sizeof(nargs);
@@ -191,91 +166,196 @@ psutil_get_arg_list(long pid)
     // skip ahead to the first argument
     for (; arg_ptr < arg_end; arg_ptr++) {
         if (*arg_ptr != '\0')
             break;
     }
 
     // iterate through arguments
     curr_arg = arg_ptr;
-    arglist = Py_BuildValue("[]");
-    if (!arglist)
+    py_retlist = Py_BuildValue("[]");
+    if (!py_retlist)
         goto error;
     while (arg_ptr < arg_end && nargs > 0) {
         if (*arg_ptr++ == '\0') {
-            arg = Py_BuildValue("s", curr_arg);
-            if (!arg)
+            py_arg = PyUnicode_DecodeFSDefault(curr_arg);
+            if (! py_arg)
                 goto error;
-            if (PyList_Append(arglist, arg))
+            if (PyList_Append(py_retlist, py_arg))
                 goto error;
-            Py_DECREF(arg);
+            Py_DECREF(py_arg);
             // iterate to next arg and decrement # of args
             curr_arg = arg_ptr;
             nargs--;
         }
     }
 
     free(procargs);
-    return arglist;
+    return py_retlist;
 
 error:
-    Py_XDECREF(arg);
-    Py_XDECREF(arglist);
+    Py_XDECREF(py_arg);
+    Py_XDECREF(py_retlist);
     if (procargs != NULL)
         free(procargs);
     return NULL;
 }
 
 
+// return process environment as a python string
+PyObject *
+psutil_get_environ(long pid) {
+    int mib[3];
+    int nargs;
+    char *procargs = NULL;
+    char *procenv = NULL;
+    char *arg_ptr;
+    char *arg_end;
+    char *env_start;
+    size_t argmax;
+    PyObject *py_ret = NULL;
+
+    // special case for PID 0 (kernel_task) where cmdline cannot be fetched
+    if (pid == 0)
+        goto empty;
+
+    // read argmax and allocate memory for argument space.
+    argmax = psutil_get_argmax();
+    if (! argmax) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    procargs = (char *)malloc(argmax);
+    if (NULL == procargs) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    // read argument space
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROCARGS2;
+    mib[2] = (pid_t)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("");
+        else
+            PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    arg_end = &procargs[argmax];
+    // copy the number of arguments to nargs
+    memcpy(&nargs, procargs, sizeof(nargs));
+
+    // skip executable path
+    arg_ptr = procargs + sizeof(nargs);
+    arg_ptr = memchr(arg_ptr, '\0', arg_end - arg_ptr);
+
+    if (arg_ptr == NULL || arg_ptr == arg_end)
+        goto empty;
+
+    // skip ahead to the first argument
+    for (; arg_ptr < arg_end; arg_ptr++) {
+        if (*arg_ptr != '\0')
+            break;
+    }
+
+    // iterate through arguments
+    while (arg_ptr < arg_end && nargs > 0) {
+        if (*arg_ptr++ == '\0')
+            nargs--;
+    }
+
+    // build an environment variable block
+    env_start = arg_ptr;
+
+    procenv = calloc(1, arg_end - arg_ptr);
+    if (procenv == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    while (*arg_ptr != '\0' && arg_ptr < arg_end) {
+        char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
+
+        if (s == NULL)
+            break;
+
+        memcpy(procenv + (arg_ptr - env_start), arg_ptr, s - arg_ptr);
+
+        arg_ptr = s + 1;
+    }
+
+    py_ret = PyUnicode_DecodeFSDefaultAndSize(
+        procenv, arg_ptr - env_start + 1);
+    if (!py_ret) {
+        // XXX: don't want to free() this as per:
+        // https://github.com/giampaolo/psutil/issues/926
+        // It sucks but not sure what else to do.
+        procargs = NULL;
+        goto error;
+    }
+
+    free(procargs);
+    free(procenv);
+
+    return py_ret;
+
+empty:
+    if (procargs != NULL)
+        free(procargs);
+    return Py_BuildValue("s", "");
+
+error:
+    Py_XDECREF(py_ret);
+    if (procargs != NULL)
+        free(procargs);
+    if (procenv != NULL)
+        free(procargs);
+    return NULL;
+}
+
+
 int
-psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp)
-{
+psutil_get_kinfo_proc(long 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;
+    mib[3] = (pid_t)pid;
 
     // fetch the info with sysctl()
     len = sizeof(struct kinfo_proc);
 
     // now read the data from sysctl
     if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {
         // raise an exception and throw errno as the error
         PyErr_SetFromErrno(PyExc_OSError);
         return -1;
     }
 
     // sysctl succeeds but len is zero, happens when process has gone away
     if (len == 0) {
-        NoSuchProcess();
+        NoSuchProcess("");
         return -1;
     }
     return 0;
 }
 
 
 /*
- * A thin wrapper around proc_pidinfo()
+ * A wrapper around proc_pidinfo().
+ * Returns 0 on failure (and Python exception gets already set).
  */
 int
-psutil_proc_pidinfo(long pid, int flavor, void *pti, int size)
-{
-    int ret = proc_pidinfo((int)pid, flavor, 0, pti, size);
-    if (ret == 0) {
-        if (! psutil_pid_exists(pid)) {
-            NoSuchProcess();
-            return 0;
-        }
-        else {
-            AccessDenied();
-            return 0;
-        }
-    }
-    else if (ret != size) {
-        AccessDenied();
+psutil_proc_pidinfo(long pid, int flavor, uint64_t arg, void *pti, int size) {
+    errno = 0;
+    int ret = proc_pidinfo((int)pid, flavor, arg, pti, size);
+    if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
+        psutil_raise_for_pid(pid, "proc_pidinfo()");
         return 0;
     }
-    else {
-        return 1;
-    }
+    return ret;
 }
--- a/third_party/python/psutil/psutil/arch/osx/process_info.h
+++ b/third_party/python/psutil/psutil/arch/osx/process_info.h
@@ -4,13 +4,14 @@
  * found in the LICENSE file.
  */
 
 #include <Python.h>
 
 typedef struct kinfo_proc kinfo_proc;
 
 int psutil_get_argmax(void);
-int psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp);
+int psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp);
 int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
-int psutil_pid_exists(long pid);
-int psutil_proc_pidinfo(long pid, int flavor, void *pti, int size);
-PyObject* psutil_get_arg_list(long pid);
+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);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/solaris/environ.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Oleksii Shevchuk.
+ * All rights reserved. Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ *
+ * Functions specific for Process.environ().
+ */
+
+#define NEW_MIB_COMPLIANT 1
+#define _STRUCTURED_PROC 1
+
+#include <Python.h>
+
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#  undef _FILE_OFFSET_BITS
+#  undef _LARGEFILE64_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/procfs.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "environ.h"
+
+
+#define STRING_SEARCH_BUF_SIZE 512
+
+
+/*
+ * Open address space of specified process and return file descriptor.
+ *  @param pid a pid of process.
+ *  @param procfs_path a path to mounted procfs filesystem.
+ *  @return file descriptor or -1 in case of error.
+ */
+static int
+open_address_space(pid_t pid, const char *procfs_path) {
+    int fd;
+    char proc_path[PATH_MAX];
+
+    snprintf(proc_path, PATH_MAX, "%s/%i/as", procfs_path, pid);
+    fd = open(proc_path, O_RDONLY);
+    if (fd < 0)
+        PyErr_SetFromErrno(PyExc_OSError);
+
+    return fd;
+}
+
+
+/*
+ * Read chunk of data by offset to specified buffer of the same size.
+ * @param fd a file descriptor.
+ * @param offset an required offset in file.
+ * @param buf a buffer where to store result.
+ * @param buf_size a size of buffer where data will be stored.
+ * @return amount of bytes stored to the buffer or -1 in case of
+ *         error.
+ */
+static int
+read_offt(int fd, off_t offset, char *buf, size_t buf_size) {
+    size_t to_read = buf_size;
+    size_t stored  = 0;
+    int r;
+
+    while (to_read) {
+        r = pread(fd, buf + stored, to_read, offset + stored);
+        if (r < 0)
+            goto error;
+        else if (r == 0)
+            break;
+
+        to_read -= r;
+        stored += r;
+    }
+
+    return stored;
+
+ error:
+    PyErr_SetFromErrno(PyExc_OSError);
+    return -1;
+}
+
+
+/*
+ * Read null-terminated string from file descriptor starting from
+ * specified offset.
+ * @param fd a file descriptor of opened address space.
+ * @param offset an offset in specified file descriptor.
+ * @return allocated null-terminated string or NULL in case of error.
+*/
+static char *
+read_cstring_offt(int fd, off_t offset) {
+    int r;
+    int i = 0;
+    off_t end = offset;
+    size_t len;
+    char buf[STRING_SEARCH_BUF_SIZE];
+    char *result = NULL;
+
+    if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    // Search end of string
+    for (;;) {
+        r = read(fd, buf, sizeof(buf));
+        if (r == -1) {
+            PyErr_SetFromErrno(PyExc_OSError);
+            goto error;
+        }
+        else if (r == 0) {
+            break;
+        }
+        else {
+            for (i=0; i<r; i++)
+                if (! buf[i])
+                    goto found;
+        }
+
+        end += r;
+    }
+
+found:
+    len = end + i - offset;
+
+    result = malloc(len+1);
+    if (! result) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    if (len) {
+        if (read_offt(fd, offset, result, len) < 0) {
+            goto error;
+        }
+    }
+
+    result[len] = '\0';
+    return result;
+
+ error:
+    if (result)
+        free(result);
+    return NULL;
+}
+
+
+/*
+ * Read block of addresses by offset, dereference them one by one
+ * and create an array of null terminated C strings from them.
+ * @param fd a file descriptor of address space of interesting process.
+ * @param offset an offset of address block in address space.
+ * @param ptr_size a size of pointer. Only 4 or 8 are valid values.
+ * @param count amount of pointers in block.
+ * @return allocated array of strings dereferenced and read by offset.
+ * Number of elements in array are count. In case of error function
+ * returns NULL.
+ */
+static char **
+read_cstrings_block(int fd, off_t offset, size_t ptr_size, size_t count) {
+    char **result = NULL;
+    char *pblock = NULL;
+    size_t pblock_size;
+    int i;
+
+    assert(ptr_size == 4 || ptr_size == 8);
+
+    if (!count)
+        goto error;
+
+    pblock_size = ptr_size * count;
+
+    pblock = malloc(pblock_size);
+    if (! pblock) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    if (read_offt(fd, offset, pblock, pblock_size) != pblock_size)
+        goto error;
+
+    result = (char **) calloc(count, sizeof(char *));
+    if (! result) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    for (i=0; i<count; i++) {
+        result[i] = read_cstring_offt(
+            fd, (ptr_size == 4?
+                ((uint32_t *) pblock)[i]:
+                ((uint64_t *) pblock)[i]));
+
+        if (!result[i])
+            goto error;
+    }
+
+    free(pblock);
+    return result;
+
+ error:
+    if (result)
+        psutil_free_cstrings_array(result, i);
+    if (pblock)
+        free(pblock);
+    return NULL;
+}
+
+
+/*
+ * Check that caller process can extract proper values from psinfo_t
+ * structure.
+ * @param info a pointer to process info (psinfo_t) structure of the
+ *             interesting process.
+ *  @return 1 in case if caller process can extract proper values from
+ *          psinfo_t structure, or 0 otherwise.
+ */
+static inline int
+is_ptr_dereference_possible(psinfo_t info) {
+#if !defined(_LP64)
+    return info.pr_dmodel == PR_MODEL_ILP32;
+#else
+    return 1;
+#endif
+}
+
+
+/*
+ * Return pointer size according to psinfo_t structure
+ * @param info a ponter to process info (psinfo_t) structure of the
+ *             interesting process.
+ * @return pointer size (4 or 8).
+ */
+static inline int
+ptr_size_by_psinfo(psinfo_t info) {
+    return info.pr_dmodel == PR_MODEL_ILP32? 4 : 8;
+}
+
+
+/*
+ * Count amount of pointers in a block which ends with NULL.
+ * @param fd a discriptor of /proc/PID/as special file.
+ * @param offt an offset of block of pointers at the file.
+ * @param ptr_size a pointer size (allowed values: {4, 8}).
+ * @return amount of non-NULL pointers or -1 in case of error.
+ */
+static int
+search_pointers_vector_size_offt(int fd, off_t offt, size_t ptr_size) {
+    int count = 0;
+    int r;
+    char buf[8];
+    static const char zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+    assert(ptr_size == 4 || ptr_size == 8);
+
+    if (lseek(fd, offt, SEEK_SET) == (off_t)-1)
+        goto error;
+
+    for (;; count ++) {
+        r = read(fd, buf, ptr_size);
+
+        if (r < 0)
+            goto error;
+
+        if (r == 0)
+            break;
+
+        if (r != ptr_size) {
+            PyErr_SetString(
+                PyExc_RuntimeError, "pointer block is truncated");
+            return -1;
+        }
+
+        if (! memcmp(buf, zeros, ptr_size))
+            break;
+    }
+
+    return count;
+
+ error:
+    PyErr_SetFromErrno(PyExc_OSError);
+    return -1;
+}
+
+
+/*
+ * Derefence and read array of strings by psinfo_t.pr_argv pointer from
+ * remote process.
+ * @param info a ponter to process info (psinfo_t) structure of the
+ *             interesting process
+ * @param procfs_path a cstring with path to mounted procfs filesystem.
+ * @param count a pointer to variable where to store amount of elements in
+ *        returned array. In case of error value of variable will not be
+          changed.
+ * @return allocated array of cstrings or NULL in case of error.
+ */
+char **
+psutil_read_raw_args(psinfo_t info, const char *procfs_path, size_t *count) {
+    int as;
+    char **result;
+
+    if (! is_ptr_dereference_possible(info)) {
+        PyErr_SetString(
+            PyExc_NotImplementedError,
+            "can't get env of a 64 bit process from a 32 bit process");
+        return NULL;
+    }
+
+    if (! (info.pr_argv && info.pr_argc)) {
+        PyErr_SetString(
+            PyExc_RuntimeError, "process doesn't have arguments block");
+
+        return NULL;
+    }
+
+    as = open_address_space(info.pr_pid, procfs_path);
+    if (as < 0)
+        return NULL;
+
+    result = read_cstrings_block(
+        as, info.pr_argv, ptr_size_by_psinfo(info), info.pr_argc
+    );
+
+    if (result && count)
+        *count = info.pr_argc;
+
+    close(as);
+
+    return result;
+}
+
+
+/*
+ * Dereference and read array of strings by psinfo_t.pr_envp pointer
+ * from remote process.
+ * @param info a ponter to process info (psinfo_t) structure of the
+ *             interesting process.
+ * @param procfs_path a cstring with path to mounted procfs filesystem.
+ * @param count a pointer to variable where to store amount of elements in
+ *        returned array. In case of error value of variable will not be
+ *        changed. To detect special case (described later) variable should be
+ *        initialized by -1 or other negative value.
+ * @return allocated array of cstrings or NULL in case of error.
+ *         Special case: count set to 0, return NULL.
+ *         Special case means there is no error acquired, but no data
+ *         retrieved.
+ *         Special case exists because the nature of the process. From the
+ *         beginning it's not clean how many pointers in envp array. Also
+ *         situation when environment is empty is common for kernel processes.
+ */
+char **
+psutil_read_raw_env(psinfo_t info, const char *procfs_path, ssize_t *count) {
+    int as;
+    int env_count;
+    int ptr_size;
+    char **result = NULL;
+
+    if (! is_ptr_dereference_possible(info)) {
+        PyErr_SetString(
+            PyExc_NotImplementedError,
+            "can't get env of a 64 bit process from a 32 bit process");
+        return NULL;
+    }
+
+    as = open_address_space(info.pr_pid, procfs_path);
+    if (as < 0)
+        return NULL;
+
+    ptr_size = ptr_size_by_psinfo(info);
+
+    env_count = search_pointers_vector_size_offt(
+        as, info.pr_envp, ptr_size);
+
+    if (env_count >= 0 && count)
+        *count = env_count;
+
+    if (env_count > 0)
+        result = read_cstrings_block(
+            as, info.pr_envp, ptr_size, env_count);
+
+    close(as);
+    return result;
+}
+
+
+/*
+ * Free array of cstrings.
+ * @param array an array of cstrings returned by psutil_read_raw_env,
+ *              psutil_read_raw_args or any other function.
+ * @param count a count of strings in the passed array
+ */
+void
+psutil_free_cstrings_array(char **array, size_t count) {
+    int i;
+
+    if (!array)
+        return;
+    for (i=0; i<count; i++) {
+        if (array[i]) {
+            free(array[i]);
+        }
+    }
+    free(array);
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/solaris/environ.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Oleksii Shevchuk.
+ * All rights reserved. Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef PROCESS_AS_UTILS_H
+#define PROCESS_AS_UTILS_H
+
+char **
+psutil_read_raw_args(psinfo_t info, const char *procfs_path, size_t *count);
+
+char **
+psutil_read_raw_env(psinfo_t info, const char *procfs_path, ssize_t *count);
+
+void
+psutil_free_cstrings_array(char **array, size_t count);
+
+#endif // PROCESS_AS_UTILS_H
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/solaris/v10/ifaddrs.c
@@ -0,0 +1,124 @@
+/* Refrences:
+ * https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+ * http://stackoverflow.com/questions/4139405/#4139811
+ * https://github.com/steve-o/openpgm/blob/master/openpgm/pgm/getifaddrs.c
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include "ifaddrs.h"
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#define SIZE(p) MAX((p).ss_len,sizeof(p))
+
+
+static struct sockaddr *
+sa_dup (struct sockaddr_storage *sa1)
+{
+    struct sockaddr *sa2;
+    size_t sz = sizeof(struct sockaddr_storage);
+    sa2 = (struct sockaddr *) calloc(1,sz);
+    memcpy(sa2,sa1,sz);
+    return(sa2);
+}
+
+
+void freeifaddrs (struct ifaddrs *ifp)
+{
+    if (NULL == ifp) return;
+    free(ifp->ifa_name);
+    free(ifp->ifa_addr);
+    free(ifp->ifa_netmask);
+    free(ifp->ifa_dstaddr);
+    freeifaddrs(ifp->ifa_next);
+    free(ifp);
+}
+
+
+int getifaddrs (struct ifaddrs **ifap)
+{
+    int sd = -1;
+    char *ccp, *ecp;
+    struct lifconf ifc;
+    struct lifreq *ifr;
+    struct lifnum lifn;
+    struct ifaddrs *cifa = NULL; /* current */
+    struct ifaddrs *pifa = NULL; /* previous */
+    const size_t IFREQSZ = sizeof(struct lifreq);
+
+    sd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sd < 0)
+        goto error;
+
+    ifc.lifc_buf = NULL;
+    *ifap = NULL;
+    /* find how much memory to allocate for the SIOCGLIFCONF call */
+    lifn.lifn_family = AF_UNSPEC;
+    lifn.lifn_flags = 0;
+    if (ioctl(sd, SIOCGLIFNUM, &lifn) < 0)
+        goto error;
+
+    /* Sun and Apple code likes to pad the interface count here in case interfaces
+     * are coming up between calls */
+    lifn.lifn_count += 4;
+
+    ifc.lifc_family = AF_UNSPEC;
+    ifc.lifc_len = lifn.lifn_count * sizeof(struct lifreq);
+    ifc.lifc_buf = calloc(1, ifc.lifc_len);
+    if (ioctl(sd, SIOCGLIFCONF, &ifc) < 0)
+        goto error;
+
+    ccp = (char *)ifc.lifc_req;
+    ecp = ccp + ifc.lifc_len;
+
+    while (ccp < ecp) {
+
+        ifr = (struct lifreq *) ccp;
+        cifa = (struct ifaddrs *) calloc(1, sizeof(struct ifaddrs));
+        cifa->ifa_next = NULL;
+        cifa->ifa_name = strdup(ifr->lifr_name);
+
+        if (pifa == NULL) *ifap = cifa; /* first one */
+        else pifa->ifa_next = cifa;
+
+        if (ioctl(sd, SIOCGLIFADDR, ifr, IFREQSZ) < 0)
+            goto error;
+        cifa->ifa_addr = sa_dup(&ifr->lifr_addr);
+
+        if (ioctl(sd, SIOCGLIFNETMASK, ifr, IFREQSZ) < 0)
+            goto error;
+        cifa->ifa_netmask = sa_dup(&ifr->lifr_addr);
+
+        cifa->ifa_flags = 0;
+        cifa->ifa_dstaddr = NULL;
+
+        if (0 == ioctl(sd, SIOCGLIFFLAGS, ifr)) /* optional */
+            cifa->ifa_flags = ifr->lifr_flags;
+
+        if (ioctl(sd, SIOCGLIFDSTADDR, ifr, IFREQSZ) < 0) {
+            if (0 == ioctl(sd, SIOCGLIFBRDADDR, ifr, IFREQSZ))
+                cifa->ifa_dstaddr = sa_dup(&ifr->lifr_addr);
+        }
+        else cifa->ifa_dstaddr = sa_dup(&ifr->lifr_addr);
+
+        pifa = cifa;
+        ccp += IFREQSZ;
+    }
+    free(ifc.lifc_buf);
+    close(sd);
+    return 0;
+error:
+    if (ifc.lifc_buf != NULL)
+        free(ifc.lifc_buf);
+    if (sd != -1)
+        close(sd);
+    return (-1);
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/solaris/v10/ifaddrs.h
@@ -0,0 +1,26 @@
+/* Reference: https://lists.samba.org/archive/samba-technical/2009-February/063079.html */
+
+
+#ifndef __IFADDRS_H__
+#define __IFADDRS_H__
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#undef  ifa_dstaddr
+#undef  ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr
+
+struct ifaddrs {
+    struct ifaddrs  *ifa_next;
+    char            *ifa_name;
+    unsigned int     ifa_flags;
+    struct sockaddr *ifa_addr;
+    struct sockaddr *ifa_netmask;
+    struct sockaddr *ifa_dstaddr;
+};
+
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+
+#endif
--- a/third_party/python/psutil/psutil/arch/windows/inet_ntop.c
+++ b/third_party/python/psutil/psutil/arch/windows/inet_ntop.c
@@ -1,41 +1,46 @@
+/*
+ * 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 <Python.h>
 #include "inet_ntop.h"
 
 // From: https://memset.wordpress.com/2010/10/09/inet_ntop-for-win32/
-PCSTR
-WSAAPI
-inet_ntop(
-    __in                                INT             Family,
-    __in                                PVOID           pAddr,
-    __out_ecount(StringBufSize)         PSTR            pStringBuf,
-    __in                                size_t          StringBufSize
-    )
-{
+PCSTR WSAAPI
+inet_ntop(__in INT family,
+          __in PVOID pAddr,
+          __out_ecount(StringBufSize) PSTR pStringBuf,
+          __in size_t StringBufSize) {
     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;
+    srcaddr.ss_family = family;
 
-    if (Family == AF_INET)
-    {
+    if (family == AF_INET) {
         dwAddressLength = sizeof(struct sockaddr_in);
         memcpy(&(srcaddr4->sin_addr), pAddr, sizeof(struct in_addr));
-    } else if (Family == AF_INET6)
-    {
+    }
+    else if (family == AF_INET6) {
         dwAddressLength = sizeof(struct sockaddr_in6);
         memcpy(&(srcaddr6->sin6_addr), pAddr, sizeof(struct in6_addr));
-    } else {
+    }
+    else {
+        PyErr_SetString(PyExc_ValueError, "invalid family");
         return NULL;
     }
 
     if (WSAAddressToString((LPSOCKADDR) &srcaddr,
-                           dwAddressLength,                           
-                           0, 
-                           pStringBuf, 
+                           dwAddressLength,
+                           0,
+                           pStringBuf,
                            (LPDWORD) &StringBufSize) != 0) {
+        PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError());
         return NULL;
     }
     return pStringBuf;
-}
\ No newline at end of file
+}
--- a/third_party/python/psutil/psutil/arch/windows/inet_ntop.h
+++ b/third_party/python/psutil/psutil/arch/windows/inet_ntop.h
@@ -1,10 +1,15 @@
+/*
+ * 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 <ws2tcpip.h>
 
-PCSTR
-WSAAPI
+PCSTR WSAAPI
 inet_ntop(
     __in                                INT             Family,
     __in                                PVOID           pAddr,
     __out_ecount(StringBufSize)         PSTR            pStringBuf,
     __in                                size_t          StringBufSize
-);
\ No newline at end of file
+);
--- a/third_party/python/psutil/psutil/arch/windows/ntextapi.h
+++ b/third_party/python/psutil/psutil/arch/windows/ntextapi.h
@@ -2,29 +2,130 @@
  * 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.
  */
 #if !defined(__NTEXTAPI_H__)
 #define __NTEXTAPI_H__
 #include <winternl.h>
 
+
+typedef struct {
+    LARGE_INTEGER IdleTime;
+    LARGE_INTEGER KernelTime;
+    LARGE_INTEGER UserTime;
+    LARGE_INTEGER DpcTime;
+    LARGE_INTEGER InterruptTime;
+    ULONG InterruptCount;
+} _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+
+typedef struct {
+    LARGE_INTEGER IdleProcessTime;
+    LARGE_INTEGER IoReadTransferCount;
+    LARGE_INTEGER IoWriteTransferCount;
+    LARGE_INTEGER IoOtherTransferCount;
+    ULONG IoReadOperationCount;
+    ULONG IoWriteOperationCount;
+    ULONG IoOtherOperationCount;
+    ULONG AvailablePages;
+    ULONG CommittedPages;
+    ULONG CommitLimit;
+    ULONG PeakCommitment;
+    ULONG PageFaultCount;
+    ULONG CopyOnWriteCount;
+    ULONG TransitionCount;
+    ULONG CacheTransitionCount;
+    ULONG DemandZeroCount;
+    ULONG PageReadCount;
+    ULONG PageReadIoCount;
+    ULONG CacheReadCount;
+    ULONG CacheIoCount;
+    ULONG DirtyPagesWriteCount;
+    ULONG DirtyWriteIoCount;
+    ULONG MappedPagesWriteCount;
+    ULONG MappedWriteIoCount;
+    ULONG PagedPoolPages;
+    ULONG NonPagedPoolPages;
+    ULONG PagedPoolAllocs;
+    ULONG PagedPoolFrees;
+    ULONG NonPagedPoolAllocs;
+    ULONG NonPagedPoolFrees;
+    ULONG FreeSystemPtes;
+    ULONG ResidentSystemCodePage;
+    ULONG TotalSystemDriverPages;
+    ULONG TotalSystemCodePages;
+    ULONG NonPagedPoolLookasideHits;
+    ULONG PagedPoolLookasideHits;
+    ULONG AvailablePagedPoolPages;
+    ULONG ResidentSystemCachePage;
+    ULONG ResidentPagedPoolPage;
+    ULONG ResidentSystemDriverPage;
+    ULONG CcFastReadNoWait;
+    ULONG CcFastReadWait;
+    ULONG CcFastReadResourceMiss;
+    ULONG CcFastReadNotPossible;
+    ULONG CcFastMdlReadNoWait;
+    ULONG CcFastMdlReadWait;
+    ULONG CcFastMdlReadResourceMiss;
+    ULONG CcFastMdlReadNotPossible;
+    ULONG CcMapDataNoWait;
+    ULONG CcMapDataWait;
+    ULONG CcMapDataNoWaitMiss;
+    ULONG CcMapDataWaitMiss;
+    ULONG CcPinMappedDataCount;
+    ULONG CcPinReadNoWait;
+    ULONG CcPinReadWait;
+    ULONG CcPinReadNoWaitMiss;
+    ULONG CcPinReadWaitMiss;
+    ULONG CcCopyReadNoWait;
+    ULONG CcCopyReadWait;
+    ULONG CcCopyReadNoWaitMiss;
+    ULONG CcCopyReadWaitMiss;
+    ULONG CcMdlReadNoWait;
+    ULONG CcMdlReadWait;
+    ULONG CcMdlReadNoWaitMiss;
+    ULONG CcMdlReadWaitMiss;
+    ULONG CcReadAheadIos;
+    ULONG CcLazyWriteIos;
+    ULONG CcLazyWritePages;
+    ULONG CcDataFlushes;
+    ULONG CcDataPages;
+    ULONG ContextSwitches;
+    ULONG FirstLevelTbFills;
+    ULONG SecondLevelTbFills;
+    ULONG SystemCalls;
+
+} _SYSTEM_PERFORMANCE_INFORMATION;
+
+
+typedef struct {
+    ULONG ContextSwitches;
+    ULONG DpcCount;
+    ULONG DpcRate;
+    ULONG TimeIncrement;
+    ULONG DpcBypassCount;
+    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 = 0,
     FreePage = 1,
     PageIn = 2,
     PoolAllocation = 3,
     DelayExecution = 4,
     Suspended = 5,
     UserRequest = 6,
@@ -56,49 +157,58 @@ typedef enum _KWAIT_REASON {
     WrPreempted = 32,
     WrYieldExecution = 33,
     WrFastMutex = 34,
     WrGuardedMutex = 35,
     WrRundown = 36,
     MaximumWaitReason = 37
 } KWAIT_REASON, *PKWAIT_REASON;
 
-typedef struct _CLIENT_ID {
+
+typedef struct _CLIENT_ID2 {
     HANDLE UniqueProcess;
     HANDLE UniqueThread;
-} CLIENT_ID, *PCLIENT_ID;
+} CLIENT_ID2, *PCLIENT_ID2;
 
-typedef struct _SYSTEM_THREAD_INFORMATION {
+#define CLIENT_ID CLIENT_ID2
+#define PCLIENT_ID PCLIENT_ID2
+
+typedef struct _SYSTEM_THREAD_INFORMATION2 {
     LARGE_INTEGER KernelTime;
     LARGE_INTEGER UserTime;
     LARGE_INTEGER CreateTime;
     ULONG WaitTime;
     PVOID StartAddress;
     CLIENT_ID ClientId;
     LONG Priority;
     LONG BasePriority;
     ULONG ContextSwitches;
     ULONG ThreadState;
     KWAIT_REASON WaitReason;
-} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
+} SYSTEM_THREAD_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;
+
+#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2
+#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2
 
 typedef struct _TEB *PTEB;
 
+
 // private
 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;
     LARGE_INTEGER SpareLi1;
     LARGE_INTEGER SpareLi2;
     LARGE_INTEGER SpareLi3;
     LARGE_INTEGER CreateTime;
     LARGE_INTEGER UserTime;
@@ -146,52 +256,55 @@ typedef struct _WINSTATION_INFO {
     FILETIME ConnectTime;
     FILETIME DisconnectTime;
     FILETIME LastInputTime;
     FILETIME LoginTime;
     BYTE Reserved3[1096];
     FILETIME CurrentTime;
 } WINSTATION_INFO, *PWINSTATION_INFO;
 
+
 typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW)
                  (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG);
 
 
 /*
  * NtQueryInformationProcess code taken from
  * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/
  * typedefs needed to compile against ntdll functions not exposted in the API
  */
 typedef LONG NTSTATUS;
 
+
 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
     HANDLE ProcessHandle,
     DWORD ProcessInformationClass,
     PVOID ProcessInformation,
     DWORD ProcessInformationLength,
     PDWORD ReturnLength
 );
 
+
 typedef NTSTATUS (NTAPI *_NtSetInformationProcess)(
     HANDLE ProcessHandle,
     DWORD ProcessInformationClass,
     PVOID ProcessInformation,
     DWORD ProcessInformationLength
 );
 
 
 typedef enum _PROCESSINFOCLASS2 {
     _ProcessBasicInformation,
     ProcessQuotaLimits,
     ProcessIoCounters,
     ProcessVmCounters,
     ProcessTimes,
     ProcessBasePriority,
     ProcessRaisePriority,
-    ProcessDebugPort,
+    _ProcessDebugPort,
     ProcessExceptionPort,
     ProcessAccessToken,
     ProcessLdtInformation,
     ProcessLdtSize,
     ProcessDefaultHardErrorMode,
     ProcessIoPortHandlers,
     ProcessPooledUsageAndLimits,
     ProcessWorkingSetWatch,
@@ -202,27 +315,31 @@ typedef enum _PROCESSINFOCLASS2 {
     ProcessHandleCount,
     ProcessAffinityMask,
     ProcessPriorityBoost,
     ProcessDeviceMap,
     ProcessSessionInformation,
     ProcessForegroundInformation,
     _ProcessWow64Information,
     /* added after XP+ */
-    ProcessImageFileName,
+    _ProcessImageFileName,
     ProcessLUIDDeviceMapsEnabled,
-    ProcessBreakOnTermination,
+    _ProcessBreakOnTermination,
     ProcessDebugObjectHandle,
     ProcessDebugFlags,
     ProcessHandleTracing,
     ProcessIoPriority,
     ProcessExecuteFlags,
     ProcessResourceManagement,
     ProcessCookie,
     ProcessImageInformation,
     MaxProcessInfoClass
 } PROCESSINFOCLASS2;
 
+
 #define PROCESSINFOCLASS PROCESSINFOCLASS2
 #define ProcessBasicInformation _ProcessBasicInformation
 #define ProcessWow64Information _ProcessWow64Information
+#define ProcessDebugPort _ProcessDebugPort
+#define ProcessImageFileName _ProcessImageFileName
+#define ProcessBreakOnTermination _ProcessBreakOnTermination
 
 #endif // __NTEXTAPI_H__
--- a/third_party/python/psutil/psutil/arch/windows/process_handles.c
+++ b/third_party/python/psutil/psutil/arch/windows/process_handles.c
@@ -1,57 +1,56 @@
 /*
  * 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 "process_handles.h"
+#include "../../_psutil_common.h"
 
 static _NtQuerySystemInformation __NtQuerySystemInformation = NULL;
 static _NtQueryObject __NtQueryObject = NULL;
 
 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;
-PVOID g_fiber = NULL;
 
 
 PVOID
-GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
-{
+GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) {
     return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
 }
 
+
 PyObject *
-psutil_get_open_files(long dwPid, HANDLE hProcess)
-{
+psutil_get_open_files(long dwPid, HANDLE hProcess) {
     OSVERSIONINFO osvi;
 
     ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
     GetVersionEx(&osvi);
 
     // Threaded version only works for Vista+
     if (osvi.dwMajorVersion >= 6)
         return psutil_get_open_files_ntqueryobject(dwPid, hProcess);
     else
         return psutil_get_open_files_getmappedfilename(dwPid, hProcess);
 }
 
+
 VOID
-psutil_get_open_files_init(BOOL threaded)
-{
+psutil_get_open_files_init(BOOL threaded) {
     if (g_initialized == TRUE)
         return;
 
     // Resolve the Windows API calls
     __NtQuerySystemInformation =
         GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
     __NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject");
 
@@ -60,29 +59,29 @@ psutil_get_open_files_init(BOOL threaded
         g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL);
         g_hEvtFinish = CreateEvent(NULL, FALSE, FALSE, NULL);
         InitializeCriticalSection(&g_cs);
     }
 
     g_initialized = TRUE;
 }
 
+
 PyObject *
-psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess)
-{
+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;
-    PyObject*                           pyListFiles = NULL;
-    PyObject*                           pyFilePath = NULL;
     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);
 
@@ -93,18 +92,18 @@ psutil_get_open_files_ntqueryobject(long
 
     {
         PyErr_SetFromWindowsErr(0);
         error = TRUE;
         goto cleanup;
     }
 
     // Py_BuildValue raises an exception if NULL is returned
-    pyListFiles = PyList_New(0);
-    if (pyListFiles == NULL) {
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL) {
         error = TRUE;
         goto cleanup;
     }
 
     do {
         if (pHandleInfo != NULL) {
             HeapFree(GetProcessHeap(), 0, pHandleInfo);
             pHandleInfo = NULL;
@@ -134,18 +133,17 @@ psutil_get_open_files_ntqueryobject(long
         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 != (HANDLE)dwPid ||
-            hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE)
+        if (hHandle->UniqueProcessId != (HANDLE)dwPid)
             goto loop_cleanup;
 
         if (!DuplicateHandle(hProcess,
                              hHandle->HandleValue,
                              GetCurrentProcess(),
                              &g_hFile,
                              0,
                              TRUE,
@@ -203,44 +201,44 @@ psutil_get_open_files_ntqueryobject(long
             /*
             printf("[%d] Filename (%#x) %#d bytes: %S\n",
                    dwPid,
                    hHandle->HandleValue,
                    g_pNameBuffer->Length,
                    g_pNameBuffer->Buffer);
             */
 
-            pyFilePath = PyUnicode_FromWideChar(g_pNameBuffer->Buffer,
+            py_path = PyUnicode_FromWideChar(g_pNameBuffer->Buffer,
                                                 g_pNameBuffer->Length/2);
-            if (pyFilePath == NULL) {
+            if (py_path == NULL) {
                 /*
                 printf("[%d] PyUnicode_FromWideChar (%#x): %#x \n",
                        dwPid,
                        hHandle->HandleValue,
                        GetLastError());
                 */
                 error = TRUE;
                 goto loop_cleanup;
             }
 
-            if (PyList_Append(pyListFiles, pyFilePath)) {
+            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(pyFilePath);
-        pyFilePath = NULL;
+        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)
@@ -259,114 +257,106 @@ cleanup:
         CloseHandle(g_hFile);
     g_hFile = NULL;
 
     if (pHandleInfo != NULL)
         HeapFree(GetProcessHeap(), 0, pHandleInfo);
     pHandleInfo = NULL;
 
     if (error) {
-        Py_XDECREF(pyListFiles);
-        pyListFiles = NULL;
+        Py_XDECREF(py_retlist);
+        py_retlist = NULL;
     }
 
     LeaveCriticalSection(&g_cs);
 
-    return pyListFiles;
+    return py_retlist;
 }
 
+
 DWORD
-psutil_NtQueryObject()
-{
+psutil_NtQueryObject() {
     DWORD dwWait = 0;
 
     if (g_hThread == NULL)
-        g_hThread = CreateThread(NULL,
-                                 0,
-                                 (LPTHREAD_START_ROUTINE)psutil_NtQueryObjectThread,
-                                 NULL,
-                                 0,
-                                 NULL);
+        g_hThread = CreateThread(
+            NULL,
+            0,
+            psutil_NtQueryObjectThread,
+            NULL,
+            0,
+            NULL);
     if (g_hThread == NULL)
         return GetLastError();
 
     // Signal the worker thread to start
     SetEvent(g_hEvtStart);
 
     // Wait for the worker thread to finish
     dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT);
 
     // 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);
 
-        // Cleanup Fiber
-        if (g_fiber != NULL)
-            DeleteFiber(g_fiber);
-        g_fiber = NULL;
-
         g_hThread = NULL;
     }
 
     return dwWait;
 }
 
-void
-psutil_NtQueryObjectThread()
-{
-    // Prevent the thread stack from leaking when this
-    // thread gets terminated due to NTQueryObject hanging
-    g_fiber = ConvertThreadToFiber(NULL);
 
+DWORD WINAPI
+psutil_NtQueryObjectThread(LPVOID lpvParam) {
     // Loop infinitely waiting for work
     while (TRUE) {
         WaitForSingleObject(g_hEvtStart, INFINITE);
 
         g_status = __NtQueryObject(g_hFile,
                                    ObjectNameInformation,
                                    g_pNameBuffer,
                                    g_dwSize,
                                    &g_dwLength);
         SetEvent(g_hEvtFinish);
     }
 }
 
+
 PyObject *
-psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess)
-{
+psutil_get_open_files_getmappedfilename(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;
     HANDLE                              hFile = NULL;
     HANDLE                              hMap = NULL;
     DWORD                               i = 0;
     BOOLEAN                             error = FALSE;
-    PyObject*                           pyListFiles = NULL;
-    PyObject*                           pyFilePath = NULL;
+    PyObject*                           py_retlist = NULL;
+    PyObject*                           py_path = NULL;
     ULONG                               dwSize = 0;
     LPVOID                              pMem = NULL;
     TCHAR                               pszFilename[MAX_PATH+1];
 
     if (g_initialized == FALSE)
         psutil_get_open_files_init(FALSE);
 
     if (__NtQuerySystemInformation == NULL || __NtQueryObject == NULL) {
         PyErr_SetFromWindowsErr(0);
         error = TRUE;
         goto cleanup;
     }
 
     // Py_BuildValue raises an exception if NULL is returned
-    pyListFiles = PyList_New(0);
-    if (pyListFiles == NULL) {
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL) {
         error = TRUE;
         goto cleanup;
     }
 
     do {
         if (pHandleInfo != NULL) {
             HeapFree(GetProcessHeap(), 0, pHandleInfo);
             pHandleInfo = NULL;
@@ -396,18 +386,17 @@ psutil_get_open_files_getmappedfilename(
         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 != (HANDLE)dwPid ||
-            hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE)
+        if (hHandle->UniqueProcessId != (HANDLE)dwPid)
             goto loop_cleanup;
 
         if (!DuplicateHandle(hProcess,
                              hHandle->HandleValue,
                              GetCurrentProcess(),
                              &hFile,
                              0,
                              TRUE,
@@ -440,17 +429,18 @@ psutil_get_open_files_getmappedfilename(
             printf("[%d] MapViewOfFile (%#x): %#x \n",
                    dwPid,
                    hHandle->HandleValue,
                    GetLastError());
             */
             goto loop_cleanup;
         }
 
-        dwSize = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH);
+        dwSize = GetMappedFileName(
+            GetCurrentProcess(), pMem, pszFilename, MAX_PATH);
         if (dwSize == 0) {
             /*
             printf("[%d] GetMappedFileName (%#x): %#x \n",
                    dwPid,
                    hHandle->HandleValue,
                    GetLastError());
             */
             goto loop_cleanup;
@@ -460,42 +450,42 @@ psutil_get_open_files_getmappedfilename(
         /*
         printf("[%d] Filename (%#x) %#d bytes: %S\n",
                dwPid,
                hHandle->HandleValue,
                dwSize,
                pszFilename);
         */
 
-        pyFilePath = PyUnicode_FromWideChar(pszFilename, dwSize);
-        if (pyFilePath == NULL) {
+        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(pyListFiles, pyFilePath)) {
+        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(pyFilePath);
-        pyFilePath = NULL;
+        Py_XDECREF(py_path);
+        py_path = NULL;
 
         if (pMem != NULL)
             UnmapViewOfFile(pMem);
         pMem = NULL;
 
         if (hMap != NULL)
             CloseHandle(hMap);
         hMap = NULL;
@@ -520,14 +510,14 @@ cleanup:
         CloseHandle(hFile);
     hFile = NULL;
 
     if (pHandleInfo != NULL)
         HeapFree(GetProcessHeap(), 0, pHandleInfo);
     pHandleInfo = NULL;
 
     if (error) {
-        Py_XDECREF(pyListFiles);
-        pyListFiles = NULL;
+        Py_XDECREF(py_retlist);
+        py_retlist = NULL;
     }
 
-    return pyListFiles;
+    return py_retlist;
 }
--- a/third_party/python/psutil/psutil/arch/windows/process_handles.h
+++ b/third_party/python/psutil/psutil/arch/windows/process_handles.h
@@ -43,30 +43,28 @@ typedef NTSTATUS (NTAPI *_NtQueryObject)
     PVOID ObjectInformation,
     ULONG ObjectInformationLength,
     PULONG ReturnLength
 );
 
 // Undocumented FILE_INFORMATION_CLASS: FileNameInformation
 static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64;
 
-typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
-{
+typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
     PVOID Object;
     HANDLE UniqueProcessId;
     HANDLE HandleValue;
     ULONG GrantedAccess;
     USHORT CreatorBackTraceIndex;
     USHORT ObjectTypeIndex;
     ULONG HandleAttributes;
     ULONG Reserved;
 } SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
 
-typedef struct _SYSTEM_HANDLE_INFORMATION_EX
-{
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
     ULONG_PTR NumberOfHandles;
     ULONG_PTR Reserved;
     SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
 } SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
 
 typedef enum _POOL_TYPE {
     NonPagedPool,
     PagedPool,
@@ -103,11 +101,11 @@ typedef struct _OBJECT_TYPE_INFORMATION 
 } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
 
 PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName);
 VOID psutil_get_open_files_init(BOOL threaded);
 PyObject* psutil_get_open_files(long pid, HANDLE processHandle);
 PyObject* psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess);
 PyObject* psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess);
 DWORD psutil_NtQueryObject(void);
-void psutil_NtQueryObjectThread(void);
+DWORD WINAPI psutil_NtQueryObjectThread(LPVOID lpvParam);
 
 #endif // __PROCESS_HANDLES_H__
--- a/third_party/python/psutil/psutil/arch/windows/process_info.c
+++ b/third_party/python/psutil/psutil/arch/windows/process_info.c
@@ -13,80 +13,295 @@
 #include <tlhelp32.h>
 
 #include "security.h"
 #include "process_info.h"
 #include "ntextapi.h"
 #include "../../_psutil_common.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.
+// ====================================================================
+
+// see http://msdn2.microsoft.com/en-us/library/aa489609.aspx
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
+#endif
+
+// http://msdn.microsoft.com/en-us/library/aa813741(VS.85).aspx
+typedef struct {
+    BYTE Reserved1[16];
+    PVOID Reserved2[5];
+    UNICODE_STRING CurrentDirectoryPath;
+    PVOID CurrentDirectoryHandle;
+    UNICODE_STRING DllPath;
+    UNICODE_STRING ImagePathName;
+    UNICODE_STRING CommandLine;
+    LPCWSTR env;
+} RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_;
+
+// 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)(
+        IN HANDLE ProcessHandle,
+        IN PVOID64 BaseAddress,
+        OUT PVOID Buffer,
+        IN ULONG64 Size,
+        OUT PULONG64 NumberOfBytesRead);
+
+typedef enum {
+    MemoryInformationBasic
+} MEMORY_INFORMATION_CLASS;
+
+typedef NTSTATUS (NTAPI *_NtWow64QueryVirtualMemory64)(
+    IN HANDLE ProcessHandle,
+    IN PVOID64 BaseAddress,
+    IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
+    OUT PMEMORY_BASIC_INFORMATION64 MemoryInformation,
+    IN ULONG64 Size,
+    OUT PULONG64 ReturnLength OPTIONAL);
+
+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
+
+
+#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)
+
+const int STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
+const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L;
+
+
+// ====================================================================
+// Process and PIDs utiilties.
+// ====================================================================
+
+
+/*
+ * 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.
+ * 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)
+        return PyErr_SetFromWindowsErr(0);
+    else  // -2
+        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_waccess(DWORD pid, DWORD dwDesiredAccess)
-{
+psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) {
     HANDLE hProcess;
-    DWORD processExitCode = 0;
 
     if (pid == 0) {
         // otherwise we'd get NoSuchProcess
-        return AccessDenied();
+        return AccessDenied("");
     }
 
     hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
-    if (hProcess == NULL) {
-        if (GetLastError() == ERROR_INVALID_PARAMETER)
-            NoSuchProcess();
-        else
-            PyErr_SetFromWindowsErr(0);
-        return NULL;
-    }
-
-    // make sure the process is running
-    GetExitCodeProcess(hProcess, &processExitCode);
-    if (processExitCode == 0) {
-        NoSuchProcess();
-        CloseHandle(hProcess);
-        return NULL;
-    }
-    return hProcess;
+    return psutil_check_phandle(hProcess, pid);
 }
 
 
 /*
  * Same as psutil_handle_from_pid_waccess but implicitly uses
  * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess
  * parameter for OpenProcess.
  */
 HANDLE
 psutil_handle_from_pid(DWORD pid) {
     DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
     return psutil_handle_from_pid_waccess(pid, dwDesiredAccess);
 }
 
 
-// fetch the PEB base address from NtQueryInformationProcess()
-PVOID
-psutil_get_peb_address(HANDLE ProcessHandle)
-{
-    _NtQueryInformationProcess NtQueryInformationProcess =
-        (_NtQueryInformationProcess)GetProcAddress(
-            GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
-    PROCESS_BASIC_INFORMATION pbi;
-
-    NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
-    return pbi.PebBaseAddress;
-}
-
-
 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
@@ -116,262 +331,581 @@ psutil_get_pids(DWORD *numberOfReturnedP
     // The number of elements is the returned size / size of each element
     *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD);
 
     return procArray;
 }
 
 
 int
-psutil_pid_is_running(DWORD pid)
-{
+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) {
-        // invalid parameter is no such process
-        if (GetLastError() == ERROR_INVALID_PARAMETER) {
-            CloseHandle(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...
-        if (GetLastError() == ERROR_ACCESS_DENIED) {
-            CloseHandle(hProcess);
+        // 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;
         }
-
-        CloseHandle(hProcess);
-        PyErr_SetFromWindowsErr(0);
-        return -1;
+        // Be strict and raise an exception; the caller is supposed
+        // to take -1 into account.
+        else {
+            PyErr_SetFromWindowsErr(err);
+            return -1;
+        }
     }
 
     if (GetExitCodeProcess(hProcess, &exitCode)) {
         CloseHandle(hProcess);
-        return (exitCode == STILL_ACTIVE);
+        // 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);
+        }
     }
-
-    // access denied means there's a process there so we'll assume
-    // it's running
-    if (GetLastError() == ERROR_ACCESS_DENIED) {
+    else {
+        err = GetLastError();
         CloseHandle(hProcess);
-        return 1;
+        // 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_SetFromWindowsErr(0);
+            return -1;
+        }
     }
-
-    PyErr_SetFromWindowsErr(0);
-    CloseHandle(hProcess);
-    return -1;
 }
 
 
-int
-psutil_pid_in_proclist(DWORD pid)
-{
-    DWORD *proclist = NULL;
-    DWORD numberOfReturnedPIDs;
-    DWORD i;
+/* 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;
 
-    proclist = psutil_get_pids(&numberOfReturnedPIDs);
-    if (proclist == NULL)
+    if (!VirtualQueryEx(hProcess, src, &info, sizeof(info))) {
+        PyErr_SetFromWindowsErr(0);
         return -1;
-    for (i = 0; i < numberOfReturnedPIDs; i++) {
-        if (pid == proclist[i]) {
-            free(proclist);
-            return 1;
-        }
     }
 
-    free(proclist);
+    *psize = info.RegionSize - ((char*)src - (char*)info.BaseAddress);
     return 0;
 }
 
 
-// Check exit code from a process handle. Return FALSE on an error also
-// XXX - not used anymore
-int
-handlep_is_running(HANDLE hProcess)
-{
-    DWORD dwCode;
+#ifndef _WIN64
+/* Given a pointer into a process's memory, figure out how much data can be
+ * read from it. */
+static int psutil_get_process_region_size64(HANDLE hProcess,
+                                           const PVOID64 src64,
+                                           PULONG64 psize) {
+    static _NtWow64QueryVirtualMemory64 NtWow64QueryVirtualMemory64 = NULL;
+    MEMORY_BASIC_INFORMATION64 info64;
+
+    if (NtWow64QueryVirtualMemory64 == NULL) {
+        NtWow64QueryVirtualMemory64 =
+            (_NtWow64QueryVirtualMemory64)GetProcAddress(
+                    GetModuleHandleA("ntdll.dll"),
+                    "NtWow64QueryVirtualMemory64");
 
-    if (NULL == hProcess)
-        return 0;
-    if (GetExitCodeProcess(hProcess, &dwCode)) {
-        if (dwCode == STILL_ACTIVE)
-            return 1;
+        if (NtWow64QueryVirtualMemory64 == NULL) {
+            PyErr_SetString(PyExc_NotImplementedError,
+                    "NtWow64QueryVirtualMemory64 missing");
+            return -1;
+        }
     }
+
+    if (!NT_SUCCESS(NtWow64QueryVirtualMemory64(
+                    hProcess,
+                    src64,
+                    0,
+                    &info64,
+                    sizeof(info64),
+                    NULL))) {
+        PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+
+    *psize = info64.RegionSize - ((char*)src64 - (char*)info64.BaseAddress);
     return 0;
 }
+#endif
 
 
+enum psutil_process_data_kind {
+    KIND_CMDLINE,
+    KIND_CWD,
+    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. */
+static int psutil_get_process_data(long pid,
+                                   enum psutil_process_data_kind kind,
+                                   WCHAR **pdata,
+                                   SIZE_T *psize) {
+    /* This function is quite complex because there are several cases to be
+       considered:
+
+       Two cases are really simple:  we (i.e. the python interpreter) and the
+       target process are both 32 bit or both 64 bit.  In that case the memory
+       layout of the structures matches up and all is well.
+
+       When we are 64 bit and the target process is 32 bit we need to use
+       custom 32 bit versions of the structures.
+
+       When we are 32 bit and the target process is 64 bit we need to use
+       custom 64 bit version of the structures.  Also we need to use separate
+       Wow64 functions to get the information.
+
+       A few helper structs are defined above so that the compiler can handle
+       calculating the correct offsets.
+
+       Additional help also came from the following sources:
+
+         https://github.com/kohsuke/winp and
+         http://wj32.org/wp/2009/01/24/howto-get-the-command-line-of-processes/
+         http://stackoverflow.com/a/14012919
+         http://www.drdobbs.com/embracing-64-bit-windows/184401966
+     */
+    static _NtQueryInformationProcess NtQueryInformationProcess = NULL;
+#ifndef _WIN64
+    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;
+#else
+    PVOID64 src64;
+    BOOL weAreWow64;
+    BOOL theyAreWow64;
+#endif
+
+    hProcess = psutil_handle_from_pid(pid);
+    if (hProcess == NULL)
+        return -1;
+
+    if (NtQueryInformationProcess == NULL) {
+        NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(
+                GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
+    }
+
+#ifdef _WIN64
+    /* 64 bit case.  Check if the target is a 32 bit process running in WoW64
+     * mode. */
+    if (!NT_SUCCESS(NtQueryInformationProcess(hProcess,
+                                              ProcessWow64Information,
+                                              &ppeb32,
+                                              sizeof(LPVOID),
+                                              NULL))) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    if (ppeb32 != NULL) {
+        /* We are 64 bit.  Target process is 32 bit running in WoW64 mode. */
+        PEB32 peb32;
+        RTL_USER_PROCESS_PARAMETERS32 procParameters32;
+
+        // read PEB
+        if (!ReadProcessMemory(hProcess, ppeb32, &peb32, sizeof(peb32), NULL)) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        // read process parameters
+        if (!ReadProcessMemory(hProcess,
+                              UlongToPtr(peb32.ProcessParameters),
+                              &procParameters32,
+                              sizeof(procParameters32),
+                              NULL)) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        switch (kind) {
+            case KIND_CMDLINE:
+                src = UlongToPtr(procParameters32.CommandLine.Buffer),
+                size = procParameters32.CommandLine.Length;
+                break;
+            case KIND_CWD:
+                src = UlongToPtr(procParameters32.CurrentDirectoryPath.Buffer);
+                size = procParameters32.CurrentDirectoryPath.Length;
+                break;
+            case KIND_ENVIRON:
+                src = UlongToPtr(procParameters32.env);
+                break;
+        }
+    } else
+#else
+    /* 32 bit case.  Check if the target is also 32 bit. */
+    if (!IsWow64Process(GetCurrentProcess(), &weAreWow64) ||
+        !IsWow64Process(hProcess, &theyAreWow64)) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    if (weAreWow64 && !theyAreWow64) {
+        /* We are 32 bit running in WoW64 mode.  Target process is 64 bit. */
+        PROCESS_BASIC_INFORMATION64 pbi64;
+        PEB64 peb64;
+        RTL_USER_PROCESS_PARAMETERS64 procParameters64;
+
+        if (NtWow64QueryInformationProcess64 == NULL) {
+            NtWow64QueryInformationProcess64 =
+                (_NtQueryInformationProcess)GetProcAddress(
+                        GetModuleHandleA("ntdll.dll"),
+                        "NtWow64QueryInformationProcess64");
+
+            if (NtWow64QueryInformationProcess64 == NULL) {
+                PyErr_SetString(PyExc_NotImplementedError,
+                                "NtWow64QueryInformationProcess64 missing");
+                goto error;
+            }
+        }
+
+        if (!NT_SUCCESS(NtWow64QueryInformationProcess64(
+                        hProcess,
+                        ProcessBasicInformation,
+                        &pbi64,
+                        sizeof(pbi64),
+                        NULL))) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        // read peb
+        if (NtWow64ReadVirtualMemory64 == NULL) {
+            NtWow64ReadVirtualMemory64 =
+                (_NtWow64ReadVirtualMemory64)GetProcAddress(
+                        GetModuleHandleA("ntdll.dll"),
+                        "NtWow64ReadVirtualMemory64");
+
+            if (NtWow64ReadVirtualMemory64 == NULL) {
+                PyErr_SetString(PyExc_NotImplementedError,
+                                "NtWow64ReadVirtualMemory64 missing");
+                goto error;
+            }
+        }
+
+        if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess,
+                                                   pbi64.PebBaseAddress,
+                                                   &peb64,
+                                                   sizeof(peb64),
+                                                   NULL))) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        // read process parameters
+        if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess,
+                                                   peb64.ProcessParameters,
+                                                   &procParameters64,
+                                                   sizeof(procParameters64),
+                                                   NULL))) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        switch (kind) {
+            case KIND_CMDLINE:
+                src64 = procParameters64.CommandLine.Buffer;
+                size = procParameters64.CommandLine.Length;
+                break;
+            case KIND_CWD:
+                src64 = procParameters64.CurrentDirectoryPath.Buffer,
+                size = procParameters64.CurrentDirectoryPath.Length;
+                break;
+            case KIND_ENVIRON:
+                src64 = procParameters64.env;
+                break;
+        }
+    } else
+#endif
+
+    /* Target process is of the same bitness as us. */
+    {
+        PROCESS_BASIC_INFORMATION pbi;
+        PEB_ peb;
+        RTL_USER_PROCESS_PARAMETERS_ procParameters;
+
+        if (!NT_SUCCESS(NtQueryInformationProcess(hProcess,
+                                                  ProcessBasicInformation,
+                                                  &pbi,
+                                                  sizeof(pbi),
+                                                  NULL))) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        // read peb
+        if (!ReadProcessMemory(hProcess,
+                               pbi.PebBaseAddress,
+                               &peb,
+                               sizeof(peb),
+                               NULL)) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        // read process parameters
+        if (!ReadProcessMemory(hProcess,
+                               peb.ProcessParameters,
+                               &procParameters,
+                               sizeof(procParameters),
+                               NULL)) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+
+        switch (kind) {
+            case KIND_CMDLINE:
+                src = procParameters.CommandLine.Buffer;
+                size = procParameters.CommandLine.Length;
+                break;
+            case KIND_CWD:
+                src = procParameters.CurrentDirectoryPath.Buffer;
+                size = procParameters.CurrentDirectoryPath.Length;
+                break;
+            case KIND_ENVIRON:
+                src = procParameters.env;
+                break;
+        }
+    }
+
+    if (kind == KIND_ENVIRON) {
+#ifndef _WIN64
+        if (weAreWow64 && !theyAreWow64) {
+            ULONG64 size64;
+
+            if (psutil_get_process_region_size64(hProcess, src64, &size64) != 0)
+                goto error;
+
+            size = (SIZE_T)size64;
+        }
+        else
+#endif
+        if (psutil_get_process_region_size(hProcess, src, &size) != 0)
+            goto error;
+    }
+
+    buffer = calloc(size + 2, 1);
+
+    if (buffer == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+#ifndef _WIN64
+    if (weAreWow64 && !theyAreWow64) {
+        if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(hProcess,
+                                                   src64,
+                                                   buffer,
+                                                   size,
+                                                   NULL))) {
+            PyErr_SetFromWindowsErr(0);
+            goto error;
+        }
+    } else
+#endif
+    if (!ReadProcessMemory(hProcess, src, buffer, size, NULL)) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    CloseHandle(hProcess);
+
+    *pdata = buffer;
+    *psize = size;
+
+    return 0;
+
+error:
+    if (hProcess != NULL)
+        CloseHandle(hProcess);
+    if (buffer != NULL)
+        free(buffer);
+    return -1;
+}
+
 /*
  * returns a Python list representing the arguments for the process
  * with given pid or NULL on error.
  */
 PyObject *
-psutil_get_arg_list(long pid)
-{
-    int nArgs, i;
+psutil_get_cmdline(long pid) {
+    PyObject *ret = NULL;
+    WCHAR *data = NULL;
+    SIZE_T size;
+    PyObject *py_retlist = NULL;
+    PyObject *py_unicode = NULL;
     LPWSTR *szArglist = NULL;
-    HANDLE hProcess = NULL;
-    PVOID pebAddress;
-    PVOID rtlUserProcParamsAddress;
-    UNICODE_STRING commandLine;
-    WCHAR *commandLineContents = NULL;
-    PyObject *arg = NULL;
-    PyObject *arg_from_wchar = NULL;
-    PyObject *argList = NULL;
+    int nArgs, i;
 
-    hProcess = psutil_handle_from_pid(pid);
-    if (hProcess == NULL)
-        return NULL;
-    pebAddress = psutil_get_peb_address(hProcess);
+    if (psutil_get_process_data(pid, KIND_CMDLINE, &data, &size) != 0)
+        goto out;
 
-    // get the address of ProcessParameters
-#ifdef _WIN64
-    if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32,
-                           &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
-#else
-    if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10,
-                           &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
-#endif
-    {
-        ////printf("Could not read the address of ProcessParameters!\n");
+    // attempt to parse the command line using Win32 API
+    szArglist = CommandLineToArgvW(data, &nArgs);
+    if (szArglist == NULL) {
         PyErr_SetFromWindowsErr(0);
-        goto error;
+        goto out;
     }
 
-    // read the CommandLine UNICODE_STRING structure
-#ifdef _WIN64
-    if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112,
-                           &commandLine, sizeof(commandLine), NULL))
-#else
-    if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40,
-                           &commandLine, sizeof(commandLine), NULL))
-#endif
-    {
-        PyErr_SetFromWindowsErr(0);
-        goto error;
-    }
-
-
-    // allocate memory to hold the command line
-    commandLineContents = (WCHAR *)malloc(commandLine.Length + 1);
-    if (commandLineContents == NULL) {
-        PyErr_NoMemory();
-        goto error;
-    }
-
-    // read the command line
-    if (!ReadProcessMemory(hProcess, commandLine.Buffer,
-                           commandLineContents, commandLine.Length, NULL))
-    {
-        PyErr_SetFromWindowsErr(0);
-        goto error;
+    // arglist parsed as array of UNICODE_STRING, so convert each to
+    // Python string object and add to arg list
+    py_retlist = PyList_New(nArgs);
+    if (py_retlist == NULL)
+        goto out;
+    for (i = 0; i < nArgs; i++) {
+        py_unicode = PyUnicode_FromWideChar(szArglist[i],
+                                            wcslen(szArglist[i]));
+        if (py_unicode == NULL)
+            goto out;
+        PyList_SET_ITEM(py_retlist, i, py_unicode);
+        py_unicode = NULL;
     }
 
-    // Null-terminate the string to prevent wcslen from returning
-    // incorrect length the length specifier is in characters, but
-    // commandLine.Length is in bytes.
-    commandLineContents[(commandLine.Length / sizeof(WCHAR))] = '\0';
+    ret = py_retlist;
+    py_retlist = NULL;
 
-    // attempt tp parse the command line using Win32 API, fall back
-    // on string cmdline version otherwise
-    szArglist = CommandLineToArgvW(commandLineContents, &nArgs);
-    if (NULL == szArglist) {
-        // failed to parse arglist
-        // encode as a UTF8 Python string object from WCHAR string
-        arg_from_wchar = PyUnicode_FromWideChar(commandLineContents,
-                                                commandLine.Length / 2);
-        if (arg_from_wchar == NULL)
-            goto error;
-#if PY_MAJOR_VERSION >= 3
-        argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar));
-#else
-        argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar));
-#endif
-        if (!argList)
-            goto error;
-    }
-    else {
-        // arglist parsed as array of UNICODE_STRING, so convert each to
-        // Python string object and add to arg list
-        argList = Py_BuildValue("[]");
-        if (argList == NULL)
-            goto error;
-        for (i = 0; i < nArgs; i++) {
-            arg_from_wchar = NULL;
-            arg = NULL;
-            arg_from_wchar = PyUnicode_FromWideChar(szArglist[i],
-                                                    wcslen(szArglist[i]));
-            if (arg_from_wchar == NULL)
-                goto error;
-#if PY_MAJOR_VERSION >= 3
-            arg = PyUnicode_FromObject(arg_from_wchar);
-#else
-            arg = PyUnicode_AsUTF8String(arg_from_wchar);
-#endif
-            if (arg == NULL)
-                goto error;
-            Py_XDECREF(arg_from_wchar);
-            if (PyList_Append(argList, arg))
-                goto error;
-            Py_XDECREF(arg);
-        }
-    }
+out:
+    LocalFree(szArglist);
+    free(data);
+    Py_XDECREF(py_unicode);
+    Py_XDECREF(py_retlist);
 
-    if (szArglist != NULL)
-        LocalFree(szArglist);
-    free(commandLineContents);
-    CloseHandle(hProcess);
-    return argList;
-
-error:
-    Py_XDECREF(arg);
-    Py_XDECREF(arg_from_wchar);
-    Py_XDECREF(argList);
-    if (hProcess != NULL)
-        CloseHandle(hProcess);
-    if (commandLineContents != NULL)
-        free(commandLineContents);
-    if (szArglist != NULL)
-        LocalFree(szArglist);
-    return NULL;
+    return ret;
 }
 
+PyObject *psutil_get_cwd(long pid) {
+    PyObject *ret = NULL;
+    WCHAR *data = NULL;
+    SIZE_T size;
 
-#define PH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes))
-#define PH_NEXT_PROCESS(Process) ( \
-   ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \
-   (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \
-        ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \
-   NULL)
+    if (psutil_get_process_data(pid, KIND_CWD, &data, &size) != 0)
+        goto out;
+
+    // convert wchar array to a Python unicode string
+    ret = PyUnicode_FromWideChar(data, wcslen(data));
+
+out:
+    free(data);
+
+    return ret;
+}
 
-const int STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
-const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L;
+/*
+ * returns a Python string containing the environment variable data for the
+ * process with given pid or NULL on error.
+ */
+PyObject *psutil_get_environ(long pid) {
+    PyObject *ret = NULL;
+    WCHAR *data = NULL;
+    SIZE_T size;
+
+    if (psutil_get_process_data(pid, KIND_ENVIRON, &data, &size) != 0)
+        goto out;
+
+    // convert wchar array to a Python unicode string
+    ret = PyUnicode_FromWideChar(data, size / 2);
+
+out:
+    free(data);
+
+    return ret;
+}
+
 
 /*
  * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure
  * fills the structure with 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.
  * On success return 1, else 0 with Python exception already set.
  */
 int
 psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
-                 PVOID *retBuffer)
-{
+                     PVOID *retBuffer) {
     static ULONG initialBufferSize = 0x4000;
     NTSTATUS status;
     PVOID buffer;
     ULONG bufferSize;
     PSYSTEM_PROCESS_INFORMATION process;
 
     // get NtQuerySystemInformation
     typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG);
@@ -403,33 +937,34 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_
             }
         }
         else {
             break;
         }
     }
 
     if (status != 0) {
-        PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed");
+        PyErr_Format(
+            PyExc_RuntimeError, "NtQuerySystemInformation() syscall failed");
         goto error;
     }
 
     if (bufferSize <= 0x20000)
         initialBufferSize = bufferSize;
 
-    process = PH_FIRST_PROCESS(buffer);
+    process = PSUTIL_FIRST_PROCESS(buffer);
     do {
         if (process->UniqueProcessId == (HANDLE)pid) {
             *retProcess = process;
             *retBuffer = buffer;
             return 1;
         }
-    } while ( (process = PH_NEXT_PROCESS(process)) );
+    } while ( (process = PSUTIL_NEXT_PROCESS(process)) );
 
-    NoSuchProcess();
+    NoSuchProcess("");
     goto error;
 
 error:
     FreeLibrary(hNtDll);
     if (buffer != NULL)
         free(buffer);
     return 0;
 }
--- a/third_party/python/psutil/psutil/arch/windows/process_info.h
+++ b/third_party/python/psutil/psutil/arch/windows/process_info.h
@@ -7,20 +7,28 @@
 #if !defined(__PROCESS_INFO_H)
 #define __PROCESS_INFO_H
 
 #include <Python.h>
 #include <windows.h>
 #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))
+
+
 DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs);
 HANDLE psutil_handle_from_pid(DWORD pid);
 HANDLE psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess);
-int psutil_handlep_is_running(HANDLE hProcess);
-int psutil_pid_in_proclist(DWORD pid);
 int psutil_pid_is_running(DWORD pid);
-PVOID psutil_get_peb_address(HANDLE ProcessHandle);
-PyObject* psutil_get_arg_list(long 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);
+PyObject* psutil_get_cwd(long pid);
+PyObject* psutil_get_environ(long pid);
+
 #endif
--- a/third_party/python/psutil/psutil/arch/windows/security.c
+++ b/third_party/python/psutil/psutil/arch/windows/security.c
@@ -103,18 +103,17 @@ psutil_has_system_privilege(HANDLE hProc
     }
 
     free(pBuffer);
     return 0;
 }
 
 
 BOOL
-psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege)
-{
+psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) {
     TOKEN_PRIVILEGES tp;
     LUID luid;
     TOKEN_PRIVILEGES tpPrevious;
     DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
 
     if (!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE;
 
     // first pass.  get current privilege setting
@@ -154,18 +153,17 @@ psutil_set_privilege(HANDLE hToken, LPCT
 
     if (GetLastError() != ERROR_SUCCESS) return FALSE;
 
     return TRUE;
 }
 
 
 int
-psutil_set_se_debug()
-{
+psutil_set_se_debug() {
     HANDLE hToken;
     if (! OpenThreadToken(GetCurrentThread(),
                           TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                           FALSE,
                           &hToken)
        ) {
         if (GetLastError() == ERROR_NO_TOKEN) {
             if (!ImpersonateSelf(SecurityImpersonation)) {
@@ -193,18 +191,17 @@ psutil_set_se_debug()
 
     RevertToSelf();
     CloseHandle(hToken);
     return 1;
 }
 
 
 int
-psutil_unset_se_debug()
-{
+psutil_unset_se_debug() {
     HANDLE hToken;
     if (! OpenThreadToken(GetCurrentThread(),
                           TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                           FALSE,
                           &hToken)
        ) {
         if (GetLastError() == ERROR_NO_TOKEN) {
             if (! ImpersonateSelf(SecurityImpersonation))
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/windows/services.c
@@ -0,0 +1,485 @@
+/*
+ * 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 <Python.h>
+#include <windows.h>
+#include <Winsvc.h>
+
+#include "services.h"
+#include "../../_psutil_common.h"
+
+// ==================================================================
+// utils
+// ==================================================================
+
+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;
+
+    sc = OpenSCManager(NULL, NULL, scm_access);
+    if (sc == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+    hService = OpenService(sc, service_name, access);
+    if (hService == NULL) {
+        CloseServiceHandle(sc);
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+    CloseServiceHandle(sc);
+    return hService;
+}
+
+
+// XXX - expose these as constants?
+static const char *
+get_startup_string(DWORD startup) {
+    switch (startup) {
+        case SERVICE_AUTO_START:
+            return "automatic";
+        case SERVICE_DEMAND_START:
+            return "manual";
+        case SERVICE_DISABLED:
+            return "disabled";
+/*
+        // drivers only (since we use EnumServicesStatusEx() with
+        // SERVICE_WIN32)
+        case SERVICE_BOOT_START:
+            return "boot-start";
+        case SERVICE_SYSTEM_START:
+            return "system-start";
+*/
+        default:
+            return "unknown";
+    }
+}
+
+
+// XXX - expose these as constants?
+static const char *
+get_state_string(DWORD state) {
+    switch (state) {
+        case SERVICE_RUNNING:
+            return "running";
+        case SERVICE_PAUSED:
+            return "paused";
+        case SERVICE_START_PENDING:
+            return "start_pending";
+        case SERVICE_PAUSE_PENDING:
+            return "pause_pending";
+        case SERVICE_CONTINUE_PENDING:
+            return "continue_pending";
+        case SERVICE_STOP_PENDING:
+            return "stop_pending";
+        case SERVICE_STOPPED:
+            return "stopped";
+        default:
+            return "unknown";
+    }
+}
+
+
+// ==================================================================
+// APIs
+// ==================================================================
+
+/*
+ * Enumerate all services.
+ */
+PyObject *
+psutil_winservice_enumerate(PyObject *self, PyObject *args) {
+    ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL;
+    BOOL ok;
+    SC_HANDLE sc = NULL;
+    DWORD bytesNeeded = 0;
+    DWORD srvCount;
+    DWORD resumeHandle = 0;
+    DWORD dwBytes = 0;
+    DWORD i;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_name = NULL;
+    PyObject *py_display_name = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+    if (sc == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        return NULL;
+    }
+
+    for (;;) {
+        ok = EnumServicesStatusExW(
+            sc,
+            SC_ENUM_PROCESS_INFO,
+            SERVICE_WIN32,  // XXX - extend this to include drivers etc.?
+            SERVICE_STATE_ALL,
+            (LPBYTE)lpService,
+            dwBytes,
+            &bytesNeeded,
+            &srvCount,
+            &resumeHandle,
+            NULL);
+        if (ok || (GetLastError() != ERROR_MORE_DATA))
+            break;
+        if (lpService)
+            free(lpService);
+        dwBytes = bytesNeeded;
+        lpService = (ENUM_SERVICE_STATUS_PROCESSW*)malloc(dwBytes);
+    }
+
+    for (i = 0; i < srvCount; i++) {
+        // Get unicode name / display name.
+        py_name = NULL;
+        py_name = PyUnicode_FromWideChar(
+            lpService[i].lpServiceName, wcslen(lpService[i].lpServiceName));
+        if (py_name == NULL)
+            goto error;
+
+        py_display_name = NULL;
+        py_display_name = PyUnicode_FromWideChar(
+            lpService[i].lpDisplayName, wcslen(lpService[i].lpDisplayName));
+        if (py_display_name == NULL)
+            goto error;
+
+        // Construct the result.
+        py_tuple = Py_BuildValue("(OO)", py_name, py_display_name);
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_display_name);
+        Py_DECREF(py_name);
+        Py_DECREF(py_tuple);
+    }
+
+    // Free resources.
+    CloseServiceHandle(sc);
+    free(lpService);
+    return py_retlist;
+
+error:
+    Py_DECREF(py_name);
+    Py_XDECREF(py_display_name);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (sc != NULL)
+        CloseServiceHandle(sc);
+    if (lpService != NULL)
+        free(lpService);
+    return NULL;
+}
+
+
+/*
+ * Get service config information. Returns:
+ * - display_name
+ * - binpath
+ * - username
+ * - startup_type
+ */
+PyObject *
+psutil_winservice_query_config(PyObject *self, PyObject *args) {
+    char *service_name;
+    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;
+    PyObject *py_unicode_binpath = NULL;
+    PyObject *py_unicode_username = NULL;
+
+    if (!PyArg_ParseTuple(args, "s", &service_name))
+        return NULL;
+    hService = psutil_get_service_handler(
+        service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG);
+    if (hService == NULL)
+        goto error;
+
+    // First call to QueryServiceConfigW() is necessary to get the
+    // right size.
+    bytesNeeded = 0;
+    QueryServiceConfigW(hService, NULL, 0, &bytesNeeded);
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+    qsc = (QUERY_SERVICE_CONFIGW *)malloc(bytesNeeded);
+    ok = QueryServiceConfigW(hService, qsc, bytesNeeded, &bytesNeeded);
+    if (ok == 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // Get unicode display name.
+    py_unicode_display_name = PyUnicode_FromWideChar(
+        qsc->lpDisplayName, wcslen(qsc->lpDisplayName));
+    if (py_unicode_display_name == NULL)
+        goto error;
+
+    // Get unicode bin path.
+    py_unicode_binpath = PyUnicode_FromWideChar(
+        qsc->lpBinaryPathName, wcslen(qsc->lpBinaryPathName));
+    if (py_unicode_binpath == NULL)
+        goto error;
+
+    // Get unicode username.
+    py_unicode_username = PyUnicode_FromWideChar(
+        qsc->lpServiceStartName, wcslen(qsc->lpServiceStartName));
+    if (py_unicode_username == NULL)
+        goto error;
+
+    // Construct result tuple.
+    py_tuple = Py_BuildValue(
+        "(OOOs)",
+        py_unicode_display_name,
+        py_unicode_binpath,
+        py_unicode_username,
+        get_startup_string(qsc->dwStartType)  // startup
+    );
+    if (py_tuple == NULL)
+        goto error;
+
+    // Free resources.
+    Py_DECREF(py_unicode_display_name);
+    Py_DECREF(py_unicode_binpath);
+    Py_DECREF(py_unicode_username);
+    free(qsc);
+    CloseServiceHandle(hService);
+    return py_tuple;
+
+error:
+    Py_XDECREF(py_unicode_display_name);
+    Py_XDECREF(py_unicode_binpath);
+    Py_XDECREF(py_unicode_username);
+    Py_XDECREF(py_tuple);
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+    if (qsc != NULL)
+        free(qsc);
+    return NULL;
+}
+
+
+/*
+ * Get service status information. Returns:
+ * - status
+ * - pid
+ */
+PyObject *
+psutil_winservice_query_status(PyObject *self, PyObject *args) {
+    char *service_name;
+    SC_HANDLE hService = NULL;
+    BOOL ok;
+    DWORD bytesNeeded = 0;
+    DWORD resumeHandle = 0;
+    DWORD dwBytes = 0;
+    SERVICE_STATUS_PROCESS  *ssp = NULL;
+    PyObject *py_tuple = NULL;
+
+    if (!PyArg_ParseTuple(args, "s", &service_name))
+        return NULL;
+    hService = psutil_get_service_handler(
+        service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_STATUS);
+    if (hService == NULL)
+        goto error;
+
+    // First call to QueryServiceStatusEx() is necessary to get the
+    // right size.
+    QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0,
+                         &bytesNeeded);
+    if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {
+        // Also services.msc fails in the same manner, so we return an
+        // empty string.
+        CloseServiceHandle(hService);
+        return Py_BuildValue("s", "");
+    }
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+    ssp = (SERVICE_STATUS_PROCESS *)HeapAlloc(
+        GetProcessHeap(), 0, bytesNeeded);
+    if (ssp == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    // Actual call.
+    ok = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)ssp,
+                              bytesNeeded, &bytesNeeded);
+    if (ok == 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    py_tuple = Py_BuildValue(
+        "(sk)",
+        get_state_string(ssp->dwCurrentState),
+        ssp->dwProcessId
+    );
+    if (py_tuple == NULL)
+        goto error;
+
+    CloseServiceHandle(hService);
+    HeapFree(GetProcessHeap(), 0, ssp);
+    return py_tuple;
+
+error:
+    Py_XDECREF(py_tuple);
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+    if (ssp != NULL)
+        HeapFree(GetProcessHeap(), 0, ssp);
+    return NULL;
+}
+
+
+/*
+ * Get service description.
+ */
+PyObject *
+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;
+    PyObject *py_retstr = NULL;
+
+    if (!PyArg_ParseTuple(args, "s", &service_name))
+        return NULL;
+    hService = psutil_get_service_handler(
+        service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG);
+    if (hService == NULL)
+        goto error;
+
+    // This first call to QueryServiceConfig2W() is necessary in order
+    // to get the right size.
+    bytesNeeded = 0;
+    QueryServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0,
+                         &bytesNeeded);
+    if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {
+        // Also services.msc fails in the same manner, so we return an
+        // empty string.
+        CloseServiceHandle(hService);
+        return Py_BuildValue("s", "");
+    }
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    scd = (SERVICE_DESCRIPTIONW *)malloc(bytesNeeded);
+    ok = QueryServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION,
+                              (LPBYTE)scd, bytesNeeded, &bytesNeeded);
+    if (ok == 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    if (scd->lpDescription == NULL) {
+        py_retstr = Py_BuildValue("s", "");
+    }
+    else {
+        py_retstr = PyUnicode_FromWideChar(
+            scd->lpDescription,  wcslen(scd->lpDescription));
+    }
+    if (!py_retstr)
+        goto error;
+
+    free(scd);
+    CloseServiceHandle(hService);
+    return py_retstr;
+
+error:
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+    if (lpService != NULL)
+        free(lpService);
+    return NULL;
+}
+
+
+/*
+ * Start service.
+ * XXX - note: this is exposed but not used.
+ */
+PyObject *
+psutil_winservice_start(PyObject *self, PyObject *args) {
+    char *service_name;
+    BOOL ok;
+    SC_HANDLE hService = NULL;
+
+    if (!PyArg_ParseTuple(args, "s", &service_name))
+        return NULL;
+    hService = psutil_get_service_handler(
+        service_name, SC_MANAGER_ALL_ACCESS, SERVICE_START);
+    if (hService == NULL) {
+        goto error;
+    }
+    ok = StartService(hService, 0, NULL);
+    if (ok == 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    Py_RETURN_NONE;
+
+error:
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+    return NULL;
+}
+
+
+/*
+ * Stop service.
+ * XXX - note: this is exposed but not used.
+ */
+PyObject *
+psutil_winservice_stop(PyObject *self, PyObject *args) {
+    char *service_name;
+    BOOL ok;
+    SC_HANDLE hService = NULL;
+    SERVICE_STATUS ssp;
+
+    if (!PyArg_ParseTuple(args, "s", &service_name))
+        return NULL;
+    hService = psutil_get_service_handler(
+        service_name, SC_MANAGER_ALL_ACCESS, SERVICE_STOP);
+    if (hService == NULL)
+        goto error;
+
+    // Note: this can hang for 30 secs.
+    Py_BEGIN_ALLOW_THREADS
+    ok = ControlService(hService, SERVICE_CONTROL_STOP, &ssp);
+    Py_END_ALLOW_THREADS
+    if (ok == 0) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    CloseServiceHandle(hService);
+    Py_RETURN_NONE;
+
+error:
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+    return NULL;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/arch/windows/services.h
@@ -0,0 +1,17 @@
+/*
+ * 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 <Python.h>
+#include <Winsvc.h>
+
+SC_HANDLE psutil_get_service_handle(
+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);
+PyObject *psutil_winservice_query_descr(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_start(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_stop(PyObject *self, PyObject *args);
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/README.rst
@@ -0,0 +1,23 @@
+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
+  on psutil::
+
+    make setup-dev-env  # install test deps (+ other things)
+    make test
+
+* To run tests on all supported Python versions install tox
+  (``pip install tox``) then run ``tox`` from within psutil root directory.
+
+* Every time a commit is pushed tests are automatically run on Travis
+  (Linux, OSX) and appveyor (Windows):
+
+  * Travis builds: https://travis-ci.org/giampaolo/psutil
+  * AppVeyor builds: https://ci.appveyor.com/project/giampaolo/psutil
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/__init__.py
@@ -0,0 +1,1198 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+"""
+Test utilities.
+"""
+
+from __future__ import print_function
+
+import atexit
+import contextlib
+import ctypes
+import errno
+import functools
+import os
+import random
+import re
+import select
+import shutil
+import socket
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import threading
+import time
+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 POSIX
+from psutil import SUNOS
+from psutil import WINDOWS
+from psutil._common import supports_ipv6
+from psutil._compat import PY3
+from psutil._compat import u
+from psutil._compat import unicode
+from psutil._compat import which
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest  # requires "pip install unittest2"
+else:
+    import unittest
+
+try:
+    from unittest import mock  # py3
+except ImportError:
+    import mock  # NOQA - requires "pip install mock"
+
+if sys.version_info >= (3, 4):
+    import enum
+else:
+    enum = None
+
+
+__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',
+    "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",
+    "HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO",
+    # subprocesses
+    'pyrun', 'reap_children', 'get_test_subprocess', 'create_zombie_proc',
+    'create_proc_children_pair',
+    # threads
+    'ThreadTask'
+    # test utils
+    'unittest', 'skip_on_access_denied', 'skip_on_not_implemented',
+    'retry_before_failing', 'run_test_module_by_name', 'get_suite',
+    'run_suite',
+    # install utils
+    'install_pip', 'install_test_deps',
+    # fs utils
+    'chdir', 'safe_rmpath', 'create_exe', 'decode_path', 'encode_path',
+    'unique_filename',
+    # os
+    'get_winver', 'get_kernel_version',
+    # sync primitives
+    'call_until', 'wait_for_pid', 'wait_for_file',
+    # network
+    'check_connection_ntuple', 'check_net_address',
+    'get_free_port', 'unix_socket_path', 'bind_socket', 'bind_unix_socket',
+    'tcp_socketpair', 'unix_socketpair', 'create_sockets',
+    # compat
+    'reload_module', 'import_module_by_path',
+    # others
+    'warn', 'copyload_shared_lib', 'is_namedtuple',
+]
+
+
+# ===================================================================
+# --- constants
+# ===================================================================
+
+# --- platforms
+
+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/)
+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'))
+
+# --- configurable defaults
+
+# how many times retry_before_failing() 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
+# be more tolerant if we're on travis / appveyor in order to avoid
+# false positives
+if TRAVIS or APPVEYOR:
+    NO_RETRIES *= 3
+    GLOBAL_TIMEOUT *= 3
+
+# --- files
+
+TESTFILE_PREFIX = '$testfn'
+TESTFN = os.path.join(os.path.realpath(os.getcwd()), TESTFILE_PREFIX)
+_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__), '..', '..'))
+SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts')
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+# --- support
+
+HAS_CPU_AFFINITY = hasattr(psutil.Process, "cpu_affinity")
+HAS_CPU_FREQ = hasattr(psutil, "cpu_freq")
+HAS_CONNECTIONS_UNIX = POSIX and not SUNOS
+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_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num")
+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()
+HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans")
+HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures")
+
+# --- misc
+
+
+def _get_py_exe():
+    def attempt(exe):
+        try:
+            subprocess.check_call(
+                [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        except Exception:
+            return None
+        else:
+            return exe
+
+    if OSX:
+        exe = \
+            attempt(sys.executable) or \
+            attempt(os.path.realpath(sys.executable)) or \
+            attempt(which("python%s.%s" % sys.version_info[:2])) or \
+            attempt(psutil.Process().exe())
+        if not exe:
+            raise ValueError("can't find python exe real abspath")
+        return exe
+    else:
+        exe = os.path.realpath(sys.executable)
+        assert os.path.exists(exe), exe
+        return exe
+
+
+PYTHON_EXE = _get_py_exe()
+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()
+_testfiles_created = set()
+
+
+@atexit.register
+def _cleanup_files():
+    DEVNULL.close()
+    for name in os.listdir(u('.')):
+        if isinstance(name, unicode):
+            prefix = u(TESTFILE_PREFIX)
+        else:
+            prefix = TESTFILE_PREFIX
+        if name.startswith(prefix):
+            try:
+                safe_rmpath(name)
+            except Exception:
+                traceback.print_exc()
+    for path in _testfiles_created:
+        try:
+            safe_rmpath(path)
+        except Exception:
+            traceback.print_exc()
+
+
+# this is executed first
+@atexit.register
+def _cleanup_procs():
+    reap_children(recursive=True)
+
+
+# ===================================================================
+# --- threads
+# ===================================================================
+
+
+class ThreadTask(threading.Thread):
+    """A thread task which does nothing expect staying alive."""
+
+    def __init__(self):
+        threading.Thread.__init__(self)
+        self._running = False
+        self._interval = 0.001
+        self._flag = threading.Event()
+
+    def __repr__(self):
+        name = self.__class__.__name__
+        return '<%s running=%s at %#x>' % (name, self._running, id(self))
+
+    def __enter__(self):
+        self.start()
+        return self
+
+    def __exit__(self, *args, **kwargs):
+        self.stop()
+
+    def start(self):
+        """Start thread and keep it running until an explicit
+        stop() request. Polls for shutdown every 'timeout' seconds.
+        """
+        if self._running:
+            raise ValueError("already started")
+        threading.Thread.start(self)
+        self._flag.wait()
+
+    def run(self):
+        self._running = True
+        self._flag.set()
+        while self._running:
+            time.sleep(self._interval)
+
+    def stop(self):
+        """Stop thread execution and and waits until it is stopped."""
+        if not self._running:
+            raise ValueError("already stopped")
+        self._running = False
+        self.join()
+
+
+# ===================================================================
+# --- subprocesses
+# ===================================================================
+
+
+def _cleanup_on_err(fun):
+    @functools.wraps(fun)
+    def wrapper(*args, **kwargs):
+        try:
+            return fun(*args, **kwargs)
+        except Exception:
+            reap_children()
+            raise
+    return wrapper
+
+
+@_cleanup_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.
+    If "cmd" is specified that is used instead of python.
+    By default stdin and stdout are redirected to /dev/null.
+    It also attemps to make sure the process is in a reasonably
+    initialized state.
+    The process is registered for cleanup on reap_children().
+    """
+    kwds.setdefault("stdin", DEVNULL)
+    kwds.setdefault("stdout", DEVNULL)
+    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
+    if cmd is None:
+        safe_rmpath(_TESTFN)
+        pyline = "from time import sleep;" \
+                 "open(r'%s', 'w').close();" \
+                 "sleep(60);" % _TESTFN
+        cmd = [PYTHON_EXE, "-c", pyline]
+        sproc = subprocess.Popen(cmd, **kwds)
+        _subprocesses_started.add(sproc)
+        wait_for_file(_TESTFN, delete=True, empty=True)
+    else:
+        sproc = subprocess.Popen(cmd, **kwds)
+        _subprocesses_started.add(sproc)
+        wait_for_pid(sproc.pid)
+    return sproc
+
+
+@_cleanup_on_err
+def create_proc_children_pair():
+    """Create a subprocess which creates another one as in:
+    A (us) -> B (child) -> C (grandchild).
+    Return a (child, grandchild) tuple.
+    The 2 processes are fully initialized and will live for 60 secs
+    and are registered for cleanup on reap_children().
+    """
+    _TESTFN2 = os.path.basename(_TESTFN) + '2'  # need to be relative
+    s = textwrap.dedent("""\
+        import subprocess, os, sys, time
+        s = "import os, time;"
+        s += "f = open('%s', 'w');"
+        s += "f.write(str(os.getpid()));"
+        s += "f.close();"
+        s += "time.sleep(60);"
+        subprocess.Popen(['%s', '-c', s])
+        time.sleep(60)
+        """ % (_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
+    # spawned as a child. We don't want that.
+    if WINDOWS:
+        subp = pyrun(s, creationflags=0)
+    else:
+        subp = pyrun(s)
+    child1 = psutil.Process(subp.pid)
+    data = wait_for_file(_TESTFN2, delete=False, empty=False)
+    os.remove(_TESTFN2)
+    child2_pid = int(data)
+    _pids_started.add(child2_pid)
+    child2 = psutil.Process(child2_pid)
+    return (child1, child2)
+
+
+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
+    src = textwrap.dedent("""\
+        import os, sys, time, socket, contextlib
+        child_pid = os.fork()
+        if child_pid > 0:
+            time.sleep(3000)
+        else:
+            # this is the zombie process
+            s = socket.socket(socket.AF_UNIX)
+            with contextlib.closing(s):
+                s.connect('%s')
+                if sys.version_info < (3, ):
+                    pid = str(os.getpid())
+                else:
+                    pid = bytes(str(os.getpid()), 'ascii')
+                s.sendall(pid)
+        """ % unix_file)
+    with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
+        sock.settimeout(GLOBAL_TIMEOUT)
+        sock.bind(unix_file)
+        sock.listen(1)
+        pyrun(src)
+        conn, _ = sock.accept()
+        try:
+            select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT)
+            zpid = int(conn.recv(1024))
+            _pids_started.add(zpid)
+            zproc = psutil.Process(zpid)
+            call_until(lambda: zproc.status(), "ret == psutil.STATUS_ZOMBIE")
+            return zpid
+        finally:
+            conn.close()
+
+
+@_cleanup_on_err
+def pyrun(src, **kwds):
+    """Run python 'src' code string in a separate interpreter.
+    Returns a subprocess.Popen instance.
+    """
+    kwds.setdefault("stdout", None)
+    kwds.setdefault("stderr", None)
+    with tempfile.NamedTemporaryFile(
+            prefix=TESTFILE_PREFIX, mode="wt", delete=False) as f:
+        _testfiles_created.add(f.name)
+        f.write(src)
+        f.flush()
+        subp = get_test_subprocess([PYTHON_EXE, f.name], **kwds)
+        wait_for_pid(subp.pid)
+    return subp
+
+
+@_cleanup_on_err
+def sh(cmd, **kwds):
+    """run cmd in a subprocess and return its output.
+    raises RuntimeError on error.
+    """
+    shell = True if isinstance(cmd, (str, unicode)) else False
+    # Prevents subprocess to open error dialogs in case of error.
+    flags = 0x8000000 if WINDOWS and shell else 0
+    kwds.setdefault("shell", shell)
+    kwds.setdefault("stdout", subprocess.PIPE)
+    kwds.setdefault("stderr", subprocess.PIPE)
+    kwds.setdefault("universal_newlines", True)
+    kwds.setdefault("creationflags", flags)
+    p = subprocess.Popen(cmd, **kwds)
+    _subprocesses_started.add(p)
+    stdout, stderr = p.communicate()
+    if p.returncode != 0:
+        raise RuntimeError(stderr)
+    if stderr:
+        warn(stderr)
+    if stdout.endswith('\n'):
+        stdout = stdout[:-1]
+    return stdout
+
+
+def reap_children(recursive=False):
+    """Terminate and wait() any subprocess started by this test suite
+    and ensure that no zombies stick around to hog resources and
+    create problems  when looking for refleaks.
+
+    If resursive is True it also tries to terminate and wait()
+    all grandchildren started by this process.
+    """
+    # This is here to make sure wait_procs() behaves properly and
+    # investigate:
+    # https://ci.appveyor.com/project/giampaolo/psutil/build/job/
+    #     jiq2cgd6stsbtn60
+    def assert_gone(pid):
+        assert not psutil.pid_exists(pid), pid
+        assert pid not in psutil.pids(), pid
+        try:
+            p = psutil.Process(pid)
+            assert not p.is_running(), pid
+        except psutil.NoSuchProcess:
+            pass
+        else:
+            assert 0, "pid %s is not gone" % pid
+
+    # Get the children here, before terminating the children sub
+    # processes as we don't want to lose the intermediate reference
+    # in case of grandchildren.
+    if recursive:
+        children = set(psutil.Process().children(recursive=True))
+    else:
+        children = set()
+
+    # Terminate subprocess.Popen instances "cleanly" by closing their
+    # fds and wiat()ing for them in order to avoid zombies.
+    while _subprocesses_started:
+        subp = _subprocesses_started.pop()
+        _pids_started.add(subp.pid)
+        try:
+            subp.terminate()
+        except OSError as err:
+            if err.errno != errno.ESRCH:
+                raise
+        if subp.stdout:
+            subp.stdout.close()
+        if subp.stderr:
+            subp.stderr.close()
+        try:
+            # Flushing a BufferedWriter may raise an error.
+            if subp.stdin:
+                subp.stdin.close()
+        finally:
+            # Wait for the process to terminate, to avoid zombies.
+            try:
+                subp.wait()
+            except OSError as err:
+                if err.errno != errno.ECHILD:
+                    raise
+
+    # Terminate started pids.
+    while _pids_started:
+        pid = _pids_started.pop()
+        try:
+            p = psutil.Process(pid)
+        except psutil.NoSuchProcess:
+            assert_gone(pid)
+        else:
+            children.add(p)
+
+    # Terminate children.
+    if children:
+        for p in children:
+            try:
+                p.terminate()
+            except psutil.NoSuchProcess:
+                pass
+        gone, alive = psutil.wait_procs(children, timeout=GLOBAL_TIMEOUT)
+        for p in alive:
+            warn("couldn't terminate process %r; attempting kill()" % p)
+            try:
+                p.kill()
+            except psutil.NoSuchProcess:
+                pass
+        gone, alive = psutil.wait_procs(alive, timeout=GLOBAL_TIMEOUT)
+        if alive:
+            for p in alive:
+                warn("process %r survived kill()" % p)
+
+        for p in children:
+            assert_gone(p.pid)
+
+
+# ===================================================================
+# --- OS
+# ===================================================================
+
+
+def get_kernel_version():
+    """Return a tuple such as (2, 6, 36)."""
+    if not POSIX:
+        raise NotImplementedError("not POSIX")
+    s = ""
+    uname = os.uname()[2]
+    for c in uname:
+        if c.isdigit() or c == '.':
+            s += c
+        else:
+            break
+    if not s:
+        raise ValueError("can't parse %r" % uname)
+    minor = 0
+    micro = 0
+    nums = s.split('.')
+    major = int(nums[0])
+    if len(nums) >= 2:
+        minor = int(nums[1])
+    if len(nums) >= 3:
+        micro = int(nums[2])
+    return (major, minor, micro)
+
+
+def get_winver():
+    if not WINDOWS:
+        raise NotImplementedError("not WINDOWS")
+    wv = sys.getwindowsversion()
+    if hasattr(wv, 'service_pack_major'):  # python >= 2.7
+        sp = wv.service_pack_major or 0
+    else:
+        r = re.search(r"\s\d$", wv[4])
+        if r:
+            sp = int(r.group(0))
+        else:
+            sp = 0
+    return (wv[0], wv[1], sp)
+
+
+# ===================================================================
+# --- sync primitives
+# ===================================================================
+
+
+class retry(object):
+    """A retry decorator."""
+
+    def __init__(self,
+                 exception=Exception,
+                 timeout=None,
+                 retries=None,
+                 interval=0.001,
+                 logfun=lambda s: print(s, file=sys.stderr),
+                 ):
+        if timeout and retries:
+            raise ValueError("timeout and retries args are mutually exclusive")
+        self.exception = exception
+        self.timeout = timeout
+        self.retries = retries
+        self.interval = interval
+        self.logfun = logfun
+
+    def __iter__(self):
+        if self.timeout:
+            stop_at = time.time() + self.timeout
+            while time.time() < stop_at:
+                yield
+        elif self.retries:
+            for _ in range(self.retries):
+                yield
+        else:
+            while True:
+                yield
+
+    def sleep(self):
+        if self.interval is not None:
+            time.sleep(self.interval)
+
+    def __call__(self, fun):
+        @functools.wraps(fun)
+        def wrapper(*args, **kwargs):
+            exc = None
+            for _ in self:
+                try:
+                    return fun(*args, **kwargs)
+                except self.exception as _:
+                    exc = _
+                    if self.logfun is not None:
+                        self.logfun(exc)
+                    self.sleep()
+                    continue
+            if PY3:
+                raise exc
+            else:
+                raise
+
+        # This way the user of the decorated function can change config
+        # parameters.
+        wrapper.decorator = self
+        return wrapper
+
+
+@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT,
+       interval=0.001)
+def wait_for_pid(pid):
+    """Wait for pid to show up in the process list then return.
+    Used in the test suite to give time the sub process to initialize.
+    """
+    psutil.Process(pid)
+    if WINDOWS:
+        # give it some more time to allow better initialization
+        time.sleep(0.01)
+
+
+@retry(exception=(EnvironmentError, AssertionError), logfun=None,
+       timeout=GLOBAL_TIMEOUT, interval=0.001)
+def wait_for_file(fname, delete=True, empty=False):
+    """Wait for a file to be written on disk with some content."""
+    with open(fname, "rb") as f:
+        data = f.read()
+    if not empty:
+        assert data
+    if delete:
+        os.remove(fname)
+    return data
+
+
+@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT,
+       interval=0.001)
+def call_until(fun, expr):
+    """Keep calling function for timeout secs and exit if eval()
+    expression is True.
+    """
+    ret = fun()
+    assert eval(expr)
+    return ret
+
+
+# ===================================================================
+# --- fs
+# ===================================================================
+
+
+def safe_rmpath(path):
+    "Convenience function for removing temporary test files or dirs"
+    try:
+        st = os.stat(path)
+        if stat.S_ISDIR(st.st_mode):
+            os.rmdir(path)
+        else:
+            os.remove(path)
+    except OSError as err:
+        if err.errno != errno.ENOENT:
+            raise
+
+
+def safe_mkdir(dir):
+    "Convenience function for creating a directory"
+    try:
+        os.mkdir(dir)
+    except OSError as err:
+        if err.errno != errno.EEXIST:
+            raise
+
+
+@contextlib.contextmanager
+def chdir(dirname):
+    "Context manager which temporarily changes the current directory."
+    curdir = os.getcwd()
+    try:
+        os.chdir(dirname)
+        yield
+    finally:
+        os.chdir(curdir)
+
+
+def create_exe(outpath, c_code=None):
+    """Creates an executable file in the given location."""
+    assert not os.path.exists(outpath), outpath
+    if c_code:
+        if not which("gcc"):
+            raise ValueError("gcc is not installed")
+        if isinstance(c_code, bool):        # c_code is True
+            c_code = textwrap.dedent(
+                """
+                #include <unistd.h>
+                int main() {
+                    pause();
+                    return 1;
+                }
+                """)
+        assert isinstance(c_code, str), c_code
+        with tempfile.NamedTemporaryFile(
+                suffix='.c', delete=False, mode='wt') as f:
+            f.write(c_code)
+        try:
+            subprocess.check_call(["gcc", f.name, "-o", outpath])
+        finally:
+            safe_rmpath(f.name)
+    else:
+        # copy python executable
+        shutil.copyfile(PYTHON_EXE, outpath)
+        if POSIX:
+            st = os.stat(outpath)
+            os.chmod(outpath, st.st_mode | stat.S_IEXEC)
+
+
+def unique_filename(prefix=TESTFILE_PREFIX, suffix=""):
+    return tempfile.mktemp(prefix=prefix, suffix=suffix)
+
+
+# ===================================================================
+# --- testing
+# ===================================================================
+
+
+class TestCase(unittest.TestCase):
+
+    # Print a full path representation of the single unit tests
+    # being run.
+    def __str__(self):
+        return "%s.%s.%s" % (
+            self.__class__.__module__, self.__class__.__name__,
+            self._testMethodName)
+
+    # assertRaisesRegexp renamed to assertRaisesRegex in 3.3;
+    # add support for the new name.
+    if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
+        assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
+
+
+# override default 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):
+    """Decorator which runs a test function and retries N times before
+    actually failing.
+    """
+    return retry(exception=AssertionError, timeout=None, retries=retries)
+
+
+def skip_on_access_denied(only_if=None):
+    """Decorator to Ignore AccessDenied exceptions."""
+    def decorator(fun):
+        @functools.wraps(fun)
+        def wrapper(*args, **kwargs):
+            try:
+                return fun(*args, **kwargs)
+            except psutil.AccessDenied:
+                if only_if is not None:
+                    if not only_if:
+                        raise
+                raise unittest.SkipTest("raises AccessDenied")
+        return wrapper
+    return decorator
+
+
+def skip_on_not_implemented(only_if=None):
+    """Decorator to Ignore NotImplementedError exceptions."""
+    def decorator(fun):
+        @functools.wraps(fun)
+        def wrapper(*args, **kwargs):
+            try:
+                return fun(*args, **kwargs)
+            except NotImplementedError:
+                if only_if is not None:
+                    if not only_if:
+                        raise
+                msg = "%r was skipped because it raised NotImplementedError" \
+                      % fun.__name__
+                raise unittest.SkipTest(msg)
+        return wrapper
+    return decorator
+
+
+# ===================================================================
+# --- network
+# ===================================================================
+
+
+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]
+
+
+@contextlib.contextmanager
+def unix_socket_path(suffix=""):
+    """A context manager which returns a non-existent file name
+    and tries to delete it on exit.
+    """
+    assert psutil.POSIX
+    path = unique_filename(suffix=suffix)
+    try:
+        yield path
+    finally:
+        try:
+            os.unlink(path)
+        except OSError:
+            pass
+
+
+def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None):
+    """Binds a generic socket."""
+    if addr is None and family in (AF_INET, AF_INET6):
+        addr = ("", 0)
+    sock = socket.socket(family, type)
+    try:
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        sock.bind(addr)
+        if type == socket.SOCK_STREAM:
+            sock.listen(10)
+        return sock
+    except Exception:
+        sock.close()
+        raise
+
+
+def bind_unix_socket(name, type=socket.SOCK_STREAM):
+    """Bind a UNIX socket."""
+    assert psutil.POSIX
+    assert not os.path.exists(name), name
+    sock = socket.socket(socket.AF_UNIX, type)
+    try:
+        sock.bind(name)
+        if type == socket.SOCK_STREAM:
+            sock.listen(10)
+    except Exception:
+        sock.close()
+        raise
+    return sock
+
+
+def tcp_socketpair(family, addr=("", 0)):
+    """Build a pair of TCP sockets connected to each other.
+    Return a (server, client) tuple.
+    """
+    with contextlib.closing(socket.socket(family, SOCK_STREAM)) as ll:
+        ll.bind(addr)
+        ll.listen(10)
+        addr = ll.getsockname()
+        c = socket.socket(family, SOCK_STREAM)
+        try:
+            c.connect(addr)
+            caddr = c.getsockname()
+            while True:
+                a, addr = ll.accept()
+                # check that we've got the correct client
+                if addr == caddr:
+                    return (a, c)
+                a.close()
+        except OSError:
+            c.close()
+            raise
+
+
+def unix_socketpair(name):
+    """Build a pair of UNIX sockets connected to each other through
+    the same UNIX file name.
+    Return a (server, client) tuple.
+    """
+    assert psutil.POSIX
+    server = client = None
+    try:
+        server = bind_unix_socket(name, type=socket.SOCK_STREAM)
+        server.setblocking(0)
+        client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        client.setblocking(0)
+        client.connect(name)
+        # new = server.accept()
+    except Exception:
+        if server is not None:
+            server.close()
+        if client is not None:
+            client.close()
+        raise
+    return (server, client)
+
+
+@contextlib.contextmanager
+def create_sockets():
+    """Open as many socket families / types as possible."""
+    socks = []
+    fname1 = fname2 = None
+    try:
+        socks.append(bind_socket(socket.AF_INET, socket.SOCK_STREAM))
+        socks.append(bind_socket(socket.AF_INET, socket.SOCK_DGRAM))
+        if supports_ipv6():
+            socks.append(bind_socket(socket.AF_INET6, socket.SOCK_STREAM))
+            socks.append(bind_socket(socket.AF_INET6, socket.SOCK_DGRAM))
+        if POSIX and HAS_CONNECTIONS_UNIX:
+            fname1 = unix_socket_path().__enter__()
+            fname2 = unix_socket_path().__enter__()
+            s1, s2 = unix_socketpair(fname1)
+            s3 = bind_unix_socket(fname2, type=socket.SOCK_DGRAM)
+            # self.addCleanup(safe_rmpath, fname1)
+            # self.addCleanup(safe_rmpath, fname2)
+            for s in (s1, s2, s3):
+                socks.append(s)
+        yield socks
+    finally:
+        for s in socks:
+            s.close()
+        if fname1 is not None:
+            safe_rmpath(fname1)
+        if fname2 is not None:
+            safe_rmpath(fname2)
+
+
+def check_net_address(addr, family):
+    """Check a net address validity. Supported families are IPv4,
+    IPv6 and MAC addresses.
+    """
+    import ipaddress  # python >= 3.3 / requires "pip install ipaddress"
+    if enum and PY3:
+        assert isinstance(family, enum.IntEnum), family
+    if family == socket.AF_INET:
+        octs = [int(x) for x in addr.split('.')]
+        assert len(octs) == 4, addr
+        for num in octs:
+            assert 0 <= num <= 255, addr
+        if not PY3:
+            addr = unicode(addr)
+        ipaddress.IPv4Address(addr)
+    elif family == socket.AF_INET6:
+        assert isinstance(addr, str), addr
+        if not PY3:
+            addr = unicode(addr)
+        ipaddress.IPv6Address(addr)
+    elif family == psutil.AF_LINK:
+        assert re.match(r'([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr
+    else:
+        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
+# ===================================================================
+
+
+def reload_module(module):
+    """Backport of importlib.reload of Python 3.3+."""
+    try:
+        import importlib
+        if not hasattr(importlib, 'reload'):  # python <=3.3
+            raise ImportError
+    except ImportError:
+        import imp
+        return imp.reload(module)
+    else:
+        return importlib.reload(module)
+
+
+def import_module_by_path(path):
+    name = os.path.splitext(os.path.basename(path))[0]
+    if sys.version_info[0] == 2:
+        import imp
+        return imp.load_source(name, path)
+    elif sys.version_info[:2] <= (3, 4):
+        from importlib.machinery import SourceFileLoader
+        return SourceFileLoader(name, path).load_module()
+    else:
+        import importlib.util
+        spec = importlib.util.spec_from_file_location(name, path)
+        mod = importlib.util.module_from_spec(spec)
+        spec.loader.exec_module(mod)
+        return mod
+
+
+# ===================================================================
+# --- others
+# ===================================================================
+
+
+def warn(msg):
+    """Raise a warning msg."""
+    warnings.warn(msg, UserWarning)
+
+
+def is_namedtuple(x):
+    """Check if object is an instance of namedtuple."""
+    t = type(x)
+    b = t.__bases__
+    if len(b) != 1 or b[0] != tuple:
+        return False
+    f = getattr(t, '_fields', None)
+    if not isinstance(f, tuple):
+        return False
+    return all(type(n) == str for n in f)
+
+
+if POSIX:
+    @contextlib.contextmanager
+    def copyload_shared_lib(dst_prefix=TESTFILE_PREFIX):
+        """Ctx manager which picks up a random shared CO lib used
+        by this process, copies it in another location and loads it
+        in memory via ctypes. Return the new absolutized path.
+        """
+        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()]
+        src = random.choice(libs)
+        shutil.copyfile(src, dst)
+        try:
+            ctypes.CDLL(dst)
+            yield dst
+        finally:
+            safe_rmpath(dst)
+else:
+    @contextlib.contextmanager
+    def copyload_shared_lib(dst_prefix=TESTFILE_PREFIX):
+        """Ctx manager which picks up a random shared DLL lib used
+        by this process, copies it in another location and loads it
+        in memory via ctypes.
+        Return the new absolutized, normcased path.
+        """
+        from ctypes import wintypes
+        from ctypes import WinError
+        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
+                'python' in os.path.basename(x.path).lower() and
+                'wow64' not in x.path.lower()]
+        src = random.choice(libs)
+        shutil.copyfile(src, dst)
+        cfile = None
+        try:
+            cfile = ctypes.WinDLL(dst)
+            yield dst
+        finally:
+            # Work around OverflowError:
+            # - https://ci.appveyor.com/project/giampaolo/psutil/build/1207/
+            #       job/o53330pbnri9bcw7
+            # - http://bugs.python.org/issue30286
+            # - http://stackoverflow.com/questions/23522055
+            if cfile is not None:
+                FreeLibrary = ctypes.windll.kernel32.FreeLibrary
+                FreeLibrary.argtypes = [wintypes.HMODULE]
+                ret = FreeLibrary(cfile._handle)
+                if ret == 0:
+                    WinError()
+            safe_rmpath(dst)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/__main__.py
@@ -0,0 +1,96 @@
+#!/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.
+
+"""
+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()
+
+
+main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_bsd.py
@@ -0,0 +1,489 @@
+#!/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.
+
+# TODO: (FreeBSD) add test for comparing connections with 'sockstat' cmd.
+
+
+"""Tests specific to all BSD platforms."""
+
+
+import datetime
+import os
+import re
+import time
+
+import psutil
+from psutil import BSD
+from psutil import FREEBSD
+from psutil import NETBSD
+from psutil import OPENBSD
+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 sh
+from psutil.tests import unittest
+from psutil.tests import which
+
+
+if BSD:
+    PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+    if os.getuid() == 0:  # muse requires root privileges
+        MUSE_AVAILABLE = which('muse')
+    else:
+        MUSE_AVAILABLE = False
+else:
+    MUSE_AVAILABLE = False
+
+
+def sysctl(cmdline):
+    """Expects a sysctl command with an argument and parse the result
+    returning only the value of interest.
+    """
+    result = sh("sysctl " + cmdline)
+    if FREEBSD:
+        result = result[result.find(": ") + 2:]
+    elif OPENBSD or NETBSD:
+        result = result[result.find("=") + 1:]
+    try:
+        return int(result)
+    except ValueError:
+        return result
+
+
+def muse(field):
+    """Thin wrapper around 'muse' cmdline utility."""
+    out = sh('muse')
+    for line in out.split('\n'):
+        if line.startswith(field):
+            break
+    else:
+        raise ValueError("line not found")
+    return int(line.split()[1])
+
+
+# =====================================================================
+# --- All BSD*
+# =====================================================================
+
+
+@unittest.skipIf(not BSD, "BSD only")
+class BSDSpecificTestCase(unittest.TestCase):
+    """Generic tests common to all BSD variants."""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    @unittest.skipIf(NETBSD, "-o lstart doesn't work on NETBSD")
+    def test_process_create_time(self):
+        output = sh("ps -o lstart -p %s" % self.pid)
+        start_ps = output.replace('STARTED', '').strip()
+        start_psutil = psutil.Process(self.pid).create_time()
+        start_psutil = time.strftime("%a %b %e %H:%M:%S %Y",
+                                     time.localtime(start_psutil))
+        self.assertEqual(start_ps, start_psutil)
+
+    def test_disks(self):
+        # test psutil.disk_usage() and psutil.disk_partitions()
+        # against "df -a"
+        def df(path):
+            out = sh('df -k "%s"' % path).strip()
+            lines = out.split('\n')
+            lines.pop(0)
+            line = lines.pop(0)
+            dev, total, used, free = line.split()[:4]
+            if dev == 'none':
+                dev = ''
+            total = int(total) * 1024
+            used = int(used) * 1024
+            free = int(free) * 1024
+            return dev, total, used, free
+
+        for part in psutil.disk_partitions(all=False):
+            usage = psutil.disk_usage(part.mountpoint)
+            dev, total, used, free = df(part.mountpoint)
+            self.assertEqual(part.device, dev)
+            self.assertEqual(usage.total, total)
+            # 10 MB tollerance
+            if abs(usage.free - free) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % (usage.free, free))
+            if abs(usage.used - used) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % (usage.used, used))
+
+    @unittest.skipIf(not which('sysctl'), "sysctl cmd not available")
+    def test_cpu_count_logical(self):
+        syst = sysctl("hw.ncpu")
+        self.assertEqual(psutil.cpu_count(logical=True), syst)
+
+    @unittest.skipIf(not which('sysctl'), "sysctl cmd not available")
+    def test_virtual_memory_total(self):
+        num = sysctl('hw.physmem')
+        self.assertEqual(num, psutil.virtual_memory().total)
+
+    def test_net_if_stats(self):
+        for name, stats in psutil.net_if_stats().items():
+            try:
+                out = sh("ifconfig %s" % name)
+            except RuntimeError:
+                pass
+            else:
+                self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
+                if "mtu" in out:
+                    self.assertEqual(stats.mtu,
+                                     int(re.findall(r'mtu (\d+)', out)[0]))
+
+
+# =====================================================================
+# --- FreeBSD
+# =====================================================================
+
+
+@unittest.skipIf(not FREEBSD, "FREEBSD only")
+class FreeBSDSpecificTestCase(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    @retry_before_failing()
+    def test_proc_memory_maps(self):
+        out = sh('procstat -v %s' % self.pid)
+        maps = psutil.Process(self.pid).memory_maps(grouped=False)
+        lines = out.split('\n')[1:]
+        while lines:
+            line = lines.pop()
+            fields = line.split()
+            _, start, stop, perms, res = fields[:5]
+            map = maps.pop()
+            self.assertEqual("%s-%s" % (start, stop), map.addr)
+            self.assertEqual(int(res), map.rss)
+            if not map.path.startswith('['):
+                self.assertEqual(fields[10], map.path)
+
+    def test_proc_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):
+        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):
+        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)
+        uids = p.uids()
+        gids = p.gids()
+        self.assertEqual(uids.real, int(ruid))
+        self.assertEqual(uids.effective, int(euid))
+        self.assertEqual(uids.saved, int(suid))
+        self.assertEqual(gids.real, int(rgid))
+        self.assertEqual(gids.effective, int(egid))
+        self.assertEqual(gids.saved, int(sgid))
+
+    @retry_before_failing()
+    def test_proc_ctx_switches(self):
+        tested = []
+        out = sh('procstat -r %s' % self.pid)
+        p = psutil.Process(self.pid)
+        for line in out.split('\n'):
+            line = line.lower().strip()
+            if ' voluntary context' in line:
+                pstat_value = int(line.split()[-1])
+                psutil_value = p.num_ctx_switches().voluntary
+                self.assertEqual(pstat_value, psutil_value)
+                tested.append(None)
+            elif ' involuntary context' in line:
+                pstat_value = int(line.split()[-1])
+                psutil_value = p.num_ctx_switches().involuntary
+                self.assertEqual(pstat_value, psutil_value)
+                tested.append(None)
+        if len(tested) != 2:
+            raise RuntimeError("couldn't find lines match in procstat out")
+
+    @retry_before_failing()
+    def test_proc_cpu_times(self):
+        tested = []
+        out = sh('procstat -r %s' % self.pid)
+        p = psutil.Process(self.pid)
+        for line in out.split('\n'):
+            line = line.lower().strip()
+            if 'user time' in line:
+                pstat_value = float('0.' + line.split()[-1].split('.')[-1])
+                psutil_value = p.cpu_times().user
+                self.assertEqual(pstat_value, psutil_value)
+                tested.append(None)
+            elif 'system time' in line:
+                pstat_value = float('0.' + line.split()[-1].split('.')[-1])
+                psutil_value = p.cpu_times().system
+                self.assertEqual(pstat_value, psutil_value)
+                tested.append(None)
+        if len(tested) != 2:
+            raise RuntimeError("couldn't find lines match in procstat out")
+
+    # --- virtual_memory(); tests against sysctl
+
+    @retry_before_failing()
+    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()
+    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()
+    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()
+    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()
+    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()
+    def test_vmem_buffers(self):
+        syst = sysctl("vfs.bufspace")
+        self.assertAlmostEqual(psutil.virtual_memory().buffers, syst,
+                               delta=MEMORY_TOLERANCE)
+
+    # --- virtual_memory(); tests against muse
+
+    @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
+    def test_muse_vmem_total(self):
+        num = muse('Total')
+        self.assertEqual(psutil.virtual_memory().total, num)
+
+    @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
+    @retry_before_failing()
+    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()
+    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()
+    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()
+    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()
+    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()
+    def test_muse_vmem_buffers(self):
+        num = muse('Buffer')
+        self.assertAlmostEqual(psutil.virtual_memory().buffers, num,
+                               delta=MEMORY_TOLERANCE)
+
+    def test_cpu_stats_ctx_switches(self):
+        self.assertAlmostEqual(psutil.cpu_stats().ctx_switches,
+                               sysctl('vm.stats.sys.v_swtch'), delta=1000)
+
+    def test_cpu_stats_interrupts(self):
+        self.assertAlmostEqual(psutil.cpu_stats().interrupts,
+                               sysctl('vm.stats.sys.v_intr'), delta=1000)
+
+    def test_cpu_stats_soft_interrupts(self):
+        self.assertAlmostEqual(psutil.cpu_stats().soft_interrupts,
+                               sysctl('vm.stats.sys.v_soft'), delta=1000)
+
+    def test_cpu_stats_syscalls(self):
+        self.assertAlmostEqual(psutil.cpu_stats().syscalls,
+                               sysctl('vm.stats.sys.v_syscall'), delta=1000)
+
+    # def test_cpu_stats_traps(self):
+    #    self.assertAlmostEqual(psutil.cpu_stats().traps,
+    #                           sysctl('vm.stats.sys.v_trap'), delta=1000)
+
+    # --- others
+
+    def test_boot_time(self):
+        s = sysctl('sysctl kern.boottime')
+        s = s[s.find(" sec = ") + 7:]
+        s = s[:s.find(',')]
+        btime = int(s)
+        self.assertEqual(btime, psutil.boot_time())
+
+    # --- sensors_battery
+
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_sensors_battery(self):
+        def secs2hours(secs):
+            m, s = divmod(secs, 60)
+            h, m = divmod(m, 60)
+            return "%d:%02d" % (h, m)
+
+        out = sh("acpiconf -i 0")
+        fields = dict([(x.split('\t')[0], x.split('\t')[-1])
+                       for x in out.split("\n")])
+        metrics = psutil.sensors_battery()
+        percent = int(fields['Remaining capacity:'].replace('%', ''))
+        remaining_time = fields['Remaining time:']
+        self.assertEqual(metrics.percent, percent)
+        if remaining_time == 'unknown':
+            self.assertEqual(metrics.secsleft, psutil.POWER_TIME_UNLIMITED)
+        else:
+            self.assertEqual(secs2hours(metrics.secsleft), remaining_time)
+
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_sensors_battery_against_sysctl(self):
+        self.assertEqual(psutil.sensors_battery().percent,
+                         sysctl("hw.acpi.battery.life"))
+        self.assertEqual(psutil.sensors_battery().power_plugged,
+                         sysctl("hw.acpi.acline") == 1)
+        secsleft = psutil.sensors_battery().secsleft
+        if secsleft < 0:
+            self.assertEqual(sysctl("hw.acpi.battery.time"), -1)
+        else:
+            self.assertEqual(secsleft, sysctl("hw.acpi.battery.time") * 60)
+
+    @unittest.skipIf(HAS_BATTERY, "has battery")
+    def test_sensors_battery_no_battery(self):
+        # If no battery is present one of these calls is supposed
+        # to fail, see:
+        # https://github.com/giampaolo/psutil/issues/1074
+        with self.assertRaises(RuntimeError):
+            sysctl("hw.acpi.battery.life")
+            sysctl("hw.acpi.battery.time")
+            sysctl("hw.acpi.acline")
+        self.assertIsNone(psutil.sensors_battery())
+
+
+# =====================================================================
+# --- OpenBSD
+# =====================================================================
+
+
+@unittest.skipIf(not OPENBSD, "OPENBSD only")
+class OpenBSDSpecificTestCase(unittest.TestCase):
+
+    def test_boot_time(self):
+        s = sysctl('kern.boottime')
+        sys_bt = datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y")
+        psutil_bt = datetime.datetime.fromtimestamp(psutil.boot_time())
+        self.assertEqual(sys_bt, psutil_bt)
+
+
+# =====================================================================
+# --- NetBSD
+# =====================================================================
+
+
+@unittest.skipIf(not NETBSD, "NETBSD only")
+class NetBSDSpecificTestCase(unittest.TestCase):
+
+    @staticmethod
+    def parse_meminfo(look_for):
+        with open('/proc/meminfo', 'rb') as f:
+            for line in f:
+                if line.startswith(look_for):
+                    return int(line.split()[1]) * 1024
+        raise ValueError("can't find %s" % look_for)
+
+    def test_vmem_total(self):
+        self.assertEqual(
+            psutil.virtual_memory().total, self.parse_meminfo("MemTotal:"))
+
+    def test_vmem_free(self):
+        self.assertAlmostEqual(
+            psutil.virtual_memory().free, self.parse_meminfo("MemFree:"),
+            delta=MEMORY_TOLERANCE)
+
+    def test_vmem_buffers(self):
+        self.assertAlmostEqual(
+            psutil.virtual_memory().buffers, self.parse_meminfo("Buffers:"),
+            delta=MEMORY_TOLERANCE)
+
+    def test_vmem_shared(self):
+        self.assertAlmostEqual(
+            psutil.virtual_memory().shared, self.parse_meminfo("MemShared:"),
+            delta=MEMORY_TOLERANCE)
+
+    def test_swapmem_total(self):
+        self.assertAlmostEqual(
+            psutil.swap_memory().total, self.parse_meminfo("SwapTotal:"),
+            delta=MEMORY_TOLERANCE)
+
+    def test_swapmem_free(self):
+        self.assertAlmostEqual(
+            psutil.swap_memory().free, self.parse_meminfo("SwapFree:"),
+            delta=MEMORY_TOLERANCE)
+
+    def test_swapmem_used(self):
+        smem = psutil.swap_memory()
+        self.assertEqual(smem.used, smem.total - smem.free)
+
+    def test_cpu_stats_interrupts(self):
+        with open('/proc/stat', 'rb') as f:
+            for line in f:
+                if line.startswith(b'intr'):
+                    interrupts = int(line.split()[1])
+                    break
+            else:
+                raise ValueError("couldn't find line")
+        self.assertAlmostEqual(
+            psutil.cpu_stats().interrupts, interrupts, delta=1000)
+
+    def test_cpu_stats_ctx_switches(self):
+        with open('/proc/stat', 'rb') as f:
+            for line in f:
+                if line.startswith(b'ctxt'):
+                    ctx_switches = int(line.split()[1])
+                    break
+            else:
+                raise ValueError("couldn't find line")
+        self.assertAlmostEqual(
+            psutil.cpu_stats().ctx_switches, ctx_switches, delta=1000)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_connections.py
@@ -0,0 +1,525 @@
+#!/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.
+
+"""Tests for net_connections() and Process.connections() APIs."""
+
+import os
+import socket
+import textwrap
+from contextlib import closing
+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 FREEBSD
+from psutil import LINUX
+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 supports_ipv6
+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 create_sockets
+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 tcp_socketpair
+from psutil.tests import TESTFN
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+from psutil.tests import unix_socket_path
+from psutil.tests import unix_socketpair
+from psutil.tests import wait_for_file
+
+
+thisproc = psutil.Process()
+
+
+class Base(object):
+
+    def setUp(self):
+        if not NETBSD:
+            # NetBSD 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:
+            # 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 get_conn_from_sock(self, sock):
+        cons = thisproc.connections(kind='all')
+        smap = dict([(c.fd, c) for c in cons])
+        if NETBSD:
+            # NetBSD opens a UNIX socket to /var/log/run
+            # so there may be more connections.
+            return smap[sock.fileno()]
+        else:
+            self.assertEqual(len(cons), 1)
+            if cons[0].fd != -1:
+                self.assertEqual(smap[sock.fileno()].fd, sock.fileno())
+            return cons[0]
+
+    def check_socket(self, sock, conn=None):
+        """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)
+
+        # fd, family, type
+        if conn.fd != -1:
+            self.assertEqual(conn.fd, sock.fileno())
+        self.assertEqual(conn.family, sock.family)
+        # see: http://bugs.python.org/issue30204
+        self.assertEqual(
+            conn.type, sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE))
+
+        # local address
+        laddr = sock.getsockname()
+        if not laddr and PY3 and isinstance(laddr, bytes):
+            # See: http://bugs.python.org/issue30205
+            laddr = laddr.decode()
+        if sock.family == AF_INET6:
+            laddr = laddr[:2]
+        if sock.family == AF_UNIX and OPENBSD:
+            # No addresses are set for UNIX sockets on OpenBSD.
+            pass
+        else:
+            self.assertEqual(conn.laddr, laddr)
+
+        # 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)
+        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:
+            conn = self.check_socket(sock)
+            assert not conn.raddr
+            self.assertEqual(conn.status, psutil.CONN_LISTEN)
+
+    @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
+    def test_tcp_v6(self):
+        addr = ("::1", get_free_port())
+        with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock:
+            conn = self.check_socket(sock)
+            assert not conn.raddr
+            self.assertEqual(conn.status, psutil.CONN_LISTEN)
+
+    def test_udp_v4(self):
+        addr = ("127.0.0.1", get_free_port())
+        with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock:
+            conn = self.check_socket(sock)
+            assert not conn.raddr
+            self.assertEqual(conn.status, psutil.CONN_NONE)
+
+    @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
+    def test_udp_v6(self):
+        addr = ("::1", get_free_port())
+        with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock:
+            conn = self.check_socket(sock)
+            assert not conn.raddr
+            self.assertEqual(conn.status, psutil.CONN_NONE)
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_unix_tcp(self):
+        with unix_socket_path() as name:
+            with closing(bind_unix_socket(name, type=SOCK_STREAM)) as sock:
+                conn = self.check_socket(sock)
+                assert not conn.raddr
+                self.assertEqual(conn.status, psutil.CONN_NONE)
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_unix_udp(self):
+        with unix_socket_path() as name:
+            with closing(bind_unix_socket(name, type=SOCK_STREAM)) as sock:
+                conn = self.check_socket(sock)
+                assert not conn.raddr
+                self.assertEqual(conn.status, psutil.CONN_NONE)
+
+
+# =====================================================================
+# --- Test connected sockets
+# =====================================================================
+
+
+class TestConnectedSocketPairs(Base, unittest.TestCase):
+    """Test socket pairs which are are actually connected to
+    each other.
+    """
+
+    # On SunOS, even after we close() it, the server socket stays around
+    # in TIME_WAIT state.
+    @unittest.skipIf(SUNOS, "unreliable on SUONS")
+    def test_tcp(self):
+        addr = ("127.0.0.1", get_free_port())
+        assert not thisproc.connections(kind='tcp4')
+        server, client = tcp_socketpair(AF_INET, addr=addr)
+        try:
+            cons = thisproc.connections(kind='tcp4')
+            self.assertEqual(len(cons), 2)
+            self.assertEqual(cons[0].status, psutil.CONN_ESTABLISHED)
+            self.assertEqual(cons[1].status, psutil.CONN_ESTABLISHED)
+            # May not be fast enough to change state so it stays
+            # commenteed.
+            # client.close()
+            # cons = thisproc.connections(kind='all')
+            # self.assertEqual(len(cons), 1)
+            # self.assertEqual(cons[0].status, psutil.CONN_CLOSE_WAIT)
+        finally:
+            server.close()
+            client.close()
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_unix(self):
+        with unix_socket_path() as name:
+            server, client = unix_socketpair(name)
+            try:
+                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:
+                    # 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 LINUX or FREEBSD or SUNOS:
+                    # remote path is never set
+                    self.assertEqual(cons[0].raddr, "")
+                    self.assertEqual(cons[1].raddr, "")
+                    # one local address should though
+                    self.assertEqual(name, cons[0].laddr or cons[1].laddr)
+                elif OPENBSD:
+                    # No addresses whatsoever here.
+                    for addr in (cons[0].laddr, cons[0].raddr,
+                                 cons[1].laddr, cons[1].raddr):
+                        self.assertEqual(addr, "")
+                else:
+                    # On other systems either the laddr or raddr
+                    # of both peers are set.
+                    self.assertEqual(cons[0].laddr or cons[1].laddr, name)
+                    self.assertEqual(cons[0].raddr or cons[1].raddr, name)
+            finally:
+                server.close()
+                client.close()
+
+    @skip_on_access_denied(only_if=OSX)
+    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.assertEqual(conn.family, family)
+            self.assertEqual(conn.type, type)
+            self.assertEqual(conn.laddr, laddr)
+            self.assertEqual(conn.raddr, raddr)
+            self.assertEqual(conn.status, status)
+            for kind in all_kinds:
+                cons = proc.connections(kind=kind)
+                if kind in kinds:
+                    assert cons
+                else:
+                    assert not cons, cons
+            # compare against system-wide connections
+            # XXX Solaris can't retrieve system-wide UNIX
+            # sockets.
+            if HAS_CONNECTIONS_UNIX:
+                self.compare_procsys_connections(proc.pid, [conn])
+
+        tcp_template = textwrap.dedent("""
+            import socket, time
+            s = socket.socket($family, socket.SOCK_STREAM)
+            s.bind(('$addr', 0))
+            s.listen(1)
+            with open('$testfn', 'w') as f:
+                f.write(str(s.getsockname()[:2]))
+            time.sleep(60)
+        """)
+
+        udp_template = textwrap.dedent("""
+            import socket, time
+            s = socket.socket($family, socket.SOCK_DGRAM)
+            s.bind(('$addr', 0))
+            with open('$testfn', 'w') as f:
+                f.write(str(s.getsockname()[:2]))
+            time.sleep(60)
+        """)
+
+        from string import Template
+        testfile = os.path.basename(TESTFN)
+        tcp4_template = Template(tcp_template).substitute(
+            family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
+        udp4_template = Template(udp_template).substitute(
+            family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
+        tcp6_template = Template(tcp_template).substitute(
+            family=int(AF_INET6), addr="::1", testfn=testfile)
+        udp6_template = Template(udp_template).substitute(
+            family=int(AF_INET6), addr="::1", testfn=testfile)
+
+        # launch various subprocess instantiating a socket of various
+        # families and types to enrich psutil results
+        tcp4_proc = pyrun(tcp4_template)
+        tcp4_addr = eval(wait_for_file(testfile))
+        udp4_proc = pyrun(udp4_template)
+        udp4_addr = eval(wait_for_file(testfile))
+        if supports_ipv6():
+            tcp6_proc = pyrun(tcp6_template)
+            tcp6_addr = eval(wait_for_file(testfile))
+            udp6_proc = pyrun(udp6_template)
+            udp6_addr = eval(wait_for_file(testfile))
+        else:
+            tcp6_proc = None
+            udp6_proc = None
+            tcp6_addr = None
+            udp6_addr = None
+
+        for p in thisproc.children():
+            cons = p.connections()
+            self.assertEqual(len(cons), 1)
+            for conn in cons:
+                # TCP v4
+                if p.pid == tcp4_proc.pid:
+                    check_conn(p, conn, AF_INET, SOCK_STREAM, tcp4_addr, (),
+                               psutil.CONN_LISTEN,
+                               ("all", "inet", "inet4", "tcp", "tcp4"))
+                # UDP v4
+                elif p.pid == udp4_proc.pid:
+                    check_conn(p, conn, AF_INET, SOCK_DGRAM, udp4_addr, (),
+                               psutil.CONN_NONE,
+                               ("all", "inet", "inet4", "udp", "udp4"))
+                # TCP v6
+                elif p.pid == getattr(tcp6_proc, "pid", None):
+                    check_conn(p, conn, AF_INET6, SOCK_STREAM, tcp6_addr, (),
+                               psutil.CONN_LISTEN,
+                               ("all", "inet", "inet6", "tcp", "tcp6"))
+                # UDP v6
+                elif p.pid == getattr(udp6_proc, "pid", None):
+                    check_conn(p, conn, AF_INET6, SOCK_DGRAM, udp6_addr, (),
+                               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))
+            # tcp
+            cons = thisproc.connections(kind='tcp')
+            self.assertEqual(len(cons), 2 if supports_ipv6() else 1)
+            for conn in cons:
+                self.assertIn(conn.family, (AF_INET, AF_INET6))
+                self.assertEqual(conn.type, SOCK_STREAM)
+            # tcp4
+            cons = thisproc.connections(kind='tcp4')
+            self.assertEqual(len(cons), 1)
+            self.assertEqual(cons[0].family, AF_INET)
+            self.assertEqual(cons[0].type, SOCK_STREAM)
+            # tcp6
+            if supports_ipv6():
+                cons = thisproc.connections(kind='tcp6')
+                self.assertEqual(len(cons), 1)
+                self.assertEqual(cons[0].family, AF_INET6)
+                self.assertEqual(cons[0].type, SOCK_STREAM)
+            # udp
+            cons = thisproc.connections(kind='udp')
+            self.assertEqual(len(cons), 2 if supports_ipv6() else 1)
+            for conn in cons:
+                self.assertIn(conn.family, (AF_INET, AF_INET6))
+                self.assertEqual(conn.type, SOCK_DGRAM)
+            # udp4
+            cons = thisproc.connections(kind='udp4')
+            self.assertEqual(len(cons), 1)
+            self.assertEqual(cons[0].family, AF_INET)
+            self.assertEqual(cons[0].type, SOCK_DGRAM)
+            # udp6
+            if supports_ipv6():
+                cons = thisproc.connections(kind='udp6')
+                self.assertEqual(len(cons), 1)
+                self.assertEqual(cons[0].family, AF_INET6)
+                self.assertEqual(cons[0].type, SOCK_DGRAM)
+            # inet
+            cons = thisproc.connections(kind='inet')
+            self.assertEqual(len(cons), 4 if supports_ipv6() else 2)
+            for conn in cons:
+                self.assertIn(conn.family, (AF_INET, AF_INET6))
+                self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
+            # inet6
+            if supports_ipv6():
+                cons = thisproc.connections(kind='inet6')
+                self.assertEqual(len(cons), 2)
+                for conn in cons:
+                    self.assertEqual(conn.family, AF_INET6)
+                    self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
+            # unix
+            if HAS_CONNECTIONS_UNIX:
+                cons = thisproc.connections(kind='unix')
+                self.assertEqual(len(cons), 3)
+                for conn in cons:
+                    self.assertEqual(conn.family, AF_UNIX)
+                    self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
+
+
+# =====================================================================
+# --- Miscellaneous tests
+# =====================================================================
+
+
+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)
+
+        with create_sockets():
+            from psutil._common import conn_tmap
+            for kind, groups in conn_tmap.items():
+                # XXX: SunOS does not retrieve UNIX sockets.
+                if kind == 'unix' and not HAS_CONNECTIONS_UNIX:
+                    continue
+                families, types_ = groups
+                cons = psutil.net_connections(kind)
+                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")
+    def test_multi_sockets_procs(self):
+        # Creates multiple sub processes, each creating different
+        # sockets. For each process check that proc.connections()
+        # and net_connections() return the same results.
+        # This is done mainly to check whether net_connections()'s
+        # pid is properly set, see:
+        # https://github.com/giampaolo/psutil/issues/1013
+        with create_sockets() as socks:
+            expected = len(socks)
+        pids = []
+        times = 10
+        for i in range(times):
+            fname = os.path.realpath(TESTFN) + str(i)
+            src = textwrap.dedent("""\
+                import time, os
+                from psutil.tests import create_sockets
+                with create_sockets():
+                    with open('%s', 'w') as f:
+                        f.write(str(os.getpid()))
+                    time.sleep(60)
+                """ % fname)
+            sproc = pyrun(src)
+            pids.append(sproc.pid)
+            self.addCleanup(safe_rmpath, fname)
+
+        # sync
+        for i in range(times):
+            fname = TESTFN + str(i)
+            wait_for_file(fname)
+
+        syscons = [x for x in psutil.net_connections(kind='all') if x.pid
+                   in pids]
+        for pid in pids:
+            self.assertEqual(len([x for x in syscons if x.pid == pid]),
+                             expected)
+            p = psutil.Process(pid)
+            self.assertEqual(len(p.connections('all')), expected)
+
+
+# =====================================================================
+# --- Miscellaneous tests
+# =====================================================================
+
+
+class TestMisc(unittest.TestCase):
+
+    def test_connection_constants(self):
+        ints = []
+        strs = []
+        for name in dir(psutil):
+            if name.startswith('CONN_'):
+                num = getattr(psutil, name)
+                str_ = str(num)
+                assert str_.isupper(), str_
+                self.assertNotIn(str, strs)
+                self.assertNotIn(num, ints)
+                ints.append(num)
+                strs.append(str_)
+        if SUNOS:
+            psutil.CONN_IDLE
+            psutil.CONN_BOUND
+        if WINDOWS:
+            psutil.CONN_DELETE_TCB
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_contracts.py
@@ -0,0 +1,651 @@
+#!/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.
+
+"""Contracts tests. These tests mainly check API sanity in terms of
+returned types and APIs availability.
+Some of these are duplicates of tests test_system.py and test_process.py
+"""
+
+import errno
+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 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 get_kernel_version
+from psutil.tests import HAS_CONNECTIONS_UNIX
+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 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
+
+
+# ===================================================================
+# --- 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)
+
+    def test_PROCFS_PATH(self):
+        self.assertEqual(hasattr(psutil, "PROCFS_PATH"),
+                         LINUX or SUNOS or AIX)
+
+    def test_win_priority(self):
+        ae = self.assertEqual
+        ae(hasattr(psutil, "ABOVE_NORMAL_PRIORITY_CLASS"), WINDOWS)
+        ae(hasattr(psutil, "BELOW_NORMAL_PRIORITY_CLASS"), WINDOWS)
+        ae(hasattr(psutil, "HIGH_PRIORITY_CLASS"), WINDOWS)
+        ae(hasattr(psutil, "IDLE_PRIORITY_CLASS"), WINDOWS)
+        ae(hasattr(psutil, "NORMAL_PRIORITY_CLASS"), WINDOWS)
+        ae(hasattr(psutil, "REALTIME_PRIORITY_CLASS"), WINDOWS)
+
+    def test_linux_ioprio(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_rlimit(self):
+        ae = self.assertEqual
+        hasit = LINUX and get_kernel_version() >= (2, 6, 36)
+        ae(hasattr(psutil.Process, "rlimit"), hasit)
+        ae(hasattr(psutil, "RLIM_INFINITY"), hasit)
+        ae(hasattr(psutil, "RLIMIT_AS"), hasit)
+        ae(hasattr(psutil, "RLIMIT_CORE"), hasit)
+        ae(hasattr(psutil, "RLIMIT_CPU"), hasit)
+        ae(hasattr(psutil, "RLIMIT_DATA"), hasit)
+        ae(hasattr(psutil, "RLIMIT_FSIZE"), hasit)
+        ae(hasattr(psutil, "RLIMIT_LOCKS"), hasit)
+        ae(hasattr(psutil, "RLIMIT_MEMLOCK"), hasit)
+        ae(hasattr(psutil, "RLIMIT_NOFILE"), hasit)
+        ae(hasattr(psutil, "RLIMIT_NPROC"), hasit)
+        ae(hasattr(psutil, "RLIMIT_RSS"), hasit)
+        ae(hasattr(psutil, "RLIMIT_STACK"), hasit)
+
+        hasit = LINUX and get_kernel_version() >= (3, 0)
+        ae(hasattr(psutil, "RLIMIT_MSGQUEUE"), hasit)
+        ae(hasattr(psutil, "RLIMIT_NICE"), hasit)
+        ae(hasattr(psutil, "RLIMIT_RTPRIO"), hasit)
+        ae(hasattr(psutil, "RLIMIT_RTTIME"), hasit)
+        ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit)
+
+    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)
+
+    def test_sensors_temperatures(self):
+        self.assertEqual(hasattr(psutil, "sensors_temperatures"), LINUX)
+
+    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)
+
+    def test_proc_environ(self):
+        self.assertEqual(hasattr(psutil.Process, "environ"),
+                         LINUX or OSX or WINDOWS)
+
+    def test_proc_uids(self):
+        self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
+
+    def test_proc_gids(self):
+        self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
+
+    def test_proc_terminal(self):
+        self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX)
+
+    def test_proc_ionice(self):
+        self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS)
+
+    def test_proc_rlimit(self):
+        self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX)
+
+    def test_proc_io_counters(self):
+        hasit = hasattr(psutil.Process, "io_counters")
+        self.assertEqual(hasit, False if OSX or SUNOS else True)
+
+    def test_proc_num_fds(self):
+        self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX)
+
+    def test_proc_num_handles(self):
+        self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS)
+
+    def test_proc_cpu_affinity(self):
+        self.assertEqual(hasattr(psutil.Process, "cpu_affinity"),
+                         LINUX or WINDOWS or FREEBSD)
+
+    def test_proc_cpu_num(self):
+        self.assertEqual(hasattr(psutil.Process, "cpu_num"),
+                         LINUX or FREEBSD or SUNOS)
+
+    def test_proc_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))
+
+
+# ===================================================================
+# --- System API types
+# ===================================================================
+
+
+class TestSystem(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
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.proc = psutil.Process()
+
+    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 test_io_counters(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for k in psutil.disk_io_counters(perdisk=True):
+            self.assertIsInstance(k, str)
+
+    def test_disk_partitions(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for disk in psutil.disk_partitions():
+            self.assertIsInstance(disk.device, str)
+            self.assertIsInstance(disk.mountpoint, str)
+            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)
+    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)
+
+    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:
+                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():
+            self.assertIsInstance(ifname, str)
+
+    def test_net_io_counters(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for ifname, _ in psutil.net_io_counters(pernic=True).items():
+            self.assertIsInstance(ifname, str)
+
+    @unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
+    def test_sensors_fans(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for name, units in psutil.sensors_fans().items():
+            self.assertIsInstance(name, str)
+            for unit in units:
+                self.assertIsInstance(unit.label, str)
+
+    @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
+    def test_sensors_temperatures(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for name, units in psutil.sensors_temperatures().items():
+            self.assertIsInstance(name, str)
+            for unit in units:
+                self.assertIsInstance(unit.label, str)
+
+    def test_users(self):
+        # Duplicate of test_system.py. Keep it anyway.
+        for user in psutil.users():
+            self.assertIsInstance(user.name, str)
+            self.assertIsInstance(user.terminal, (str, type(None)))
+            self.assertIsInstance(user.host, (str, type(None)))
+            self.assertIsInstance(user.pid, (int, type(None)))
+
+
+# ===================================================================
+# --- Featch all processes test
+# ===================================================================
+
+
+class TestFetchAllProcesses(unittest.TestCase):
+    """Test which iterates over all running processes and performs
+    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
+        excluded_names = set([
+            'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
+            'as_dict', 'parent', 'children', 'memory_info_ex', 'oneshot',
+        ])
+        if LINUX and not HAS_RLIMIT:
+            excluded_names.add('rlimit')
+        attrs = []
+        for name in dir(psutil.Process):
+            if name.startswith("_"):
+                continue
+            if name in excluded_names:
+                continue
+            attrs.append(name)
+
+        default = object()
+        failures = []
+        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)
+
+        if failures:
+            self.fail(''.join(failures))
+
+        # we should always have a non-empty list, not including PID 0 etc.
+        # special cases.
+        assert valid_procs
+
+    def cmdline(self, ret, proc):
+        self.assertIsInstance(ret, list)
+        for part in ret:
+            self.assertIsInstance(part, str)
+
+    def exe(self, ret, proc):
+        self.assertIsInstance(ret, (str, type(None)))
+        if not ret:
+            self.assertEqual(ret, '')
+        else:
+            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
+                    assert os.access(ret, os.X_OK)
+
+    def pid(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        self.assertGreaterEqual(ret, 0)
+
+    def ppid(self, ret, proc):
+        self.assertIsInstance(ret, (int, long))
+        self.assertGreaterEqual(ret, 0)
+
+    def name(self, ret, proc):
+        self.assertIsInstance(ret, str)
+        # on AIX, "<exiting>" processes don't have names
+        if not AIX:
+            assert ret
+
+    def create_time(self, ret, proc):
+        self.assertIsInstance(ret, float)
+        try:
+            self.assertGreaterEqual(ret, 0)
+        except AssertionError:
+            # XXX
+            if OPENBSD and proc.status() == psutil.STATUS_ZOMBIE:
+                pass
+            else:
+                raise
+        # this can't be taken for granted on all platforms
+        # self.assertGreaterEqual(ret, psutil.boot_time())
+        # make sure returned value can be pretty printed
+        # with strftime
+        time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret))
+
+    def uids(self, ret, proc):
+        assert is_namedtuple(ret)
+        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)
+        # note: testing all gids as above seems not to be reliable for
+        # gid == 30 (nodoby); not sure why.
+        for gid in ret:
+            self.assertIsInstance(gid, int)
+            if not OSX 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)
+        assert ret
+        self.assertNotEqual(ret, '?')  # XXX
+        self.assertIn(ret, VALID_PROC_STATUSES)
+
+    def io_counters(self, ret, proc):
+        assert is_namedtuple(ret)
+        for field in ret:
+            self.assertIsInstance(field, (int, long))
+            if field != -1:
+                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.assertGreaterEqual(ret.ioclass, 0)
+            self.assertGreaterEqual(ret.value, 0)
+        else:
+            self.assertGreaterEqual(ret, 0)
+            self.assertIn(ret, (0, 1, 2))
+
+    def num_threads(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        self.assertGreaterEqual(ret, 1)
+
+    def threads(self, ret, proc):
+        self.assertIsInstance(ret, list)
+        for t in ret:
+            assert is_namedtuple(t)
+            self.assertGreaterEqual(t.id, 0)
+            self.assertGreaterEqual(t.user_time, 0)
+            self.assertGreaterEqual(t.system_time, 0)
+            for field in t:
+                self.assertIsInstance(field, (int, float))
+
+    def cpu_times(self, ret, proc):
+        assert is_namedtuple(ret)
+        for n in ret:
+            self.assertIsInstance(n, float)
+            self.assertGreaterEqual(n, 0)
+        # TODO: check ntuple fields
+
+    def cpu_percent(self, ret, proc):
+        self.assertIsInstance(ret, float)
+        assert 0.0 <= ret <= 100.0, ret
+
+    def cpu_num(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        if FREEBSD and ret == -1:
+            return
+        self.assertGreaterEqual(ret, 0)
+        if psutil.cpu_count() == 1:
+            self.assertEqual(ret, 0)
+        self.assertIn(ret, list(range(psutil.cpu_count())))
+
+    def memory_info(self, ret, proc):
+        assert is_namedtuple(ret)
+        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:
+            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)
+            self.assertGreaterEqual(ret.peak_pagefile, ret.pagefile)
+
+    def memory_full_info(self, ret, proc):
+        assert is_namedtuple(ret)
+        total = psutil.virtual_memory().total
+        for name in ret._fields:
+            value = getattr(ret, name)
+            self.assertIsInstance(value, (int, long))
+            self.assertGreaterEqual(value, 0, msg=(name, value))
+            self.assertLessEqual(value, total, msg=(name, value, total))
+
+        if LINUX:
+            self.assertGreaterEqual(ret.pss, ret.uss)
+
+    def open_files(self, ret, proc):
+        self.assertIsInstance(ret, list)
+        for f in ret:
+            self.assertIsInstance(f.fd, int)
+            self.assertIsInstance(f.path, str)
+            if WINDOWS:
+                self.assertEqual(f.fd, -1)
+            elif LINUX:
+                self.assertIsInstance(f.position, int)
+                self.assertIsInstance(f.mode, str)
+                self.assertIsInstance(f.flags, int)
+                self.assertGreaterEqual(f.position, 0)
+                self.assertIn(f.mode, ('r', 'w', 'a', 'r+', 'a+'))
+                self.assertGreater(f.flags, 0)
+            elif BSD and not f.path:
+                # XXX see: https://github.com/giampaolo/psutil/issues/595
+                continue
+            assert os.path.isabs(f.path), f
+            assert os.path.isfile(f.path), f
+
+    def num_fds(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        self.assertGreaterEqual(ret, 0)
+
+    def connections(self, ret, proc):
+        self.assertEqual(len(ret), len(set(ret)))
+        for conn in ret:
+            check_connection_ntuple(conn)
+
+    def cwd(self, ret, proc):
+        if ret:     # 'ret' can be None or empty
+            self.assertIsInstance(ret, str)
+            assert os.path.isabs(ret), ret
+            try:
+                st = os.stat(ret)
+            except OSError as err:
+                if WINDOWS and err.errno in \
+                        psutil._psplatform.ACCESS_DENIED_SET:
+                    pass
+                # directory has been removed in mean time
+                elif err.errno != errno.ENOENT:
+                    raise
+            else:
+                assert stat.S_ISDIR(st.st_mode)
+
+    def memory_percent(self, ret, proc):
+        self.assertIsInstance(ret, float)
+        assert 0 <= ret <= 100, ret
+
+    def is_running(self, ret, proc):
+        self.assertIsInstance(ret, bool)
+
+    def cpu_affinity(self, ret, proc):
+        self.assertIsInstance(ret, list)
+        assert ret != [], ret
+        cpus = range(psutil.cpu_count())
+        for n in ret:
+            self.assertIsInstance(n, int)
+            self.assertIn(n, cpus)
+
+    def terminal(self, ret, proc):
+        self.assertIsInstance(ret, (str, type(None)))
+        if ret is not None:
+            assert os.path.isabs(ret), ret
+            assert os.path.exists(ret), ret
+
+    def memory_maps(self, ret, proc):
+        for nt in ret:
+            self.assertIsInstance(nt.addr, str)
+            self.assertIsInstance(nt.perms, str)
+            self.assertIsInstance(nt.path, str)
+            for fname in nt._fields:
+                value = getattr(nt, fname)
+                if fname == 'path':
+                    if not value.startswith('['):
+                        assert os.path.isabs(nt.path), nt.path
+                        # 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
+                else:
+                    self.assertIsInstance(value, (int, long))
+                    self.assertGreaterEqual(value, 0)
+
+    def num_handles(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        self.assertGreaterEqual(ret, 0)
+
+    def nice(self, ret, proc):
+        self.assertIsInstance(ret, int)
+        if POSIX:
+            assert -20 <= ret <= 20, ret
+        else:
+            priorities = [getattr(psutil, x) for x in dir(psutil)
+                          if x.endswith('_PRIORITY_CLASS')]
+            self.assertIn(ret, priorities)
+
+    def num_ctx_switches(self, ret, proc):
+        assert is_namedtuple(ret)
+        for value in ret:
+            self.assertIsInstance(value, (int, long))
+            self.assertGreaterEqual(value, 0)
+
+    def rlimit(self, ret, proc):
+        self.assertIsInstance(ret, tuple)
+        self.assertEqual(len(ret), 2)
+        self.assertGreaterEqual(ret[0], -1)
+        self.assertGreaterEqual(ret[1], -1)
+
+    def environ(self, ret, proc):
+        self.assertIsInstance(ret, dict)
+        for k, v in ret.items():
+            self.assertIsInstance(k, str)
+            self.assertIsInstance(v, str)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_linux.py
@@ -0,0 +1,1910 @@
+#!/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.
+
+"""Linux specific tests."""
+
+from __future__ import division
+import collections
+import contextlib
+import errno
+import glob
+import io
+import os
+import pprint
+import re
+import shutil
+import socket
+import struct
+import tempfile
+import textwrap
+import time
+import warnings
+
+import psutil
+from psutil import LINUX
+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_RLIMIT
+from psutil.tests import MEMORY_TOLERANCE
+from psutil.tests import mock
+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 safe_rmpath
+from psutil.tests import sh
+from psutil.tests import skip_on_not_implemented
+from psutil.tests import TESTFN
+from psutil.tests import ThreadTask
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+from psutil.tests import which
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+SIOCGIFADDR = 0x8915
+SIOCGIFCONF = 0x8912
+SIOCGIFHWADDR = 0x8927
+if LINUX:
+    SECTOR_SIZE = 512
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+def get_ipv4_address(ifname):
+    import fcntl
+    ifname = ifname[:15]
+    if PY3:
+        ifname = bytes(ifname, 'ascii')
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    with contextlib.closing(s):
+        return socket.inet_ntoa(
+            fcntl.ioctl(s.fileno(),
+                        SIOCGIFADDR,
+                        struct.pack('256s', ifname))[20:24])
+
+
+def get_mac_address(ifname):
+    import fcntl
+    ifname = ifname[:15]
+    if PY3:
+        ifname = bytes(ifname, 'ascii')
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    with contextlib.closing(s):
+        info = fcntl.ioctl(
+            s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname))
+        if PY3:
+            def ord(x):
+                return x
+        else:
+            import __builtin__
+            ord = __builtin__.ord
+        return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
+
+
+def free_swap():
+    """Parse 'free' cmd and return swap memory's s total, used and free
+    values.
+    """
+    out = sh('free -b')
+    lines = out.split('\n')
+    for line in lines:
+        if line.startswith('Swap'):
+            _, total, used, free = line.split()
+            nt = collections.namedtuple('free', 'total used free')
+            return nt(int(total), int(used), int(free))
+    raise ValueError(
+        "can't find 'Swap' in 'free' output:\n%s" % '\n'.join(lines))
+
+
+def free_physmem():
+    """Parse 'free' cmd and return physical memory's total, used
+    and free values.
+    """
+    # Note: free can have 2 different formats, invalidating 'shared'
+    # 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')
+    lines = out.split('\n')
+    for line in lines:
+        if line.startswith('Mem'):
+            total, used, free, shared = \
+                [int(x) for x in line.split()[1:5]]
+            nt = collections.namedtuple(
+                'free', 'total used free shared output')
+            return nt(total, used, free, shared, out)
+    raise ValueError(
+        "can't find 'Mem' in 'free' output:\n%s" % '\n'.join(lines))
+
+
+def vmstat(stat):
+    out = sh("vmstat -s")
+    for line in out.split("\n"):
+        line = line.strip()
+        if stat in line:
+            return int(line.split(' ')[0])
+    raise ValueError("can't find %r in 'vmstat' output" % stat)
+
+
+def get_free_version_info():
+    out = sh("free -V").strip()
+    return tuple(map(int, out.split()[-1].split('.')))
+
+
+# =====================================================================
+# --- system virtual memory
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemVirtualMemory(unittest.TestCase):
+
+    def test_total(self):
+        # free_value = free_physmem().total
+        # psutil_value = psutil.virtual_memory().total
+        # self.assertEqual(free_value, psutil_value)
+        vmstat_value = vmstat('total memory') * 1024
+        psutil_value = psutil.virtual_memory().total
+        self.assertAlmostEqual(vmstat_value, psutil_value)
+
+    # Older versions of procps used slab memory to calculate used memory.
+    # This got changed in:
+    # https://gitlab.com/procps-ng/procps/commit/
+    #     05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+    @unittest.skipIf(LINUX and get_free_version_info() < (3, 3, 12),
+                     "old free version")
+    @retry_before_failing()
+    def test_used(self):
+        free = free_physmem()
+        free_value = free.used
+        psutil_value = psutil.virtual_memory().used
+        self.assertAlmostEqual(
+            free_value, psutil_value, delta=MEMORY_TOLERANCE,
+            msg='%s %s \n%s' % (free_value, psutil_value, free.output))
+
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    @retry_before_failing()
+    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()
+    def test_buffers(self):
+        vmstat_value = vmstat('buffer memory') * 1024
+        psutil_value = psutil.virtual_memory().buffers
+        self.assertAlmostEqual(
+            vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
+
+    # https://travis-ci.org/giampaolo/psutil/jobs/226719664
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    @retry_before_failing()
+    def test_active(self):
+        vmstat_value = vmstat('active memory') * 1024
+        psutil_value = psutil.virtual_memory().active
+        self.assertAlmostEqual(
+            vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
+
+    # https://travis-ci.org/giampaolo/psutil/jobs/227242952
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    @retry_before_failing()
+    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()
+    def test_shared(self):
+        free = free_physmem()
+        free_value = free.shared
+        if free_value == 0:
+            raise unittest.SkipTest("free does not support 'shared' column")
+        psutil_value = psutil.virtual_memory().shared
+        self.assertAlmostEqual(
+            free_value, psutil_value, delta=MEMORY_TOLERANCE,
+            msg='%s %s \n%s' % (free_value, psutil_value, free.output))
+
+    @retry_before_failing()
+    def test_available(self):
+        # "free" output format has changed at some point:
+        # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098
+        out = sh("free -b")
+        lines = out.split('\n')
+        if 'available' not in lines[0]:
+            raise unittest.SkipTest("free does not support 'available' column")
+        else:
+            free_value = int(lines[1].split()[-1])
+            psutil_value = psutil.virtual_memory().available
+            self.assertAlmostEqual(
+                free_value, psutil_value, delta=MEMORY_TOLERANCE,
+                msg='%s %s \n%s' % (free_value, psutil_value, out))
+
+    def test_warnings_on_misses(self):
+        # 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 warnings.catch_warnings(record=True) as ws:
+                warnings.simplefilter("always")
+                ret = psutil.virtual_memory()
+                assert m.called
+                self.assertEqual(len(ws), 1)
+                w = ws[0]
+                assert w.filename.endswith('psutil/_pslinux.py')
+                self.assertIn(
+                    "memory stats couldn't be determined", str(w.message))
+                self.assertIn("cached", str(w.message))
+                self.assertIn("shared", str(w.message))
+                self.assertIn("active", str(w.message))
+                self.assertIn("inactive", str(w.message))
+                self.assertIn("buffers", str(w.message))
+                self.assertIn("available", str(w.message))
+                self.assertEqual(ret.cached, 0)
+                self.assertEqual(ret.active, 0)
+                self.assertEqual(ret.inactive, 0)
+                self.assertEqual(ret.shared, 0)
+                self.assertEqual(ret.buffers, 0)
+                self.assertEqual(ret.available, 0)
+
+    def test_avail_old_percent(self):
+        # Make sure that our calculation of avail mem for old kernels
+        # is off by max 10%.
+        from psutil._pslinux import calculate_avail_vmem
+        from psutil._pslinux import open_binary
+
+        mems = {}
+        with open_binary('/proc/meminfo') as f:
+            for line in f:
+                fields = line.split()
+                mems[fields[0]] = int(fields[1]) * 1024
+
+        a = calculate_avail_vmem(mems)
+        if b'MemAvailable:' in mems:
+            b = mems[b'MemAvailable:']
+            diff_percent = abs(a - b) / a * 100
+            self.assertLess(diff_percent, 10)
+
+    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 warnings.catch_warnings(record=True) as ws:
+                ret = psutil.virtual_memory()
+            assert m.called
+            self.assertEqual(ret.available, 6574984 * 1024)
+            w = ws[0]
+            self.assertIn(
+                "inactive memory stats couldn't be determined", str(w.message))
+
+    def test_avail_old_missing_fields(self):
+        # 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("""\
+                    Active:          9444728 kB
+                    Active(anon):    6145416 kB
+                    Buffers:          287952 kB
+                    Cached:          4818144 kB
+                    Inactive(file):  1578132 kB
+                    Inactive(anon):   574764 kB
+                    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:
+            with warnings.catch_warnings(record=True) as ws:
+                ret = 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))
+
+    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("""\
+                    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
+                    MemFree:         2057400 kB
+                    MemTotal:       16325648 kB
+                    Shmem:            577588 kB
+                    SReclaimable:     346648 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()
+            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))
+
+
+# =====================================================================
+# --- system swap memory
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemSwapMemory(unittest.TestCase):
+
+    @staticmethod
+    def meminfo_has_swap_info():
+        """Return True if /proc/meminfo provides swap metrics."""
+        with open("/proc/meminfo") as f:
+            data = f.read()
+        return 'SwapTotal:' in data and 'SwapFree:' in data
+
+    def test_total(self):
+        free_value = free_swap().total
+        psutil_value = psutil.swap_memory().total
+        return self.assertAlmostEqual(
+            free_value, psutil_value, delta=MEMORY_TOLERANCE)
+
+    @retry_before_failing()
+    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()
+    def test_free(self):
+        free_value = free_swap().free
+        psutil_value = psutil.swap_memory().free
+        return self.assertAlmostEqual(
+            free_value, psutil_value, delta=MEMORY_TOLERANCE)
+
+    def test_missing_sin_sout(self):
+        with mock.patch('psutil._pslinux.open', create=True) as m:
+            with warnings.catch_warnings(record=True) as ws:
+                warnings.simplefilter("always")
+                ret = psutil.swap_memory()
+                assert m.called
+                self.assertEqual(len(ws), 1)
+                w = ws[0]
+                assert w.filename.endswith('psutil/_pslinux.py')
+                self.assertIn(
+                    "'sin' and 'sout' swap memory stats couldn't "
+                    "be determined", str(w.message))
+                self.assertEqual(ret.sin, 0)
+                self.assertEqual(ret.sout, 0)
+
+    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 warnings.catch_warnings(record=True) as ws:
+                warnings.simplefilter("always")
+                ret = psutil.swap_memory()
+                assert m.called
+                self.assertEqual(len(ws), 1)
+                w = ws[0]
+                assert w.filename.endswith('psutil/_pslinux.py')
+                self.assertIn(
+                    "'sin' and 'sout' swap memory stats couldn't "
+                    "be determined and were set to 0",
+                    str(w.message))
+                self.assertEqual(ret.sin, 0)
+                self.assertEqual(ret.sout, 0)
+
+    def test_meminfo_against_sysinfo(self):
+        # Make sure the content of /proc/meminfo about swap memory
+        # matches sysinfo() syscall, see:
+        # https://github.com/giampaolo/psutil/issues/1015
+        if not self.meminfo_has_swap_info():
+            return unittest.skip("/proc/meminfo has no swap metrics")
+        with mock.patch('psutil._pslinux.cext.linux_sysinfo') as m:
+            swap = psutil.swap_memory()
+        assert not m.called
+        import psutil._psutil_linux as cext
+        _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
+        total *= unit_multiplier
+        free *= unit_multiplier
+        self.assertEqual(swap.total, total)
+        self.assertEqual(swap.free, free)
+
+    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:
+            psutil.swap_memory()
+            assert m.called
+
+
+# =====================================================================
+# --- system CPU
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemCPU(unittest.TestCase):
+
+    @unittest.skipIf(TRAVIS, "unknown failure on travis")
+    def test_cpu_times(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('.')))
+        if kernel_ver_info >= (2, 6, 11):
+            self.assertIn('steal', fields)
+        else:
+            self.assertNotIn('steal', fields)
+        if kernel_ver_info >= (2, 6, 24):
+            self.assertIn('guest', fields)
+        else:
+            self.assertNotIn('guest', fields)
+        if kernel_ver_info >= (3, 2, 0):
+            self.assertIn('guest_nice', fields)
+        else:
+            self.assertNotIn('guest_nice', fields)
+
+    @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):
+        with open("/sys/devices/system/cpu/online") as f:
+            value = f.read().strip()
+        if "-" in str(value):
+            value = int(value.split('-')[1]) + 1
+            self.assertEqual(psutil.cpu_count(), value)
+
+    @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):
+        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):
+        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):
+        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):
+        import psutil._pslinux
+        original = psutil._pslinux.cpu_count_logical()
+        # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in
+        # order to cause the parsing of /proc/cpuinfo and /proc/stat.
+        with mock.patch(
+                'psutil._pslinux.os.sysconf', side_effect=ValueError) as m:
+            self.assertEqual(psutil._pslinux.cpu_count_logical(), original)
+            assert m.called
+
+            # 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:
+                self.assertIsNone(psutil._pslinux.cpu_count_logical())
+                self.assertEqual(m.call_count, 2)
+                # /proc/stat should be the last one
+                self.assertEqual(m.call_args[0][0], '/proc/stat')
+
+            # Let's push this a bit further and make sure /proc/cpuinfo
+            # parsing works as expected.
+            with open('/proc/cpuinfo', 'rb') as f:
+                cpuinfo_data = f.read()
+            fake_file = io.BytesIO(cpuinfo_data)
+            with mock.patch('psutil._pslinux.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):
+                self.assertEqual(psutil._pslinux.cpu_count_logical(), original)
+
+    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(TRAVIS, "fails on Travis")
+    @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
+    def test_cpu_freq_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 []
+            else:
+                flags.append(None)
+                return orig_glob(pattern)
+
+        flags = []
+        orig_glob = glob.glob
+        with mock.patch("psutil._pslinux.glob.glob", side_effect=glob_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 open_mock(name, *args, **kwargs):
+            if name.endswith('/scaling_cur_freq'):
+                return io.BytesIO(b"500000")
+            elif name.endswith('/scaling_min_freq'):
+                return io.BytesIO(b"600000")
+            elif name.endswith('/scaling_max_freq'):
+                return io.BytesIO(b"700000")
+            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=['/sys/devices/system/cpu/cpufreq/policy0']):
+                freq = psutil.cpu_freq()
+                self.assertEqual(freq.current, 500.0)
+                self.assertEqual(freq.min, 600.0)
+                self.assertEqual(freq.max, 700.0)
+
+    @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
+    def test_cpu_freq_emulate_multi_cpu(self):
+        def open_mock(name, *args, **kwargs):
+            if name.endswith('/scaling_cur_freq'):
+                return io.BytesIO(b"100000")
+            elif name.endswith('/scaling_min_freq'):
+                return io.BytesIO(b"200000")
+            elif name.endswith('/scaling_max_freq'):
+                return io.BytesIO(b"300000")
+            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)
+
+    @unittest.skipIf(TRAVIS, "fails on Travis")
+    @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
+    def test_cpu_freq_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, "")
+            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
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemCPUStats(unittest.TestCase):
+
+    @unittest.skipIf(TRAVIS, "fails on Travis")
+    def test_ctx_switches(self):
+        vmstat_value = vmstat("context switches")
+        psutil_value = psutil.cpu_stats().ctx_switches
+        self.assertAlmostEqual(vmstat_value, psutil_value, delta=500)
+
+    @unittest.skipIf(TRAVIS, "fails on Travis")
+    def test_interrupts(self):
+        vmstat_value = vmstat("interrupts")
+        psutil_value = psutil.cpu_stats().interrupts
+        self.assertAlmostEqual(vmstat_value, psutil_value, delta=500)
+
+
+# =====================================================================
+# --- system network
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemNetwork(unittest.TestCase):
+
+    def test_net_if_addrs_ips(self):
+        for name, addrs in psutil.net_if_addrs().items():
+            for addr in addrs:
+                if addr.family == psutil.AF_LINK:
+                    self.assertEqual(addr.address, get_mac_address(name))
+                elif addr.family == socket.AF_INET:
+                    self.assertEqual(addr.address, get_ipv4_address(name))
+                # TODO: test for AF_INET6 family
+
+    def test_net_if_stats(self):
+        for name, stats in psutil.net_if_stats().items():
+            try:
+                out = sh("ifconfig %s" % name)
+            except RuntimeError:
+                pass
+            else:
+                # Not always reliable.
+                # self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
+                self.assertEqual(stats.mtu,
+                                 int(re.findall(r'(?i)MTU[: ](\d+)', out)[0]))
+
+    @retry_before_failing()
+    def test_net_io_counters(self):
+        def ifconfig(nic):
+            ret = {}
+            out = sh("ifconfig %s" % name)
+            ret['packets_recv'] = int(
+                re.findall(r'RX packets[: ](\d+)', out)[0])
+            ret['packets_sent'] = int(
+                re.findall(r'TX packets[: ](\d+)', out)[0])
+            ret['errin'] = int(re.findall(r'errors[: ](\d+)', out)[0])
+            ret['errout'] = int(re.findall(r'errors[: ](\d+)', out)[1])
+            ret['dropin'] = int(re.findall(r'dropped[: ](\d+)', out)[0])
+            ret['dropout'] = int(re.findall(r'dropped[: ](\d+)', out)[1])
+            ret['bytes_recv'] = int(
+                re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
+            ret['bytes_sent'] = int(
+                re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
+            return ret
+
+        nio = psutil.net_io_counters(pernic=True, nowrap=False)
+        for name, stats in nio.items():
+            try:
+                ifconfig_ret = ifconfig(name)
+            except RuntimeError:
+                continue
+            self.assertAlmostEqual(
+                stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5)
+            self.assertAlmostEqual(
+                stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5)
+            self.assertAlmostEqual(
+                stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024)
+            self.assertAlmostEqual(
+                stats.packets_sent, ifconfig_ret['packets_sent'], delta=1024)
+            self.assertAlmostEqual(
+                stats.errin, ifconfig_ret['errin'], delta=10)
+            self.assertAlmostEqual(
+                stats.errout, ifconfig_ret['errout'], delta=10)
+            self.assertAlmostEqual(
+                stats.dropin, ifconfig_ret['dropin'], delta=10)
+            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))
+
+    @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):
+        # see: https://github.com/giampaolo/psutil/issues/623
+        try:
+            s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+            self.addCleanup(s.close)
+            s.bind(("::1", 0))
+        except socket.error:
+            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:
+            psutil.net_connections(kind='unix')
+            assert m.called
+
+
+# =====================================================================
+# --- system disk
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSystemDisks(unittest.TestCase):
+
+    @unittest.skipIf(not hasattr(os, 'statvfs'), "os.statvfs() not available")
+    @skip_on_not_implemented()
+    def test_disk_partitions_and_usage(self):
+        # test psutil.disk_usage() and psutil.disk_partitions()
+        # against "df -a"
+        def df(path):
+            out = sh('df -P -B 1 "%s"' % path).strip()
+            lines = out.split('\n')
+            lines.pop(0)
+            line = lines.pop(0)
+            dev, total, used, free = line.split()[:4]
+            if dev == 'none':
+                dev = ''
+            total, used, free = int(total), int(used), int(free)
+            return dev, total, used, free
+
+        for part in psutil.disk_partitions(all=False):
+            usage = psutil.disk_usage(part.mountpoint)
+            dev, total, used, free = df(part.mountpoint)
+            self.assertEqual(usage.total, total)
+            # 10 MB tollerance
+            if abs(usage.free - free) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % (usage.free, free))
+            if abs(usage.used - used) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % (usage.used, used))
+
+    def test_disk_partitions_mocked(self):
+        # Test that ZFS partitions are returned.
+        with open("/proc/filesystems", "r") as f:
+            data = f.read()
+        if 'zfs' in data:
+            for part in psutil.disk_partitions():
+                if part.fstype == 'zfs':
+                    break
+            else:
+                self.fail("couldn't find any ZFS partition")
+        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',
+                            return_value=fake_file, create=True) as m1:
+                with mock.patch(
+                        'psutil._pslinux.cext.disk_partitions',
+                        return_value=[('/dev/sdb3', '/', 'zfs', 'rw')]) as m2:
+                    ret = psutil.disk_partitions()
+                    assert m1.called
+                    assert m2.called
+                    assert ret
+                    self.assertEqual(ret[0].fstype, 'zfs')
+
+    def test_disk_io_counters_kernel_2_4_mocked(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
+
+                       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):
+        # 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
+
+                       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):
+        # 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
+
+                       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)
+
+        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)
+
+            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)
+
+
+# =====================================================================
+# --- misc
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestMisc(unittest.TestCase):
+
+    def test_boot_time(self):
+        vmstat_value = vmstat('boot time')
+        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):
+        my_procfs = tempfile.mkdtemp()
+
+        with open(os.path.join(my_procfs, 'stat'), 'w') as f:
+            f.write('cpu   0 0 0 0 0 0 0 0 0 0\n')
+            f.write('cpu0  0 0 0 0 0 0 0 0 0 0\n')
+            f.write('cpu1  0 0 0 0 0 0 0 0 0 0\n')
+
+        try:
+            orig_open = open
+
+            def open_mock(name, *args, **kwargs):
+                if name.startswith('/proc'):
+                    raise IOError(errno.ENOENT, 'rejecting access for test')
+                return orig_open(name, *args, **kwargs)
+
+            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)
+                self.assertRaises(IOError, psutil.cpu_percent)
+                self.assertRaises(IOError, psutil.cpu_percent, percpu=True)
+                self.assertRaises(IOError, psutil.cpu_times_percent)
+                self.assertRaises(
+                    IOError, psutil.cpu_times_percent, percpu=True)
+
+                psutil.PROCFS_PATH = my_procfs
+
+                self.assertEqual(psutil.cpu_percent(), 0)
+                self.assertEqual(sum(psutil.cpu_times_percent()), 0)
+
+                # since we don't know the number of CPUs at import time,
+                # we awkwardly say there are none until the second call
+                per_cpu_percent = psutil.cpu_percent(percpu=True)
+                self.assertEqual(sum(per_cpu_percent), 0)
+
+                # ditto awkward length
+                per_cpu_times_percent = psutil.cpu_times_percent(percpu=True)
+                self.assertEqual(sum(map(sum, per_cpu_times_percent)), 0)
+
+                # much user, very busy
+                with open(os.path.join(my_procfs, 'stat'), 'w') as f:
+                    f.write('cpu   1 0 0 0 0 0 0 0 0 0\n')
+                    f.write('cpu0  1 0 0 0 0 0 0 0 0 0\n')
+                    f.write('cpu1  1 0 0 0 0 0 0 0 0 0\n')
+
+                self.assertNotEqual(psutil.cpu_percent(), 0)
+                self.assertNotEqual(
+                    sum(psutil.cpu_percent(percpu=True)), 0)
+                self.assertNotEqual(sum(psutil.cpu_times_percent()), 0)
+                self.assertNotEqual(
+                    sum(map(sum, psutil.cpu_times_percent(percpu=True))), 0)
+        finally:
+            shutil.rmtree(my_procfs)
+            reload_module(psutil)
+
+        self.assertEqual(psutil.PROCFS_PATH, '/proc')
+
+    def test_boot_time_mocked(self):
+        with mock.patch('psutil._pslinux.open', create=True) as m:
+            self.assertRaises(
+                RuntimeError,
+                psutil._pslinux.boot_time)
+            assert m.called
+
+    def test_users_mocked(self):
+        # Make sure ':0' and ':0.0' (returned by C ext) are converted
+        # to 'localhost'.
+        with mock.patch('psutil._pslinux.cext.users',
+                        return_value=[('giampaolo', 'pts/2', ':0',
+                                       1436573184.0, True, 2)]) as m:
+            self.assertEqual(psutil.users()[0].host, 'localhost')
+            assert m.called
+        with mock.patch('psutil._pslinux.cext.users',
+                        return_value=[('giampaolo', 'pts/2', ':0.0',
+                                       1436573184.0, True, 2)]) as m:
+            self.assertEqual(psutil.users()[0].host, 'localhost')
+            assert m.called
+        # ...otherwise it should be returned as-is
+        with mock.patch('psutil._pslinux.cext.users',
+                        return_value=[('giampaolo', 'pts/2', 'foo',
+                                       1436573184.0, True, 2)]) as m:
+            self.assertEqual(psutil.users()[0].host, 'foo')
+            assert m.called
+
+    def test_procfs_path(self):
+        tdir = tempfile.mkdtemp()
+        try:
+            psutil.PROCFS_PATH = tdir
+            self.assertRaises(IOError, psutil.virtual_memory)
+            self.assertRaises(IOError, psutil.cpu_times)
+            self.assertRaises(IOError, psutil.cpu_times, percpu=True)
+            self.assertRaises(IOError, psutil.boot_time)
+            # self.assertRaises(IOError, psutil.pids)
+            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_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
+        # - Process(tid) is supposed to work
+        # - pids() should not return the TID
+        # See: https://github.com/giampaolo/psutil/issues/687
+        t = ThreadTask()
+        t.start()
+        try:
+            p = psutil.Process()
+            tid = p.threads()[1].id
+            assert not psutil.pid_exists(tid), tid
+            pt = psutil.Process(tid)
+            pt.as_dict()
+            self.assertNotIn(tid, psutil.pids())
+        finally:
+            t.stop()
+
+    def test_pid_exists_no_proc_status(self):
+        # 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):
+            assert psutil.pid_exists(os.getpid())
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+@unittest.skipIf(not HAS_BATTERY, "no battery")
+class TestSensorsBattery(unittest.TestCase):
+
+    @unittest.skipIf(not which("acpi"), "acpi utility not available")
+    def test_percent(self):
+        out = sh("acpi -b")
+        acpi_value = int(out.split(",")[1].strip().replace('%', ''))
+        psutil_value = psutil.sensors_battery().percent
+        self.assertAlmostEqual(acpi_value, psutil_value, delta=1)
+
+    @unittest.skipIf(not which("acpi"), "acpi utility not available")
+    def test_power_plugged(self):
+        out = sh("acpi -b")
+        if 'unknown' in out.lower():
+            return unittest.skip("acpi output not reliable")
+        if 'discharging at zero rate' in out:
+            plugged = True
+        else:
+            plugged = "Charging" in out.split('\n')[0]
+        self.assertEqual(psutil.sensors_battery().power_plugged, plugged)
+
+    def test_emulate_power_plugged(self):
+        # Pretend the AC power cable is connected.
+        def open_mock(name, *args, **kwargs):
+            if name.endswith("AC0/online") or name.endswith("AC/online"):
+                return io.BytesIO(b"1")
+            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().power_plugged, True)
+            self.assertEqual(
+                psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED)
+            assert m.called
+
+    def test_emulate_power_plugged_2(self):
+        # Same as above but pretend /AC0/online does not exist in which
+        # case code relies on /status file.
+        def open_mock(name, *args, **kwargs):
+            if name.endswith("AC0/online") or name.endswith("AC/online"):
+                raise IOError(errno.ENOENT, "")
+            elif name.endswith("/status"):
+                return io.StringIO(u("charging"))
+            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().power_plugged, True)
+            assert m.called
+
+    def test_emulate_power_not_plugged(self):
+        # Pretend the AC power cable is not connected.
+        def open_mock(name, *args, **kwargs):
+            if name.endswith("AC0/online") or name.endswith("AC/online"):
+                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:
+            self.assertEqual(psutil.sensors_battery().power_plugged, False)
+            assert m.called
+
+    def test_emulate_power_not_plugged_2(self):
+        # Same as above but pretend /AC0/online does not exist in which
+        # case code relies on /status file.
+        def open_mock(name, *args, **kwargs):
+            if name.endswith("AC0/online") or name.endswith("AC/online"):
+                raise IOError(errno.ENOENT, "")
+            elif name.endswith("/status"):
+                return io.StringIO(u("discharging"))
+            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().power_plugged, False)
+            assert m.called
+
+    def test_emulate_power_undetermined(self):
+        # Pretend we can't know whether the AC power cable not
+        # connected (assert fallback to False).
+        def open_mock(name, *args, **kwargs):
+            if name.startswith("/sys/class/power_supply/AC0/online") or \
+                    name.startswith("/sys/class/power_supply/AC/online"):
+                raise IOError(errno.ENOENT, "")
+            elif name.startswith("/sys/class/power_supply/BAT0/status"):
+                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) as m:
+            self.assertIsNone(psutil.sensors_battery().power_plugged)
+            assert m.called
+
+    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
+
+    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:
+            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
+
+    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
+
+
+@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 open_mock(name, *args, **kwargs):
+            if name.endswith('/name'):
+                return io.StringIO(u("name"))
+            elif name.endswith('/temp1_label'):
+                return io.StringIO(u("label"))
+            elif name.endswith('/temp1_input'):
+                return io.BytesIO(b"30000")
+            elif name.endswith('/temp1_max'):
+                return io.BytesIO(b"40000")
+            elif name.endswith('/temp1_crit'):
+                return io.BytesIO(b"50000")
+            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=['/sys/class/hwmon/hwmon0/temp1']):
+                temp = psutil.sensors_temperatures()['name'][0]
+                self.assertEqual(temp.label, 'label')
+                self.assertEqual(temp.current, 30.0)
+                self.assertEqual(temp.high, 40.0)
+                self.assertEqual(temp.critical, 50.0)
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestSensorsFans(unittest.TestCase):
+
+    def test_emulate_data(self):
+        def open_mock(name, *args, **kwargs):
+            if name.endswith('/name'):
+                return io.StringIO(u("name"))
+            elif name.endswith('/fan1_label'):
+                return io.StringIO(u("label"))
+            elif name.endswith('/fan1_input'):
+                return io.StringIO(u("2000"))
+            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=['/sys/class/hwmon/hwmon2/fan1']):
+                fan = psutil.sensors_fans()['name'][0]
+                self.assertEqual(fan.label, 'label')
+                self.assertEqual(fan.current, 2000)
+
+
+# =====================================================================
+# --- test process
+# =====================================================================
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestProcess(unittest.TestCase):
+
+    def setUp(self):
+        safe_rmpath(TESTFN)
+
+    tearDown = setUp
+
+    def test_memory_full_info(self):
+        src = textwrap.dedent("""
+            import time
+            with open("%s", "w") as f:
+                time.sleep(10)
+            """ % TESTFN)
+        sproc = pyrun(src)
+        self.addCleanup(reap_children)
+        call_until(lambda: os.listdir('.'), "'%s' not in ret" % TESTFN)
+        p = psutil.Process(sproc.pid)
+        time.sleep(.1)
+        mem = p.memory_full_info()
+        maps = p.memory_maps(grouped=False)
+        self.assertAlmostEqual(
+            mem.uss, sum([x.private_dirty + x.private_clean for x in maps]),
+            delta=4096)
+        self.assertAlmostEqual(
+            mem.pss, sum([x.pss for x in maps]), delta=4096)
+        self.assertAlmostEqual(
+            mem.swap, sum([x.swap for x in maps]), delta=4096)
+
+    # On PYPY file descriptors are not closed fast enough.
+    @unittest.skipIf(PYPY, "unreliable on PYPY")
+    def test_open_files_mode(self):
+        def get_test_file():
+            p = psutil.Process()
+            giveup_at = time.time() + 2
+            while True:
+                for file in p.open_files():
+                    if file.path == os.path.abspath(TESTFN):
+                        return file
+                    elif time.time() > giveup_at:
+                        break
+            raise RuntimeError("timeout looking for test file")
+
+        #
+        with open(TESTFN, "w"):
+            self.assertEqual(get_test_file().mode, "w")
+        with open(TESTFN, "r"):
+            self.assertEqual(get_test_file().mode, "r")
+        with open(TESTFN, "a"):
+            self.assertEqual(get_test_file().mode, "a")
+        #
+        with open(TESTFN, "r+"):
+            self.assertEqual(get_test_file().mode, "r+")
+        with open(TESTFN, "w+"):
+            self.assertEqual(get_test_file().mode, "r+")
+        with open(TESTFN, "a+"):
+            self.assertEqual(get_test_file().mode, "a+")
+        # note: "x" bit is not supported
+        if PY3:
+            safe_rmpath(TESTFN)
+            with open(TESTFN, "x"):
+                self.assertEqual(get_test_file().mode, "w")
+            safe_rmpath(TESTFN)
+            with open(TESTFN, "x+"):
+                self.assertEqual(get_test_file().mode, "r+")
+
+    def test_open_files_file_gone(self):
+        # simulates a file which gets deleted during open_files()
+        # execution
+        p = psutil.Process()
+        files = p.open_files()
+        with tempfile.NamedTemporaryFile():
+            # give the kernel some time to see the new file
+            call_until(p.open_files, "len(ret) != %i" % len(files))
+            with mock.patch('psutil._pslinux.os.readlink',
+                            side_effect=OSError(errno.ENOENT, "")) as m:
+                files = p.open_files()
+                assert not files
+                assert m.called
+            # also simulate the case where os.readlink() returns EINVAL
+            # in which case psutil is supposed to 'continue'
+            with mock.patch('psutil._pslinux.os.readlink',
+                            side_effect=OSError(errno.EINVAL, "")) as m:
+                self.assertEqual(p.open_files(), [])
+                assert m.called
+
+    def test_open_files_fd_gone(self):
+        # Simulate a case where /proc/{pid}/fdinfo/{fd} disappears
+        # while iterating through fds.
+        # https://travis-ci.org/giampaolo/psutil/jobs/225694530
+        p = psutil.Process()
+        files = p.open_files()
+        with tempfile.NamedTemporaryFile():
+            # give the kernel some time to see the new file
+            call_until(p.open_files, "len(ret) != %i" % len(files))
+            patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+            with mock.patch(patch_point,
+                            side_effect=IOError(errno.ENOENT, "")) as m:
+                files = p.open_files()
+                assert not files
+                assert m.called
+
+    # --- mocked tests
+
+    def test_terminal_mocked(self):
+        with mock.patch('psutil._pslinux._psposix.get_terminal_map',
+                        return_value={}) as m:
+            self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal())
+            assert m.called
+
+    # TODO: re-enable this test.
+    # def test_num_ctx_switches_mocked(self):
+    #     with mock.patch('psutil._pslinux.open', create=True) as m:
+    #         self.assertRaises(
+    #             NotImplementedError,
+    #             psutil._pslinux.Process(os.getpid()).num_ctx_switches)
+    #         assert m.called
+
+    def test_cmdline_mocked(self):
+        # 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',
+                        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',
+                        return_value=fake_file, create=True) as m:
+            self.assertEqual(p.cmdline(), ['foo', 'bar', ''])
+            assert m.called
+
+    def test_cmdline_spaces_mocked(self):
+        # see: https://github.com/giampaolo/psutil/issues/1179
+        p = psutil.Process()
+        fake_file = io.StringIO(u('foo bar '))
+        with mock.patch('psutil._pslinux.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',
+                        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)'):
+            self.assertEqual(psutil.Process().exe(), "/home/foo")
+            self.assertEqual(psutil.Process().cwd(), "/home/foo")
+
+    def test_threads_mocked(self):
+        # Test the case where os.listdir() returns a file (thread)
+        # which no longer exists by the time we open() it (race
+        # condition). threads() is supposed to ignore that instead
+        # of raising NSP.
+        def open_mock(name, *args, **kwargs):
+            if name.startswith('/proc/%s/task' % 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:
+            ret = psutil.Process().threads()
+            assert m.called
+            self.assertEqual(ret, [])
+
+        # ...but if it bumps into something != ENOENT we want an
+        # exception.
+        def open_mock(name, *args, **kwargs):
+            if name.startswith('/proc/%s/task' % os.getpid()):
+                raise IOError(errno.EPERM, "")
+            else:
+                return orig_open(name, *args, **kwargs)
+
+        with mock.patch(patch_point, side_effect=open_mock):
+            self.assertRaises(psutil.AccessDenied, psutil.Process().threads)
+
+    def test_exe_mocked(self):
+        with mock.patch('psutil._pslinux.readlink',
+                        side_effect=OSError(errno.ENOENT, "")) as m1:
+            with mock.patch('psutil.Process.cmdline',
+                            side_effect=psutil.AccessDenied(0, "")) as m2:
+                # No such file error; might be raised also if /proc/pid/exe
+                # path actually exists for system processes with low pids
+                # (about 0-20). In this case psutil is supposed to return
+                # an empty string.
+                ret = psutil.Process().exe()
+                assert m1.called
+                assert m2.called
+                self.assertEqual(ret, "")
+
+                # ...but if /proc/pid no longer exist we're supposed to treat
+                # it as an alias for zombie process
+                with mock.patch('psutil._pslinux.os.path.lexists',
+                                return_value=False):
+                    self.assertRaises(
+                        psutil.ZombieProcess, psutil.Process().exe)
+
+    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:
+            p = psutil.Process()
+            with self.assertRaises(IOError) as err:
+                p.memory_maps()
+            self.assertEqual(err.exception.errno, errno.ENOENT)
+            assert m.called
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_zombie(self):
+        # Emulate a case where rlimit() raises ENOSYS, which may
+        # happen in case of zombie process:
+        # https://travis-ci.org/giampaolo/psutil/jobs/51368273
+        with mock.patch("psutil._pslinux.cext.linux_prlimit",
+                        side_effect=OSError(errno.ENOSYS, "")) as m:
+            p = psutil.Process()
+            p.name()
+            with self.assertRaises(psutil.ZombieProcess) as exc:
+                p.rlimit(psutil.RLIMIT_NOFILE)
+            assert m.called
+        self.assertEqual(exc.exception.pid, p.pid)
+        self.assertEqual(exc.exception.name, p.name())
+
+    def test_cwd_zombie(self):
+        with mock.patch("psutil._pslinux.os.readlink",
+                        side_effect=OSError(errno.ENOENT, "")) as m:
+            p = psutil.Process()
+            p.name()
+            with self.assertRaises(psutil.ZombieProcess) as exc:
+                p.cwd()
+            assert m.called
+        self.assertEqual(exc.exception.pid, p.pid)
+        self.assertEqual(exc.exception.name, p.name())
+
+    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):
+            p = psutil.Process()
+            self.assertEqual(p.name(), 'cat')
+            self.assertEqual(p.status(), psutil.STATUS_ZOMBIE)
+            self.assertEqual(p.ppid(), 1)
+            self.assertEqual(
+                p.create_time(), 6 / CLOCK_TICKS + psutil.boot_time())
+            cpu = p.cpu_times()
+            self.assertEqual(cpu.user, 2 / CLOCK_TICKS)
+            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(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):
+            p = psutil.Process()
+            self.assertEqual(p.num_ctx_switches().voluntary, 12)
+            self.assertEqual(p.num_ctx_switches().involuntary, 13)
+            self.assertEqual(p.num_threads(), 66)
+            uids = p.uids()
+            self.assertEqual(uids.real, 1000)
+            self.assertEqual(uids.effective, 1001)
+            self.assertEqual(uids.saved, 1002)
+            gids = p.gids()
+            self.assertEqual(gids.real, 1004)
+            self.assertEqual(gids.effective, 1005)
+            self.assertEqual(gids.saved, 1006)
+            self.assertEqual(p._proc._get_eligible_cpus(), list(range(0, 8)))
+
+
+@unittest.skipIf(not LINUX, "LINUX only")
+class TestProcessAgainstStatus(unittest.TestCase):
+    """/proc/pid/stat and /proc/pid/status have many values in common.
+    Whenever possible, psutil uses /proc/pid/stat (it's faster).
+    For all those cases we check that the value found in
+    /proc/pid/stat (by psutil) matches the one found in
+    /proc/pid/status.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.proc = psutil.Process()
+
+    def read_status_file(self, linestart):
+        with psutil._psplatform.open_text(
+                '/proc/%s/status' % self.proc.pid) as f:
+            for line in f:
+                line = line.strip()
+                if line.startswith(linestart):
+                    value = line.partition('\t')[2]
+                    try:
+                        return int(value)
+                    except ValueError:
+                        return value
+            raise ValueError("can't find %r" % linestart)
+
+    def test_name(self):
+        value = self.read_status_file("Name:")
+        self.assertEqual(self.proc.name(), value)
+
+    def test_status(self):
+        value = self.read_status_file("State:")
+        value = value[value.find('(') + 1:value.rfind(')')]
+        value = value.replace(' ', '-')
+        self.assertEqual(self.proc.status(), value)
+
+    def test_ppid(self):
+        value = self.read_status_file("PPid:")
+        self.assertEqual(self.proc.ppid(), value)
+
+    def test_num_threads(self):
+        value = self.read_status_file("Threads:")
+        self.assertEqual(self.proc.num_threads(), value)
+
+    def test_uids(self):
+        value = self.read_status_file("Uid:")
+        value = tuple(map(int, value.split()[1:4]))
+        self.assertEqual(self.proc.uids(), value)
+
+    def test_gids(self):
+        value = self.read_status_file("Gid:")
+        value = tuple(map(int, value.split()[1:4]))
+        self.assertEqual(self.proc.gids(), value)
+
+    @retry_before_failing()
+    def test_num_ctx_switches(self):
+        value = self.read_status_file("voluntary_ctxt_switches:")
+        self.assertEqual(self.proc.num_ctx_switches().voluntary, value)
+        value = self.read_status_file("nonvoluntary_ctxt_switches:")
+        self.assertEqual(self.proc.num_ctx_switches().involuntary, value)
+
+    def test_cpu_affinity(self):
+        value = self.read_status_file("Cpus_allowed_list:")
+        if '-' in str(value):
+            min_, max_ = map(int, value.split('-'))
+            self.assertEqual(
+                self.proc.cpu_affinity(), list(range(min_, max_ + 1)))
+
+    def test_cpu_affinity_eligible_cpus(self):
+        value = self.read_status_file("Cpus_allowed_list:")
+        with mock.patch("psutil._pslinux.per_cpu_times") as m:
+            self.proc._proc._get_eligible_cpus()
+        if '-' in str(value):
+            assert not m.called
+        else:
+            assert m.called
+
+
+# =====================================================================
+# --- test utils
+# =====================================================================
+
+
+@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")
+            assert m.called
+
+    def test_cat(self):
+        fname = os.path.abspath(TESTFN)
+        with open(fname, "wt") as f:
+            f.write("foo ")
+        self.assertEqual(psutil._psplatform.cat(TESTFN, binary=False), "foo")
+        self.assertEqual(psutil._psplatform.cat(TESTFN, binary=True), b"foo")
+        self.assertEqual(
+            psutil._psplatform.cat(TESTFN + '??', fallback="bar"), "bar")
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_memory_leaks.py
@@ -0,0 +1,599 @@
+#!/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.
+
+"""
+Tests for detecting function memory leaks (typically the ones
+implemented in C). It does so by calling a function many times and
+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).
+"""
+
+from __future__ import print_function
+import errno
+import functools
+import gc
+import os
+import sys
+import threading
+import time
+
+import psutil
+import psutil._common
+from psutil import LINUX
+from psutil import OPENBSD
+from psutil import OSX
+from psutil import POSIX
+from psutil import SUNOS
+from psutil import WINDOWS
+from psutil._compat import xrange
+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_ENVIRON
+from psutil.tests import HAS_IONICE
+from psutil.tests import HAS_MEMORY_MAPS
+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 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
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+
+
+LOOPS = 1000
+MEMORY_TOLERANCE = 4096
+RETRY_FOR = 3
+
+SKIP_PYTHON_IMPL = True if TRAVIS else False
+cext = psutil._psplatform.cext
+thisproc = psutil.Process()
+SKIP_PYTHON_IMPL = True if TRAVIS else False
+
+
+# ===================================================================
+# utils
+# ===================================================================
+
+
+def skip_if_linux():
+    return unittest.skipIf(LINUX and SKIP_PYTHON_IMPL,
+                           "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
+
+
+class TestMemLeak(unittest.TestCase):
+    """Base framework class which calls a function many times and
+    produces a failure if process memory usage keeps increasing
+    between calls or over time.
+    """
+    tolerance = MEMORY_TOLERANCE
+    loops = LOOPS
+    retry_for = RETRY_FOR
+
+    def setUp(self):
+        gc.collect()
+
+    def execute(self, fun, *args, **kwargs):
+        """Test a callable."""
+        def call_many_times():
+            for x in xrange(loops):
+                self._call(fun, *args, **kwargs)
+            del x
+            gc.collect()
+
+        tolerance = kwargs.pop('tolerance_', None) or self.tolerance
+        loops = kwargs.pop('loops_', None) or self.loops
+        retry_for = kwargs.pop('retry_for_', None) or self.retry_for
+
+        # warm up
+        for x in range(10):
+            self._call(fun, *args, **kwargs)
+        self.assertEqual(gc.garbage, [])
+        self.assertEqual(threading.active_count(), 1)
+        self.assertEqual(thisproc.children(), [])
+
+        # Get 2 distinct memory samples, before and after having
+        # called fun repeadetly.
+        # step 1
+        call_many_times()
+        mem1 = self._get_mem()
+        # step 2
+        call_many_times()
+        mem2 = self._get_mem()
+
+        diff1 = mem2 - mem1
+        if diff1 > tolerance:
+            # This doesn't necessarily mean we have a leak yet.
+            # At this point we assume that after having called the
+            # function so many times the memory usage is stabilized
+            # and if there are no leaks it should not increase
+            # anymore.
+            # Let's keep calling fun for 3 more seconds and fail if
+            # we notice any difference.
+            ncalls = 0
+            stop_at = time.time() + retry_for
+            while time.time() <= stop_at:
+                self._call(fun, *args, **kwargs)
+                ncalls += 1
+
+            del stop_at
+            gc.collect()
+            mem3 = self._get_mem()
+            diff2 = mem3 - mem2
+
+            if mem3 > mem2:
+                # failure
+                extra_proc_mem = bytes2human(diff1 + diff2)
+                print("exta proc mem: %s" % extra_proc_mem, file=sys.stderr)
+                msg = "+%s after %s calls, +%s after another %s calls, "
+                msg += "+%s extra proc mem"
+                msg = msg % (
+                    bytes2human(diff1), loops, bytes2human(diff2), ncalls,
+                    extra_proc_mem)
+                self.fail(msg)
+
+    def execute_w_exc(self, exc, fun, *args, **kwargs):
+        """Convenience function which tests a callable raising
+        an exception.
+        """
+        def call():
+            self.assertRaises(exc, fun, *args, **kwargs)
+
+        self.execute(call)
+
+    @staticmethod
+    def _get_mem():
+        # By using USS memory it seems it's less likely to bump
+        # into false positives.
+        if LINUX or WINDOWS or OSX:
+            return thisproc.memory_full_info().uss
+        else:
+            return thisproc.memory_info().rss
+
+    @staticmethod
+    def _call(fun, *args, **kwargs):
+        fun(*args, **kwargs)
+
+
+# ===================================================================
+# Process class
+# ===================================================================
+
+
+class TestProcessObjectLeaks(TestMemLeak):
+    """Test leaks of Process class methods."""
+
+    proc = thisproc
+
+    def test_coverage(self):
+        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"))
+        for name in dir(psutil.Process):
+            if name.startswith('_'):
+                continue
+            if name in skip:
+                continue
+            self.assertTrue(hasattr(self, "test_" + name), msg=name)
+
+    @skip_if_linux()
+    def test_name(self):
+        self.execute(self.proc.name)
+
+    @skip_if_linux()
+    def test_cmdline(self):
+        self.execute(self.proc.cmdline)
+
+    @skip_if_linux()
+    def test_exe(self):
+        self.execute(self.proc.exe)
+
+    @skip_if_linux()
+    def test_ppid(self):
+        self.execute(self.proc.ppid)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    @skip_if_linux()
+    def test_uids(self):
+        self.execute(self.proc.uids)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    @skip_if_linux()
+    def test_gids(self):
+        self.execute(self.proc.gids)
+
+    @skip_if_linux()
+    def test_status(self):
+        self.execute(self.proc.status)
+
+    def test_nice_get(self):
+        self.execute(self.proc.nice)
+
+    def test_nice_set(self):
+        niceness = thisproc.nice()
+        self.execute(self.proc.nice, niceness)
+
+    @unittest.skipIf(not HAS_IONICE, "not supported")
+    def test_ionice_get(self):
+        self.execute(self.proc.ionice)
+
+    @unittest.skipIf(not HAS_IONICE, "not supported")
+    def test_ionice_set(self):
+        if WINDOWS:
+            value = thisproc.ionice()
+            self.execute(self.proc.ionice, value)
+        else:
+            self.execute(self.proc.ionice, psutil.IOPRIO_CLASS_NONE)
+            fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0)
+            self.execute_w_exc(OSError, fun)
+
+    @unittest.skipIf(not HAS_PROC_IO_COUNTERS, "not supported")
+    @skip_if_linux()
+    def test_io_counters(self):
+        self.execute(self.proc.io_counters)
+
+    @unittest.skipIf(POSIX, "worthless on POSIX")
+    def test_username(self):
+        self.execute(self.proc.username)
+
+    @skip_if_linux()
+    def test_create_time(self):
+        self.execute(self.proc.create_time)
+
+    @skip_if_linux()
+    @skip_on_access_denied(only_if=OPENBSD)
+    def test_num_threads(self):
+        self.execute(self.proc.num_threads)
+
+    @unittest.skipIf(not WINDOWS, "WINDOWS only")
+    def test_num_handles(self):
+        self.execute(self.proc.num_handles)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    @skip_if_linux()
+    def test_num_fds(self):
+        self.execute(self.proc.num_fds)
+
+    @skip_if_linux()
+    def test_num_ctx_switches(self):
+        self.execute(self.proc.num_ctx_switches)
+
+    @skip_if_linux()
+    @skip_on_access_denied(only_if=OPENBSD)
+    def test_threads(self):
+        self.execute(self.proc.threads)
+
+    @skip_if_linux()
+    def test_cpu_times(self):
+        self.execute(self.proc.cpu_times)
+
+    @skip_if_linux()
+    @unittest.skipIf(not HAS_PROC_CPU_NUM, "not supported")
+    def test_cpu_num(self):
+        self.execute(self.proc.cpu_num)
+
+    @skip_if_linux()
+    def test_memory_info(self):
+        self.execute(self.proc.memory_info)
+
+    @skip_if_linux()
+    def test_memory_full_info(self):
+        self.execute(self.proc.memory_full_info)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    @skip_if_linux()
+    def test_terminal(self):
+        self.execute(self.proc.terminal)
+
+    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
+                     "worthless on POSIX (pure python)")
+    def test_resume(self):
+        self.execute(self.proc.resume)
+
+    @skip_if_linux()
+    def test_cwd(self):
+        self.execute(self.proc.cwd)
+
+    @unittest.skipIf(not HAS_CPU_AFFINITY, "not supported")
+    def test_cpu_affinity_get(self):
+        self.execute(self.proc.cpu_affinity)
+
+    @unittest.skipIf(not HAS_CPU_AFFINITY, "not supported")
+    def test_cpu_affinity_set(self):
+        affinity = thisproc.cpu_affinity()
+        self.execute(self.proc.cpu_affinity, affinity)
+        if not TRAVIS:
+            self.execute_w_exc(ValueError, self.proc.cpu_affinity, [-1])
+
+    @skip_if_linux()
+    def test_open_files(self):
+        safe_rmpath(TESTFN)  # needed after UNIX socket test has run
+        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):
+        self.execute(self.proc.memory_maps)
+
+    @unittest.skipIf(not LINUX, "LINUX only")
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_get(self):
+        self.execute(self.proc.rlimit, psutil.RLIMIT_NOFILE)
+
+    @unittest.skipIf(not LINUX, "LINUX only")
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_set(self):
+        limit = thisproc.rlimit(psutil.RLIMIT_NOFILE)
+        self.execute(self.proc.rlimit, psutil.RLIMIT_NOFILE, limit)
+        self.execute_w_exc(OSError, self.proc.rlimit, -1)
+
+    @skip_if_linux()
+    # Windows implementation is based on a single system-wide
+    # function (tested later).
+    @unittest.skipIf(WINDOWS, "worthless on WINDOWS")
+    def test_connections(self):
+        # TODO: UNIX sockets are temporarily implemented by parsing
+        # 'pfiles' cmd  output; we don't want that part of the code to
+        # be executed.
+        with create_sockets():
+            kind = 'inet' if SUNOS else 'all'
+            self.execute(self.proc.connections, kind)
+
+    @unittest.skipIf(not HAS_ENVIRON, "not supported")
+    def test_environ(self):
+        self.execute(self.proc.environ)
+
+    @unittest.skipIf(not WINDOWS, "WINDOWS only")
+    def test_proc_info(self):
+        self.execute(cext.proc_info, os.getpid())
+
+
+class TestTerminatedProcessLeaks(TestProcessObjectLeaks):
+    """Repeat the tests above looking for leaks occurring when dealing
+    with terminated processes raising NoSuchProcess exception.
+    The C functions are still invoked but will follow different code
+    paths. We'll check those code paths.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestTerminatedProcessLeaks, cls).setUpClass()
+        p = get_test_subprocess()
+        cls.proc = psutil.Process(p.pid)
+        cls.proc.kill()
+        cls.proc.wait()
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestTerminatedProcessLeaks, cls).tearDownClass()
+        reap_children()
+
+    def _call(self, fun, *args, **kwargs):
+        try:
+            fun(*args, **kwargs)
+        except psutil.NoSuchProcess:
+            pass
+
+    if WINDOWS:
+
+        def test_kill(self):
+            self.execute(self.proc.kill)
+
+        def test_terminate(self):
+            self.execute(self.proc.terminate)
+
+        def test_suspend(self):
+            self.execute(self.proc.suspend)
+
+        def test_resume(self):
+            self.execute(self.proc.resume)
+
+        def test_wait(self):
+            self.execute(self.proc.wait)
+
+        def test_proc_info(self):
+            # test dual implementation
+            def call():
+                try:
+                    return cext.proc_info(self.proc.pid)
+                except OSError as err:
+                    if err.errno != errno.ESRCH:
+                        raise
+
+            self.execute(call)
+
+
+# ===================================================================
+# system APIs
+# ===================================================================
+
+
+class TestModuleFunctionsLeaks(TestMemLeak):
+    """Test leaks of psutil module functions."""
+
+    def test_coverage(self):
+        skip = set((
+            "version_info", "__version__", "process_iter", "wait_procs",
+            "cpu_percent", "cpu_times_percent", "cpu_count"))
+        for name in psutil.__all__:
+            if not name.islower():
+                continue
+            if name in skip:
+                continue
+            self.assertTrue(hasattr(self, "test_" + name), msg=name)
+
+    # --- cpu
+
+    @skip_if_linux()
+    def test_cpu_count_logical(self):
+        self.execute(psutil.cpu_count, logical=True)
+
+    @skip_if_linux()
+    def test_cpu_count_physical(self):
+        self.execute(psutil.cpu_count, logical=False)
+
+    @skip_if_linux()
+    def test_cpu_times(self):
+        self.execute(psutil.cpu_times)
+
+    @skip_if_linux()
+    def test_per_cpu_times(self):
+        self.execute(psutil.cpu_times, percpu=True)
+
+    def test_cpu_stats(self):
+        self.execute(psutil.cpu_stats)
+
+    @skip_if_linux()
+    @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
+    def test_cpu_freq(self):
+        self.execute(psutil.cpu_freq)
+
+    # --- 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)")
+    def test_swap_memory(self):
+        self.execute(psutil.swap_memory)
+
+    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
+                     "worthless on POSIX (pure python)")
+    def test_pid_exists(self):
+        self.execute(psutil.pid_exists, os.getpid())
+
+    # --- disk
+
+    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
+                     "worthless on POSIX (pure python)")
+    def test_disk_usage(self):
+        self.execute(psutil.disk_usage, '.')
+
+    def test_disk_partitions(self):
+        self.execute(psutil.disk_partitions)
+
+    @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
+                     '/proc/diskstats not available on this Linux version')
+    @skip_if_linux()
+    def test_disk_io_counters(self):
+        self.execute(psutil.disk_io_counters, nowrap=False)
+
+    # --- proc
+
+    @skip_if_linux()
+    def test_pids(self):
+        self.execute(psutil.pids)
+
+    # --- net
+
+    @skip_if_linux()
+    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")
+    def test_net_connections(self):
+        with create_sockets():
+            self.execute(psutil.net_connections)
+
+    def test_net_if_addrs(self):
+        # Note: verified that on Windows this was a false positive.
+        self.execute(psutil.net_if_addrs,
+                     tolerance_=80 * 1024 if WINDOWS else None)
+
+    @unittest.skipIf(TRAVIS, "EPERM on travis")
+    def test_net_if_stats(self):
+        self.execute(psutil.net_if_stats)
+
+    # --- sensors
+
+    @skip_if_linux()
+    @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported")
+    def test_sensors_battery(self):
+        self.execute(psutil.sensors_battery)
+
+    @skip_if_linux()
+    @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
+    def test_sensors_temperatures(self):
+        self.execute(psutil.sensors_temperatures)
+
+    @skip_if_linux()
+    @unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
+    def test_sensors_fans(self):
+        self.execute(psutil.sensors_fans)
+
+    # --- others
+
+    @skip_if_linux()
+    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)
+
+    if WINDOWS:
+
+        # --- win services
+
+        def test_win_service_iter(self):
+            self.execute(cext.winservice_enumerate)
+
+        def test_win_service_get(self):
+            pass
+
+        def test_win_service_get_config(self):
+            name = next(psutil.win_service_iter()).name()
+            self.execute(cext.winservice_query_config, name)
+
+        def test_win_service_get_status(self):
+            name = next(psutil.win_service_iter()).name()
+            self.execute(cext.winservice_query_status, name)
+
+        def test_win_service_get_description(self):
+            name = next(psutil.win_service_iter()).name()
+            self.execute(cext.winservice_query_descr, name)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_misc.py
@@ -0,0 +1,1039 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# 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.
+
+"""
+Miscellaneous tests.
+"""
+
+import ast
+import collections
+import contextlib
+import errno
+import json
+import os
+import pickle
+import socket
+import stat
+
+from psutil import LINUX
+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._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 create_proc_children_pair
+from psutil.tests import create_sockets
+from psutil.tests import create_zombie_proc
+from psutil.tests import DEVNULL
+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_SENSORS_BATTERY
+from psutil.tests import HAS_SENSORS_FANS
+from psutil.tests import HAS_SENSORS_TEMPERATURES
+from psutil.tests import import_module_by_path
+from psutil.tests import is_namedtuple
+from psutil.tests import mock
+from psutil.tests import PYTHON_EXE
+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_rmpath
+from psutil.tests import SCRIPTS_DIR
+from psutil.tests import sh
+from psutil.tests import tcp_socketpair
+from psutil.tests import TESTFN
+from psutil.tests import TOX
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+from psutil.tests import unix_socket_path
+from psutil.tests import unix_socketpair
+from psutil.tests import wait_for_file
+from psutil.tests import wait_for_pid
+import psutil
+import psutil.tests
+
+
+# ===================================================================
+# --- Misc / generic tests.
+# ===================================================================
+
+
+class TestMisc(unittest.TestCase):
+
+    def test_process__repr__(self, func=repr):
+        p = psutil.Process()
+        r = func(p)
+        self.assertIn("psutil.Process", r)
+        self.assertIn("pid=%s" % p.pid, r)
+        self.assertIn("name=", r)
+        self.assertIn(p.name(), r)
+        with mock.patch.object(psutil.Process, "name",
+                               side_effect=psutil.ZombieProcess(os.getpid())):
+            p = psutil.Process()
+            r = func(p)
+            self.assertIn("pid=%s" % p.pid, r)
+            self.assertIn("zombie", r)
+            self.assertNotIn("name=", r)
+        with mock.patch.object(psutil.Process, "name",
+                               side_effect=psutil.NoSuchProcess(os.getpid())):
+            p = psutil.Process()
+            r = func(p)
+            self.assertIn("pid=%s" % p.pid, r)
+            self.assertIn("terminated", r)
+            self.assertNotIn("name=", r)
+        with mock.patch.object(psutil.Process, "name",
+                               side_effect=psutil.AccessDenied(os.getpid())):
+            p = psutil.Process()
+            r = func(p)
+            self.assertIn("pid=%s" % p.pid, r)
+            self.assertNotIn("name=", r)
+
+    def test_process__str__(self):
+        self.test_process__repr__(func=str)
+
+    def test_no_such_process__repr__(self, func=repr):
+        self.assertEqual(
+            repr(psutil.NoSuchProcess(321)),
+            "psutil.NoSuchProcess process no longer exists (pid=321)")
+        self.assertEqual(
+            repr(psutil.NoSuchProcess(321, name='foo')),
+            "psutil.NoSuchProcess process no longer exists (pid=321, "
+            "name='foo')")
+        self.assertEqual(
+            repr(psutil.NoSuchProcess(321, msg='foo')),
+            "psutil.NoSuchProcess foo")
+
+    def test_zombie_process__repr__(self, func=repr):
+        self.assertEqual(
+            repr(psutil.ZombieProcess(321)),
+            "psutil.ZombieProcess process still exists but it's a zombie "
+            "(pid=321)")
+        self.assertEqual(
+            repr(psutil.ZombieProcess(321, name='foo')),
+            "psutil.ZombieProcess process still exists but it's a zombie "
+            "(pid=321, name='foo')")
+        self.assertEqual(
+            repr(psutil.ZombieProcess(321, name='foo', ppid=1)),
+            "psutil.ZombieProcess process still exists but it's a zombie "
+            "(pid=321, name='foo', ppid=1)")
+        self.assertEqual(
+            repr(psutil.ZombieProcess(321, msg='foo')),
+            "psutil.ZombieProcess foo")
+
+    def test_access_denied__repr__(self, func=repr):
+        self.assertEqual(
+            repr(psutil.AccessDenied(321)),
+            "psutil.AccessDenied (pid=321)")
+        self.assertEqual(
+            repr(psutil.AccessDenied(321, name='foo')),
+            "psutil.AccessDenied (pid=321, name='foo')")
+        self.assertEqual(
+            repr(psutil.AccessDenied(321, msg='foo')),
+            "psutil.AccessDenied foo")
+
+    def test_timeout_expired__repr__(self, func=repr):
+        self.assertEqual(
+            repr(psutil.TimeoutExpired(321)),
+            "psutil.TimeoutExpired timeout after 321 seconds")
+        self.assertEqual(
+            repr(psutil.TimeoutExpired(321, pid=111)),
+            "psutil.TimeoutExpired timeout after 321 seconds (pid=111)")
+        self.assertEqual(
+            repr(psutil.TimeoutExpired(321, pid=111, name='foo')),
+            "psutil.TimeoutExpired timeout after 321 seconds "
+            "(pid=111, name='foo')")
+
+    def test_process__eq__(self):
+        p1 = psutil.Process()
+        p2 = psutil.Process()
+        self.assertEqual(p1, p2)
+        p2._ident = (0, 0)
+        self.assertNotEqual(p1, p2)
+        self.assertNotEqual(p1, 'foo')
+
+    def test_process__hash__(self):
+        s = set([psutil.Process(), psutil.Process()])
+        self.assertEqual(len(s), 1)
+
+    def test__all__(self):
+        dir_psutil = dir(psutil)
+        for name in dir_psutil:
+            if name in ('callable', 'error', 'namedtuple', 'tests',
+                        'long', 'test', 'NUM_CPUS', 'BOOT_TIME',
+                        'TOTAL_PHYMEM'):
+                continue
+            if not name.startswith('_'):
+                try:
+                    __import__(name)
+                except ImportError:
+                    if name not in psutil.__all__:
+                        fun = getattr(psutil, name)
+                        if fun is None:
+                            continue
+                        if (fun.__doc__ is not None and
+                                'deprecated' not in fun.__doc__.lower()):
+                            self.fail('%r not in psutil.__all__' % name)
+
+        # Import 'star' will break if __all__ is inconsistent, see:
+        # https://github.com/giampaolo/psutil/issues/656
+        # Can't do `from psutil import *` as it won't work on python 3
+        # so we simply iterate over __all__.
+        for name in psutil.__all__:
+            self.assertIn(name, dir_psutil)
+
+    def test_version(self):
+        self.assertEqual('.'.join([str(x) for x in psutil.version_info]),
+                         psutil.__version__)
+
+    def test_process_as_dict_no_new_names(self):
+        # See https://github.com/giampaolo/psutil/issues/813
+        p = psutil.Process()
+        p.foo = '1'
+        self.assertNotIn('foo', p.as_dict())
+
+    def test_memoize(self):
+        @memoize
+        def foo(*args, **kwargs):
+            "foo docstring"
+            calls.append(None)
+            return (args, kwargs)
+
+        calls = []
+        # no args
+        for x in range(2):
+            ret = foo()
+            expected = ((), {})
+            self.assertEqual(ret, expected)
+            self.assertEqual(len(calls), 1)
+        # with args
+        for x in range(2):
+            ret = foo(1)
+            expected = ((1, ), {})
+            self.assertEqual(ret, expected)
+            self.assertEqual(len(calls), 2)
+        # with args + kwargs
+        for x in range(2):
+            ret = foo(1, bar=2)
+            expected = ((1, ), {'bar': 2})
+            self.assertEqual(ret, expected)
+            self.assertEqual(len(calls), 3)
+        # clear cache
+        foo.cache_clear()
+        ret = foo()
+        expected = ((), {})
+        self.assertEqual(ret, expected)
+        self.assertEqual(len(calls), 4)
+        # docstring
+        self.assertEqual(foo.__doc__, "foo docstring")
+
+    def test_memoize_when_activated(self):
+        class Foo:
+
+            @memoize_when_activated
+            def foo(self):
+                calls.append(None)
+
+        f = Foo()
+        calls = []
+        f.foo()
+        f.foo()
+        self.assertEqual(len(calls), 2)
+
+        # activate
+        calls = []
+        f.foo.cache_activate()
+        f.foo()
+        f.foo()
+        self.assertEqual(len(calls), 1)
+
+        # deactivate
+        calls = []
+        f.foo.cache_deactivate()
+        f.foo()
+        f.foo()
+        self.assertEqual(len(calls), 2)
+
+    def test_parse_environ_block(self):
+        from psutil._common import parse_environ_block
+
+        def k(s):
+            return s.upper() if WINDOWS else s
+
+        self.assertEqual(parse_environ_block("a=1\0"),
+                         {k("a"): "1"})
+        self.assertEqual(parse_environ_block("a=1\0b=2\0\0"),
+                         {k("a"): "1", k("b"): "2"})
+        self.assertEqual(parse_environ_block("a=1\0b=\0\0"),
+                         {k("a"): "1", k("b"): ""})
+        # ignore everything after \0\0
+        self.assertEqual(parse_environ_block("a=1\0b=2\0\0c=3\0"),
+                         {k("a"): "1", k("b"): "2"})
+        # ignore everything that is not an assignment
+        self.assertEqual(parse_environ_block("xxx\0a=1\0"), {k("a"): "1"})
+        self.assertEqual(parse_environ_block("a=1\0=b=2\0"), {k("a"): "1"})
+        # do not fail if the block is incomplete
+        self.assertEqual(parse_environ_block("a=1\0b=2"), {k("a"): "1"})
+
+    def test_supports_ipv6(self):
+        self.addCleanup(supports_ipv6.cache_clear)
+        if supports_ipv6():
+            with mock.patch('psutil._common.socket') as s:
+                s.has_ipv6 = False
+                supports_ipv6.cache_clear()
+                assert not supports_ipv6()
+
+            supports_ipv6.cache_clear()
+            with mock.patch('psutil._common.socket.socket',
+                            side_effect=socket.error) as s:
+                assert not supports_ipv6()
+                assert s.called
+
+            supports_ipv6.cache_clear()
+            with mock.patch('psutil._common.socket.socket',
+                            side_effect=socket.gaierror) as s:
+                assert not supports_ipv6()
+                supports_ipv6.cache_clear()
+                assert s.called
+
+            supports_ipv6.cache_clear()
+            with mock.patch('psutil._common.socket.socket.bind',
+                            side_effect=socket.gaierror) as s:
+                assert not supports_ipv6()
+                supports_ipv6.cache_clear()
+                assert s.called
+        else:
+            with self.assertRaises(Exception):
+                sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+                sock.bind(("::1", 0))
+
+    def test_isfile_strict(self):
+        from psutil._common import isfile_strict
+        this_file = os.path.abspath(__file__)
+        assert isfile_strict(this_file)
+        assert not isfile_strict(os.path.dirname(this_file))
+        with mock.patch('psutil._common.os.stat',
+                        side_effect=OSError(errno.EPERM, "foo")):
+            self.assertRaises(OSError, isfile_strict, this_file)
+        with mock.patch('psutil._common.os.stat',
+                        side_effect=OSError(errno.EACCES, "foo")):
+            self.assertRaises(OSError, isfile_strict, this_file)
+        with mock.patch('psutil._common.os.stat',
+                        side_effect=OSError(errno.EINVAL, "foo")):
+            assert not isfile_strict(this_file)
+        with mock.patch('psutil._common.stat.S_ISREG', return_value=False):
+            assert not isfile_strict(this_file)
+
+    def test_serialization(self):
+        def check(ret):
+            if json is not None:
+                json.loads(json.dumps(ret))
+            a = pickle.dumps(ret)
+            b = pickle.loads(a)
+            self.assertEqual(ret, b)
+
+        check(psutil.Process().as_dict())
+        check(psutil.virtual_memory())
+        check(psutil.swap_memory())
+        check(psutil.cpu_times())
+        check(psutil.cpu_times_percent(interval=0))
+        check(psutil.net_io_counters())
+        if LINUX and not os.path.exists('/proc/diskstats'):
+            pass
+        else:
+            if not APPVEYOR:
+                check(psutil.disk_io_counters())
+        check(psutil.disk_partitions())
+        check(psutil.disk_usage(os.getcwd()))
+        check(psutil.users())
+
+    def test_setup_script(self):
+        setup_py = os.path.join(ROOT_DIR, 'setup.py')
+        if TRAVIS and not os.path.exists(setup_py):
+            return self.skipTest("can't find setup.py")
+        module = import_module_by_path(setup_py)
+        self.assertRaises(SystemExit, module.setup)
+        self.assertEqual(module.get_version(), psutil.__version__)
+
+    def test_ad_on_process_creation(self):
+        # We are supposed to be able to instantiate Process also in case
+        # of zombie processes or access denied.
+        with mock.patch.object(psutil.Process, 'create_time',
+                               side_effect=psutil.AccessDenied) as meth:
+            psutil.Process()
+            assert meth.called
+        with mock.patch.object(psutil.Process, 'create_time',
+                               side_effect=psutil.ZombieProcess(1)) as meth:
+            psutil.Process()
+            assert meth.called
+        with mock.patch.object(psutil.Process, 'create_time',
+                               side_effect=ValueError) as meth:
+            with self.assertRaises(ValueError):
+                psutil.Process()
+            assert meth.called
+
+    def test_sanity_version_check(self):
+        # see: https://github.com/giampaolo/psutil/issues/564
+        with mock.patch(
+                "psutil._psplatform.cext.version", return_value="0.0.0"):
+            with self.assertRaises(ImportError) as cm:
+                reload_module(psutil)
+            self.assertIn("version conflict", str(cm.exception).lower())
+
+
+# ===================================================================
+# --- Tests for wrap_numbers() function.
+# ===================================================================
+
+
+nt = collections.namedtuple('foo', 'a b c')
+
+
+class TestWrapNumbers(unittest.TestCase):
+
+    def setUp(self):
+        wrap_numbers.cache_clear()
+
+    tearDown = setUp
+
+    def test_first_call(self):
+        input = {'disk1': nt(5, 5, 5)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+
+    def test_input_hasnt_changed(self):
+        input = {'disk1': nt(5, 5, 5)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+
+    def test_increase_but_no_wrap(self):
+        input = {'disk1': nt(5, 5, 5)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        input = {'disk1': nt(10, 15, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        input = {'disk1': nt(20, 25, 30)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        input = {'disk1': nt(20, 25, 30)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+
+    def test_wrap(self):
+        # let's say 100 is the threshold
+        input = {'disk1': nt(100, 100, 100)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        # first wrap restarts from 10
+        input = {'disk1': nt(100, 100, 10)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(100, 100, 110)})
+        # then it remains the same
+        input = {'disk1': nt(100, 100, 10)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(100, 100, 110)})
+        # then it goes up
+        input = {'disk1': nt(100, 100, 90)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(100, 100, 190)})
+        # then it wraps again
+        input = {'disk1': nt(100, 100, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(100, 100, 210)})
+        # and remains the same
+        input = {'disk1': nt(100, 100, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(100, 100, 210)})
+        # now wrap another num
+        input = {'disk1': nt(50, 100, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(150, 100, 210)})
+        # and again
+        input = {'disk1': nt(40, 100, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(190, 100, 210)})
+        # keep it the same
+        input = {'disk1': nt(40, 100, 20)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(190, 100, 210)})
+
+    def test_changing_keys(self):
+        # Emulate a case where the second call to disk_io()
+        # (or whatever) provides a new disk, then the new disk
+        # disappears on the third call.
+        input = {'disk1': nt(5, 5, 5)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        input = {'disk1': nt(5, 5, 5),
+                 'disk2': nt(7, 7, 7)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        input = {'disk1': nt(8, 8, 8)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+
+    def test_changing_keys_w_wrap(self):
+        input = {'disk1': nt(50, 50, 50),
+                 'disk2': nt(100, 100, 100)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        # disk 2 wraps
+        input = {'disk1': nt(50, 50, 50),
+                 'disk2': nt(100, 100, 10)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(50, 50, 50),
+                          'disk2': nt(100, 100, 110)})
+        # disk 2 disappears
+        input = {'disk1': nt(50, 50, 50)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+
+        # then it appears again; the old wrap is supposed to be
+        # gone.
+        input = {'disk1': nt(50, 50, 50),
+                 'disk2': nt(100, 100, 100)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        # remains the same
+        input = {'disk1': nt(50, 50, 50),
+                 'disk2': nt(100, 100, 100)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'), input)
+        # and then wraps again
+        input = {'disk1': nt(50, 50, 50),
+                 'disk2': nt(100, 100, 10)}
+        self.assertEqual(wrap_numbers(input, 'disk_io'),
+                         {'disk1': nt(50, 50, 50),
+                          'disk2': nt(100, 100, 110)})
+
+    def test_real_data(self):
+        d = {'nvme0n1': (300, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048),
+             'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8),
+             'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28),
+             'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348)}
+        self.assertEqual(wrap_numbers(d, 'disk_io'), d)
+        self.assertEqual(wrap_numbers(d, 'disk_io'), d)
+        # decrease this   ↓
+        d = {'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048),
+             'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8),
+             'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28),
+             'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348)}
+        out = wrap_numbers(d, 'disk_io')
+        self.assertEqual(out['nvme0n1'][0], 400)
+
+    # --- cache tests
+
+    def test_cache_first_call(self):
+        input = {'disk1': nt(5, 5, 5)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        self.assertEqual(cache[1], {'disk_io': {}})
+        self.assertEqual(cache[2], {'disk_io': {}})
+
+    def test_cache_call_twice(self):
+        input = {'disk1': nt(5, 5, 5)}
+        wrap_numbers(input, 'disk_io')
+        input = {'disk1': nt(10, 10, 10)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        self.assertEqual(
+            cache[1],
+            {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}})
+        self.assertEqual(cache[2], {'disk_io': {}})
+
+    def test_cache_wrap(self):
+        # let's say 100 is the threshold
+        input = {'disk1': nt(100, 100, 100)}
+        wrap_numbers(input, 'disk_io')
+
+        # first wrap restarts from 10
+        input = {'disk1': nt(100, 100, 10)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        self.assertEqual(
+            cache[1],
+            {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100}})
+        self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}})
+
+        def assert_():
+            cache = wrap_numbers.cache_info()
+            self.assertEqual(
+                cache[1],
+                {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0,
+                             ('disk1', 2): 100}})
+            self.assertEqual(cache[2],
+                             {'disk_io': {'disk1': set([('disk1', 2)])}})
+
+        # then it remains the same
+        input = {'disk1': nt(100, 100, 10)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        assert_()
+
+        # then it goes up
+        input = {'disk1': nt(100, 100, 90)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        assert_()
+
+        # then it wraps again
+        input = {'disk1': nt(100, 100, 20)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        self.assertEqual(
+            cache[1],
+            {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190}})
+        self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}})
+
+    def test_cache_changing_keys(self):
+        input = {'disk1': nt(5, 5, 5)}
+        wrap_numbers(input, 'disk_io')
+        input = {'disk1': nt(5, 5, 5),
+                 'disk2': nt(7, 7, 7)}
+        wrap_numbers(input, 'disk_io')
+        cache = wrap_numbers.cache_info()
+        self.assertEqual(cache[0], {'disk_io': input})
+        self.assertEqual(
+            cache[1],
+            {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}})
+        self.assertEqual(cache[2], {'disk_io': {}})
+
+    def test_cache_clear(self):
+        input = {'disk1': nt(5, 5, 5)}
+        wrap_numbers(input, 'disk_io')
+        wrap_numbers(input, 'disk_io')
+        wrap_numbers.cache_clear('disk_io')
+        self.assertEqual(wrap_numbers.cache_info(), ({}, {}, {}))
+        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")
+    def test_cache_clear_public_apis(self):
+        psutil.disk_io_counters()
+        psutil.net_io_counters()
+        caches = wrap_numbers.cache_info()
+        for cache in caches:
+            self.assertIn('psutil.disk_io_counters', cache)
+            self.assertIn('psutil.net_io_counters', cache)
+
+        psutil.disk_io_counters.cache_clear()
+        caches = wrap_numbers.cache_info()
+        for cache in caches:
+            self.assertIn('psutil.net_io_counters', cache)
+            self.assertNotIn('psutil.disk_io_counters', cache)
+
+        psutil.net_io_counters.cache_clear()
+        caches = wrap_numbers.cache_info()
+        self.assertEqual(caches, ({}, {}, {}))
+
+
+# ===================================================================
+# --- Example script tests
+# ===================================================================
+
+
+@unittest.skipIf(TOX, "can't test on TOX")
+# See: https://travis-ci.org/giampaolo/psutil/jobs/295224806
+@unittest.skipIf(TRAVIS and not os.path.exists(SCRIPTS_DIR),
+                 "can't locate scripts directory")
+class TestScripts(unittest.TestCase):
+    """Tests for scripts in the "scripts" directory."""
+
+    @staticmethod
+    def assert_stdout(exe, *args, **kwargs):
+        exe = '%s' % os.path.join(SCRIPTS_DIR, exe)
+        cmd = [PYTHON_EXE, exe]
+        for arg in args:
+            cmd.append(arg)
+        try:
+            out = sh(cmd, **kwargs).strip()
+        except RuntimeError as err:
+            if 'AccessDenied' in str(err):
+                return str(err)
+            else:
+                raise
+        assert out, out
+        return out
+
+    @staticmethod
+    def assert_syntax(exe, args=None):
+        exe = os.path.join(SCRIPTS_DIR, exe)
+        if PY3:
+            f = open(exe, 'rt', encoding='utf8')
+        else:
+            f = open(exe, 'rt')
+        with f:
+            src = f.read()
+        ast.parse(src)
+
+    def test_coverage(self):
+        # make sure all example scripts have a test method defined
+        meths = dir(self)
+        for name in os.listdir(SCRIPTS_DIR):
+            if name.endswith('.py'):
+                if 'test_' + os.path.splitext(name)[0] not in meths:
+                    # self.assert_stdout(name)
+                    self.fail('no test defined for %r script'
+                              % os.path.join(SCRIPTS_DIR, name))
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_executable(self):
+        for name in os.listdir(SCRIPTS_DIR):
+            if name.endswith('.py'):
+                path = os.path.join(SCRIPTS_DIR, name)
+                if not stat.S_IXUSR & os.stat(path)[stat.ST_MODE]:
+                    self.fail('%r is not executable' % path)
+
+    def test_disk_usage(self):
+        self.assert_stdout('disk_usage.py')
+
+    def test_free(self):
+        self.assert_stdout('free.py')
+
+    def test_meminfo(self):
+        self.assert_stdout('meminfo.py')
+
+    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")
+    def test_who(self):
+        self.assert_stdout('who.py')
+
+    def test_ps(self):
+        self.assert_stdout('ps.py')
+
+    def test_pstree(self):
+        self.assert_stdout('pstree.py')
+
+    def test_netstat(self):
+        self.assert_stdout('netstat.py')
+
+    # permission denied on travis
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    def test_ifconfig(self):
+        self.assert_stdout('ifconfig.py')
+
+    @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
+    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):
+        self.assert_stdout('procsmem.py', stderr=DEVNULL)
+
+    def test_killall(self):
+        self.assert_syntax('killall.py')
+
+    def test_nettop(self):
+        self.assert_syntax('nettop.py')
+
+    def test_top(self):
+        self.assert_syntax('top.py')
+
+    def test_iotop(self):
+        self.assert_syntax('iotop.py')
+
+    def test_pidof(self):
+        output = self.assert_stdout('pidof.py', psutil.Process().name())
+        self.assertIn(str(os.getpid()), output)
+
+    @unittest.skipIf(not WINDOWS, "WINDOWS only")
+    def test_winservices(self):
+        self.assert_stdout('winservices.py')
+
+    def test_cpu_distribution(self):
+        self.assert_syntax('cpu_distribution.py')
+
+    @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    def test_temperatures(self):
+        self.assert_stdout('temperatures.py')
+
+    @unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
+    def test_fans(self):
+        self.assert_stdout('fans.py')
+
+    @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported")
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_battery(self):
+        self.assert_stdout('battery.py')
+
+    def test_sensors(self):
+        self.assert_stdout('sensors.py')
+
+
+# ===================================================================
+# --- Unit tests for test utilities.
+# ===================================================================
+
+
+class TestRetryDecorator(unittest.TestCase):
+
+    @mock.patch('time.sleep')
+    def test_retry_success(self, sleep):
+        # Fail 3 times out of 5; make sure the decorated fun returns.
+
+        @retry(retries=5, interval=1, logfun=None)
+        def foo():
+            while queue:
+                queue.pop()
+                1 / 0
+            return 1
+
+        queue = list(range(3))
+        self.assertEqual(foo(), 1)
+        self.assertEqual(sleep.call_count, 3)
+
+    @mock.patch('time.sleep')
+    def test_retry_failure(self, sleep):
+        # Fail 6 times out of 5; th function is supposed to raise exc.
+
+        @retry(retries=5, interval=1, logfun=None)
+        def foo():
+            while queue:
+                queue.pop()
+                1 / 0
+            return 1
+
+        queue = list(range(6))
+        self.assertRaises(ZeroDivisionError, foo)
+        self.assertEqual(sleep.call_count, 5)
+
+    @mock.patch('time.sleep')
+    def test_exception_arg(self, sleep):
+        @retry(exception=ValueError, interval=1)
+        def foo():
+            raise TypeError
+
+        self.assertRaises(TypeError, foo)
+        self.assertEqual(sleep.call_count, 0)
+
+    @mock.patch('time.sleep')
+    def test_no_interval_arg(self, sleep):
+        # if interval is not specified sleep is not supposed to be called
+
+        @retry(retries=5, interval=None, logfun=None)
+        def foo():
+            1 / 0
+
+        self.assertRaises(ZeroDivisionError, foo)
+        self.assertEqual(sleep.call_count, 0)
+
+    @mock.patch('time.sleep')
+    def test_retries_arg(self, sleep):
+
+        @retry(retries=5, interval=1, logfun=None)
+        def foo():
+            1 / 0
+
+        self.assertRaises(ZeroDivisionError, foo)
+        self.assertEqual(sleep.call_count, 5)
+
+    @mock.patch('time.sleep')
+    def test_retries_and_timeout_args(self, sleep):
+        self.assertRaises(ValueError, retry, retries=5, timeout=1)
+
+
+class TestSyncTestUtils(unittest.TestCase):
+
+    def tearDown(self):
+        safe_rmpath(TESTFN)
+
+    def test_wait_for_pid(self):
+        wait_for_pid(os.getpid())
+        nopid = max(psutil.pids()) + 99999
+        with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
+            self.assertRaises(psutil.NoSuchProcess, wait_for_pid, nopid)
+
+    def test_wait_for_file(self):
+        with open(TESTFN, 'w') as f:
+            f.write('foo')
+        wait_for_file(TESTFN)
+        assert not os.path.exists(TESTFN)
+
+    def test_wait_for_file_empty(self):
+        with open(TESTFN, 'w'):
+            pass
+        wait_for_file(TESTFN, empty=True)
+        assert not os.path.exists(TESTFN)
+
+    def test_wait_for_file_no_file(self):
+        with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
+            self.assertRaises(IOError, wait_for_file, TESTFN)
+
+    def test_wait_for_file_no_delete(self):
+        with open(TESTFN, 'w') as f:
+            f.write('foo')
+        wait_for_file(TESTFN, delete=False)
+        assert os.path.exists(TESTFN)
+
+    def test_call_until(self):
+        ret = call_until(lambda: 1, "ret == 1")
+        self.assertEqual(ret, 1)
+
+
+class TestFSTestUtils(unittest.TestCase):
+
+    def setUp(self):
+        safe_rmpath(TESTFN)
+
+    tearDown = setUp
+
+    def test_safe_rmpath(self):
+        # test file is removed
+        open(TESTFN, 'w').close()
+        safe_rmpath(TESTFN)
+        assert not os.path.exists(TESTFN)
+        # test no exception if path does not exist
+        safe_rmpath(TESTFN)
+        # test dir is removed
+        os.mkdir(TESTFN)
+        safe_rmpath(TESTFN)
+        assert not os.path.exists(TESTFN)
+        # test other exceptions are raised
+        with mock.patch('psutil.tests.os.stat',
+                        side_effect=OSError(errno.EINVAL, "")) as m:
+            with self.assertRaises(OSError):
+                safe_rmpath(TESTFN)
+            assert m.called
+
+    def test_chdir(self):
+        base = os.getcwd()
+        os.mkdir(TESTFN)
+        with chdir(TESTFN):
+            self.assertEqual(os.getcwd(), os.path.join(base, TESTFN))
+        self.assertEqual(os.getcwd(), base)
+
+
+class TestProcessUtils(unittest.TestCase):
+
+    def test_reap_children(self):
+        subp = get_test_subprocess()
+        p = psutil.Process(subp.pid)
+        assert p.is_running()
+        reap_children()
+        assert not p.is_running()
+        assert not psutil.tests._pids_started
+        assert not psutil.tests._subprocesses_started
+
+    def test_create_proc_children_pair(self):
+        p1, p2 = create_proc_children_pair()
+        self.assertNotEqual(p1.pid, p2.pid)
+        assert p1.is_running()
+        assert p2.is_running()
+        children = psutil.Process().children(recursive=True)
+        self.assertEqual(len(children), 2)
+        self.assertIn(p1, children)
+        self.assertIn(p2, children)
+        self.assertEqual(p1.ppid(), os.getpid())
+        self.assertEqual(p2.ppid(), p1.pid)
+
+        # make sure both of them are cleaned up
+        reap_children()
+        assert not p1.is_running()
+        assert not p2.is_running()
+        assert not psutil.tests._pids_started
+        assert not psutil.tests._subprocesses_started
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_create_zombie_proc(self):
+        zpid = create_zombie_proc()
+        self.addCleanup(reap_children, recursive=True)
+        p = psutil.Process(zpid)
+        self.assertEqual(p.status(), psutil.STATUS_ZOMBIE)
+
+
+class TestNetUtils(unittest.TestCase):
+
+    def bind_socket(self):
+        port = get_free_port()
+        with contextlib.closing(bind_socket(addr=('', port))) as s:
+            self.assertEqual(s.getsockname()[1], port)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_bind_unix_socket(self):
+        with unix_socket_path() as name:
+            sock = bind_unix_socket(name)
+            with contextlib.closing(sock):
+                self.assertEqual(sock.family, socket.AF_UNIX)
+                self.assertEqual(sock.type, socket.SOCK_STREAM)
+                self.assertEqual(sock.getsockname(), name)
+                assert os.path.exists(name)
+                assert stat.S_ISSOCK(os.stat(name).st_mode)
+        # UDP
+        with unix_socket_path() as name:
+            sock = bind_unix_socket(name, type=socket.SOCK_DGRAM)
+            with contextlib.closing(sock):
+                self.assertEqual(sock.type, socket.SOCK_DGRAM)
+
+    def tcp_tcp_socketpair(self):
+        addr = ("127.0.0.1", get_free_port())
+        server, client = tcp_socketpair(socket.AF_INET, addr=addr)
+        with contextlib.closing(server):
+            with contextlib.closing(client):
+                # Ensure they are connected and the positions are
+                # correct.
+                self.assertEqual(server.getsockname(), addr)
+                self.assertEqual(client.getpeername(), addr)
+                self.assertNotEqual(client.getsockname(), addr)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_unix_socketpair(self):
+        p = psutil.Process()
+        num_fds = p.num_fds()
+        assert not p.connections(kind='unix')
+        with unix_socket_path() as name:
+            server, client = unix_socketpair(name)
+            try:
+                assert os.path.exists(name)
+                assert stat.S_ISSOCK(os.stat(name).st_mode)
+                self.assertEqual(p.num_fds() - num_fds, 2)
+                self.assertEqual(len(p.connections(kind='unix')), 2)
+                self.assertEqual(server.getsockname(), name)
+                self.assertEqual(client.getpeername(), name)
+            finally:
+                client.close()
+                server.close()
+
+    def test_create_sockets(self):
+        with create_sockets() as socks:
+            fams = collections.defaultdict(int)
+            types = collections.defaultdict(int)
+            for s in socks:
+                fams[s.family] += 1
+                # work around http://bugs.python.org/issue30204
+                types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1
+            self.assertGreaterEqual(fams[socket.AF_INET], 2)
+            if supports_ipv6():
+                self.assertGreaterEqual(fams[socket.AF_INET6], 2)
+            if POSIX and HAS_CONNECTIONS_UNIX:
+                self.assertGreaterEqual(fams[socket.AF_UNIX], 2)
+            self.assertGreaterEqual(types[socket.SOCK_STREAM], 2)
+            self.assertGreaterEqual(types[socket.SOCK_DGRAM], 2)
+
+
+class TestOtherUtils(unittest.TestCase):
+
+    def test_is_namedtuple(self):
+        assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3))
+        assert not is_namedtuple(tuple())
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_osx.py
@@ -0,0 +1,303 @@
+#!/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.
+
+"""OSX specific tests."""
+
+import os
+import re
+import time
+
+import psutil
+from psutil import OSX
+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 sh
+from psutil.tests import unittest
+
+
+PAGESIZE = os.sysconf("SC_PAGE_SIZE") if OSX else None
+
+
+def sysctl(cmdline):
+    """Expects a sysctl command with an argument and parse the result
+    returning only the value of interest.
+    """
+    out = sh(cmdline)
+    result = out.split()[1]
+    try:
+        return int(result)
+    except ValueError:
+        return result
+
+
+def vm_stat(field):
+    """Wrapper around 'vm_stat' cmdline utility."""
+    out = sh('vm_stat')
+    for line in out.split('\n'):
+        if field in line:
+            break
+    else:
+        raise ValueError("line not found")
+    return int(re.search(r'\d+', line).group(0)) * PAGESIZE
+
+
+# http://code.activestate.com/recipes/578019/
+def human2bytes(s):
+    SYMBOLS = {
+        'customary': ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'),
+    }
+    init = s
+    num = ""
+    while s and s[0:1].isdigit() or s[0:1] == '.':
+        num += s[0]
+        s = s[1:]
+    num = float(num)
+    letter = s.strip()
+    for name, sset in SYMBOLS.items():
+        if letter in sset:
+            break
+    else:
+        if letter == 'k':
+            sset = SYMBOLS['customary']
+            letter = letter.upper()
+        else:
+            raise ValueError("can't interpret %r" % init)
+    prefix = {sset[0]: 1}
+    for i, s in enumerate(sset[1:]):
+        prefix[s] = 1 << (i + 1) * 10
+    return int(num * prefix[letter])
+
+
+@unittest.skipIf(not OSX, "OSX only")
+class TestProcess(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    def test_process_create_time(self):
+        output = sh("ps -o lstart -p %s" % self.pid)
+        start_ps = output.replace('STARTED', '').strip()
+        hhmmss = start_ps.split(' ')[-2]
+        year = start_ps.split(' ')[-1]
+        start_psutil = psutil.Process(self.pid).create_time()
+        self.assertEqual(
+            hhmmss,
+            time.strftime("%H:%M:%S", time.localtime(start_psutil)))
+        self.assertEqual(
+            year,
+            time.strftime("%Y", time.localtime(start_psutil)))
+
+
+@unittest.skipIf(not OSX, "OSX only")
+class TestZombieProcessAPIs(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        zpid = create_zombie_proc()
+        cls.p = psutil.Process(zpid)
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children(recursive=True)
+
+    def test_pidtask_info(self):
+        self.assertEqual(self.p.status(), psutil.STATUS_ZOMBIE)
+        self.p.ppid()
+        self.p.uids()
+        self.p.gids()
+        self.p.terminal()
+        self.p.create_time()
+
+    def test_exe(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.exe)
+
+    def test_cmdline(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.cmdline)
+
+    def test_environ(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.environ)
+
+    def test_cwd(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.cwd)
+
+    def test_memory_full_info(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.memory_full_info)
+
+    def test_cpu_times(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.cpu_times)
+
+    def test_num_ctx_switches(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.num_ctx_switches)
+
+    def test_num_threads(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.num_threads)
+
+    def test_open_files(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.open_files)
+
+    def test_connections(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.connections)
+
+    def test_num_fds(self):
+        self.assertRaises(psutil.ZombieProcess, self.p.num_fds)
+
+    def test_threads(self):
+        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")
+class TestSystemAPIs(unittest.TestCase):
+
+    # --- disk
+
+    def test_disks(self):
+        # test psutil.disk_usage() and psutil.disk_partitions()
+        # against "df -a"
+        def df(path):
+            out = sh('df -k "%s"' % path).strip()
+            lines = out.split('\n')
+            lines.pop(0)
+            line = lines.pop(0)
+            dev, total, used, free = line.split()[:4]
+            if dev == 'none':
+                dev = ''
+            total = int(total) * 1024
+            used = int(used) * 1024
+            free = int(free) * 1024
+            return dev, total, used, free
+
+        for part in psutil.disk_partitions(all=False):
+            usage = psutil.disk_usage(part.mountpoint)
+            dev, total, used, free = df(part.mountpoint)
+            self.assertEqual(part.device, dev)
+            self.assertEqual(usage.total, total)
+            # 10 MB tollerance
+            if abs(usage.free - free) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % usage.free, free)
+            if abs(usage.used - used) > 10 * 1024 * 1024:
+                self.fail("psutil=%s, df=%s" % usage.used, used)
+
+    # --- cpu
+
+    def test_cpu_count_logical(self):
+        num = sysctl("sysctl hw.logicalcpu")
+        self.assertEqual(num, psutil.cpu_count(logical=True))
+
+    def test_cpu_count_physical(self):
+        num = sysctl("sysctl hw.physicalcpu")
+        self.assertEqual(num, psutil.cpu_count(logical=False))
+
+    def test_cpu_freq(self):
+        freq = psutil.cpu_freq()
+        self.assertEqual(
+            freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency"))
+        self.assertEqual(
+            freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min"))
+        self.assertEqual(
+            freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max"))
+
+    # --- virtual mem
+
+    def test_vmem_total(self):
+        sysctl_hwphymem = sysctl('sysctl hw.memsize')
+        self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total)
+
+    @retry_before_failing()
+    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()
+    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()
+    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()
+    def test_vmem_wired(self):
+        vmstat_val = vm_stat("wired")
+        psutil_val = psutil.virtual_memory().wired
+        self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
+
+    # --- swap mem
+
+    @retry_before_failing()
+    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()
+    def test_swapmem_sout(self):
+        vmstat_val = vm_stat("Pageout")
+        psutil_val = psutil.swap_memory().sout
+        self.assertEqual(psutil_val, vmstat_val)
+
+    # Not very reliable.
+    # def test_swapmem_total(self):
+    #     out = sh('sysctl vm.swapusage')
+    #     out = out.replace('vm.swapusage: ', '')
+    #     total, used, free = re.findall('\d+.\d+\w', out)
+    #     psutil_smem = psutil.swap_memory()
+    #     self.assertEqual(psutil_smem.total, human2bytes(total))
+    #     self.assertEqual(psutil_smem.used, human2bytes(used))
+    #     self.assertEqual(psutil_smem.free, human2bytes(free))
+
+    # --- network
+
+    def test_net_if_stats(self):
+        for name, stats in psutil.net_if_stats().items():
+            try:
+                out = sh("ifconfig %s" % name)
+            except RuntimeError:
+                pass
+            else:
+                self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
+                self.assertEqual(stats.mtu,
+                                 int(re.findall(r'mtu (\d+)', out)[0]))
+
+    # --- sensors_battery
+
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_sensors_battery(self):
+        out = sh("pmset -g batt")
+        percent = re.search("(\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()
+        self.assertEqual(psutil_result.power_plugged, power_plugged)
+        self.assertEqual(psutil_result.percent, int(percent))
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_posix.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# 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.
+
+"""POSIX specific tests."""
+
+import datetime
+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 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 get_kernel_version
+from psutil.tests import get_test_subprocess
+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 sh
+from psutil.tests import skip_on_access_denied
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+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.
+    """
+    if not LINUX:
+        cmd = cmd.replace(" --no-headers ", " ")
+    if SUNOS:
+        cmd = cmd.replace("-o start", "-o stime")
+    if AIX:
+        cmd = cmd.replace("-o rss", "-o rssize")
+    output = sh(cmd)
+    if not LINUX:
+        output = output.split('\n')[1].strip()
+    try:
+        return int(output)
+    except ValueError:
+        return output
+
+# ps "-o" field names differ wildly between platforms.
+# "comm" means "only executable name" but is not available on BSD platforms.
+# "args" means "command with all its arguments", and is also not available
+# on BSD platforms.
+# "command" is like "args" on most platforms, but like "comm" on AIX,
+# and not available on SUNOS.
+# so for the executable name we can use "comm" on Solaris and split "command"
+# on other platforms.
+# to get the cmdline (with args) we have to use "args" on AIX and
+# Solaris, and can use "command" on all others.
+
+
+def ps_name(pid):
+    field = "command"
+    if SUNOS:
+        field = "comm"
+    return ps("ps --no-headers -o %s -p %s" % (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))
+
+
+@unittest.skipIf(not POSIX, "POSIX only")
+class TestProcess(unittest.TestCase):
+    """Compare psutil results against 'ps' command line utility (mainly)."""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess([PYTHON_EXE, "-E", "-O"],
+                                      stdin=subprocess.PIPE).pid
+        wait_for_pid(cls.pid)
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    def test_ppid(self):
+        ppid_ps = ps("ps --no-headers -o ppid -p %s" % 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_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_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_psutil = psutil.Process(self.pid).username()
+        self.assertEqual(username_ps, username_psutil)
+
+    def test_username_no_resolution(self):
+        # Emulate a case where the system can't resolve the uid to
+        # a username in which case psutil is supposed to return
+        # the stringified uid.
+        p = psutil.Process()
+        with mock.patch("psutil.pwd.getpwuid", side_effect=KeyError) as fun:
+            self.assertEqual(p.username(), str(p.uids().real))
+            assert fun.called
+
+    @skip_on_access_denied()
+    @retry_before_failing()
+    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_psutil = psutil.Process(self.pid).memory_info()[0] / 1024
+        self.assertEqual(rss_ps, rss_psutil)
+
+    @skip_on_access_denied()
+    @retry_before_failing()
+    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_psutil = psutil.Process(self.pid).memory_info()[1] / 1024
+        self.assertEqual(vsz_ps, vsz_psutil)
+
+    def test_name(self):
+        name_ps = ps_name(self.pid)
+        # 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
+        # be "pythonX.Y".
+        name_ps = re.sub(r"\d.\d", "", name_ps)
+        name_psutil = re.sub(r"\d.\d", "", name_psutil)
+        self.assertEqual(name_ps, name_psutil)
+
+    def test_name_long(self):
+        # On UNIX the kernel truncates the name to the first 15
+        # characters. In such a case psutil tries to determine the
+        # full name from the cmdline.
+        name = "long-program-name"
+        cmdline = ["long-program-name-extended", "foo", "bar"]
+        with mock.patch("psutil._psplatform.Process.name",
+                        return_value=name):
+            with mock.patch("psutil._psplatform.Process.cmdline",
+                            return_value=cmdline):
+                p = psutil.Process()
+                self.assertEqual(p.name(), "long-program-name-extended")
+
+    def test_name_long_cmdline_ad_exc(self):
+        # Same as above but emulates a case where cmdline() raises
+        # AccessDenied in which case psutil is supposed to return
+        # the truncated name instead of crashing.
+        name = "long-program-name"
+        with mock.patch("psutil._psplatform.Process.name",
+                        return_value=name):
+            with mock.patch("psutil._psplatform.Process.cmdline",
+                            side_effect=psutil.AccessDenied(0, "")):
+                p = psutil.Process()
+                self.assertEqual(p.name(), "long-program-name")
+
+    def test_name_long_cmdline_nsp_exc(self):
+        # Same as above but emulates a case where cmdline() raises NSP
+        # which is supposed to propagate.
+        name = "long-program-name"
+        with mock.patch("psutil._psplatform.Process.name",
+                        return_value=name):
+            with mock.patch("psutil._psplatform.Process.cmdline",
+                            side_effect=psutil.NoSuchProcess(0, "")):
+                p = psutil.Process()
+                self.assertRaises(psutil.NoSuchProcess, p.name)
+
+    @unittest.skipIf(OSX 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_psutil = psutil.Process(self.pid).create_time()
+        time_psutil_tstamp = datetime.datetime.fromtimestamp(
+            time_psutil).strftime("%H:%M:%S")
+        # sometimes ps shows the time rounded up instead of down, so we check
+        # for both possible values
+        round_time_psutil = round(time_psutil)
+        round_time_psutil_tstamp = datetime.datetime.fromtimestamp(
+            round_time_psutil).strftime("%H:%M:%S")
+        self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp])
+
+    def test_exe(self):
+        ps_pathname = ps_name(self.pid)
+        psutil_pathname = psutil.Process(self.pid).exe()
+        try:
+            self.assertEqual(ps_pathname, psutil_pathname)
+        except AssertionError:
+            # certain platforms such as BSD are more accurate returning:
+            # "/usr/local/bin/python2.7"
+            # ...instead of:
+            # "/usr/local/bin/python"
+            # We do not want to consider this difference in accuracy
+            # an error.
+            adjusted_ps_pathname = ps_pathname[:len(ps_pathname)]
+            self.assertEqual(ps_pathname, adjusted_ps_pathname)
+
+    def test_cmdline(self):
+        ps_cmdline = ps_args(self.pid)
+        psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline())
+        self.assertEqual(ps_cmdline, psutil_cmdline)
+
+    # On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an
+    # incorrect value (20); the real deal is getpriority(2) which
+    # returns 0; psutil relies on it, see:
+    # https://github.com/giampaolo/psutil/issues/1082
+    # AIX has the same issue
+    @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)
+        psutil_nice = psutil.Process().nice()
+        self.assertEqual(ps_nice, psutil_nice)
+
+    def test_num_fds(self):
+        # Note: this fails from time to time; I'm keen on thinking
+        # it doesn't mean something is broken
+        def call(p, attr):
+            args = ()
+            attr = getattr(p, name, None)
+            if attr is not None and callable(attr):
+                if name == 'rlimit':
+                    args = (psutil.RLIMIT_NOFILE,)
+                attr(*args)
+            else:
+                attr
+
+        p = psutil.Process(os.getpid())
+        failures = []
+        ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice',
+                         'send_signal', 'wait', 'children', 'as_dict',
+                         'memory_info_ex']
+        if LINUX and get_kernel_version() < (2, 6, 36):
+            ignored_names.append('rlimit')
+        if LINUX and get_kernel_version() < (2, 6, 23):
+            ignored_names.append('num_ctx_switches')
+        for name in dir(psutil.Process):
+            if (name.startswith('_') or name in ignored_names):
+                continue
+            else:
+                try:
+                    num1 = p.num_fds()
+                    for x in range(2):
+                        call(p, name)
+                    num2 = p.num_fds()
+                except psutil.AccessDenied:
+                    pass
+                else:
+                    if abs(num2 - num1) > 1:
+                        fail = "failure while processing Process.%s method " \
+                               "(before=%s, after=%s)" % (name, num1, num2)
+                        failures.append(fail)
+        if failures:
+            self.fail('\n' + '\n'.join(failures))
+
+
+@unittest.skipIf(not POSIX, "POSIX only")
+class TestSystemAPIs(unittest.TestCase):
+    """Test some system APIs."""
+
+    @retry_before_failing()
+    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_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:
+            pids_ps.insert(0, 0)
+        self.assertEqual(pids_ps, pids_psutil)
+
+    # 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")
+    def test_nic_names(self):
+        output = sh("ifconfig -a")
+        for nic in psutil.net_io_counters(pernic=True).keys():
+            for line in output.split():
+                if line.startswith(nic):
+                    break
+            else:
+                self.fail(
+                    "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()
+    def test_users(self):
+        out = sh("who")
+        lines = out.split('\n')
+        users = [x.split()[0] for x in lines]
+        terminals = [x.split()[1] for x in lines]
+        self.assertEqual(len(users), len(psutil.users()))
+        for u in psutil.users():
+            self.assertIn(u.name, users)
+            self.assertIn(u.terminal, terminals)
+
+    def test_pid_exists_let_raise(self):
+        # According to "man 2 kill" possible error values for kill
+        # are (EINVAL, EPERM, ESRCH). Test that any other errno
+        # results in an exception.
+        with mock.patch("psutil._psposix.os.kill",
+                        side_effect=OSError(errno.EBADF, "")) as m:
+            self.assertRaises(OSError, psutil._psposix.pid_exists, os.getpid())
+            assert m.called
+
+    def test_os_waitpid_let_raise(self):
+        # os.waitpid() is supposed to catch EINTR and ECHILD only.
+        # Test that any other errno results in an exception.
+        with mock.patch("psutil._psposix.os.waitpid",
+                        side_effect=OSError(errno.EBADF, "")) as m:
+            self.assertRaises(OSError, psutil._psposix.wait_pid, os.getpid())
+            assert m.called
+
+    def test_os_waitpid_eintr(self):
+        # os.waitpid() is supposed to "retry" on EINTR.
+        with mock.patch("psutil._psposix.os.waitpid",
+                        side_effect=OSError(errno.EINTR, "")) as m:
+            self.assertRaises(
+                psutil._psposix.TimeoutExpired,
+                psutil._psposix.wait_pid, os.getpid(), timeout=0.01)
+            assert m.called
+
+    def test_os_waitpid_bad_ret_status(self):
+        # Simulate os.waitpid() returning a bad status.
+        with mock.patch("psutil._psposix.os.waitpid",
+                        return_value=(1, -1)) as m:
+            self.assertRaises(ValueError,
+                              psutil._psposix.wait_pid, os.getpid())
+            assert m.called
+
+    # AIX can return '-' in df output instead of numbers, e.g. for /proc
+    @unittest.skipIf(AIX, "unreliable on AIX")
+    def test_disk_usage(self):
+        def df(device):
+            out = sh("df -k %s" % device).strip()
+            line = out.split('\n')[1]
+            fields = line.split()
+            total = int(fields[1]) * 1024
+            used = int(fields[2]) * 1024
+            free = int(fields[3]) * 1024
+            percent = float(fields[4].replace('%', ''))
+            return (total, used, free, percent)
+
+        tolerance = 4 * 1024 * 1024  # 4MB
+        for part in psutil.disk_partitions(all=False):
+            usage = psutil.disk_usage(part.mountpoint)
+            try:
+                total, used, free, percent = df(part.device)
+            except RuntimeError as err:
+                # see:
+                # https://travis-ci.org/giampaolo/psutil/jobs/138338464
+                # https://travis-ci.org/giampaolo/psutil/jobs/138343361
+                err = str(err).lower()
+                if "no such file or directory" in err or \
+                        "raw devices not supported" in err or \
+                        "permission denied" in err:
+                    continue
+                else:
+                    raise
+            else:
+                self.assertAlmostEqual(usage.total, total, delta=tolerance)
+                self.assertAlmostEqual(usage.used, used, delta=tolerance)
+                self.assertAlmostEqual(usage.free, free, delta=tolerance)
+                self.assertAlmostEqual(usage.percent, percent, delta=1)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_process.py
@@ -0,0 +1,1565 @@
+#!/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.
+
+"""Tests for psutil.Process class."""
+
+import collections
+import errno
+import getpass
+import itertools
+import os
+import signal
+import socket
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+import types
+
+import psutil
+
+from psutil import AIX
+from psutil import BSD
+from psutil import LINUX
+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 long
+from psutil._compat import PY3
+from psutil.tests import APPVEYOR
+from psutil.tests import call_until
+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
+from psutil.tests import HAS_MEMORY_MAPS
+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_THREADS
+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 safe_rmpath
+from psutil.tests import sh
+from psutil.tests import skip_on_access_denied
+from psutil.tests import skip_on_not_implemented
+from psutil.tests import TESTFILE_PREFIX
+from psutil.tests import TESTFN
+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
+
+
+# ===================================================================
+# --- psutil.Process class tests
+# ===================================================================
+
+class TestProcess(unittest.TestCase):
+    """Tests for psutil.Process class."""
+
+    def setUp(self):
+        safe_rmpath(TESTFN)
+
+    def tearDown(self):
+        reap_children()
+
+    def test_pid(self):
+        p = psutil.Process()
+        self.assertEqual(p.pid, os.getpid())
+        sproc = get_test_subprocess()
+        self.assertEqual(psutil.Process(sproc.pid).pid, sproc.pid)
+        with self.assertRaises(AttributeError):
+            p.pid = 33
+
+    def test_kill(self):
+        sproc = get_test_subprocess()
+        test_pid = sproc.pid
+        p = psutil.Process(test_pid)
+        p.kill()
+        sig = p.wait()
+        self.assertFalse(psutil.pid_exists(test_pid))
+        if POSIX:
+            self.assertEqual(sig, -signal.SIGKILL)
+
+    def test_terminate(self):
+        sproc = get_test_subprocess()
+        test_pid = sproc.pid
+        p = psutil.Process(test_pid)
+        p.terminate()
+        sig = p.wait()
+        self.assertFalse(psutil.pid_exists(test_pid))
+        if POSIX:
+            self.assertEqual(sig, -signal.SIGTERM)
+
+    def test_send_signal(self):
+        sig = signal.SIGKILL if POSIX else signal.SIGTERM
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.send_signal(sig)
+        exit_sig = p.wait()
+        self.assertFalse(psutil.pid_exists(p.pid))
+        if POSIX:
+            self.assertEqual(exit_sig, -sig)
+            #
+            sproc = get_test_subprocess()
+            p = psutil.Process(sproc.pid)
+            p.send_signal(sig)
+            with mock.patch('psutil.os.kill',
+                            side_effect=OSError(errno.ESRCH, "")):
+                with self.assertRaises(psutil.NoSuchProcess):
+                    p.send_signal(sig)
+            #
+            sproc = get_test_subprocess()
+            p = psutil.Process(sproc.pid)
+            p.send_signal(sig)
+            with mock.patch('psutil.os.kill',
+                            side_effect=OSError(errno.EPERM, "")):
+                with self.assertRaises(psutil.AccessDenied):
+                    psutil.Process().send_signal(sig)
+            # Sending a signal to process with PID 0 is not allowed as
+            # it would affect every process in the process group of
+            # the calling process (os.getpid()) instead of PID 0").
+            if 0 in psutil.pids():
+                p = psutil.Process(0)
+                self.assertRaises(ValueError, p.send_signal, signal.SIGTERM)
+
+    def test_wait(self):
+        # check exit code signal
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.kill()
+        code = p.wait()
+        if POSIX:
+            self.assertEqual(code, -signal.SIGKILL)
+        else:
+            self.assertEqual(code, signal.SIGTERM)
+        self.assertFalse(p.is_running())
+
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.terminate()
+        code = p.wait()
+        if POSIX:
+            self.assertEqual(code, -signal.SIGTERM)
+        else:
+            self.assertEqual(code, signal.SIGTERM)
+        self.assertFalse(p.is_running())
+
+        # check sys.exit() code
+        code = "import time, sys; time.sleep(0.01); sys.exit(5);"
+        sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
+        p = psutil.Process(sproc.pid)
+        self.assertEqual(p.wait(), 5)
+        self.assertFalse(p.is_running())
+
+        # Test wait() issued twice.
+        # It is not supposed to raise NSP when the process is gone.
+        # On UNIX this should return None, on Windows it should keep
+        # returning the exit code.
+        sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
+        p = psutil.Process(sproc.pid)
+        self.assertEqual(p.wait(), 5)
+        self.assertIn(p.wait(), (5, None))
+
+        # test timeout
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.name()
+        self.assertRaises(psutil.TimeoutExpired, p.wait, 0.01)
+
+        # timeout < 0 not allowed
+        self.assertRaises(ValueError, p.wait, -1)
+
+    def test_wait_non_children(self):
+        # Test wait() against a process which is not our direct
+        # child.
+        p1, p2 = create_proc_children_pair()
+        self.assertRaises(psutil.TimeoutExpired, p1.wait, 0.01)
+        self.assertRaises(psutil.TimeoutExpired, p2.wait, 0.01)
+        # We also terminate the direct child otherwise the
+        # grandchild will hang until the parent is gone.
+        p1.terminate()
+        p2.terminate()
+        ret1 = p1.wait()
+        ret2 = p2.wait()
+        if POSIX:
+            self.assertEqual(ret1, -signal.SIGTERM)
+            # For processes which are not our children we're supposed
+            # to get None.
+            self.assertEqual(ret2, None)
+        else:
+            self.assertEqual(ret1, signal.SIGTERM)
+            self.assertEqual(ret1, signal.SIGTERM)
+
+    def test_wait_timeout_0(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        self.assertRaises(psutil.TimeoutExpired, p.wait, 0)
+        p.kill()
+        stop_at = time.time() + 2
+        while True:
+            try:
+                code = p.wait(0)
+            except psutil.TimeoutExpired:
+                if time.time() >= stop_at:
+                    raise
+            else:
+                break
+        if POSIX:
+            self.assertEqual(code, -signal.SIGKILL)
+        else:
+            self.assertEqual(code, signal.SIGTERM)
+        self.assertFalse(p.is_running())
+
+    def test_cpu_percent(self):
+        p = psutil.Process()
+        p.cpu_percent(interval=0.001)
+        p.cpu_percent(interval=0.001)
+        for x in range(100):
+            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)
+
+    def test_cpu_percent_numcpus_none(self):
+        # See: https://github.com/giampaolo/psutil/issues/1087
+        with mock.patch('psutil.cpu_count', return_value=None) as m:
+            psutil.Process().cpu_percent()
+            assert m.called
+
+    def test_cpu_times(self):
+        times = psutil.Process().cpu_times()
+        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
+        # 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)))
+
+    def test_cpu_times_2(self):
+        user_time, kernel_time = psutil.Process().cpu_times()[:2]
+        utime, ktime = os.times()[:2]
+
+        # Use os.times()[:2] as base values to compare our results
+        # using a tolerance  of +/- 0.1 seconds.
+        # It will fail if the difference between the values is > 0.1s.
+        if (max([user_time, utime]) - min([user_time, utime])) > 0.1:
+            self.fail("expected: %s, found: %s" % (utime, user_time))
+
+        if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1:
+            self.fail("expected: %s, found: %s" % (ktime, kernel_time))
+
+    @unittest.skipIf(not HAS_PROC_CPU_NUM, "not supported")
+    def test_cpu_num(self):
+        p = psutil.Process()
+        num = p.cpu_num()
+        self.assertGreaterEqual(num, 0)
+        if psutil.cpu_count() == 1:
+            self.assertEqual(num, 0)
+        self.assertIn(p.cpu_num(), range(psutil.cpu_count()))
+
+    def test_create_time(self):
+        sproc = get_test_subprocess()
+        now = time.time()
+        p = psutil.Process(sproc.pid)
+        create_time = p.create_time()
+
+        # Use time.time() as base value to compare our result using a
+        # tolerance of +/- 1 second.
+        # It will fail if the difference between the values is > 2s.
+        difference = abs(create_time - now)
+        if difference > 2:
+            self.fail("expected: %s, found: %s, difference: %s"
+                      % (now, create_time, difference))
+
+        # make sure returned value can be pretty printed with strftime
+        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')
+    def test_terminal(self):
+        terminal = psutil.Process().terminal()
+        if sys.stdin.isatty() or sys.stdout.isatty():
+            tty = os.path.realpath(sh('tty'))
+            self.assertEqual(terminal, tty)
+        else:
+            self.assertIsNone(terminal)
+
+    @unittest.skipIf(not HAS_PROC_IO_COUNTERS, 'not supported')
+    @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:
+            f.read()
+        io2 = p.io_counters()
+        if not BSD and not AIX:
+            self.assertGreater(io2.read_count, io1.read_count)
+            self.assertEqual(io2.write_count, io1.write_count)
+            if LINUX:
+                self.assertGreater(io2.read_chars, io1.read_chars)
+                self.assertEqual(io2.write_chars, io1.write_chars)
+        else:
+            self.assertGreaterEqual(io2.read_bytes, io1.read_bytes)
+            self.assertGreaterEqual(io2.write_bytes, io1.write_bytes)
+
+        # test writes
+        io1 = p.io_counters()
+        with tempfile.TemporaryFile(prefix=TESTFILE_PREFIX) as f:
+            if PY3:
+                f.write(bytes("x" * 1000000, 'ascii'))
+            else:
+                f.write("x" * 1000000)
+        io2 = p.io_counters()
+        self.assertGreaterEqual(io2.write_count, io1.write_count)
+        self.assertGreaterEqual(io2.write_bytes, io1.write_bytes)
+        self.assertGreaterEqual(io2.read_count, io1.read_count)
+        self.assertGreaterEqual(io2.read_bytes, io1.read_bytes)
+        if LINUX:
+            self.assertGreater(io2.write_chars, io1.write_chars)
+            self.assertGreaterEqual(io2.read_chars, io1.read_chars)
+
+        # sanity check
+        for i in range(len(io2)):
+            if BSD and i >= 2:
+                # On BSD read_bytes and write_bytes are always set to -1.
+                continue
+            self.assertGreaterEqual(io2[i], 0)
+            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")
+            self.assertRaisesRegex(
+                ValueError, "can't specify value with IOPRIO_CLASS_NONE",
+                p.ionice, psutil.IOPRIO_CLASS_NONE, 1)
+            self.assertRaisesRegex(
+                ValueError, "can't specify value with IOPRIO_CLASS_IDLE",
+                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)
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_get(self):
+        import resource
+        p = psutil.Process(os.getpid())
+        names = [x for x in dir(psutil) if x.startswith('RLIMIT')]
+        assert names, names
+        for name in names:
+            value = getattr(psutil, name)
+            self.assertGreaterEqual(value, 0)
+            if name in dir(resource):
+                self.assertEqual(value, getattr(resource, name))
+                # XXX - On PyPy RLIMIT_INFINITY returned by
+                # resource.getrlimit() is reported as a very big long
+                # number instead of -1. It looks like a bug with PyPy.
+                if PYPY:
+                    continue
+                self.assertEqual(p.rlimit(value), resource.getrlimit(value))
+            else:
+                ret = p.rlimit(value)
+                self.assertEqual(len(ret), 2)
+                self.assertGreaterEqual(ret[0], -1)
+                self.assertGreaterEqual(ret[1], -1)
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_set(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))
+        self.assertEqual(p.rlimit(psutil.RLIMIT_NOFILE), (5, 5))
+        # If pid is 0 prlimit() applies to the calling process and
+        # we don't want that.
+        with self.assertRaises(ValueError):
+            psutil._psplatform.Process(0).rlimit(0)
+        with self.assertRaises(ValueError):
+            p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5))
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit(self):
+        p = psutil.Process()
+        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)
+        try:
+            p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard))
+            with open(TESTFN, "wb") as f:
+                f.write(b"X" * 1024)
+            # write() or flush() doesn't always cause the exception
+            # but close() will.
+            with self.assertRaises(IOError) as exc:
+                with open(TESTFN, "wb") as f:
+                    f.write(b"X" * 1025)
+            self.assertEqual(exc.exception.errno if PY3 else exc.exception[0],
+                             errno.EFBIG)
+        finally:
+            p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))
+            self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard))
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_infinity(self):
+        # First set a limit, then re-set it by specifying INFINITY
+        # and assume we overridden the previous limit.
+        p = psutil.Process()
+        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)
+        try:
+            p.rlimit(psutil.RLIMIT_FSIZE, (1024, hard))
+            p.rlimit(psutil.RLIMIT_FSIZE, (psutil.RLIM_INFINITY, hard))
+            with open(TESTFN, "wb") as f:
+                f.write(b"X" * 2048)
+        finally:
+            p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))
+            self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard))
+
+    @unittest.skipIf(not HAS_RLIMIT, "not supported")
+    def test_rlimit_infinity_value(self):
+        # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really
+        # big number on a platform with large file support.  On these
+        # platforms we need to test that the get/setrlimit functions
+        # properly convert the number to a C long long and that the
+        # conversion doesn't raise an error.
+        p = psutil.Process()
+        soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)
+        self.assertEqual(psutil.RLIM_INFINITY, hard)
+        p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))
+
+    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)
+        p = psutil.Process()
+        if OPENBSD:
+            try:
+                step1 = p.num_threads()
+            except psutil.AccessDenied:
+                raise unittest.SkipTest("on OpenBSD this requires root access")
+        else:
+            step1 = p.num_threads()
+
+        with ThreadTask():
+            step2 = p.num_threads()
+            self.assertEqual(step2, step1 + 1)
+
+    @unittest.skipIf(not WINDOWS, 'WINDOWS only')
+    def test_num_handles(self):
+        # a better test is done later into test/_windows.py
+        p = psutil.Process()
+        self.assertGreater(p.num_handles(), 0)
+
+    @unittest.skipIf(not HAS_THREADS, 'not supported')
+    def test_threads(self):
+        p = psutil.Process()
+        if OPENBSD:
+            try:
+                step1 = p.threads()
+            except psutil.AccessDenied:
+                raise unittest.SkipTest("on OpenBSD this requires root access")
+        else:
+            step1 = p.threads()
+
+        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)
+    @unittest.skipIf(not HAS_THREADS, 'not supported')
+    def test_threads_2(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        if OPENBSD:
+            try:
+                p.threads()
+            except psutil.AccessDenied:
+                raise unittest.SkipTest(
+                    "on OpenBSD this requires root access")
+        self.assertAlmostEqual(
+            p.cpu_times().user,
+            sum([x.user_time for x in p.threads()]), delta=0.1)
+        self.assertAlmostEqual(
+            p.cpu_times().system,
+            sum([x.system_time for x in p.threads()]), delta=0.1)
+
+    def test_memory_info(self):
+        p = psutil.Process()
+
+        # step 1 - get a base value to compare our results
+        rss1, vms1 = p.memory_info()[:2]
+        percent1 = p.memory_percent()
+        self.assertGreater(rss1, 0)
+        self.assertGreater(vms1, 0)
+
+        # step 2 - allocate some memory
+        memarr = [None] * 1500000
+
+        rss2, vms2 = p.memory_info()[:2]
+        percent2 = p.memory_percent()
+
+        # step 3 - make sure that the memory usage bumped up
+        self.assertGreater(rss2, rss1)
+        self.assertGreaterEqual(vms2, vms1)  # vms might be equal
+        self.assertGreater(percent2, percent1)
+        del memarr
+
+        if WINDOWS:
+            mem = p.memory_info()
+            self.assertEqual(mem.rss, mem.wset)
+            self.assertEqual(mem.vms, mem.pagefile)
+
+        mem = p.memory_info()
+        for name in mem._fields:
+            self.assertGreaterEqual(getattr(mem, name), 0)
+
+    def test_memory_full_info(self):
+        total = psutil.virtual_memory().total
+        mem = psutil.Process().memory_full_info()
+        for name in mem._fields:
+            value = getattr(mem, name)
+            self.assertGreaterEqual(value, 0, msg=(name, value))
+            self.assertLessEqual(value, total, msg=(name, value, total))
+        if LINUX or WINDOWS or OSX:
+            self.assertGreaterEqual(mem.uss, 0)
+        if LINUX:
+            self.assertGreaterEqual(mem.pss, 0)
+            self.assertGreaterEqual(mem.swap, 0)
+
+    @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
+    def test_memory_maps(self):
+        p = psutil.Process()
+        maps = p.memory_maps()
+        paths = [x for x in maps]
+        self.assertEqual(len(paths), len(set(paths)))
+        ext_maps = p.memory_maps(grouped=False)
+
+        for nt in maps:
+            if not nt.path.startswith('['):
+                assert os.path.isabs(nt.path), nt.path
+                if POSIX:
+                    try:
+                        assert os.path.exists(nt.path) or \
+                            os.path.islink(nt.path), nt.path
+                    except AssertionError:
+                        if not LINUX:
+                            raise
+                        else:
+                            # https://github.com/giampaolo/psutil/issues/759
+                            with open('/proc/self/smaps') as f:
+                                data = f.read()
+                            if "%s (deleted)" % nt.path not in data:
+                                raise
+                else:
+                    # XXX - On Windows we have this strange behavior with
+                    # 64 bit dlls: they are visible via explorer but cannot
+                    # be accessed via os.stat() (wtf?).
+                    if '64' not in os.path.basename(nt.path):
+                        assert os.path.exists(nt.path), nt.path
+        for nt in ext_maps:
+            for fname in nt._fields:
+                value = getattr(nt, fname)
+                if fname == 'path':
+                    continue
+                elif fname in ('addr', 'perms'):
+                    assert value, value
+                else:
+                    self.assertIsInstance(value, (int, long))
+                    assert value >= 0, value
+
+    @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
+    def test_memory_maps_lists_lib(self):
+        # Make sure a newly loaded shared lib is listed.
+        with copyload_shared_lib() as path:
+            def normpath(p):
+                return os.path.realpath(os.path.normcase(p))
+            libpaths = [normpath(x.path)
+                        for x in psutil.Process().memory_maps()]
+            self.assertIn(normpath(path), libpaths)
+
+    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
+        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
+
+    def test_is_running(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        assert p.is_running()
+        assert p.is_running()
+        p.kill()
+        p.wait()
+        assert not p.is_running()
+        assert not p.is_running()
+
+    def test_exe(self):
+        sproc = get_test_subprocess()
+        exe = psutil.Process(sproc.pid).exe()
+        try:
+            self.assertEqual(exe, PYTHON_EXE)
+        except AssertionError:
+            if WINDOWS and len(exe) == len(PYTHON_EXE):
+                # on Windows we don't care about case sensitivity
+                normcase = os.path.normcase
+                self.assertEqual(normcase(exe), normcase(PYTHON_EXE))
+            else:
+                # certain platforms such as BSD are more accurate returning:
+                # "/usr/local/bin/python2.7"
+                # ...instead of:
+                # "/usr/local/bin/python"
+                # We do not want to consider this difference in accuracy
+                # an error.
+                ver = "%s.%s" % (sys.version_info[0], sys.version_info[1])
+                try:
+                    self.assertEqual(exe.replace(ver, ''),
+                                     PYTHON_EXE.replace(ver, ''))
+                except AssertionError:
+                    # Tipically OSX. Really not sure what to do here.
+                    pass
+
+        out = sh([exe, "-c", "import os; print('hey')"])
+        self.assertEqual(out, 'hey')
+
+    def test_cmdline(self):
+        cmdline = [PYTHON_EXE, "-c", "import time; time.sleep(60)"]
+        sproc = get_test_subprocess(cmdline)
+        try:
+            self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()),
+                             ' '.join(cmdline))
+        except AssertionError:
+            # XXX - most of the times the underlying sysctl() call on Net
+            # and Open BSD returns a truncated string.
+            # Also /proc/pid/cmdline behaves the same so it looks
+            # like this is a kernel bug.
+            # XXX - AIX truncates long arguments in /proc/pid/cmdline
+            if NETBSD or OPENBSD or AIX:
+                self.assertEqual(
+                    psutil.Process(sproc.pid).cmdline()[0], PYTHON_EXE)
+            else:
+                raise
+
+    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)
+
+    # XXX
+    @unittest.skipIf(SUNOS, "broken on SUNOS")
+    @unittest.skipIf(AIX, "broken on AIX")
+    def test_prog_w_funky_name(self):
+        # Test that name(), exe() and cmdline() correctly handle programs
+        # with funky chars such as spaces and ")", see:
+        # https://github.com/giampaolo/psutil/issues/628
+
+        def rm():
+            # Try to limit occasional failures on Appveyor:
+            # https://ci.appveyor.com/project/giampaolo/psutil/build/1350/
+            #     job/lbo3bkju55le850n
+            try:
+                safe_rmpath(funky_path)
+            except OSError:
+                pass
+
+        funky_path = TESTFN + 'foo bar )'
+        create_exe(funky_path)
+        self.addCleanup(rm)
+        cmdline = [funky_path, "-c",
+                   "import time; [time.sleep(0.01) for x in range(3000)];"
+                   "arg1", "arg2", "", "arg3", ""]
+        sproc = get_test_subprocess(cmdline)
+        p = psutil.Process(sproc.pid)
+        # ...in order to try to prevent occasional failures on travis
+        if TRAVIS:
+            wait_for_pid(p.pid)
+        self.assertEqual(p.cmdline(), cmdline)
+        self.assertEqual(p.name(), os.path.basename(funky_path))
+        self.assertEqual(os.path.normcase(p.exe()),
+                         os.path.normcase(funky_path))
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_uids(self):
+        p = psutil.Process()
+        real, effective, saved = p.uids()
+        # os.getuid() refers to "real" uid
+        self.assertEqual(real, os.getuid())
+        # os.geteuid() refers to "effective" uid
+        self.assertEqual(effective, os.geteuid())
+        # No such thing as os.getsuid() ("saved" uid), but starting
+        # from python 2.7 we have os.getresuid() which returns all
+        # of them.
+        if hasattr(os, "getresuid"):
+            self.assertEqual(os.getresuid(), p.uids())
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_gids(self):
+        p = psutil.Process()
+        real, effective, saved = p.gids()
+        # os.getuid() refers to "real" uid
+        self.assertEqual(real, os.getgid())
+        # os.geteuid() refers to "effective" uid
+        self.assertEqual(effective, os.getegid())
+        # No such thing as os.getsgid() ("saved" gid), but starting
+        # from python 2.7 we have os.getresgid() which returns all
+        # of them.
+        if hasattr(os, "getresuid"):
+            self.assertEqual(os.getresgid(), p.gids())
+
+    def test_nice(self):
+        p = psutil.Process()
+        self.assertRaises(TypeError, p.nice, "str")
+        if WINDOWS:
+            try:
+                init = p.nice()
+                if sys.version_info > (3, 4):
+                    self.assertIsInstance(init, enum.IntEnum)
+                else:
+                    self.assertIsInstance(init, int)
+                self.assertEqual(init, psutil.NORMAL_PRIORITY_CLASS)
+                p.nice(psutil.HIGH_PRIORITY_CLASS)
+                self.assertEqual(p.nice(), psutil.HIGH_PRIORITY_CLASS)
+                p.nice(psutil.NORMAL_PRIORITY_CLASS)
+                self.assertEqual(p.nice(), psutil.NORMAL_PRIORITY_CLASS)
+            finally:
+                p.nice(psutil.NORMAL_PRIORITY_CLASS)
+        else:
+            first_nice = p.nice()
+            try:
+                if hasattr(os, "getpriority"):
+                    self.assertEqual(
+                        os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice())
+                p.nice(1)
+                self.assertEqual(p.nice(), 1)
+                if hasattr(os, "getpriority"):
+                    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:
+                    p.nice(0)
+                    self.assertEqual(p.nice(), 0)
+            except psutil.AccessDenied:
+                pass
+            finally:
+                try:
+                    p.nice(first_nice)
+                except psutil.AccessDenied:
+                    pass
+
+    def test_status(self):
+        p = psutil.Process()
+        self.assertEqual(p.status(), psutil.STATUS_RUNNING)
+
+    def test_username(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        username = p.username()
+        if WINDOWS:
+            domain, username = username.split('\\')
+            self.assertEqual(username, getpass.getuser())
+            if 'USERDOMAIN' in os.environ:
+                self.assertEqual(domain, os.environ['USERDOMAIN'])
+        else:
+            self.assertEqual(username, getpass.getuser())
+
+    def test_cwd(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        self.assertEqual(p.cwd(), os.getcwd())
+
+    def test_cwd_2(self):
+        cmd = [PYTHON_EXE, "-c",
+               "import os, time; os.chdir('..'); time.sleep(60)"]
+        sproc = get_test_subprocess(cmd)
+        p = psutil.Process(sproc.pid)
+        call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
+
+    @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported')
+    def test_cpu_affinity(self):
+        p = psutil.Process()
+        initial = p.cpu_affinity()
+        assert initial, initial
+        self.addCleanup(p.cpu_affinity, initial)
+
+        if hasattr(os, "sched_getaffinity"):
+            self.assertEqual(initial, list(os.sched_getaffinity(p.pid)))
+        self.assertEqual(len(initial), len(set(initial)))
+
+        all_cpus = list(range(len(psutil.cpu_percent(percpu=True))))
+        # Work around travis failure:
+        # https://travis-ci.org/giampaolo/psutil/builds/284173194
+        for n in all_cpus if not TRAVIS else initial:
+            p.cpu_affinity([n])
+            self.assertEqual(p.cpu_affinity(), [n])
+            if hasattr(os, "sched_getaffinity"):
+                self.assertEqual(p.cpu_affinity(),
+                                 list(os.sched_getaffinity(p.pid)))
+            # also test num_cpu()
+            if hasattr(p, "num_cpu"):
+                self.assertEqual(p.cpu_affinity()[0], p.num_cpu())
+
+        # [] is an alias for "all eligible CPUs"; on Linux this may
+        # not be equal to all available CPUs, see:
+        # https://github.com/giampaolo/psutil/issues/956
+        p.cpu_affinity([])
+        if LINUX:
+            self.assertEqual(p.cpu_affinity(), p._proc._get_eligible_cpus())
+        else:
+            self.assertEqual(p.cpu_affinity(), all_cpus)
+        if hasattr(os, "sched_getaffinity"):
+            self.assertEqual(p.cpu_affinity(),
+                             list(os.sched_getaffinity(p.pid)))
+        #
+        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))
+
+    @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported')
+    def test_cpu_affinity_errs(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10]
+        self.assertRaises(ValueError, p.cpu_affinity, invalid_cpu)
+        self.assertRaises(ValueError, p.cpu_affinity, range(10000, 11000))
+        self.assertRaises(TypeError, p.cpu_affinity, [0, "1"])
+        self.assertRaises(ValueError, p.cpu_affinity, [0, -1])
+
+    @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported')
+    def test_cpu_affinity_all_combinations(self):
+        p = psutil.Process()
+        initial = p.cpu_affinity()
+        assert initial, initial
+        self.addCleanup(p.cpu_affinity, initial)
+
+        # All possible CPU set combinations.
+        combos = []
+        for l in range(0, len(initial) + 1):
+            for subset in itertools.combinations(initial, l):
+                if subset:
+                    combos.append(list(subset))
+
+        for combo in combos:
+            p.cpu_affinity(combo)
+            self.assertEqual(p.cpu_affinity(), combo)
+
+    # TODO: #595
+    @unittest.skipIf(BSD, "broken on BSD")
+    # can't find any process file on Appveyor
+    @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
+    def test_open_files(self):
+        # current process
+        p = psutil.Process()
+        files = p.open_files()
+        self.assertFalse(TESTFN in files)
+        with open(TESTFN, 'wb') as f:
+            f.write(b'x' * 1024)
+            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:
+                        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
+
+        # another process
+        cmdline = "import time; f = open(r'%s', 'r'); time.sleep(60);" % TESTFN
+        sproc = get_test_subprocess([PYTHON_EXE, "-c", cmdline])
+        p = psutil.Process(sproc.pid)
+
+        for x in range(100):
+            filenames = [x.path for x in p.open_files()]
+            if TESTFN in filenames:
+                break
+            time.sleep(.01)
+        else:
+            self.assertIn(TESTFN, filenames)
+        for file in filenames:
+            assert os.path.isfile(file), file
+
+    # TODO: #595
+    @unittest.skipIf(BSD, "broken on BSD")
+    # can't find any process file on Appveyor
+    @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
+    def test_open_files_2(self):
+        # test fd and path fields
+        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():
+                    break
+            else:
+                self.fail("no file found; files=%s" % repr(p.open_files()))
+            self.assertEqual(file.path, fileobj.name)
+            if WINDOWS:
+                self.assertEqual(file.fd, -1)
+            else:
+                self.assertEqual(file.fd, fileobj.fileno())
+            # test positions
+            ntuple = p.open_files()[0]
+            self.assertEqual(ntuple[0], ntuple.path)
+            self.assertEqual(ntuple[1], ntuple.fd)
+            # test file is gone
+            self.assertNotIn(fileobj.name, p.open_files())
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_num_fds(self):
+        p = psutil.Process()
+        start = p.num_fds()
+        file = open(TESTFN, 'w')
+        self.addCleanup(file.close)
+        self.assertEqual(p.num_fds(), start + 1)
+        sock = socket.socket()
+        self.addCleanup(sock.close)
+        self.assertEqual(p.num_fds(), start + 2)
+        file.close()
+        sock.close()
+        self.assertEqual(p.num_fds(), start)
+
+    @skip_on_not_implemented(only_if=LINUX)
+    @unittest.skipIf(OPENBSD or NETBSD, "not reliable on OPENBSD & NETBSD")
+    def test_num_ctx_switches(self):
+        p = psutil.Process()
+        before = sum(p.num_ctx_switches())
+        for x in range(500000):
+            after = sum(p.num_ctx_switches())
+            if after > before:
+                return
+        self.fail("num ctx switches still the same after 50.000 iterations")
+
+    def test_ppid(self):
+        if hasattr(os, 'getppid'):
+            self.assertEqual(psutil.Process().ppid(), os.getppid())
+        this_parent = os.getpid()
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        self.assertEqual(p.ppid(), this_parent)
+        # no other process is supposed to have us as parent
+        reap_children(recursive=True)
+        if APPVEYOR:
+            # Occasional failures, see:
+            # https://ci.appveyor.com/project/giampaolo/psutil/build/
+            #     job/0hs623nenj7w4m33
+            return
+        for p in psutil.process_iter():
+            if p.pid == sproc.pid:
+                continue
+            # XXX: sometimes this fails on Windows; not sure why.
+            self.assertNotEqual(p.ppid(), this_parent, msg=p)
+
+    def test_parent(self):
+        this_parent = os.getpid()
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        self.assertEqual(p.parent().pid, this_parent)
+
+    def test_parent_disappeared(self):
+        # Emulate a case where the parent process disappeared.
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        with mock.patch("psutil.Process",
+                        side_effect=psutil.NoSuchProcess(0, 'foo')):
+            self.assertIsNone(p.parent())
+
+    def test_children(self):
+        p = psutil.Process()
+        self.assertEqual(p.children(), [])
+        self.assertEqual(p.children(recursive=True), [])
+        # On Windows we set the flag to 0 in order to cancel out the
+        # CREATE_NO_WINDOW flag (enabled by default) which creates
+        # an extra "conhost.exe" child.
+        sproc = get_test_subprocess(creationflags=0)
+        children1 = p.children()
+        children2 = p.children(recursive=True)
+        for children in (children1, children2):
+            self.assertEqual(len(children), 1)
+            self.assertEqual(children[0].pid, sproc.pid)
+            self.assertEqual(children[0].ppid(), os.getpid())
+
+    def test_children_recursive(self):
+        # Test children() against two sub processes, p1 and p2, where
+        # p1 (our child) spawned p2 (our grandchild).
+        p1, p2 = create_proc_children_pair()
+        p = psutil.Process()
+        self.assertEqual(p.children(), [p1])
+        self.assertEqual(p.children(recursive=True), [p1, p2])
+        # If the intermediate process is gone there's no way for
+        # children() to recursively find it.
+        p1.terminate()
+        p1.wait()
+        self.assertEqual(p.children(recursive=True), [])
+
+    def test_children_duplicates(self):
+        # find the process which has the highest number of children
+        table = collections.defaultdict(int)
+        for p in psutil.process_iter():
+            try:
+                table[p.ppid()] += 1
+            except psutil.Error:
+                pass
+        # this is the one, now let's make sure there are no duplicates
+        pid = sorted(table.items(), key=lambda x: x[1])[-1][0]
+        p = psutil.Process(pid)
+        try:
+            c = p.children(recursive=True)
+        except psutil.AccessDenied:  # windows
+            pass
+        else:
+            self.assertEqual(len(c), len(set(c)))
+
+    def test_suspend_resume(self):
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.suspend()
+        for x in range(100):
+            if p.status() == psutil.STATUS_STOPPED:
+                break
+            time.sleep(0.01)
+        p.resume()
+        self.assertNotEqual(p.status(), psutil.STATUS_STOPPED)
+
+    def test_invalid_pid(self):
+        self.assertRaises(TypeError, psutil.Process, "1")
+        self.assertRaises(ValueError, psutil.Process, -1)
+
+    def test_as_dict(self):
+        p = psutil.Process()
+        d = p.as_dict(attrs=['exe', 'name'])
+        self.assertEqual(sorted(d.keys()), ['exe', 'name'])
+
+        p = psutil.Process(min(psutil.pids()))
+        d = p.as_dict(attrs=['connections'], ad_value='foo')
+        if not isinstance(d['connections'], list):
+            self.assertEqual(d['connections'], 'foo')
+
+        # Test ad_value is set on AccessDenied.
+        with mock.patch('psutil.Process.nice', create=True,
+                        side_effect=psutil.AccessDenied):
+            self.assertEqual(
+                p.as_dict(attrs=["nice"], ad_value=1), {"nice": 1})
+
+        # Test that NoSuchProcess bubbles up.
+        with mock.patch('psutil.Process.nice', create=True,
+                        side_effect=psutil.NoSuchProcess(p.pid, "name")):
+            self.assertRaises(
+                psutil.NoSuchProcess, p.as_dict, attrs=["nice"])
+
+        # Test that ZombieProcess is swallowed.
+        with mock.patch('psutil.Process.nice', create=True,
+                        side_effect=psutil.ZombieProcess(p.pid, "name")):
+            self.assertEqual(
+                p.as_dict(attrs=["nice"], ad_value="foo"), {"nice": "foo"})
+
+        # By default APIs raising NotImplementedError are
+        # supposed to be skipped.
+        with mock.patch('psutil.Process.nice', create=True,
+                        side_effect=NotImplementedError):
+            d = p.as_dict()
+            self.assertNotIn('nice', list(d.keys()))
+            # ...unless the user explicitly asked for some attr.
+            with self.assertRaises(NotImplementedError):
+                p.as_dict(attrs=["nice"])
+
+        # errors
+        with self.assertRaises(TypeError):
+            p.as_dict('name')
+        with self.assertRaises(ValueError):
+            p.as_dict(['foo'])
+        with self.assertRaises(ValueError):
+            p.as_dict(['foo', 'bar'])
+
+    def test_oneshot(self):
+        with mock.patch("psutil._psplatform.Process.cpu_times") as m:
+            p = psutil.Process()
+            with p.oneshot():
+                p.cpu_times()
+                p.cpu_times()
+            self.assertEqual(m.call_count, 1)
+
+        with mock.patch("psutil._psplatform.Process.cpu_times") as m:
+            p.cpu_times()
+            p.cpu_times()
+        self.assertEqual(m.call_count, 2)
+
+    def test_oneshot_twice(self):
+        # Test the case where the ctx manager is __enter__ed twice.
+        # The second __enter__ is supposed to resut in a NOOP.
+        with mock.patch("psutil._psplatform.Process.cpu_times") as m1:
+            with mock.patch("psutil._psplatform.Process.oneshot_enter") as m2:
+                p = psutil.Process()
+                with p.oneshot():
+                    p.cpu_times()
+                    p.cpu_times()
+                    with p.oneshot():
+                        p.cpu_times()
+                        p.cpu_times()
+                self.assertEqual(m1.call_count, 1)
+                self.assertEqual(m2.call_count, 1)
+
+        with mock.patch("psutil._psplatform.Process.cpu_times") as m:
+            p.cpu_times()
+            p.cpu_times()
+        self.assertEqual(m.call_count, 2)
+
+    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)
+        # >>> time.sleep(2)  # time-consuming task, process dies in meantime
+        #  >>> proc.name()
+        # Refers to Issue #15
+        sproc = get_test_subprocess()
+        p = psutil.Process(sproc.pid)
+        p.terminate()
+        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)
+
+        excluded_names = ['pid', 'is_running', 'wait', 'create_time',
+                          'oneshot', 'memory_info_ex']
+        if LINUX and not HAS_RLIMIT:
+            excluded_names.append('rlimit')
+        for name in dir(p):
+            if (name.startswith('_') or
+                    name in excluded_names):
+                continue
+            try:
+                meth = getattr(p, name)
+                # get/set methods
+                if name == 'nice':
+                    if POSIX:
+                        ret = meth(1)
+                    else:
+                        ret = meth(psutil.NORMAL_PRIORITY_CLASS)
+                elif name == 'ionice':
+                    ret = meth()
+                    ret = meth(2)
+                elif name == 'rlimit':
+                    ret = meth(psutil.RLIMIT_NOFILE)
+                    ret = meth(psutil.RLIMIT_NOFILE, (5, 5))
+                elif name == 'cpu_affinity':
+                    ret = meth()
+                    ret = meth([0])
+                elif name == 'send_signal':
+                    ret = meth(signal.SIGTERM)
+                else:
+                    ret = meth()
+            except psutil.ZombieProcess:
+                self.fail("ZombieProcess for %r was not supposed to happen" %
+                          name)
+            except psutil.NoSuchProcess:
+                pass
+            except psutil.AccessDenied:
+                if OPENBSD and name in ('threads', 'num_threads'):
+                    pass
+                else:
+                    raise
+            except NotImplementedError:
+                pass
+            else:
+                self.fail(
+                    "NoSuchProcess exception not raised for %r, retval=%s" % (
+                        name, ret))
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_zombie_process(self):
+        def succeed_or_zombie_p_exc(fun, *args, **kwargs):
+            try:
+                return fun(*args, **kwargs)
+            except (psutil.ZombieProcess, psutil.AccessDenied):
+                pass
+
+        zpid = create_zombie_proc()
+        self.addCleanup(reap_children, recursive=True)
+        # A zombie process should always be instantiable
+        zproc = psutil.Process(zpid)
+        # ...and at least its status always be querable
+        self.assertEqual(zproc.status(), psutil.STATUS_ZOMBIE)
+        # ...and it should be considered 'running'
+        self.assertTrue(zproc.is_running())
+        # ...and as_dict() shouldn't crash
+        zproc.as_dict()
+        # if cmdline succeeds it should be an empty list
+        ret = succeed_or_zombie_p_exc(zproc.suspend)
+        if ret is not None:
+            self.assertEqual(ret, [])
+
+        if hasattr(zproc, "rlimit"):
+            succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE)
+            succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE,
+                                    (5, 5))
+        # set methods
+        succeed_or_zombie_p_exc(zproc.parent)
+        if hasattr(zproc, 'cpu_affinity'):
+            try:
+                succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+            except ValueError as err:
+                if TRAVIS and LINUX and "not eligible" in str(err):
+                    # https://travis-ci.org/giampaolo/psutil/jobs/279890461
+                    pass
+                else:
+                    raise
+
+        succeed_or_zombie_p_exc(zproc.nice, 0)
+        if hasattr(zproc, 'ionice'):
+            if LINUX:
+                succeed_or_zombie_p_exc(zproc.ionice, 2, 0)
+            else:
+                succeed_or_zombie_p_exc(zproc.ionice, 0)  # Windows
+        if hasattr(zproc, 'rlimit'):
+            succeed_or_zombie_p_exc(zproc.rlimit,
+                                    psutil.RLIMIT_NOFILE, (5, 5))
+        succeed_or_zombie_p_exc(zproc.suspend)
+        succeed_or_zombie_p_exc(zproc.resume)
+        succeed_or_zombie_p_exc(zproc.terminate)
+        succeed_or_zombie_p_exc(zproc.kill)
+
+        # ...its parent should 'see' it
+        # edit: not true on BSD and OSX
+        # descendants = [x.pid for x in psutil.Process().children(
+        #                recursive=True)]
+        # self.assertIn(zpid, descendants)
+        # XXX should we also assume ppid be usable?  Note: this
+        # would be an important use case as the only way to get
+        # rid of a zombie is to kill its parent.
+        # 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:
+            # For some reason this started failing all of the sudden.
+            # Maybe they upgraded OSX 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()])
+            psutil._pmap = {}
+            self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_zombie_process_is_running_w_exc(self):
+        # Emulate a case where internally is_running() raises
+        # ZombieProcess.
+        p = psutil.Process()
+        with mock.patch("psutil.Process",
+                        side_effect=psutil.ZombieProcess(0)) as m:
+            assert p.is_running()
+            assert m.called
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_zombie_process_status_w_exc(self):
+        # Emulate a case where internally status() raises
+        # ZombieProcess.
+        p = psutil.Process()
+        with mock.patch("psutil._psplatform.Process.status",
+                        side_effect=psutil.ZombieProcess(0)) as m:
+            self.assertEqual(p.status(), psutil.STATUS_ZOMBIE)
+            assert m.called
+
+    def test_pid_0(self):
+        # Process(0) is supposed to work on all platforms except Linux
+        if 0 not in psutil.pids():
+            self.assertRaises(psutil.NoSuchProcess, psutil.Process, 0)
+            return
+
+        # test all methods
+        p = psutil.Process(0)
+        for name in psutil._as_dict_attrnames:
+            if name == 'pid':
+                continue
+            meth = getattr(p, name)
+            try:
+                ret = meth()
+            except psutil.AccessDenied:
+                pass
+            else:
+                if name in ("uids", "gids"):
+                    self.assertEqual(ret.real, 0)
+                elif name == "username":
+                    if POSIX:
+                        self.assertEqual(p.username(), 'root')
+                    elif WINDOWS:
+                        self.assertEqual(p.username(), 'NT AUTHORITY\\SYSTEM')
+                elif name == "name":
+                    assert name, name
+
+        if hasattr(p, 'rlimit'):
+            try:
+                p.rlimit(psutil.RLIMIT_FSIZE)
+            except psutil.AccessDenied:
+                pass
+
+        p.as_dict()
+
+        if not OPENBSD:
+            self.assertIn(0, psutil.pids())
+            self.assertTrue(psutil.pid_exists(0))
+
+    @unittest.skipIf(not HAS_ENVIRON, "not supported")
+    def test_environ(self):
+        def clean_dict(d):
+            # Most of these are problematic on Travis.
+            d.pop("PSUTIL_TESTING", None)
+            d.pop("PLAT", None)
+            d.pop("HOME", None)
+            if OSX:
+                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()])
+
+        self.maxDiff = None
+        p = psutil.Process()
+        d1 = clean_dict(p.environ())
+        d2 = clean_dict(os.environ.copy())
+        self.assertEqual(d1, d2)
+
+    @unittest.skipIf(not HAS_ENVIRON, "not supported")
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_weird_environ(self):
+        # environment variables can contain values without an equals sign
+        code = textwrap.dedent("""
+            #include <unistd.h>
+            #include <fcntl.h>
+            char * const argv[] = {"cat", 0};
+            char * const envp[] = {"A=1", "X", "C=3", 0};
+            int main(void) {
+                /* Close stderr on exec so parent can wait for the execve to
+                 * finish. */
+                if (fcntl(2, F_SETFD, FD_CLOEXEC) != 0)
+                    return 0;
+                return execve("/bin/cat", argv, envp);
+            }
+            """)
+        path = TESTFN
+        create_exe(path, c_code=code)
+        self.addCleanup(safe_rmpath, path)
+        sproc = get_test_subprocess([path],
+                                    stdin=subprocess.PIPE,
+                                    stderr=subprocess.PIPE)
+        p = psutil.Process(sproc.pid)
+        wait_for_pid(p.pid)
+        self.assertTrue(p.is_running())
+        # Wait for process to exec or exit.
+        self.assertEqual(sproc.stderr.read(), b"")
+        self.assertEqual(p.environ(), {"A": "1", "C": "3"})
+        sproc.communicate()
+        self.assertEqual(sproc.returncode, 0)
+
+
+# ===================================================================
+# --- Limited user tests
+# ===================================================================
+
+
+if POSIX and os.getuid() == 0:
+    class LimitedUserTestCase(TestProcess):
+        """Repeat the previous tests by using a limited user.
+        Executed only on UNIX and only if the user who run the test script
+        is root.
+        """
+        # the uid/gid the test suite runs under
+        if hasattr(os, 'getuid'):
+            PROCESS_UID = os.getuid()
+            PROCESS_GID = os.getgid()
+
+        def __init__(self, *args, **kwargs):
+            TestProcess.__init__(self, *args, **kwargs)
+            # re-define all existent test methods in order to
+            # ignore AccessDenied exceptions
+            for attr in [x for x in dir(self) if x.startswith('test')]:
+                meth = getattr(self, attr)
+
+                def test_(self):
+                    try:
+                        meth()
+                    except psutil.AccessDenied:
+                        pass
+                setattr(self, attr, types.MethodType(test_, self))
+
+        def setUp(self):
+            safe_rmpath(TESTFN)
+            TestProcess.setUp(self)
+            os.setegid(1000)
+            os.seteuid(1000)
+
+        def tearDown(self):
+            os.setegid(self.PROCESS_UID)
+            os.seteuid(self.PROCESS_GID)
+            TestProcess.tearDown(self)
+
+        def test_nice(self):
+            try:
+                psutil.Process().nice(-1)
+            except psutil.AccessDenied:
+                pass
+            else:
+                self.fail("exception not raised")
+
+        def test_zombie_process(self):
+            # causes problems if test test suite is run as root
+            pass
+
+
+# ===================================================================
+# --- psutil.Popen tests
+# ===================================================================
+
+
+class TestPopen(unittest.TestCase):
+    """Tests for psutil.Popen class."""
+
+    def tearDown(self):
+        reap_children()
+
+    def test_misc(self):
+        # XXX this test causes a ResourceWarning on Python 3 because
+        # psutil.__subproc instance doesn't get propertly freed.
+        # Not sure what to do though.
+        cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
+        with psutil.Popen(cmd, stdout=subprocess.PIPE,
+                          stderr=subprocess.PIPE) as proc:
+            proc.name()
+            proc.cpu_times()
+            proc.stdin
+            self.assertTrue(dir(proc))
+            self.assertRaises(AttributeError, getattr, proc, 'foo')
+            proc.terminate()
+
+    def test_ctx_manager(self):
+        with psutil.Popen([PYTHON_EXE, "-V"],
+                          stdout=subprocess.PIPE,
+                          stderr=subprocess.PIPE,
+                          stdin=subprocess.PIPE) as proc:
+            proc.communicate()
+        assert proc.stdout.closed
+        assert proc.stderr.closed
+        assert proc.stdin.closed
+        self.assertEqual(proc.returncode, 0)
+
+    def test_kill_terminate(self):
+        # subprocess.Popen()'s terminate(), kill() and send_signal() do
+        # not raise exception after the process is gone. psutil.Popen
+        # diverges from that.
+        cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
+        with psutil.Popen(cmd, stdout=subprocess.PIPE,
+                          stderr=subprocess.PIPE) as proc:
+            proc.terminate()
+            proc.wait()
+            self.assertRaises(psutil.NoSuchProcess, proc.terminate)
+            self.assertRaises(psutil.NoSuchProcess, proc.kill)
+            self.assertRaises(psutil.NoSuchProcess, proc.send_signal,
+                              signal.SIGTERM)
+            if WINDOWS and sys.version_info >= (2, 7):
+                self.assertRaises(psutil.NoSuchProcess, proc.send_signal,
+                                  signal.CTRL_C_EVENT)
+                self.assertRaises(psutil.NoSuchProcess, proc.send_signal,
+                                  signal.CTRL_BREAK_EVENT)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_sunos.py
@@ -0,0 +1,45 @@
+#!/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.
+
+"""Sun OS specific tests."""
+
+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
+
+
+@unittest.skipIf(not SUNOS, "SUNOS only")
+class SunOSSpecificTestCase(unittest.TestCase):
+
+    def test_swap_memory(self):
+        out = sh('env PATH=/usr/sbin:/sbin:%s swap -l' % os.environ['PATH'])
+        lines = out.strip().split('\n')[1:]
+        if not lines:
+            raise ValueError('no swap device(s) configured')
+        total = free = 0
+        for line in lines:
+            line = line.split()
+            t, f = line[-2:]
+            total += int(int(t) * 512)
+            free += int(int(f) * 512)
+        used = total - free
+
+        psutil_swap = psutil.swap_memory()
+        self.assertEqual(psutil_swap.total, total)
+        self.assertEqual(psutil_swap.used, used)
+        self.assertEqual(psutil_swap.free, free)
+
+    def test_cpu_count(self):
+        out = sh("/usr/sbin/psrinfo")
+        self.assertEqual(psutil.cpu_count(), len(out.split('\n')))
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_system.py
@@ -0,0 +1,862 @@
+#!/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.
+
+"""Tests for system APIS."""
+
+import contextlib
+import datetime
+import errno
+import os
+import pprint
+import shutil
+import signal
+import socket
+import sys
+import tempfile
+import time
+
+import psutil
+from psutil import AIX
+from psutil import BSD
+from psutil import FREEBSD
+from psutil import LINUX
+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 long
+from psutil.tests import APPVEYOR
+from psutil.tests import ASCII_FS
+from psutil.tests import check_net_address
+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_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 reap_children
+from psutil.tests import retry_before_failing
+from psutil.tests import run_test_module_by_name
+from psutil.tests import safe_rmpath
+from psutil.tests import TESTFN
+from psutil.tests import TESTFN_UNICODE
+from psutil.tests import TRAVIS
+from psutil.tests import unittest
+
+
+# ===================================================================
+# --- System-related API tests
+# ===================================================================
+
+
+class TestSystemAPIs(unittest.TestCase):
+    """Tests for system-related APIs."""
+
+    def setUp(self):
+        safe_rmpath(TESTFN)
+
+    def tearDown(self):
+        reap_children()
+
+    def test_process_iter(self):
+        self.assertIn(os.getpid(), [x.pid for x in psutil.process_iter()])
+        sproc = get_test_subprocess()
+        self.assertIn(sproc.pid, [x.pid for x in psutil.process_iter()])
+        p = psutil.Process(sproc.pid)
+        p.kill()
+        p.wait()
+        self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()])
+
+        with mock.patch('psutil.Process',
+                        side_effect=psutil.NoSuchProcess(os.getpid())):
+            self.assertEqual(list(psutil.process_iter()), [])
+        with mock.patch('psutil.Process',
+                        side_effect=psutil.AccessDenied(os.getpid())):
+            with self.assertRaises(psutil.AccessDenied):
+                list(psutil.process_iter())
+
+    def test_prcess_iter_w_params(self):
+        for p in psutil.process_iter(attrs=['pid']):
+            self.assertEqual(list(p.info.keys()), ['pid'])
+        with self.assertRaises(ValueError):
+            list(psutil.process_iter(attrs=['foo']))
+        with mock.patch("psutil._psplatform.Process.cpu_times",
+                        side_effect=psutil.AccessDenied(0, "")) as m:
+            for p in psutil.process_iter(attrs=["pid", "cpu_times"]):
+                self.assertIsNone(p.info['cpu_times'])
+                self.assertGreaterEqual(p.info['pid'], 0)
+            assert m.called
+        with mock.patch("psutil._psplatform.Process.cpu_times",
+                        side_effect=psutil.AccessDenied(0, "")) as m:
+            flag = object()
+            for p in psutil.process_iter(
+                    attrs=["pid", "cpu_times"], ad_value=flag):
+                self.assertIs(p.info['cpu_times'], flag)
+                self.assertGreaterEqual(p.info['pid'], 0)
+            assert m.called
+
+    def test_wait_procs(self):
+        def callback(p):
+            pids.append(p.pid)
+
+        pids = []
+        sproc1 = get_test_subprocess()
+        sproc2 = get_test_subprocess()
+        sproc3 = get_test_subprocess()
+        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
+        self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1)
+        self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1)
+        t = time.time()
+        gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback)
+
+        self.assertLess(time.time() - t, 0.5)
+        self.assertEqual(gone, [])
+        self.assertEqual(len(alive), 3)
+        self.assertEqual(pids, [])
+        for p in alive:
+            self.assertFalse(hasattr(p, 'returncode'))
+
+        @retry_before_failing(30)
+        def test(procs, callback):
+            gone, alive = psutil.wait_procs(procs, timeout=0.03,
+                                            callback=callback)
+            self.assertEqual(len(gone), 1)
+            self.assertEqual(len(alive), 2)
+            return gone, alive
+
+        sproc3.terminate()
+        gone, alive = test(procs, callback)
+        self.assertIn(sproc3.pid, [x.pid for x in gone])
+        if POSIX:
+            self.assertEqual(gone.pop().returncode, -signal.SIGTERM)
+        else:
+            self.assertEqual(gone.pop().returncode, 1)
+        self.assertEqual(pids, [sproc3.pid])
+        for p in alive:
+            self.assertFalse(hasattr(p, 'returncode'))
+
+        @retry_before_failing(30)
+        def test(procs, callback):
+            gone, alive = psutil.wait_procs(procs, timeout=0.03,
+                                            callback=callback)
+            self.assertEqual(len(gone), 3)
+            self.assertEqual(len(alive), 0)
+            return gone, alive
+
+        sproc1.terminate()
+        sproc2.terminate()
+        gone, alive = test(procs, callback)
+        self.assertEqual(set(pids), set([sproc1.pid, sproc2.pid, sproc3.pid]))
+        for p in gone:
+            self.assertTrue(hasattr(p, 'returncode'))
+
+    def test_wait_procs_no_timeout(self):
+        sproc1 = get_test_subprocess()
+        sproc2 = get_test_subprocess()
+        sproc3 = get_test_subprocess()
+        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
+        for p in procs:
+            p.terminate()
+        gone, alive = psutil.wait_procs(procs)
+
+    def test_boot_time(self):
+        bt = psutil.boot_time()
+        self.assertIsInstance(bt, float)
+        self.assertGreater(bt, 0)
+        self.assertLess(bt, time.time())
+
+    @unittest.skipIf(not POSIX, 'POSIX only')
+    def test_PAGESIZE(self):
+        # pagesize is used internally to perform different calculations
+        # and it's determined by using SC_PAGE_SIZE; make sure
+        # getpagesize() returns the same value.
+        import resource
+        self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize())
+
+    def test_virtual_memory(self):
+        mem = psutil.virtual_memory()
+        assert mem.total > 0, mem
+        assert mem.available > 0, mem
+        assert 0 <= mem.percent <= 100, mem
+        assert mem.used > 0, mem
+        assert mem.free >= 0, mem
+        for name in mem._fields:
+            value = getattr(mem, name)
+            if name != 'percent':
+                self.assertIsInstance(value, (int, long))
+            if name != 'total':
+                if not value >= 0:
+                    self.fail("%r < 0 (%s)" % (name, value))
+                if value > mem.total:
+                    self.fail("%r > total (total=%s, %s=%s)"
+                              % (name, mem.total, name, value))
+
+    def test_swap_memory(self):
+        mem = psutil.swap_memory()
+        self.assertEqual(
+            mem._fields, ('total', 'used', 'free', 'percent', 'sin', 'sout'))
+
+        assert mem.total >= 0, mem
+        assert mem.used >= 0, mem
+        if mem.total > 0:
+            # likely a system with no swap partition
+            assert mem.free > 0, mem
+        else:
+            assert mem.free == 0, mem
+        assert 0 <= mem.percent <= 100, mem
+        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)
+
+    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):
+        logical = psutil.cpu_count()
+        self.assertEqual(logical, len(psutil.cpu_times(percpu=True)))
+        self.assertGreaterEqual(logical, 1)
+        #
+        if os.path.exists("/proc/cpuinfo"):
+            with open("/proc/cpuinfo") as fd:
+                cpuinfo_data = fd.read()
+            if "physical id" not in cpuinfo_data:
+                raise unittest.SkipTest("cpuinfo doesn't include physical id")
+        physical = psutil.cpu_count(logical=False)
+        self.assertGreaterEqual(physical, 1)
+        self.assertGreaterEqual(logical, physical)
+
+    def test_cpu_count_none(self):
+        # https://github.com/giampaolo/psutil/issues/1085
+        for val in (-1, 0, None):
+            with mock.patch('psutil._psplatform.cpu_count_logical',
+                            return_value=val) as m:
+                self.assertIsNone(psutil.cpu_count())
+                assert m.called
+            with mock.patch('psutil._psplatform.cpu_count_physical',
+                            return_value=val) as m:
+                self.assertIsNone(psutil.cpu_count(logical=False))
+                assert m.called
+
+    def test_cpu_times(self):
+        # Check type, value >= 0, str().
+        total = 0
+        times = psutil.cpu_times()
+        sum(times)
+        for cp_time in times:
+            self.assertIsInstance(cp_time, float)
+            self.assertGreaterEqual(cp_time, 0.0)
+            total += cp_time
+        self.assertEqual(total, sum(times))
+        str(times)
+        # 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
+        # if not WINDOWS:
+        #     last = psutil.cpu_times()
+        #     for x in range(100):
+        #         new = psutil.cpu_times()
+        #         for field in new._fields:
+        #             new_t = getattr(new, field)
+        #             last_t = getattr(last, field)
+        #             self.assertGreaterEqual(new_t, last_t,
+        #                                     msg="%s %s" % (new_t, last_t))
+        #         last = new
+
+    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)
+
+    def test_per_cpu_times(self):
+        # Check type, value >= 0, str().
+        for times in psutil.cpu_times(percpu=True):
+            total = 0
+            sum(times)
+            for cp_time in times:
+                self.assertIsInstance(cp_time, float)
+                self.assertGreaterEqual(cp_time, 0.0)
+                total += cp_time
+            self.assertEqual(total, sum(times))
+            str(times)
+        self.assertEqual(len(psutil.cpu_times(percpu=True)[0]),
+                         len(psutil.cpu_times(percpu=False)))
+
+        # Note: in theory CPU times are always supposed to increase over
+        # time or remain the same but never go backwards. In practice
+        # sometimes this is not the case.
+        # This issue seemd to be afflict Windows:
+        # https://github.com/giampaolo/psutil/issues/392
+        # ...but it turns out also Linux (rarely) behaves the same.
+        # last = psutil.cpu_times(percpu=True)
+        # for x in range(100):
+        #     new = psutil.cpu_times(percpu=True)
+        #     for index in range(len(new)):
+        #         newcpu = new[index]
+        #         lastcpu = last[index]
+        #         for field in newcpu._fields:
+        #             new_t = getattr(newcpu, field)
+        #             last_t = getattr(lastcpu, field)
+        #             self.assertGreaterEqual(
+        #                 new_t, last_t, msg="%s %s" % (lastcpu, newcpu))
+        #     last = new
+
+    def test_per_cpu_times_2(self):
+        # Simulate some work load then make sure time have increased
+        # between calls.
+        tot1 = psutil.cpu_times(percpu=True)
+        stop_at = time.time() + 0.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()
+
+    def test_cpu_times_comparison(self):
+        # Make sure the sum of all per cpu times is almost equal to
+        # base "one cpu" times.
+        base = psutil.cpu_times()
+        per_cpu = psutil.cpu_times(percpu=True)
+        summed_values = base._make([sum(num) for num in zip(*per_cpu)])
+        for field in base._fields:
+            self.assertAlmostEqual(
+                getattr(base, field), getattr(summed_values, field), delta=1)
+
+    def _test_cpu_percent(self, percent, last_ret, new_ret):
+        try:
+            self.assertIsInstance(percent, float)
+            self.assertGreaterEqual(percent, 0.0)
+            self.assertIsNot(percent, -0.0)
+            self.assertLessEqual(percent, 100.0 * psutil.cpu_count())
+        except AssertionError as err:
+            raise AssertionError("\n%s\nlast=%s\nnew=%s" % (
+                err, pprint.pformat(last_ret), pprint.pformat(new_ret)))
+
+    def test_cpu_percent(self):
+        last = psutil.cpu_percent(interval=0.001)
+        for x in range(100):
+            new = psutil.cpu_percent(interval=None)
+            self._test_cpu_percent(new, last, new)
+            last = new
+        with self.assertRaises(ValueError):
+            psutil.cpu_percent(interval=-1)
+
+    def test_per_cpu_percent(self):
+        last = psutil.cpu_percent(interval=0.001, percpu=True)
+        self.assertEqual(len(last), psutil.cpu_count())
+        for x in range(100):
+            new = psutil.cpu_percent(interval=None, percpu=True)
+            for percent in new:
+                self._test_cpu_percent(percent, last, new)
+            last = new
+        with self.assertRaises(ValueError):
+            psutil.cpu_percent(interval=-1, percpu=True)
+
+    def test_cpu_times_percent(self):
+        last = psutil.cpu_times_percent(interval=0.001)
+        for x in range(100):
+            new = psutil.cpu_times_percent(interval=None)
+            for percent in new:
+                self._test_cpu_percent(percent, last, new)
+            self._test_cpu_percent(sum(new), last, new)
+            last = new
+
+    def test_per_cpu_times_percent(self):
+        last = psutil.cpu_times_percent(interval=0.001, percpu=True)
+        self.assertEqual(len(last), psutil.cpu_count())
+        for x in range(100):
+            new = psutil.cpu_times_percent(interval=None, percpu=True)
+            for cpu in new:
+                for percent in cpu:
+                    self._test_cpu_percent(percent, last, new)
+                self._test_cpu_percent(sum(cpu), last, new)
+            last = new
+
+    def test_per_cpu_times_percent_negative(self):
+        # see: https://github.com/giampaolo/psutil/issues/645
+        psutil.cpu_times_percent(percpu=True)
+        zero_times = [x._make([0 for x in range(len(x._fields))])
+                      for x in psutil.cpu_times(percpu=True)]
+        with mock.patch('psutil.cpu_times', return_value=zero_times):
+            for cpu in psutil.cpu_times_percent(percpu=True):
+                for percent in cpu:
+                    self._test_cpu_percent(percent, None, None)
+
+    def test_disk_usage(self):
+        usage = psutil.disk_usage(os.getcwd())
+        self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent'))
+
+        assert usage.total > 0, usage
+        assert usage.used > 0, usage
+        assert usage.free > 0, usage
+        assert usage.total > usage.used, usage
+        assert usage.total > usage.free, usage
+        assert 0 <= usage.percent <= 100, usage.percent
+        if hasattr(shutil, 'disk_usage'):
+            # py >= 3.3, see: http://bugs.python.org/issue12442
+            shutil_usage = shutil.disk_usage(os.getcwd())
+            tolerance = 5 * 1024 * 1024  # 5MB
+            self.assertEqual(usage.total, shutil_usage.total)
+            self.assertAlmostEqual(usage.free, shutil_usage.free,
+                                   delta=tolerance)
+            self.assertAlmostEqual(usage.used, shutil_usage.used,
+                                   delta=tolerance)
+
+        # if path does not exist OSError ENOENT is expected across
+        # all platforms
+        fname = tempfile.mktemp()
+        with self.assertRaises(OSError) as exc:
+            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
+        if ASCII_FS:
+            with self.assertRaises(UnicodeEncodeError):
+                psutil.disk_usage(TESTFN_UNICODE)
+
+    def test_disk_usage_bytes(self):
+        psutil.disk_usage(b'.')
+
+    def test_disk_partitions(self):
+        # all = False
+        ls = psutil.disk_partitions(all=False)
+        # on travis we get:
+        #     self.assertEqual(p.cpu_affinity(), [n])
+        # AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7,... != [0]
+        self.assertTrue(ls, msg=ls)
+        for disk in ls:
+            self.assertIsInstance(disk.device, str)
+            self.assertIsInstance(disk.mountpoint, str)
+            self.assertIsInstance(disk.fstype, str)
+            self.assertIsInstance(disk.opts, str)
+            if WINDOWS and 'cdrom' in disk.opts:
+                continue
+            if not POSIX:
+                assert os.path.exists(disk.device), disk
+            else:
+                # 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
+            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:
+                try:
+                    os.stat(disk.mountpoint)
+                except OSError as err:
+                    if TRAVIS and OSX 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
+            self.assertIsInstance(disk.fstype, str)
+            self.assertIsInstance(disk.opts, str)
+
+        def find_mount_point(path):
+            path = os.path.abspath(path)
+            while not os.path.ismount(path):
+                path = os.path.dirname(path)
+            return path.lower()
+
+        mount = find_mount_point(__file__)
+        mounts = [x.mountpoint.lower() for x in
+                  psutil.disk_partitions(all=True)]
+        self.assertIn(mount, mounts)
+        psutil.disk_usage(mount)
+
+    def test_net_io_counters(self):
+        def check_ntuple(nt):
+            self.assertEqual(nt[0], nt.bytes_sent)
+            self.assertEqual(nt[1], nt.bytes_recv)
+            self.assertEqual(nt[2], nt.packets_sent)
+            self.assertEqual(nt[3], nt.packets_recv)
+            self.assertEqual(nt[4], nt.errin)
+            self.assertEqual(nt[5], nt.errout)
+            self.assertEqual(nt[6], nt.dropin)
+            self.assertEqual(nt[7], nt.dropout)
+            assert nt.bytes_sent >= 0, nt
+            assert nt.bytes_recv >= 0, nt
+            assert nt.packets_sent >= 0, nt
+            assert nt.packets_recv >= 0, nt
+            assert nt.errin >= 0, nt
+            assert nt.errout >= 0, nt
+            assert nt.dropin >= 0, nt
+            assert nt.dropout >= 0, nt
+
+        ret = psutil.net_io_counters(pernic=False)
+        check_ntuple(ret)
+        ret = psutil.net_io_counters(pernic=True)
+        self.assertNotEqual(ret, [])
+        for key in ret:
+            self.assertTrue(key)
+            self.assertIsInstance(key, str)
+            check_ntuple(ret[key])
+
+    def test_net_io_counters_no_nics(self):
+        # Emulate a case where no NICs are installed, see:
+        # https://github.com/giampaolo/psutil/issues/1062
+        with mock.patch('psutil._psplatform.net_io_counters',
+                        return_value={}) as m:
+            self.assertIsNone(psutil.net_io_counters(pernic=False))
+            self.assertEqual(psutil.net_io_counters(pernic=True), {})
+            assert m.called
+
+    def test_net_if_addrs(self):
+        nics = psutil.net_if_addrs()
+        assert nics, nics
+
+        nic_stats = psutil.net_if_stats()
+
+        # Not reliable on all platforms (net_if_addrs() reports more
+        # interfaces).
+        # self.assertEqual(sorted(nics.keys()),
+        #                  sorted(psutil.net_io_counters(pernic=True).keys()))
+
+        families = set([socket.AF_INET, socket.AF_INET6, psutil.AF_LINK])
+        for nic, addrs in nics.items():
+            self.assertIsInstance(nic, str)
+            self.assertEqual(len(set(addrs)), len(addrs))
+            for addr in addrs:
+                self.assertIsInstance(addr.family, int)
+                self.assertIsInstance(addr.address, str)
+                self.assertIsInstance(addr.netmask, (str, type(None)))
+                self.assertIsInstance(addr.broadcast, (str, type(None)))
+                self.assertIn(addr.family, families)
+                if sys.version_info >= (3, 4):
+                    self.assertIsInstance(addr.family, enum.IntEnum)
+                if nic_stats[nic].isup:
+                    # Do not test binding to addresses of interfaces
+                    # that are down
+                    if addr.family == socket.AF_INET:
+                        s = socket.socket(addr.family)
+                        with contextlib.closing(s):
+                            s.bind((addr.address, 0))
+                    elif addr.family == socket.AF_INET6:
+                        info = socket.getaddrinfo(
+                            addr.address, 0, socket.AF_INET6,
+                            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
+                        af, socktype, proto, canonname, sa = info
+                        s = socket.socket(af, socktype, proto)
+                        with contextlib.closing(s):
+                            s.bind(sa)
+                for ip in (addr.address, addr.netmask, addr.broadcast,
+                           addr.ptp):
+                    if ip is not None:
+                        # TODO: skip AF_INET6 for now because I get:
+                        # AddressValueError: Only hex digits permitted in
+                        # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'
+                        if addr.family != socket.AF_INET6:
+                            check_net_address(ip, addr.family)
+                # broadcast and ptp addresses are mutually exclusive
+                if addr.broadcast:
+                    self.assertIsNone(addr.ptp)
+                elif addr.ptp:
+                    self.assertIsNone(addr.broadcast)
+
+        if BSD or OSX or SUNOS:
+            if hasattr(socket, "AF_LINK"):
+                self.assertEqual(psutil.AF_LINK, socket.AF_LINK)
+        elif LINUX:
+            self.assertEqual(psutil.AF_LINK, socket.AF_PACKET)
+        elif WINDOWS:
+            self.assertEqual(psutil.AF_LINK, -1)
+
+    def test_net_if_addrs_mac_null_bytes(self):
+        # Simulate that the underlying C function returns an incomplete
+        # MAC address. psutil is supposed to fill it with null bytes.
+        # https://github.com/giampaolo/psutil/issues/786
+        if POSIX:
+            ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)]
+        else:
+            ret = [('em1', -1, '06-3d-29', None, None, None)]
+        with mock.patch('psutil._psplatform.net_if_addrs',
+                        return_value=ret) as m:
+            addr = psutil.net_if_addrs()['em1'][0]
+            assert m.called
+            if POSIX:
+                self.assertEqual(addr.address, '06:3d:29:00:00:00')
+            else:
+                self.assertEqual(addr.address, '06-3d-29-00-00-00')
+
+    @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")  # raises EPERM
+    def test_net_if_stats(self):
+        nics = psutil.net_if_stats()
+        assert nics, nics
+        all_duplexes = (psutil.NIC_DUPLEX_FULL,
+                        psutil.NIC_DUPLEX_HALF,
+                        psutil.NIC_DUPLEX_UNKNOWN)
+        for name, stats in nics.items():
+            self.assertIsInstance(name, str)
+            isup, duplex, speed, mtu = stats
+            self.assertIsInstance(isup, bool)
+            self.assertIn(duplex, all_duplexes)
+            self.assertIn(duplex, all_duplexes)
+            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), {})
+            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)
+
+    @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
+    def test_sensors_temperatures(self):
+        temps = psutil.sensors_temperatures()
+        for name, entries in temps.items():
+            self.assertIsInstance(name, str)
+            for entry in entries:
+                self.assertIsInstance(entry.label, str)
+                if entry.current is not None:
+                    self.assertGreaterEqual(entry.current, 0)
+                if entry.high is not None:
+                    self.assertGreaterEqual(entry.high, 0)
+                if entry.critical is not None:
+                    self.assertGreaterEqual(entry.critical, 0)
+
+    @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
+    def test_sensors_temperatures_fahreneit(self):
+        d = {'coretemp': [('label', 50.0, 60.0, 70.0)]}
+        with mock.patch("psutil._psplatform.sensors_temperatures",
+                        return_value=d) as m:
+            temps = psutil.sensors_temperatures(
+                fahrenheit=True)['coretemp'][0]
+            assert m.called
+            self.assertEqual(temps.current, 122.0)
+            self.assertEqual(temps.high, 140.0)
+            self.assertEqual(temps.critical, 158.0)
+
+    @unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported")
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_sensors_battery(self):
+        ret = psutil.sensors_battery()
+        self.assertGreaterEqual(ret.percent, 0)
+        self.assertLessEqual(ret.percent, 100)
+        if ret.secsleft not in (psutil.POWER_TIME_UNKNOWN,
+                                psutil.POWER_TIME_UNLIMITED):
+            self.assertGreaterEqual(ret.secsleft, 0)
+        else:
+            if ret.secsleft == psutil.POWER_TIME_UNLIMITED:
+                self.assertTrue(ret.power_plugged)
+        self.assertIsInstance(ret.power_plugged, bool)
+
+    @unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
+    def test_sensors_fans(self):
+        fans = psutil.sensors_fans()
+        for name, entries in fans.items():
+            self.assertIsInstance(name, str)
+            for entry in entries:
+                self.assertIsInstance(entry.label, str)
+                self.assertIsInstance(entry.current, (int, long))
+                self.assertGreaterEqual(entry.current, 0)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_unicode.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# 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.
+
+"""
+Notes about unicode handling in psutil
+======================================
+
+In psutil these are the APIs returning or dealing with a string
+('not tested' means they are not tested to deal with non-ASCII strings):
+
+* Process.cmdline()
+* Process.connections('unix')
+* Process.cwd()
+* Process.environ()
+* Process.exe()
+* Process.memory_maps()
+* Process.name()
+* Process.open_files()
+* Process.username()             (not tested)
+
+* disk_io_counters()             (not tested)
+* disk_partitions()              (not tested)
+* disk_usage(str)
+* net_connections('unix')
+* net_if_addrs()                 (not tested)
+* net_if_stats()                 (not tested)
+* net_io_counters()              (not tested)
+* sensors_fans()                 (not tested)
+* sensors_temperatures()         (not tested)
+* users()                        (not tested)
+
+* WindowsService.binpath()       (not tested)
+* WindowsService.description()   (not tested)
+* WindowsService.display_name()  (not tested)
+* WindowsService.name()          (not tested)
+* WindowsService.status()        (not tested)
+* WindowsService.username()      (not tested)
+
+In here we create a unicode path with a funky non-ASCII name and (where
+possible) make psutil return it back (e.g. on name(), exe(), open_files(),
+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
+import traceback
+import warnings
+from contextlib import closing
+
+from psutil import BSD
+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 ASCII_FS
+from psutil.tests import bind_unix_socket
+from psutil.tests import chdir
+from psutil.tests import copyload_shared_lib
+from psutil.tests import create_exe
+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 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
+from psutil.tests import TESTFILE_PREFIX
+from psutil.tests import TESTFN
+from psutil.tests import TESTFN_UNICODE
+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):
+    if APPVEYOR:
+        # TODO - this is quite random and I'm not sure why it happens,
+        # nor I can reproduce it locally:
+        # https://ci.appveyor.com/project/giampaolo/psutil/build/job/
+        #     jiq2cgd6stsbtn60
+        # safe_rmpath() happens after reap_children() so this is weird
+        # Perhaps wait_procs() on Windows is broken? Maybe because
+        # of STILL_ACTIVE?
+        # https://github.com/giampaolo/psutil/blob/
+        #     68c7a70728a31d8b8b58f4be6c4c0baa2f449eda/psutil/arch/
+        #     windows/process_info.c#L146
+        try:
+            return _safe_rmpath(path)
+        except WindowsError:
+            traceback.print_exc()
+    else:
+        return _safe_rmpath(path)
+
+
+def subprocess_supports_unicode(name):
+    """Return True if both the fs and the subprocess module can
+    deal with a unicode file name.
+    """
+    if PY3:
+        return True
+    try:
+        safe_rmpath(name)
+        create_exe(name)
+        get_test_subprocess(cmd=[name])
+    except UnicodeEncodeError:
+        return False
+    else:
+        return True
+    finally:
+        reap_children()
+
+
+# An invalid unicode string.
+if PY3:
+    INVALID_NAME = (TESTFN.encode('utf8') + b"f\xc0\x80").decode(
+        'utf8', 'surrogateescape')
+else:
+    INVALID_NAME = TESTFN + "f\xc0\x80"
+
+
+# ===================================================================
+# FS APIs
+# ===================================================================
+
+
+class _BaseFSAPIsTests(object):
+    funky_name = None
+
+    @classmethod
+    def setUpClass(cls):
+        safe_rmpath(cls.funky_name)
+        create_exe(cls.funky_name)
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+        safe_rmpath(cls.funky_name)
+
+    def tearDown(self):
+        reap_children()
+
+    def expect_exact_path_match(self):
+        raise NotImplementedError("must be implemented in subclass")
+
+    def test_proc_exe(self):
+        subp = get_test_subprocess(cmd=[self.funky_name])
+        p = psutil.Process(subp.pid)
+        exe = p.exe()
+        self.assertIsInstance(exe, str)
+        if self.expect_exact_path_match():
+            self.assertEqual(exe, 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()
+        self.assertIsInstance(name, str)
+        if self.expect_exact_path_match():
+            self.assertEqual(name, os.path.basename(self.funky_name))
+
+    def test_proc_cmdline(self):
+        subp = get_test_subprocess(cmd=[self.funky_name])
+        p = psutil.Process(subp.pid)
+        cmdline = p.cmdline()
+        for part in cmdline:
+            self.assertIsInstance(part, str)
+        if self.expect_exact_path_match():
+            self.assertEqual(cmdline, [self.funky_name])
+
+    def test_proc_cwd(self):
+        dname = self.funky_name + "2"
+        self.addCleanup(safe_rmpath, dname)
+        safe_mkdir(dname)
+        with chdir(dname):
+            p = psutil.Process()
+            cwd = p.cwd()
+        self.assertIsInstance(p.cwd(), str)
+        if self.expect_exact_path_match():
+            self.assertEqual(cwd, dname)
+
+    def test_proc_open_files(self):
+        p = psutil.Process()
+        start = set(p.open_files())
+        with open(self.funky_name, 'rb'):
+            new = set(p.open_files())
+        path = (new - start).pop().path
+        self.assertIsInstance(path, str)
+        if BSD and not path:
+            # XXX - see https://github.com/giampaolo/psutil/issues/595
+            return self.skipTest("open_files on BSD is broken")
+        if self.expect_exact_path_match():
+            self.assertEqual(os.path.normcase(path),
+                             os.path.normcase(self.funky_name))
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    def test_proc_connections(self):
+        suffix = os.path.basename(self.funky_name)
+        with unix_socket_path(suffix=suffix) as name:
+            try:
+                sock = bind_unix_socket(name)
+            except UnicodeEncodeError:
+                if PY3:
+                    raise
+                else:
+                    raise unittest.SkipTest("not supported")
+            with closing(sock):
+                conn = psutil.Process().connections('unix')[0]
+                self.assertIsInstance(conn.laddr, str)
+                # AF_UNIX addr not set on OpenBSD
+                if not OPENBSD:
+                    self.assertEqual(conn.laddr, name)
+
+    @unittest.skipIf(not POSIX, "POSIX only")
+    @unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets")
+    @skip_on_access_denied()
+    def test_net_connections(self):
+        def find_sock(cons):
+            for conn in cons:
+                if os.path.basename(conn.laddr).startswith(TESTFILE_PREFIX):
+                    return conn
+            raise ValueError("connection not found")
+
+        suffix = os.path.basename(self.funky_name)
+        with unix_socket_path(suffix=suffix) as name:
+            try:
+                sock = bind_unix_socket(name)
+            except UnicodeEncodeError:
+                if PY3:
+                    raise
+                else:
+                    raise unittest.SkipTest("not supported")
+            with closing(sock):
+                cons = psutil.net_connections(kind='unix')
+                # AF_UNIX addr not set on OpenBSD
+                if not OPENBSD:
+                    conn = find_sock(cons)
+                    self.assertIsInstance(conn.laddr, str)
+                    self.assertEqual(conn.laddr, name)
+
+    def test_disk_usage(self):
+        dname = self.funky_name + "2"
+        self.addCleanup(safe_rmpath, dname)
+        safe_mkdir(dname)
+        psutil.disk_usage(dname)
+
+    @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
+    @unittest.skipIf(not PY3, "ctypes does not support unicode on PY2")
+    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.
+        with copyload_shared_lib(dst_prefix=self.funky_name) as funky_path:
+            def normpath(p):
+                return os.path.realpath(os.path.normcase(p))
+            libpaths = [normpath(x.path)
+                        for x in psutil.Process().memory_maps()]
+            # ...just to have a clearer msg in case of failure
+            libpaths = [x for x in libpaths if TESTFILE_PREFIX in x]
+            self.assertIn(normpath(funky_path), libpaths)
+            for path in libpaths:
+                self.assertIsInstance(path, str)
+
+
+@unittest.skipIf(OSX 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")
+class TestFSAPIs(_BaseFSAPIsTests, unittest.TestCase):
+    """Test FS APIs with a funky, valid, UTF8 path name."""
+    funky_name = TESTFN_UNICODE
+
+    @classmethod
+    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)
+
+
+@unittest.skipIf(OSX and TRAVIS, "unreliable on TRAVIS")  # TODO
+@unittest.skipIf(not subprocess_supports_unicode(INVALID_NAME),
+                 "subprocess can't deal with invalid unicode")
+class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase):
+    """Test FS APIs with a funky, invalid path name."""
+    funky_name = INVALID_NAME
+
+    @classmethod
+    def expect_exact_path_match(cls):
+        # Invalid unicode names are supposed to work on Python 2.
+        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
+# ===================================================================
+
+
+class TestNonFSAPIS(unittest.TestCase):
+    """Unicode tests for non fs-related APIs."""
+
+    def tearDown(self):
+        reap_children()
+
+    @unittest.skipIf(not HAS_ENVIRON, "not supported")
+    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
+        # it's not able to handle with non-ASCII env vars, so
+        # we use "è", which is part of the extended ASCII table
+        # (unicode point <= 255).
+        env = os.environ.copy()
+        funky_str = TESTFN_UNICODE if PY3 else 'è'
+        env['FUNNY_ARG'] = funky_str
+        sproc = get_test_subprocess(env=env)
+        p = psutil.Process(sproc.pid)
+        env = p.environ()
+        for k, v in env.items():
+            self.assertIsInstance(k, str)
+            self.assertIsInstance(v, str)
+        self.assertEqual(env['FUNNY_ARG'], funky_str)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_windows.py
@@ -0,0 +1,838 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*
+
+# 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.
+
+"""Windows specific tests."""
+
+import datetime
+import errno
+import glob
+import os
+import platform
+import re
+import signal
+import subprocess
+import sys
+import time
+import warnings
+
+import psutil
+from psutil import WINDOWS
+from psutil._compat import callable
+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 reap_children
+from psutil.tests import retry_before_failing
+from psutil.tests import run_test_module_by_name
+from psutil.tests import sh
+from psutil.tests import unittest
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore")
+    try:
+        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
+
+# are we a 64 bit process
+IS_64_BIT = sys.maxsize > 2**32
+
+
+def wrap_exceptions(fun):
+    def wrapper(self, *args, **kwargs):
+        try:
+            return fun(self, *args, **kwargs)
+        except OSError as err:
+            from psutil._pswindows import ACCESS_DENIED_SET
+            if err.errno in ACCESS_DENIED_SET:
+                raise psutil.AccessDenied(None, None)
+            if err.errno == errno.ESRCH:
+                raise psutil.NoSuchProcess(None, None)
+            raise
+    return wrapper
+
+
+# ===================================================================
+# System APIs
+# ===================================================================
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestSystemAPIs(unittest.TestCase):
+
+    def test_nic_names(self):
+        out = sh('ipconfig /all')
+        nics = psutil.net_io_counters(pernic=True).keys()
+        for nic in nics:
+            if "pseudo-interface" in nic.replace(' ', '-').lower():
+                continue
+            if nic not in out:
+                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),
+                         psutil.virtual_memory().total)
+
+    # @unittest.skipIf(wmi is None, "wmi module is not installed")
+    # def test__UPTIME(self):
+    #     # _UPTIME constant is not public but it is used internally
+    #     # as value to return for pid 0 creation time.
+    #     # WMI behaves the same.
+    #     w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+    #     p = psutil.Process(0)
+    #     wmic_create = str(w.CreationDate.split('.')[0])
+    #     psutil_create = time.strftime("%Y%m%d%H%M%S",
+    #                                   time.localtime(p.create_time()))
+
+    # Note: this test is not very reliable
+    @unittest.skipIf(APPVEYOR, "test not relieable on appveyor")
+    @retry_before_failing()
+    def test_pids(self):
+        # Note: this test might fail if the OS is starting/killing
+        # other processes in the meantime
+        w = wmi.WMI().Win32_Process()
+        wmi_pids = set([x.ProcessId for x in w])
+        psutil_pids = set(psutil.pids())
+        self.assertEqual(wmi_pids, psutil_pids)
+
+    @retry_before_failing()
+    def test_disks(self):
+        ps_parts = psutil.disk_partitions(all=True)
+        wmi_parts = wmi.WMI().Win32_LogicalDisk()
+        for ps_part in ps_parts:
+            for wmi_part in wmi_parts:
+                if ps_part.device.replace('\\', '') == wmi_part.DeviceID:
+                    if not ps_part.mountpoint:
+                        # this is usually a CD-ROM with no disk inserted
+                        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
+                    self.assertEqual(usage.total, int(wmi_part.Size))
+                    wmi_free = int(wmi_part.FreeSpace)
+                    self.assertEqual(usage.free, wmi_free)
+                    # 10 MB tollerance
+                    if abs(usage.free - wmi_free) > 10 * 1024 * 1024:
+                        self.fail("psutil=%s, wmi=%s" % (
+                            usage.free, wmi_free))
+                    break
+            else:
+                self.fail("can't find partition %s" % repr(ps_part))
+
+    def test_disk_usage(self):
+        for disk in psutil.disk_partitions():
+            sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint)
+            psutil_value = psutil.disk_usage(disk.mountpoint)
+            self.assertAlmostEqual(sys_value[0], psutil_value.free,
+                                   delta=1024 * 1024)
+            self.assertAlmostEqual(sys_value[1], psutil_value.total,
+                                   delta=1024 * 1024)
+            self.assertEqual(psutil_value.used,
+                             psutil_value.total - psutil_value.free)
+
+    def test_disk_partitions(self):
+        sys_value = [
+            x + '\\' for x in win32api.GetLogicalDriveStrings().split("\\\x00")
+            if x and not x.startswith('A:')]
+        psutil_value = [x.mountpoint for x in psutil.disk_partitions(all=True)]
+        self.assertEqual(sys_value, psutil_value)
+
+    def test_net_if_stats(self):
+        ps_names = set(cext.net_if_stats())
+        wmi_adapters = wmi.WMI().Win32_NetworkAdapter()
+        wmi_names = set()
+        for wmi_adapter in wmi_adapters:
+            wmi_names.add(wmi_adapter.Name)
+            wmi_names.add(wmi_adapter.NetConnectionID)
+        self.assertTrue(ps_names & wmi_names,
+                        "no common entries in %s, %s" % (ps_names, wmi_names))
+
+    def test_boot_time(self):
+        wmi_os = wmi.WMI().Win32_OperatingSystem()
+        wmi_btime_str = wmi_os[0].LastBootUpTime.split('.')[0]
+        wmi_btime_dt = datetime.datetime.strptime(
+            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):
+        # https://github.com/giampaolo/psutil/issues/1007
+        with mock.patch('psutil._pswindows.cext.boot_time', return_value=5):
+            self.assertEqual(psutil.boot_time(), 5)
+        with mock.patch('psutil._pswindows.cext.boot_time', return_value=4):
+            self.assertEqual(psutil.boot_time(), 5)
+        with mock.patch('psutil._pswindows.cext.boot_time', return_value=6):
+            self.assertEqual(psutil.boot_time(), 5)
+        with mock.patch('psutil._pswindows.cext.boot_time', return_value=333):
+            self.assertEqual(psutil.boot_time(), 333)
+
+
+# ===================================================================
+# sensors_battery()
+# ===================================================================
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestSensorsBattery(unittest.TestCase):
+
+    def test_has_battery(self):
+        if win32api.GetPwrCapabilities()['SystemBatteriesPresent']:
+            self.assertIsNotNone(psutil.sensors_battery())
+        else:
+            self.assertIsNone(psutil.sensors_battery())
+
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_percent(self):
+        w = wmi.WMI()
+        battery_wmi = w.query('select * from Win32_Battery')[0]
+        battery_psutil = psutil.sensors_battery()
+        self.assertAlmostEqual(
+            battery_psutil.percent, battery_wmi.EstimatedChargeRemaining,
+            delta=1)
+
+    @unittest.skipIf(not HAS_BATTERY, "no battery")
+    def test_power_plugged(self):
+        w = wmi.WMI()
+        battery_wmi = w.query('select * from Win32_Battery')[0]
+        battery_psutil = psutil.sensors_battery()
+        # Status codes:
+        # https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx
+        self.assertEqual(battery_psutil.power_plugged,
+                         battery_wmi.BatteryStatus == 2)
+
+    def test_emulate_no_battery(self):
+        with mock.patch("psutil._pswindows.cext.sensors_battery",
+                        return_value=(0, 128, 0, 0)) as m:
+            self.assertIsNone(psutil.sensors_battery())
+            assert m.called
+
+    def test_emulate_power_connected(self):
+        with mock.patch("psutil._pswindows.cext.sensors_battery",
+                        return_value=(1, 0, 0, 0)) as m:
+            self.assertEqual(psutil.sensors_battery().secsleft,
+                             psutil.POWER_TIME_UNLIMITED)
+            assert m.called
+
+    def test_emulate_power_charging(self):
+        with mock.patch("psutil._pswindows.cext.sensors_battery",
+                        return_value=(0, 8, 0, 0)) as m:
+            self.assertEqual(psutil.sensors_battery().secsleft,
+                             psutil.POWER_TIME_UNLIMITED)
+            assert m.called
+
+    def test_emulate_secs_left_unknown(self):
+        with mock.patch("psutil._pswindows.cext.sensors_battery",
+                        return_value=(0, 0, 0, -1)) as m:
+            self.assertEqual(psutil.sensors_battery().secsleft,
+                             psutil.POWER_TIME_UNKNOWN)
+            assert m.called
+
+
+# ===================================================================
+# Process APIs
+# ===================================================================
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestProcess(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    def test_issue_24(self):
+        p = psutil.Process(0)
+        self.assertRaises(psutil.AccessDenied, p.kill)
+
+    def test_special_pid(self):
+        p = psutil.Process(4)
+        self.assertEqual(p.name(), 'System')
+        # use __str__ to access all common Process properties to check
+        # that nothing strange happens
+        str(p)
+        p.username()
+        self.assertTrue(p.create_time() >= 0.0)
+        try:
+            rss, vms = p.memory_info()[:2]
+        except psutil.AccessDenied:
+            # expected on Windows Vista and Windows 7
+            if not platform.uname()[1] in ('vista', 'win-7', 'win7'):
+                raise
+        else:
+            self.assertTrue(rss > 0)
+
+    def test_send_signal(self):
+        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()
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, os.getpid())
+        after = p.num_handles()
+        self.assertEqual(after, before + 1)
+        win32api.CloseHandle(handle)
+        self.assertEqual(p.num_handles(), before)
+
+    def test_handles_leak(self):
+        # Call all Process methods and make sure no handles are left
+        # open. This is here mainly to make sure functions using
+        # OpenProcess() always call CloseHandle().
+        def call(p, attr):
+            attr = getattr(p, name, None)
+            if attr is not None and callable(attr):
+                attr()
+            else:
+                attr
+
+        p = psutil.Process(self.pid)
+        failures = []
+        for name in dir(psutil.Process):
+            if name.startswith('_') \
+                    or name in ('terminate', 'kill', 'suspend', 'resume',
+                                'nice', 'send_signal', 'wait', 'children',
+                                'as_dict', 'memory_info_ex'):
+                continue
+            else:
+                try:
+                    call(p, name)
+                    num1 = p.num_handles()
+                    call(p, name)
+                    num2 = p.num_handles()
+                except (psutil.NoSuchProcess, psutil.AccessDenied):
+                    pass
+                else:
+                    if num2 > num1:
+                        fail = \
+                            "failure while processing Process.%s method " \
+                            "(before=%s, after=%s)" % (name, num1, num2)
+                        failures.append(fail)
+        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):
+        p = psutil.Process(get_test_subprocess().pid)
+        p.send_signal(signal.CTRL_C_EVENT)
+        p.send_signal(signal.CTRL_BREAK_EVENT)
+        p.kill()
+        p.wait()
+        self.assertRaises(psutil.NoSuchProcess,
+                          p.send_signal, signal.CTRL_C_EVENT)
+        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))
+
+    def test_cmdline(self):
+        sys_value = re.sub(' +', ' ', win32api.GetCommandLine()).strip()
+        psutil_value = ' '.join(psutil.Process().cmdline())
+        self.assertEqual(sys_value, psutil_value)
+
+    # XXX - occasional failures
+
+    # def test_cpu_times(self):
+    #     handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+    #                                   win32con.FALSE, os.getpid())
+    #     self.addCleanup(win32api.CloseHandle, handle)
+    #     sys_value = win32process.GetProcessTimes(handle)
+    #     psutil_value = psutil.Process().cpu_times()
+    #     self.assertAlmostEqual(
+    #         psutil_value.user, sys_value['UserTime'] / 10000000.0,
+    #         delta=0.2)
+    #     self.assertAlmostEqual(
+    #         psutil_value.user, sys_value['KernelTime'] / 10000000.0,
+    #         delta=0.2)
+
+    def test_nice(self):
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, os.getpid())
+        self.addCleanup(win32api.CloseHandle, handle)
+        sys_value = win32process.GetPriorityClass(handle)
+        psutil_value = psutil.Process().nice()
+        self.assertEqual(psutil_value, sys_value)
+
+    def test_memory_info(self):
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, self.pid)
+        self.addCleanup(win32api.CloseHandle, handle)
+        sys_value = win32process.GetProcessMemoryInfo(handle)
+        psutil_value = psutil.Process(self.pid).memory_info()
+        self.assertEqual(
+            sys_value['PeakWorkingSetSize'], psutil_value.peak_wset)
+        self.assertEqual(
+            sys_value['WorkingSetSize'], psutil_value.wset)
+        self.assertEqual(
+            sys_value['QuotaPeakPagedPoolUsage'], psutil_value.peak_paged_pool)
+        self.assertEqual(
+            sys_value['QuotaPagedPoolUsage'], psutil_value.paged_pool)
+        self.assertEqual(
+            sys_value['QuotaPeakNonPagedPoolUsage'],
+            psutil_value.peak_nonpaged_pool)
+        self.assertEqual(
+            sys_value['QuotaNonPagedPoolUsage'], psutil_value.nonpaged_pool)
+        self.assertEqual(
+            sys_value['PagefileUsage'], psutil_value.pagefile)
+        self.assertEqual(
+            sys_value['PeakPagefileUsage'], psutil_value.peak_pagefile)
+
+        self.assertEqual(psutil_value.rss, psutil_value.wset)
+        self.assertEqual(psutil_value.vms, psutil_value.pagefile)
+
+    def test_wait(self):
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, self.pid)
+        self.addCleanup(win32api.CloseHandle, handle)
+        p = psutil.Process(self.pid)
+        p.terminate()
+        psutil_value = p.wait()
+        sys_value = win32process.GetExitCodeProcess(handle)
+        self.assertEqual(psutil_value, sys_value)
+
+    def test_cpu_affinity(self):
+        def from_bitmask(x):
+            return [i for i in range(64) if (1 << i) & x]
+
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, self.pid)
+        self.addCleanup(win32api.CloseHandle, handle)
+        sys_value = from_bitmask(
+            win32process.GetProcessAffinityMask(handle)[0])
+        psutil_value = psutil.Process(self.pid).cpu_affinity()
+        self.assertEqual(psutil_value, sys_value)
+
+    def test_io_counters(self):
+        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
+                                      win32con.FALSE, os.getpid())
+        self.addCleanup(win32api.CloseHandle, handle)
+        sys_value = win32process.GetProcessIoCounters(handle)
+        psutil_value = psutil.Process().io_counters()
+        self.assertEqual(
+            psutil_value.read_count, sys_value['ReadOperationCount'])
+        self.assertEqual(
+            psutil_value.write_count, sys_value['WriteOperationCount'])
+        self.assertEqual(
+            psutil_value.read_bytes, sys_value['ReadTransferCount'])
+        self.assertEqual(
+            psutil_value.write_bytes, sys_value['WriteTransferCount'])
+        self.assertEqual(
+            psutil_value.other_count, sys_value['OtherOperationCount'])
+        self.assertEqual(
+            psutil_value.other_bytes, sys_value['OtherTransferCount'])
+
+    def test_num_handles(self):
+        import ctypes
+        import ctypes.wintypes
+        PROCESS_QUERY_INFORMATION = 0x400
+        handle = ctypes.windll.kernel32.OpenProcess(
+            PROCESS_QUERY_INFORMATION, 0, os.getpid())
+        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)
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestProcessWMI(unittest.TestCase):
+    """Compare Process API results with WMI."""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    def test_name(self):
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        self.assertEqual(p.name(), w.Caption)
+
+    def test_exe(self):
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        # Note: wmi reports the exe as a lower case string.
+        # Being Windows paths case-insensitive we ignore that.
+        self.assertEqual(p.exe().lower(), w.ExecutablePath.lower())
+
+    def test_cmdline(self):
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        self.assertEqual(' '.join(p.cmdline()),
+                         w.CommandLine.replace('"', ''))
+
+    def test_username(self):
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        domain, _, username = w.GetOwner()
+        username = "%s\\%s" % (domain, username)
+        self.assertEqual(p.username(), username)
+
+    def test_memory_rss(self):
+        time.sleep(0.1)
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        rss = p.memory_info().rss
+        self.assertEqual(rss, int(w.WorkingSetSize))
+
+    def test_memory_vms(self):
+        time.sleep(0.1)
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        vms = p.memory_info().vms
+        # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx
+        # ...claims that PageFileUsage is represented in Kilo
+        # bytes but funnily enough on certain platforms bytes are
+        # returned instead.
+        wmi_usage = int(w.PageFileUsage)
+        if (vms != wmi_usage) and (vms != wmi_usage * 1024):
+            self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms))
+
+    def test_create_time(self):
+        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
+        p = psutil.Process(self.pid)
+        wmic_create = str(w.CreationDate.split('.')[0])
+        psutil_create = time.strftime("%Y%m%d%H%M%S",
+                                      time.localtime(p.create_time()))
+        self.assertEqual(wmic_create, psutil_create)
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestDualProcessImplementation(unittest.TestCase):
+    """
+    Certain APIs on Windows have 2 internal implementations, one
+    based on documented Windows APIs, another one based
+    NtQuerySystemInformation() which gets called as fallback in
+    case the first fails because of limited permission error.
+    Here we test that the two methods return the exact same value,
+    see:
+    https://github.com/giampaolo/psutil/issues/304
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pid = get_test_subprocess().pid
+
+    @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()
+        with mock.patch("psutil._psplatform.cext.proc_memory_info",
+                        side_effect=OSError(errno.EPERM, "msg")) as fun:
+            mem_2 = psutil.Process(self.pid).memory_info()
+            self.assertEqual(len(mem_1), len(mem_2))
+            for i in range(len(mem_1)):
+                self.assertGreaterEqual(mem_1[i], 0)
+                self.assertGreaterEqual(mem_2[i], 0)
+                self.assertAlmostEqual(mem_1[i], mem_2[i], delta=512)
+            assert fun.called
+
+    def test_create_time(self):
+        ctime = psutil.Process(self.pid).create_time()
+        with mock.patch("psutil._psplatform.cext.proc_create_time",
+                        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",
+                        side_effect=OSError(errno.EPERM, "msg")) as fun:
+            cpu_times_2 = psutil.Process(self.pid).cpu_times()
+            assert fun.called
+            self.assertAlmostEqual(
+                cpu_times_1.user, cpu_times_2.user, delta=0.01)
+            self.assertAlmostEqual(
+                cpu_times_1.system, cpu_times_2.system, delta=0.01)
+
+    def test_io_counters(self):
+        io_counters_1 = psutil.Process(self.pid).io_counters()
+        with mock.patch("psutil._psplatform.cext.proc_io_counters",
+                        side_effect=OSError(errno.EPERM, "msg")) as fun:
+            io_counters_2 = psutil.Process(self.pid).io_counters()
+            for i in range(len(io_counters_1)):
+                self.assertAlmostEqual(
+                    io_counters_1[i], io_counters_2[i], delta=5)
+            assert fun.called
+
+    def test_num_handles(self):
+        num_handles = psutil.Process(self.pid).num_handles()
+        with mock.patch("psutil._psplatform.cext.proc_num_handles",
+                        side_effect=OSError(errno.EPERM, "msg")) as fun:
+            self.assertEqual(psutil.Process(self.pid).num_handles(),
+                             num_handles)
+            assert fun.called
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class RemoteProcessTestCase(unittest.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
+    have a different bitness.
+    """
+
+    @staticmethod
+    def find_other_interpreter():
+        # find a python interpreter that is of the opposite bitness from us
+        code = "import sys; sys.stdout.write(str(sys.maxsize > 2**32))"
+
+        # XXX: a different and probably more stable approach might be to access
+        # the registry but accessing 64 bit paths from a 32 bit process
+        for filename in glob.glob(r"C:\Python*\python.exe"):
+            proc = subprocess.Popen(args=[filename, "-c", code],
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.STDOUT)
+            output, _ = proc.communicate()
+            if output == str(not IS_64_BIT):
+                return filename
+
+    @classmethod
+    def setUpClass(cls):
+        other_python = cls.find_other_interpreter()
+
+        if other_python is None:
+            raise unittest.SkipTest(
+                "could not find interpreter with opposite bitness")
+
+        if IS_64_BIT:
+            cls.python64 = sys.executable
+            cls.python32 = other_python
+        else:
+            cls.python64 = other_python
+            cls.python32 = sys.executable
+
+    test_args = ["-c", "import sys; sys.stdin.read()"]
+
+    def setUp(self):
+        env = os.environ.copy()
+        env["THINK_OF_A_NUMBER"] = str(os.getpid())
+        self.proc32 = get_test_subprocess([self.python32] + self.test_args,
+                                          env=env,
+                                          stdin=subprocess.PIPE)
+        self.proc64 = get_test_subprocess([self.python64] + self.test_args,
+                                          env=env,
+                                          stdin=subprocess.PIPE)
+
+    def tearDown(self):
+        self.proc32.communicate()
+        self.proc64.communicate()
+        reap_children()
+
+    @classmethod
+    def tearDownClass(cls):
+        reap_children()
+
+    def test_cmdline_32(self):
+        p = psutil.Process(self.proc32.pid)
+        self.assertEqual(len(p.cmdline()), 3)
+        self.assertEqual(p.cmdline()[1:], self.test_args)
+
+    def test_cmdline_64(self):
+        p = psutil.Process(self.proc64.pid)
+        self.assertEqual(len(p.cmdline()), 3)
+        self.assertEqual(p.cmdline()[1:], self.test_args)
+
+    def test_cwd_32(self):
+        p = psutil.Process(self.proc32.pid)
+        self.assertEqual(p.cwd(), os.getcwd())
+
+    def test_cwd_64(self):
+        p = psutil.Process(self.proc64.pid)
+        self.assertEqual(p.cwd(), os.getcwd())
+
+    def test_environ_32(self):
+        p = psutil.Process(self.proc32.pid)
+        e = p.environ()
+        self.assertIn("THINK_OF_A_NUMBER", e)
+        self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid()))
+
+    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()))
+
+
+# ===================================================================
+# Windows services
+# ===================================================================
+
+
+@unittest.skipIf(not WINDOWS, "WINDOWS only")
+class TestServices(unittest.TestCase):
+
+    def test_win_service_iter(self):
+        valid_statuses = set([
+            "running",
+            "paused",
+            "start",
+            "pause",
+            "continue",
+            "stop",
+            "stopped",
+        ])
+        valid_start_types = set([
+            "automatic",
+            "manual",
+            "disabled",
+        ])
+        valid_statuses = set([
+            "running",
+            "paused",
+            "start_pending",
+            "pause_pending",
+            "continue_pending",
+            "stop_pending",
+            "stopped"
+        ])
+        for serv in psutil.win_service_iter():
+            data = serv.as_dict()
+            self.assertIsInstance(data['name'], str)
+            self.assertNotEqual(data['name'].strip(), "")
+            self.assertIsInstance(data['display_name'], str)
+            self.assertIsInstance(data['username'], str)
+            self.assertIn(data['status'], valid_statuses)
+            if data['pid'] is not None:
+                psutil.Process(data['pid'])
+            self.assertIsInstance(data['binpath'], str)
+            self.assertIsInstance(data['username'], str)
+            self.assertIsInstance(data['start_type'], str)
+            self.assertIn(data['start_type'], valid_start_types)
+            self.assertIn(data['status'], valid_statuses)
+            self.assertIsInstance(data['description'], str)
+            pid = serv.pid()
+            if pid is not None:
+                p = psutil.Process(pid)
+                self.assertTrue(p.is_running())
+            # win_service_get
+            s = psutil.win_service_get(serv.name())
+            # test __eq__
+            self.assertEqual(serv, s)
+
+    def test_win_service_get(self):
+        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, "")
+        with mock.patch("psutil._psplatform.cext.winservice_query_status",
+                        side_effect=exc):
+            self.assertRaises(psutil.NoSuchProcess, service.status)
+        with mock.patch("psutil._psplatform.cext.winservice_query_config",
+                        side_effect=exc):
+            self.assertRaises(psutil.NoSuchProcess, service.username)
+
+        # test AccessDenied
+        exc = WindowsError(
+            psutil._psplatform.cext.ERROR_ACCESS_DENIED, "")
+        with mock.patch("psutil._psplatform.cext.winservice_query_status",
+                        side_effect=exc):
+            self.assertRaises(psutil.AccessDenied, service.status)
+        with mock.patch("psutil._psplatform.cext.winservice_query_config",
+                        side_effect=exc):
+            self.assertRaises(psutil.AccessDenied, service.username)
+
+        # test __str__ and __repr__
+        self.assertIn(service.name(), str(service))
+        self.assertIn(service.display_name(), str(service))
+        self.assertIn(service.name(), repr(service))
+        self.assertIn(service.display_name(), repr(service))
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/battery.py
@@ -0,0 +1,48 @@
+#!/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.
+
+"""
+Show battery information.
+
+$ python scripts/battery.py
+charge:     74%
+left:       2:11:31
+status:     discharging
+plugged in: no
+"""
+
+from __future__ import print_function
+import sys
+
+import psutil
+
+
+def secs2hours(secs):
+    mm, ss = divmod(secs, 60)
+    hh, mm = divmod(mm, 60)
+    return "%d:%02d:%02d" % (hh, mm, ss)
+
+
+def main():
+    if not hasattr(psutil, "sensors_battery"):
+        return sys.exit("platform not supported")
+    batt = psutil.sensors_battery()
+    if batt is None:
+        return sys.exit("no battery is installed")
+
+    print("charge:     %s%%" % round(batt.percent, 2))
+    if batt.power_plugged:
+        print("status:     %s" % (
+            "charging" if batt.percent < 100 else "fully charged"))
+        print("plugged in: yes")
+    else:
+        print("left:       %s" % secs2hours(batt.secsleft))
+        print("status:     %s" % "discharging")
+        print("plugged in: no")
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/cpu_distribution.py
@@ -0,0 +1,96 @@
+#!/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.
+
+"""
+Shows CPU workload split across different CPUs.
+
+$ python scripts/cpu_workload.py
+CPU 0     CPU 1     CPU 2     CPU 3     CPU 4     CPU 5     CPU 6     CPU 7
+19.8      20.6      18.2      15.8      6.9       17.3      5.0       20.4
+gvfsd     pytho     kwork     chrom     unity     kwork     kwork     kwork
+chrom     chrom     indic     ibus-     whoop     nfsd      (sd-p     gvfsd
+ibus-     cat       at-sp     chrom     Modem     nfsd4     light     upsta
+ibus-     iprt-     ibus-     nacl_     cfg80     kwork     nfsd      bluet
+chrom     irqba     gpg-a     chrom     ext4-     biose     nfsd      dio/n
+chrom     acpid     bamfd     nvidi     kwork     scsi_     sshd      rpc.m
+upsta     rsysl     dbus-     nfsd      biose     scsi_     ext4-     polki
+rtkit     avahi     upowe     Netwo     scsi_     biose     UVM T     irq/9
+light     rpcbi     snapd     cron      ipv6_     biose     kwork     dbus-
+agett     kvm-i     avahi     kwork     biose     biose     scsi_     syste
+nfsd      syste     rpc.i     biose     biose     kbloc     kthro     UVM g
+nfsd      kwork     kwork     biose     vmsta     kwork     crypt     kaudi
+nfsd      scsi_     charg     biose     md        ksoft     kwork     kwork
+memca     biose     ksmd      ecryp     ksoft     watch     migra     nvme
+therm     biose     kcomp     kswap     migra     cpuhp     watch     biose
+syste     biose     kdevt     khuge     watch               cpuhp     biose
+led_w     devfr     kwork     write     cpuhp                         biose
+rpcio     oom_r     ksoft     kwork     syste                         biose
+kwork     kwork     watch     migra                                   acpi_
+biose     ksoft     cpuhp     watch                                   watch
+biose     migra               cpuhp                                   kinte
+biose     watch               rcu_s                                   netns
+biose     cpuhp               kthre                                   kwork
+cpuhp                                                                 ksoft
+watch                                                                 migra
+rcu_b                                                                 cpuhp
+kwork
+"""
+
+from __future__ import print_function
+import collections
+import os
+import sys
+import time
+
+import psutil
+
+
+if not hasattr(psutil.Process, "cpu_num"):
+    sys.exit("platform not supported")
+
+
+def clean_screen():
+    if psutil.POSIX:
+        os.system('clear')
+    else:
+        os.system('cls')
+
+
+def main():
+    total = psutil.cpu_count()
+    while True:
+        # header
+        clean_screen()
+        cpus_percent = psutil.cpu_percent(percpu=True)
+        for i in range(total):
+            print("CPU %-6i" % i, end="")
+        print()
+        for percent in cpus_percent:
+            print("%-10s" % percent, end="")
+        print()
+
+        # processes
+        procs = collections.defaultdict(list)
+        for p in psutil.process_iter(attrs=['name', 'cpu_num']):
+            procs[p.info['cpu_num']].append(p.info['name'][:5])
+
+        end_marker = [[] for x in range(total)]
+        while True:
+            for num in range(total):
+                try:
+                    pname = procs[num].pop()
+                except IndexError:
+                    pname = ""
+                print("%-10s" % pname[:10], end="")
+            print()
+            if procs.values() == end_marker:
+                break
+
+        time.sleep(1)
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/disk_usage.py
@@ -0,0 +1,63 @@
+#!/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.
+
+"""
+List all mounted disk partitions a-la "df -h" command.
+
+$ python scripts/disk_usage.py
+Device               Total     Used     Free  Use %      Type  Mount
+/dev/sdb3            18.9G    14.7G     3.3G    77%      ext4  /
+/dev/sda6           345.9G    83.8G   244.5G    24%      ext4  /home
+/dev/sda1           296.0M    43.1M   252.9M    14%      vfat  /boot/efi
+/dev/sda2           600.0M   312.4M   287.6M    52%   fuseblk  /media/Recovery
+"""
+
+import sys
+import os
+import psutil
+
+
+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 '%.1f%s' % (value, s)
+    return "%sB" % n
+
+
+def main():
+    templ = "%-17s %8s %8s %8s %5s%% %9s  %s"
+    print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type",
+                   "Mount"))
+    for part in psutil.disk_partitions(all=False):
+        if os.name == 'nt':
+            if 'cdrom' in part.opts or part.fstype == '':
+                # skip cd-rom drives with no disk in it; they may raise
+                # ENOENT, pop-up a Windows GUI error for a non-ready
+                # partition or just hang.
+                continue
+        usage = psutil.disk_usage(part.mountpoint)
+        print(templ % (
+            part.device,
+            bytes2human(usage.total),
+            bytes2human(usage.used),
+            bytes2human(usage.free),
+            int(usage.percent),
+            part.fstype,
+            part.mountpoint))
+
+
+if __name__ == '__main__':
+    sys.exit(main())
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/fans.py
@@ -0,0 +1,36 @@
+#!/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.
+
+"""
+Show fans information.
+
+$ python fans.py
+asus
+    cpu_fan              3200 RPM
+"""
+
+from __future__ import print_function
+import sys
+
+import psutil
+
+
+def main():
+    if not hasattr(psutil, "sensors_fans"):
+        return sys.exit("platform not supported")
+    fans = psutil.sensors_fans()
+    if not fans:
+        print("no fans detected")
+        return
+    for name, entries in fans.items():
+        print(name)
+        for entry in entries:
+            print("    %-20s %s RPM" % (entry.label or name, entry.current))
+        print()
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/free.py
@@ -0,0 +1,42 @@
+#!/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.
+
+"""
+A clone of 'free' cmdline utility.
+
+$ python scripts/free.py
+             total       used       free     shared    buffers      cache
+Mem:      10125520    8625996    1499524          0     349500    3307836
+Swap:            0          0          0
+"""
+
+import psutil
+
+
+def main():
+    virt = psutil.virtual_memory()
+    swap = psutil.swap_memory()
+    templ = "%-7s %10s %10s %10s %10s %10s %10s"
+    print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache'))
+    print(templ % (
+        'Mem:',
+        int(virt.total / 1024),
+        int(virt.used / 1024),
+        int(virt.free / 1024),
+        int(getattr(virt, 'shared', 0) / 1024),
+        int(getattr(virt, 'buffers', 0) / 1024),
+        int(getattr(virt, 'cached', 0) / 1024)))
+    print(templ % (
+        'Swap:', int(swap.total / 1024),
+        int(swap.used / 1024),
+        int(swap.free / 1024),
+        '',
+        '',
+        ''))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/ifconfig.py
@@ -0,0 +1,117 @@
+#!/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.
+
+"""
+A clone of 'ifconfig' on UNIX.
+
+$ python scripts/ifconfig.py
+lo:
+    stats          : speed=0MB, duplex=?, mtu=65536, up=yes
+    incoming       : bytes=1.95M, pkts=22158, errs=0, drops=0
+    outgoing       : bytes=1.95M, pkts=22158, errs=0, drops=0
+    IPv4 address   : 127.0.0.1
+         netmask   : 255.0.0.0
+    IPv6 address   : ::1
+         netmask   : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+    MAC  address   : 00:00:00:00:00:00
+
+docker0:
+    stats          : speed=0MB, duplex=?, mtu=1500, up=yes
+    incoming       : bytes=3.48M, pkts=65470, errs=0, drops=0
+    outgoing       : bytes=164.06M, pkts=112993, errs=0, drops=0
+    IPv4 address   : 172.17.0.1
+         broadcast : 172.17.0.1
+         netmask   : 255.255.0.0
+    IPv6 address   : fe80::42:27ff:fe5e:799e%docker0
+         netmask   : ffff:ffff:ffff:ffff::
+    MAC  address   : 02:42:27:5e:79:9e
+         broadcast : ff:ff:ff:ff:ff:ff
+
+wlp3s0:
+    stats          : speed=0MB, duplex=?, mtu=1500, up=yes
+    incoming       : bytes=7.04G, pkts=5637208, errs=0, drops=0
+    outgoing       : bytes=372.01M, pkts=3200026, errs=0, drops=0
+    IPv4 address   : 10.0.0.2
+         broadcast : 10.255.255.255
+         netmask   : 255.0.0.0
+    IPv6 address   : fe80::ecb3:1584:5d17:937%wlp3s0
+         netmask   : ffff:ffff:ffff:ffff::
+    MAC  address   : 48:45:20:59:a4:0c
+         broadcast : ff:ff:ff:ff:ff:ff
+"""
+
+from __future__ import print_function
+import socket
+
+import psutil
+
+
+af_map = {
+    socket.AF_INET: 'IPv4',
+    socket.AF_INET6: 'IPv6',
+    psutil.AF_LINK: 'MAC',
+}
+
+duplex_map = {
+    psutil.NIC_DUPLEX_FULL: "full",
+    psutil.NIC_DUPLEX_HALF: "half",
+    psutil.NIC_DUPLEX_UNKNOWN: "?",
+}
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K'
+    >>> bytes2human(100001221)
+    '95.4 M'
+    """
+    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 '%.2fB' % (n)
+
+
+def main():
+    stats = psutil.net_if_stats()
+    io_counters = psutil.net_io_counters(pernic=True)
+    for nic, addrs in psutil.net_if_addrs().items():
+        print("%s:" % (nic))
+        if nic in stats:
+            st = stats[nic]
+            print("    stats          : ", end='')
+            print("speed=%sMB, duplex=%s, mtu=%s, up=%s" % (
+                st.speed, duplex_map[st.duplex], st.mtu,
+                "yes" if st.isup else "no"))
+        if nic in io_counters:
+            io = io_counters[nic]
+            print("    incoming       : ", end='')
+            print("bytes=%s, pkts=%s, errs=%s, drops=%s" % (
+                bytes2human(io.bytes_recv), io.packets_recv, io.errin,
+                io.dropin))
+            print("    outgoing       : ", end='')
+            print("bytes=%s, pkts=%s, errs=%s, drops=%s" % (
+                bytes2human(io.bytes_sent), io.packets_sent, io.errout,
+                io.dropout))
+        for addr in addrs:
+            print("    %-4s" % af_map.get(addr.family, addr.family), end="")
+            print(" address   : %s" % addr.address)
+            if addr.broadcast:
+                print("         broadcast : %s" % addr.broadcast)
+            if addr.netmask:
+                print("         netmask   : %s" % addr.netmask)
+            if addr.ptp:
+                print("      p2p       : %s" % addr.ptp)
+        print("")
+
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/README
@@ -0,0 +1,2 @@
+This directory contains scripts which are meant to be used internally
+(benchmarks, CI automation, etc.).
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/bench_oneshot.py
@@ -0,0 +1,154 @@
+#!/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.
+
+"""
+A simple micro benchmark script which prints the speedup when using
+Process.oneshot() ctx manager.
+See: https://github.com/giampaolo/psutil/issues/799
+"""
+
+from __future__ import print_function, division
+import sys
+import timeit
+import textwrap
+
+import psutil
+
+
+ITERATIONS = 1000
+
+# The list of Process methods which gets collected in one shot and
+# as such get advantage of the speedup.
+names = [
+    'cpu_times',
+    'cpu_percent',
+    'memory_info',
+    'memory_percent',
+    'ppid',
+    'parent',
+]
+
+if psutil.POSIX:
+    names.append('uids')
+    names.append('username')
+
+if psutil.LINUX:
+    names += [
+        # 'memory_full_info',
+        # 'memory_maps',
+        'cpu_num',
+        'cpu_times',
+        'gids',
+        'name',
+        'num_ctx_switches',
+        'num_threads',
+        'ppid',
+        'status',
+        'terminal',
+        'uids',
+    ]
+elif psutil.BSD:
+    names = [
+        'cpu_times',
+        'gids',
+        'io_counters',
+        'memory_full_info',
+        'memory_info',
+        'name',
+        'num_ctx_switches',
+        'ppid',
+        'status',
+        'terminal',
+        'uids',
+    ]
+    if psutil.FREEBSD:
+        names.append('cpu_num')
+elif psutil.SUNOS:
+    names += [
+        'cmdline',
+        'gids',
+        'memory_full_info',
+        'memory_info',
+        'name',
+        'num_threads',
+        'ppid',
+        'status',
+        'terminal',
+        'uids',
+    ]
+elif psutil.OSX:
+    names += [
+        'cpu_times',
+        'create_time',
+        'gids',
+        'memory_info',
+        'name',
+        'num_ctx_switches',
+        'num_threads',
+        'ppid',
+        'terminal',
+        'uids',
+    ]
+elif psutil.WINDOWS:
+    names += [
+        'num_ctx_switches',
+        'num_threads',
+        # dual implementation, called in case of AccessDenied
+        'num_handles',
+        'cpu_times',
+        'create_time',
+        'num_threads',
+        'io_counters',
+        'memory_info',
+    ]
+
+names = sorted(set(names))
+
+setup = textwrap.dedent("""
+    from __main__ import names
+    import psutil
+
+    def call_normal(funs):
+        for fun in funs:
+            fun()
+
+    def call_oneshot(funs):
+        with p.oneshot():
+            for fun in funs:
+                fun()
+
+    p = psutil.Process()
+    funs = [getattr(p, n) for n in names]
+    """)
+
+
+def main():
+    print("%s methods involved on platform %r (%s iterations, psutil %s):" % (
+        len(names), sys.platform, ITERATIONS, psutil.__version__))
+    for name in sorted(names):
+        print("    " + name)
+
+    # "normal" run
+    elapsed1 = timeit.timeit(
+        "call_normal(funs)", setup=setup, number=ITERATIONS)
+    print("normal:  %.3f secs" % elapsed1)
+
+    # "one shot" run
+    elapsed2 = timeit.timeit(
+        "call_oneshot(funs)", setup=setup, number=ITERATIONS)
+    print("onshot:  %.3f secs" % elapsed2)
+
+    # done
+    if elapsed2 < elapsed1:
+        print("speedup: +%.2fx" % (elapsed1 / elapsed2))
+    elif elapsed2 > elapsed1:
+        print("slowdown: -%.2fx" % (elapsed2 / elapsed1))
+    else:
+        print("same speed")
+
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/bench_oneshot_2.py
@@ -0,0 +1,53 @@
+#!/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.
+
+"""
+Same as bench_oneshot.py but uses perf module instead, which is
+supposed to be more precise.
+"""
+
+import sys
+
+import perf  # requires "pip install perf"
+
+import psutil
+from bench_oneshot import names
+
+
+p = psutil.Process()
+funs = [getattr(p, n) for n in names]
+
+
+def call_normal():
+    for fun in funs:
+        fun()
+
+
+def call_oneshot():
+    with p.oneshot():
+        for fun in funs:
+            fun()
+
+
+def add_cmdline_args(cmd, args):
+    cmd.append(args.benchmark)
+
+
+def main():
+    runner = perf.Runner()
+
+    args = runner.parse_args()
+    if not args.worker:
+        print("%s methods involved on platform %r (psutil %s):" % (
+            len(names), sys.platform, psutil.__version__))
+        for name in sorted(names):
+            print("    " + name)
+
+    runner.bench_func("normal", call_normal)
+    runner.bench_func("oneshot", call_oneshot)
+
+
+main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/check_broken_links.py
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola', Himanshu Shekhar.
+# All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+"""
+Checks for broken links in file names specified as command line
+parameters.
+
+There are a ton of a solutions available for validating URLs in string
+using regex, but less for searching, of which very few are accurate.
+This snippet is intended to just do the required work, and avoid
+complexities. Django Validator has pretty good regex for validation,
+but we have to find urls instead of validating them (REFERENCES [7]).
+There's always room for improvement.
+
+Method:
+* Match URLs using regex (REFERENCES [1]])
+* Some URLs need to be fixed, as they have < (or) > due to inefficient
+  regex.
+* Remove duplicates (because regex is not 100% efficient as of now).
+* Check validity of URL, using HEAD request. (HEAD to save bandwidth)
+  Uses requests module for others are painful to use. REFERENCES[9]
+  Handles redirects, http, https, ftp as well.
+
+REFERENCES:
+Using [1] with some modificatons for including ftp
+[1] http://stackoverflow.com/a/6883094/5163807
+[2] http://stackoverflow.com/a/31952097/5163807
+[3] http://daringfireball.net/2010/07/improved_regex_for_matching_urls
+[4] https://mathiasbynens.be/demo/url-regex
+[5] https://github.com/django/django/blob/master/django/core/validators.py
+[6] https://data.iana.org/TLD/tlds-alpha-by-domain.txt
+[7] https://codereview.stackexchange.com/questions/19663/http-url-validating
+[8] https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
+[9] http://docs.python-requests.org/
+
+Author: Himanshu Shekhar <https://github.com/himanshub16> (2017)
+"""
+
+from __future__ import print_function
+
+import concurrent.futures
+import functools
+import os
+import re
+import sys
+import traceback
+
+import requests
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+REGEX = re.compile(
+    r'(?:http|ftp|https)?://'
+    r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
+REQUEST_TIMEOUT = 15
+# There are some status codes sent by websites on HEAD request.
+# Like 503 by Microsoft, and 401 by Apple
+# They need to be sent GET request
+RETRY_STATUSES = [503, 401, 403]
+
+
+def memoize(fun):
+    """A memoize decorator."""
+    @functools.wraps(fun)
+    def wrapper(*args, **kwargs):
+        key = (args, frozenset(sorted(kwargs.items())))
+        try:
+            return cache[key]
+        except KeyError:
+            ret = cache[key] = fun(*args, **kwargs)
+            return ret
+
+    cache = {}
+    return wrapper
+
+
+def sanitize_url(url):
+    url = url.rstrip(',')
+    url = url.rstrip('.')
+    url = url.lstrip('(')
+    url = url.rstrip(')')
+    url = url.lstrip('[')
+    url = url.rstrip(']')
+    url = url.lstrip('<')
+    url = url.rstrip('>')
+    return url
+
+
+def find_urls(s):
+    matches = REGEX.findall(s) or []
+    return list(set([sanitize_url(x) for x in matches]))
+
+
+def parse_rst(fname):
+    """Look for links in a .rst file."""
+    with open(fname) as f:
+        text = f.read()
+    urls = find_urls(text)
+    # HISTORY file has a lot of dead links.
+    if fname == 'HISTORY.rst' and urls:
+        urls = [
+            x for x in urls if
+            not x.startswith('https://github.com/giampaolo/psutil/issues')]
+    return urls
+
+
+def parse_py(fname):
+    """Look for links in a .py file."""
+    with open(fname) as f:
+        lines = f.readlines()
+    urls = set()
+    for i, line in enumerate(lines):
+        for url in find_urls(line):
+            # comment block
+            if line.lstrip().startswith('# '):
+                subidx = i + 1
+                while True:
+                    nextline = lines[subidx].strip()
+                    if re.match('^#     .+', nextline):
+                        url += nextline[1:].strip()
+                    else:
+                        break
+                    subidx += 1
+            urls.add(url)
+    return list(urls)
+
+
+def parse_c(fname):
+    """Look for links in a .py file."""
+    with open(fname) as f:
+        lines = f.readlines()
+    urls = set()
+    for i, line in enumerate(lines):
+        for url in find_urls(line):
+            # comment block //
+            if line.lstrip().startswith('// '):
+                subidx = i + 1
+                while True:
+                    nextline = lines[subidx].strip()
+                    if re.match('^//     .+', nextline):
+                        url += nextline[2:].strip()
+                    else:
+                        break
+                    subidx += 1
+            # comment block /*
+            elif line.lstrip().startswith('* '):
+                subidx = i + 1
+                while True:
+                    nextline = lines[subidx].strip()
+                    if re.match('^\*     .+', nextline):
+                        url += nextline[1:].strip()
+                    else:
+                        break
+                    subidx += 1
+            urls.add(url)
+    return list(urls)
+
+
+def parse_generic(fname):
+    with open(fname) as f:
+        text = f.read()
+    return find_urls(text)
+
+
+def get_urls(fname):
+    """Extracts all URLs in fname and return them as a list."""
+    if fname.endswith('.rst'):
+        return parse_rst(fname)
+    elif fname.endswith('.py'):
+        return parse_py(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'):
+                return parse_py(fname)
+        return parse_generic(fname)
+
+
+@memoize
+def validate_url(url):
+    """Validate the URL by attempting an HTTP connection.
+    Makes an HTTP-HEAD request for each URL.
+    """
+    try:
+        res = requests.head(url, timeout=REQUEST_TIMEOUT)
+        # some websites deny 503, like Microsoft
+        # and some send 401, like Apple, observations
+        if (not res.ok) and (res.status_code in RETRY_STATUSES):
+            res = requests.get(url, timeout=REQUEST_TIMEOUT)
+        return res.ok
+    except requests.exceptions.RequestException:
+        return False
+
+
+def parallel_validator(urls):
+    """validates all urls in parallel
+    urls: tuple(filename, url)
+    """
+    fails = []  # list of tuples (filename, url)
+    current = 0
+    total = len(urls)
+    with concurrent.futures.ThreadPoolExecutor() as executor:
+        fut_to_url = {executor.submit(validate_url, url[1]): url
+                      for url in urls}
+        for fut in concurrent.futures.as_completed(fut_to_url):
+            current += 1
+            sys.stdout.write("\r%s / %s" % (current, total))
+            sys.stdout.flush()
+            fname, url = fut_to_url[fut]
+            try:
+                ok = fut.result()
+            except Exception:
+                fails.append((fname, url))
+                print()
+                print("warn: error while validating %s" % url, file=sys.stderr)
+                traceback.print_exc()
+            else:
+                if not ok:
+                    fails.append((fname, url))
+
+    print()
+    return fails
+
+
+def main():
+    files = sys.argv[1:]
+    if not files:
+        print("usage: %s <FILES...>" % sys.argv[0], file=sys.stderr)
+        return sys.exit(1)
+
+    all_urls = []
+    for fname in files:
+        urls = get_urls(fname)
+        if urls:
+            print("%4s %s" % (len(urls), fname))
+            for url in urls:
+                all_urls.append((fname, url))
+
+    fails = parallel_validator(all_urls)
+    if not fails:
+        print("all links are valid; cheers!")
+    else:
+        for fail in fails:
+            fname, url = fail
+            print("%-30s: %s " % (fname, url))
+        print('-' * 20)
+        print("total: %s fails!" % len(fails))
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    try:
+        main()
+    except (KeyboardInterrupt, SystemExit):
+        os._exit(0)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/download_exes.py
@@ -0,0 +1,188 @@
+#!/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.
+
+"""
+Script which downloads exe and wheel files hosted on AppVeyor:
+https://ci.appveyor.com/project/giampaolo/psutil
+Readapted from the original recipe of Ibarra Corretge'
+<saghul@gmail.com>:
+http://code.saghul.net/index.php/2015/09/09/
+"""
+
+from __future__ import print_function
+import argparse
+import concurrent.futures
+import errno
+import os
+import requests
+import shutil
+import sys
+
+from psutil import __version__ as PSUTIL_VERSION
+
+
+BASE_URL = 'https://ci.appveyor.com/api'
+PY_VERSIONS = ['2.7', '3.4', '3.5', '3.6']
+TIMEOUT = 30
+COLORS = True
+
+
+def exit(msg=""):
+    if msg:
+        print(hilite(msg, ok=False), file=sys.stderr)
+    sys.exit(1)
+
+
+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
+
+
+COLORS = term_supports_colors()
+
+
+def hilite(s, ok=True, bold=False):
+    """Return an highlighted version of 'string'."""
+    if not 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 safe_makedirs(path):
+    try:
+        os.makedirs(path)
+    except OSError as err:
+        if err.errno == errno.EEXIST:
+            if not os.path.isdir(path):
+                raise
+        else:
+            raise
+
+
+def safe_rmtree(path):
+    def onerror(fun, path, excinfo):
+        exc = excinfo[1]
+        if exc.errno != errno.ENOENT:
+            raise
+
+    shutil.rmtree(path, onerror=onerror)
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K'
+    >>> bytes2human(100001221)
+    '95.4 M'
+    """
+    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 '%.2f B' % (n)
+
+
+def download_file(url):
+    local_fname = url.split('/')[-1]
+    local_fname = os.path.join('dist', local_fname)
+    safe_makedirs('dist')
+    r = requests.get(url, stream=True, timeout=TIMEOUT)
+    tot_bytes = 0
+    with open(local_fname, 'wb') as f:
+        for chunk in r.iter_content(chunk_size=16384):
+            if chunk:    # filter out keep-alive new chunks
+                f.write(chunk)
+                tot_bytes += len(chunk)
+    return local_fname
+
+
+def get_file_urls(options):
+    session = requests.Session()
+    data = session.get(
+        BASE_URL + '/projects/' + options.user + '/' + options.project,
+        timeout=TIMEOUT)
+    data = data.json()
+
+    urls = []
+    for job in (job['jobId'] for job in data['build']['jobs']):
+        job_url = BASE_URL + '/buildjobs/' + job + '/artifacts'
+        data = session.get(job_url, timeout=TIMEOUT)
+        data = data.json()
+        for item in data:
+            file_url = job_url + '/' + item['fileName']
+            urls.append(file_url)
+    if not urls:
+        exit("no artifacts found")
+    for url in sorted(urls, key=lambda x: os.path.basename(x)):
+        yield url
+
+
+def rename_27_wheels():
+    # See: https://github.com/giampaolo/psutil/issues/810
+    src = 'dist/psutil-%s-cp27-cp27m-win32.whl' % PSUTIL_VERSION
+    dst = 'dist/psutil-%s-cp27-none-win32.whl' % PSUTIL_VERSION
+    print("rename: %s\n        %s" % (src, dst))
+    os.rename(src, dst)
+    src = 'dist/psutil-%s-cp27-cp27m-win_amd64.whl' % PSUTIL_VERSION
+    dst = 'dist/psutil-%s-cp27-none-win_amd64.whl' % PSUTIL_VERSION
+    print("rename: %s\n        %s" % (src, dst))
+    os.rename(src, dst)
+
+
+def main(options):
+    safe_rmtree('dist')
+    urls = get_file_urls(options)
+    completed = 0
+    exc = None
+    with concurrent.futures.ThreadPoolExecutor() as e:
+        fut_to_url = {e.submit(download_file, url): url for url in urls}
+        for fut in concurrent.futures.as_completed(fut_to_url):
+            url = fut_to_url[fut]
+            try:
+                local_fname = fut.result()
+            except Exception as _:
+                exc = _
+                print("error while downloading %s: %s" % (url, exc))
+            else:
+                completed += 1
+                print("downloaded %-45s %s" % (
+                    local_fname, bytes2human(os.path.getsize(local_fname))))
+    # 2 wheels (32 and 64 bit) per supported python version
+    expected = len(PY_VERSIONS) * 2
+    if expected != completed:
+        return exit("expected %s files, got %s" % (expected, completed))
+    if exc:
+        return exit()
+    rename_27_wheels()
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(
+        description='AppVeyor artifact downloader')
+    parser.add_argument('--user', required=True)
+    parser.add_argument('--project', required=True)
+    args = parser.parse_args()
+    main(args)
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/generate_manifest.py
@@ -0,0 +1,35 @@
+#!/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.
+
+"""
+Generate MANIFEST.in file.
+"""
+
+import os
+import subprocess
+
+
+IGNORED_EXTS = ('.png', '.jpg', '.jpeg')
+IGNORED_FILES = ('.travis.yml', 'appveyor.yml')
+
+
+def sh(cmd):
+    return subprocess.check_output(
+        cmd, shell=True, universal_newlines=True).strip()
+
+
+def main():
+    files = sh("git ls-files").split('\n')
+    for file in files:
+        if file.startswith('.ci/') or \
+                os.path.splitext(file)[1].lower() in IGNORED_EXTS or \
+                file in IGNORED_FILES:
+            continue
+        print("include " + file)
+
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/print_timeline.py
@@ -0,0 +1,53 @@
+#!/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.
+
+"""
+Prints releases' timeline in RST format.
+"""
+
+import subprocess
+
+
+entry = """\
+- {date}:
+  `{ver} <https://pypi.python.org/pypi?name=psutil&version={ver}&:action=files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#{nodotver}>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/{prevtag}...{tag}#files_bucket>`__"""  # NOQA
+
+
+def sh(cmd):
+    return subprocess.check_output(
+        cmd, shell=True, universal_newlines=True).strip()
+
+
+def get_tag_date(tag):
+    out = sh(r"git log -1 --format=%ai {}".format(tag))
+    return out.split(' ')[0]
+
+
+def main():
+    releases = []
+    out = sh("git tags")
+    for line in out.split('\n'):
+        tag = line.split(' ')[0]
+        ver = tag.replace('release-', '')
+        nodotver = ver.replace('.', '')
+        date = get_tag_date(tag)
+        releases.append((tag, ver, nodotver, date))
+    releases.sort(reverse=True)
+
+    for i, rel in enumerate(releases):
+        tag, ver, nodotver, date = rel
+        try:
+            prevtag = releases[i + 1][0]
+        except IndexError:
+            # get first commit
+            prevtag = sh("git rev-list --max-parents=0 HEAD")
+        print(entry.format(**locals()))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/winmake.py
@@ -0,0 +1,508 @@
+#!/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.
+
+
+"""Shortcuts for various tasks, emulating UNIX "make" on Windows.
+This is supposed to be invoked by "make.bat" and not used directly.
+This was originally written as a bat file but they suck so much
+that they should be deemed illegal!
+"""
+
+from __future__ import print_function
+import errno
+import fnmatch
+import functools
+import os
+import shutil
+import site
+import ssl
+import subprocess
+import sys
+import tempfile
+
+
+PYTHON = os.getenv('PYTHON', sys.executable)
+TSCRIPT = os.getenv('TSCRIPT', 'psutil\\tests\\__main__.py')
+GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
+PY3 = sys.version_info[0] == 3
+DEPS = [
+    "coverage",
+    "flake8",
+    "ipaddress",
+    "mock",
+    "nose",
+    "pdbpp",
+    "perf",
+    "pip",
+    "pypiwin32",
+    "pyreadline",
+    "setuptools",
+    "unittest2",
+    "wheel",
+    "wmi",
+    "requests"
+]
+_cmds = {}
+if PY3:
+    basestring = str
+
+# ===================================================================
+# utils
+# ===================================================================
+
+
+def safe_print(text, file=sys.stdout, flush=False):
+    """Prints a (unicode) string to the console, encoded depending on
+    the stdout/file encoding (eg. cp437 on Windows). This is to avoid
+    encoding errors in case of funky path names.
+    Works with Python 2 and 3.
+    """
+    if not isinstance(text, basestring):
+        return print(text, file=file)
+    try:
+        file.write(text)
+    except UnicodeEncodeError:
+        bytes_string = text.encode(file.encoding, 'backslashreplace')
+        if hasattr(file, 'buffer'):
+            file.buffer.write(bytes_string)
+        else:
+            text = bytes_string.decode(file.encoding, 'strict')
+            file.write(text)
+    file.write("\n")
+
+
+def sh(cmd, nolog=False):
+    if not nolog:
+        safe_print("cmd: " + cmd)
+    p = subprocess.Popen(cmd, shell=True, env=os.environ, cwd=os.getcwd())
+    p.communicate()
+    if p.returncode != 0:
+        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):
+        try:
+            os.remove(path)
+        except OSError as err:
+            if err.errno != errno.ENOENT:
+                raise
+        else:
+            safe_print("rm %s" % path)
+
+    def safe_rmtree(path):
+        def onerror(fun, path, excinfo):
+            exc = excinfo[1]
+            if exc.errno != errno.ENOENT:
+                raise
+
+        existed = os.path.isdir(path)
+        shutil.rmtree(path, onerror=onerror)
+        if existed:
+            safe_print("rmdir -f %s" % path)
+
+    if "*" not in pattern:
+        if directory:
+            safe_rmtree(pattern)
+        else:
+            safe_remove(pattern)
+        return
+
+    for root, subdirs, subfiles in os.walk('.'):
+        root = os.path.normpath(root)
+        if root.startswith('.git/'):
+            continue
+        found = fnmatch.filter(subdirs if directory else subfiles, pattern)
+        for name in found:
+            path = os.path.join(root, name)
+            if directory:
+                safe_print("rmdir -f %s" % path)
+                safe_rmtree(path)
+            else:
+                safe_print("rm %s" % path)
+                safe_remove(path)
+
+
+def safe_remove(path):
+    try:
+        os.remove(path)
+    except OSError as err:
+        if err.errno != errno.ENOENT:
+            raise
+    else:
+        safe_print("rm %s" % path)
+
+
+def safe_rmtree(path):
+    def onerror(fun, path, excinfo):
+        exc = excinfo[1]
+        if exc.errno != errno.ENOENT:
+            raise
+
+    existed = os.path.isdir(path)
+    shutil.rmtree(path, onerror=onerror)
+    if existed:
+        safe_print("rmdir -f %s" % path)
+
+
+def recursive_rm(*patterns):
+    """Recursively remove a file or matching a list of patterns."""
+    for root, subdirs, subfiles in os.walk(u'.'):
+        root = os.path.normpath(root)
+        if root.startswith('.git/'):
+            continue
+        for file in subfiles:
+            for pattern in patterns:
+                if fnmatch.fnmatch(file, pattern):
+                    safe_remove(os.path.join(root, file))
+        for dir in subdirs:
+            for pattern in patterns:
+                if fnmatch.fnmatch(dir, pattern):
+                    safe_rmtree(os.path.join(root, dir))
+
+
+def test_setup():
+    os.environ['PYTHONWARNINGS'] = 'all'
+    os.environ['PSUTIL_TESTING'] = '1'
+    os.environ['PSUTIL_DEBUG'] = '1'
+
+
+# ===================================================================
+# commands
+# ===================================================================
+
+
+@cmd
+def help():
+    """Print this help"""
+    safe_print('Run "make [-p <PYTHON>] <target>" where <target> 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)
+    # 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)
+
+
+@cmd
+def build_wheel():
+    """Create wheel file."""
+    build()
+    sh("%s setup.py bdist_wheel" % PYTHON)
+
+
+@cmd
+def install_pip():
+    """Install pip"""
+    try:
+        import pip  # NOQA
+    except ImportError:
+        if PY3:
+            from urllib.request import urlopen
+        else:
+            from urllib2 import urlopen
+
+        if hasattr(ssl, '_create_unverified_context'):
+            ctx = ssl._create_unverified_context()
+        else:
+            ctx = None
+        kw = dict(context=ctx) if ctx else {}
+        safe_print("downloading %s" % GET_PIP_URL)
+        req = urlopen(GET_PIP_URL, **kw)
+        data = req.read()
+
+        tfile = os.path.join(tempfile.gettempdir(), 'get-pip.py')
+        with open(tfile, 'wb') as f:
+            f.write(data)
+
+        try:
+            sh('%s %s --user' % (PYTHON, tfile))
+        finally:
+            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.
+    # On "import psutil" tests may import a psutil version living in
+    # C:\PythonXY\Lib\site-packages which is not what we want, so
+    # we try both "pip uninstall psutil" and manually remove stuff
+    # from site-packages.
+    clean()
+    install_pip()
+    here = os.getcwd()
+    try:
+        os.chdir('C:\\')
+        while True:
+            try:
+                import psutil  # NOQA
+            except ImportError:
+                break
+            else:
+                sh("%s -m pip uninstall -y psutil" % PYTHON)
+    finally:
+        os.chdir(here)
+
+    for dir in site.getsitepackages():
+        for name in os.listdir(dir):
+            if name.startswith('psutil'):
+                rm(os.path.join(dir, name))
+
+
+@cmd
+def clean():
+    """Deletes dev files"""
+    recursive_rm(
+        "$testfn*",
+        "*.bak",
+        "*.core",
+        "*.egg-info",
+        "*.orig",
+        "*.pyc",
+        "*.pyd",
+        "*.pyo",
+        "*.rej",
+        "*.so",
+        "*.~",
+        "*__pycache__",
+        ".coverage",
+        ".tox",
+    )
+    safe_rmtree("build")
+    safe_rmtree(".coverage")
+    safe_rmtree("dist")
+    safe_rmtree("docs/_build")
+    safe_rmtree("htmlcov")
+    safe_rmtree("tmp")
+
+
+@cmd
+def setup_dev_env():
+    """Install useful deps"""
+    install_pip()
+    install_git_hooks()
+    sh("%s -m pip install -U %s" % (PYTHON, " ".join(DEPS)))
+
+
+@cmd
+def flake8():
+    """Run flake8 against all py files"""
+    py_files = subprocess.check_output("git ls-files")
+    if PY3:
+        py_files = py_files.decode()
+    py_files = [x for x in py_files.split() if x.endswith('.py')]
+    py_files = ' '.join(py_files)
+    sh("%s -m flake8 %s" % (PYTHON, py_files), nolog=True)
+
+
+@cmd
+def test():
+    """Run tests"""
+    install()
+    test_setup()
+    sh("%s %s" % (PYTHON, TSCRIPT))
+
+
+@cmd
+def coverage():
+    """Run coverage tests."""
+    # Note: coverage options are controlled by .coveragerc file
+    install()
+    test_setup()
+    sh("%s -m coverage run %s" % (PYTHON, TSCRIPT))
+    sh("%s -m coverage report" % PYTHON)
+    sh("%s -m coverage html" % PYTHON)
+    sh("%s -m webbrowser -t htmlcov/index.html" % PYTHON)
+
+
+@cmd
+def test_process():
+    """Run process tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_process" % PYTHON)
+
+
+@cmd
+def test_system():
+    """Run system tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_system" % PYTHON)
+
+
+@cmd
+def test_platform():
+    """Run windows only tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_windows" % PYTHON)
+
+
+@cmd
+def test_misc():
+    """Run misc tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_misc" % PYTHON)
+
+
+@cmd
+def test_unicode():
+    """Run unicode tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_unicode" % PYTHON)
+
+
+@cmd
+def test_connections():
+    """Run connections tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_connections" % PYTHON)
+
+
+@cmd
+def test_contracts():
+    """Run contracts tests"""
+    install()
+    test_setup()
+    sh("%s -m unittest -v psutil.tests.test_contracts" % PYTHON)
+
+
+@cmd
+def test_by_name():
+    """Run test by name"""
+    try:
+        safe_print(sys.argv)
+        name = sys.argv[2]
+    except IndexError:
+        sys.exit('second arg missing')
+    install()
+    test_setup()
+    sh("%s -m unittest -v %s" % (PYTHON, name))
+
+
+@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()
+    test_setup()
+    sh("%s psutil\\tests\\test_memory_leaks.py" % PYTHON)
+
+
+@cmd
+def install_git_hooks():
+    if os.path.isdir('.git'):
+        shutil.copy(".git-pre-commit", ".git\\hooks\\pre-commit")
+
+
+@cmd
+def bench_oneshot():
+    install()
+    sh("%s -Wa scripts\\internal\\bench_oneshot.py" % PYTHON)
+
+
+@cmd
+def bench_oneshot_2():
+    install()
+    sh("%s -Wa scripts\\internal\\bench_oneshot_2.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 = '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 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()
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/iotop.py
@@ -0,0 +1,182 @@
+#!/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.
+
+"""
+A clone of iotop (http://guichaz.free.fr/iotop/) showing real time
+disk I/O statistics.
+
+It works on Linux only (FreeBSD and OSX are missing support for IO
+counters).
+It doesn't work on Windows as curses module is required.
+
+Example output:
+
+$ python scripts/iotop.py
+Total DISK READ: 0.00 B/s | Total DISK WRITE: 472.00 K/s
+PID   USER      DISK READ  DISK WRITE  COMMAND
+13155 giampao    0.00 B/s  428.00 K/s  /usr/bin/google-chrome-beta
+3260  giampao    0.00 B/s    0.00 B/s  bash
+3779  giampao    0.00 B/s    0.00 B/s  gnome-session --session=ubuntu
+3830  giampao    0.00 B/s    0.00 B/s  /usr/bin/dbus-launch
+3831  giampao    0.00 B/s    0.00 B/s  //bin/dbus-daemon --fork --print-pid 5
+3841  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi-bus-launcher
+3845  giampao    0.00 B/s    0.00 B/s  /bin/dbus-daemon
+3848  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi2-core/at-spi2-registryd
+3862  giampao    0.00 B/s    0.00 B/s  /usr/lib/gnome-settings-daemon
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+"""
+
+import atexit
+import time
+import sys
+try:
+    import curses
+except ImportError:
+    sys.exit('platform not supported')
+
+import psutil
+
+
+# --- curses stuff
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+# --- /curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K/s'
+    >>> bytes2human(100001221)
+    '95.4 M/s'
+    """
+    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/s' % (value, s)
+    return '%.2f B/s' % (n)
+
+
+def poll(interval):
+    """Calculate IO usage by comparing IO statics before and
+    after the interval.
+    Return a tuple including all currently running processes
+    sorted by IO activity and total disks I/O activity.
+    """
+    # first get a list of all processes and disk io counters
+    procs = [p for p in psutil.process_iter()]
+    for p in procs[:]:
+        try:
+            p._before = p.io_counters()
+        except psutil.Error:
+            procs.remove(p)
+            continue
+    disks_before = psutil.disk_io_counters()
+
+    # sleep some time
+    time.sleep(interval)
+
+    # then retrieve the same info again
+    for p in procs[:]:
+        with p.oneshot():
+            try:
+                p._after = p.io_counters()
+                p._cmdline = ' '.join(p.cmdline())
+                if not p._cmdline:
+                    p._cmdline = p.name()
+                p._username = p.username()
+            except (psutil.NoSuchProcess, psutil.ZombieProcess):
+                procs.remove(p)
+    disks_after = psutil.disk_io_counters()
+
+    # finally calculate results by comparing data before and
+    # after the interval
+    for p in procs:
+        p._read_per_sec = p._after.read_bytes - p._before.read_bytes
+        p._write_per_sec = p._after.write_bytes - p._before.write_bytes
+        p._total = p._read_per_sec + p._write_per_sec
+
+    disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes
+    disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes
+
+    # sort processes by total disk IO so that the more intensive
+    # ones get listed first
+    processes = sorted(procs, key=lambda p: p._total, reverse=True)
+
+    return (processes, disks_read_per_sec, disks_write_per_sec)
+
+
+def refresh_window(procs, disks_read, disks_write):
+    """Print results on screen by using curses."""
+    curses.endwin()
+    templ = "%-5s %-7s %11s %11s  %s"
+    win.erase()
+
+    disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \
+                % (bytes2human(disks_read), bytes2human(disks_write))
+    print_line(disks_tot)
+
+    header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND")
+    print_line(header, highlight=True)
+
+    for p in procs:
+        line = templ % (
+            p.pid,
+            p._username[:7],
+            bytes2human(p._read_per_sec),
+            bytes2human(p._write_per_sec),
+            p._cmdline)
+        try:
+            print_line(line)
+        except curses.error:
+            break
+    win.refresh()
+
+
+def main():
+    try:
+        interval = 0
+        while True:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/killall.py
@@ -0,0 +1,34 @@
+#!/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.
+
+"""
+Kill a process by name.
+"""
+
+import os
+import sys
+import psutil
+
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: %s name' % __file__)
+    else:
+        NAME = sys.argv[1]
+
+    killed = []
+    for proc in psutil.process_iter():
+        if proc.name() == NAME and proc.pid != os.getpid():
+            proc.kill()
+            killed.append(proc.pid)
+    if not killed:
+        sys.exit('%s: no process found' % NAME)
+    else:
+        sys.exit(0)
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/meminfo.py
@@ -0,0 +1,69 @@
+#!/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.
+
+"""
+Print system memory information.
+
+$ python scripts/meminfo.py
+MEMORY
+------
+Total      :    9.7G
+Available  :    4.9G
+Percent    :    49.0
+Used       :    8.2G
+Free       :    1.4G
+Active     :    5.6G
+Inactive   :    2.1G
+Buffers    :  341.2M
+Cached     :    3.2G
+
+SWAP
+----
+Total      :      0B
+Used       :      0B
+Free       :      0B
+Percent    :     0.0
+Sin        :      0B
+Sout       :      0B
+"""
+
+import psutil
+
+
+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 '%.1f%s' % (value, s)
+    return "%sB" % n
+
+
+def pprint_ntuple(nt):
+    for name in nt._fields:
+        value = getattr(nt, name)
+        if name != 'percent':
+            value = bytes2human(value)
+        print('%-10s : %7s' % (name.capitalize(), value))
+
+
+def main():
+    print('MEMORY\n------')
+    pprint_ntuple(psutil.virtual_memory())
+    print('\nSWAP\n----')
+    pprint_ntuple(psutil.swap_memory())
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/netstat.py
@@ -0,0 +1,62 @@
+#!/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.
+
+"""
+A clone of 'netstat -antp' on Linux.
+
+$ python scripts/netstat.py
+Proto Local address      Remote address   Status        PID    Program name
+tcp   127.0.0.1:48256    127.0.0.1:45884  ESTABLISHED   13646  chrome
+tcp   127.0.0.1:47073    127.0.0.1:45884  ESTABLISHED   13646  chrome
+tcp   127.0.0.1:47072    127.0.0.1:45884  ESTABLISHED   13646  chrome
+tcp   127.0.0.1:45884    -                LISTEN        13651  GoogleTalkPlugi
+tcp   127.0.0.1:60948    -                LISTEN        13651  GoogleTalkPlugi
+tcp   172.17.42.1:49102  127.0.0.1:19305  CLOSE_WAIT    13651  GoogleTalkPlugi
+tcp   172.17.42.1:55797  127.0.0.1:443    CLOSE_WAIT    13651  GoogleTalkPlugi
+...
+"""
+
+import socket
+from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
+
+import psutil
+
+
+AD = "-"
+AF_INET6 = getattr(socket, 'AF_INET6', object())
+proto_map = {
+    (AF_INET, SOCK_STREAM): 'tcp',
+    (AF_INET6, SOCK_STREAM): 'tcp6',
+    (AF_INET, SOCK_DGRAM): 'udp',
+    (AF_INET6, SOCK_DGRAM): 'udp6',
+}
+
+
+def main():
+    templ = "%-5s %-30s %-30s %-13s %-6s %s"
+    print(templ % (
+        "Proto", "Local address", "Remote address", "Status", "PID",
+        "Program name"))
+    proc_names = {}
+    for p in psutil.process_iter(attrs=['pid', 'name']):
+        proc_names[p.info['pid']] = p.info['name']
+    for c in psutil.net_connections(kind='inet'):
+        laddr = "%s:%s" % (c.laddr)
+        raddr = ""
+        if c.raddr:
+            raddr = "%s:%s" % (c.raddr)
+        print(templ % (
+            proto_map[(c.family, c.type)],
+            laddr,
+            raddr or AD,
+            c.status,
+            c.pid or AD,
+            proc_names.get(c.pid, '?')[:15],
+        ))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/nettop.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+#
+# $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $
+#
+# 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.
+
+"""
+Shows real-time network statistics.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+
+$ python scripts/nettop.py
+-----------------------------------------------------------
+total bytes:           sent: 1.49 G       received: 4.82 G
+total packets:         sent: 7338724      received: 8082712
+
+wlan0                     TOTAL         PER-SEC
+-----------------------------------------------------------
+bytes-sent               1.29 G        0.00 B/s
+bytes-recv               3.48 G        0.00 B/s
+pkts-sent               7221782               0
+pkts-recv               6753724               0
+
+eth1                      TOTAL         PER-SEC
+-----------------------------------------------------------
+bytes-sent             131.77 M        0.00 B/s
+bytes-recv               1.28 G        0.00 B/s
+pkts-sent                     0               0
+pkts-recv               1214470               0
+"""
+
+import atexit
+import time
+import sys
+try:
+    import curses
+except ImportError:
+    sys.exit('platform not supported')
+
+import psutil
+
+
+# --- curses stuff
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+# --- curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K'
+    >>> bytes2human(100001221)
+    '95.4 M'
+    """
+    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 '%.2f B' % (n)
+
+
+def poll(interval):
+    """Retrieve raw stats within an interval window."""
+    tot_before = psutil.net_io_counters()
+    pnic_before = psutil.net_io_counters(pernic=True)
+    # sleep some time
+    time.sleep(interval)
+    tot_after = psutil.net_io_counters()
+    pnic_after = psutil.net_io_counters(pernic=True)
+    return (tot_before, tot_after, pnic_before, pnic_after)
+
+
+def refresh_window(tot_before, tot_after, pnic_before, pnic_after):
+    """Print stats on screen."""
+    global lineno
+
+    # totals
+    print_line("total bytes:           sent: %-10s   received: %s" % (
+        bytes2human(tot_after.bytes_sent),
+        bytes2human(tot_after.bytes_recv))
+    )
+    print_line("total packets:         sent: %-10s   received: %s" % (
+        tot_after.packets_sent, tot_after.packets_recv))
+
+    # per-network interface details: let's sort network interfaces so
+    # that the ones which generated more traffic are shown first
+    print_line("")
+    nic_names = list(pnic_after.keys())
+    nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True)
+    for name in nic_names:
+        stats_before = pnic_before[name]
+        stats_after = pnic_after[name]
+        templ = "%-15s %15s %15s"
+        print_line(templ % (name, "TOTAL", "PER-SEC"), highlight=True)
+        print_line(templ % (
+            "bytes-sent",
+            bytes2human(stats_after.bytes_sent),
+            bytes2human(
+                stats_after.bytes_sent - stats_before.bytes_sent) + '/s',
+        ))
+        print_line(templ % (
+            "bytes-recv",
+            bytes2human(stats_after.bytes_recv),
+            bytes2human(
+                stats_after.bytes_recv - stats_before.bytes_recv) + '/s',
+        ))
+        print_line(templ % (
+            "pkts-sent",
+            stats_after.packets_sent,
+            stats_after.packets_sent - stats_before.packets_sent,
+        ))
+        print_line(templ % (
+            "pkts-recv",
+            stats_after.packets_recv,
+            stats_after.packets_recv - stats_before.packets_recv,
+        ))
+        print_line("")
+    win.refresh()
+    lineno = 0
+
+
+def main():
+    try:
+        interval = 0
+        while True:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/pidof.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""
+A clone of 'pidof' cmdline utility.
+$ pidof python
+1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119
+"""
+
+from __future__ import print_function
+import psutil
+import sys
+
+
+def pidof(pgname):
+    pids = []
+    for proc in psutil.process_iter(attrs=['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:
+            pids.append(str(proc.pid))
+    return pids
+
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: %s pgname' % __file__)
+    else:
+        pgname = sys.argv[1]
+    pids = pidof(pgname)
+    if pids:
+        print(" ".join(pids))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/pmap.py
@@ -0,0 +1,58 @@
+#!/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.
+
+"""
+A clone of 'pmap' utility on Linux, 'vmmap' on OSX and 'procstat -v' on BSD.
+Report memory map of a process.
+
+$ python scripts/pmap.py 32402
+pid=32402, name=hg
+Address                 RSS  Mode    Mapping
+0000000000400000      1200K  r-xp    /usr/bin/python2.7
+0000000000838000         4K  r--p    /usr/bin/python2.7
+0000000000839000       304K  rw-p    /usr/bin/python2.7
+00000000008ae000        68K  rw-p    [anon]
+000000000275e000      5396K  rw-p    [heap]
+00002b29bb1e0000       124K  r-xp    /lib/x86_64-linux-gnu/ld-2.17.so
+00002b29bb203000         8K  rw-p    [anon]
+00002b29bb220000       528K  rw-p    [anon]
+00002b29bb2d8000       768K  rw-p    [anon]
+00002b29bb402000         4K  r--p    /lib/x86_64-linux-gnu/ld-2.17.so
+00002b29bb403000         8K  rw-p    /lib/x86_64-linux-gnu/ld-2.17.so
+00002b29bb405000        60K  r-xp    /lib/x86_64-linux-gnu/libpthread-2.17.so
+00002b29bb41d000         0K  ---p    /lib/x86_64-linux-gnu/libpthread-2.17.so
+00007fff94be6000        48K  rw-p    [stack]
+00007fff94dd1000         4K  r-xp    [vdso]
+ffffffffff600000         0K  r-xp    [vsyscall]
+...
+"""
+
+import sys
+
+import psutil
+
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: pmap <pid>')
+    p = psutil.Process(int(sys.argv[1]))
+    print("pid=%s, name=%s" % (p.pid, p.name()))
+    templ = "%-16s %10s  %-7s %s"
+    print(templ % ("Address", "RSS", "Mode", "Mapping"))
+    total_rss = 0
+    for m in p.memory_maps(grouped=False):
+        total_rss += m.rss
+        print(templ % (
+            m.addr.split('-')[0].zfill(16),
+            str(m.rss / 1024) + 'K',
+            m.perms,
+            m.path))
+    print("-" * 33)
+    print(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/procinfo.py
@@ -0,0 +1,339 @@
+#!/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.
+
+"""
+Print detailed information about a process.
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+
+$ python scripts/procinfo.py
+pid           4600
+name          chrome
+parent        4554 (bash)
+exe           /opt/google/chrome/chrome
+cwd           /home/giampaolo
+cmdline       /opt/google/chrome/chrome
+started       2016-09-19 11:12
+cpu-tspent    27:27.68
+cpu-times     user=8914.32, system=3530.59,
+              children_user=1.46, children_system=1.31
+cpu-affinity  [0, 1, 2, 3, 4, 5, 6, 7]
+memory        rss=520.5M, vms=1.9G, shared=132.6M, text=95.0M, lib=0B,
+              data=816.5M, dirty=0B
+memory %      3.26
+user          giampaolo
+uids          real=1000, effective=1000, saved=1000
+uids          real=1000, effective=1000, saved=1000
+terminal      /dev/pts/2
+status        sleeping
+nice          0
+ionice        class=IOPriority.IOPRIO_CLASS_NONE, value=0
+num-threads   47
+num-fds       379
+I/O           read_count=96.6M, write_count=80.7M,
+              read_bytes=293.2M, write_bytes=24.5G
+ctx-switches  voluntary=30426463, involuntary=460108
+children      PID    NAME
+              4605   cat
+              4606   cat
+              4609   chrome
+              4669   chrome
+open-files    PATH
+              /opt/google/chrome/icudtl.dat
+              /opt/google/chrome/snapshot_blob.bin
+              /opt/google/chrome/natives_blob.bin
+              /opt/google/chrome/chrome_100_percent.pak
+              [...]
+connections   PROTO LOCAL ADDR            REMOTE ADDR               STATUS
+              UDP   10.0.0.3:3693         *:*                       NONE
+              TCP   10.0.0.3:55102        172.217.22.14:443         ESTABLISHED
+              UDP   10.0.0.3:35172        *:*                       NONE
+              TCP   10.0.0.3:32922        172.217.16.163:443        ESTABLISHED
+              UDP   :::5353               *:*                       NONE
+              UDP   10.0.0.3:59925        *:*                       NONE
+threads       TID              USER          SYSTEM
+              11795             0.7            1.35
+              11796            0.68            1.37
+              15887            0.74            0.03
+              19055            0.77            0.01
+              [...]
+              total=47
+res-limits    RLIMIT                     SOFT       HARD
+              virtualmem             infinity   infinity
+              coredumpsize                  0   infinity
+              cputime                infinity   infinity
+              datasize               infinity   infinity
+              filesize               infinity   infinity
+              locks                  infinity   infinity
+              memlock                   65536      65536
+              msgqueue                 819200     819200
+              nice                          0          0
+              openfiles                  8192      65536
+              maxprocesses              63304      63304
+              rss                    infinity   infinity
+              realtimeprio                  0          0
+              rtimesched             infinity   infinity
+              sigspending               63304      63304
+              stack                   8388608   infinity
+mem-maps      RSS      PATH
+              381.4M   [anon]
+              62.8M    /opt/google/chrome/chrome
+              15.8M    /home/giampaolo/.config/google-chrome/Default/History
+              6.6M     /home/giampaolo/.config/google-chrome/Default/Favicons
+              [...]
+"""
+
+import argparse
+import datetime
+import socket
+import sys
+
+import psutil
+
+
+ACCESS_DENIED = ''
+NON_VERBOSE_ITERATIONS = 4
+RLIMITS_MAP = {
+    "RLIMIT_AS": "virtualmem",
+    "RLIMIT_CORE": "coredumpsize",
+    "RLIMIT_CPU": "cputime",
+    "RLIMIT_DATA": "datasize",
+    "RLIMIT_FSIZE": "filesize",
+    "RLIMIT_LOCKS": "locks",
+    "RLIMIT_MEMLOCK": "memlock",
+    "RLIMIT_MSGQUEUE": "msgqueue",
+    "RLIMIT_NICE": "nice",
+    "RLIMIT_NOFILE": "openfiles",
+    "RLIMIT_NPROC": "maxprocesses",
+    "RLIMIT_RSS": "rss",
+    "RLIMIT_RTPRIO": "realtimeprio",
+    "RLIMIT_RTTIME": "rtimesched",
+    "RLIMIT_SIGPENDING": "sigspending",
+    "RLIMIT_STACK": "stack",
+}
+
+
+def convert_bytes(n):
+    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 '%.1f%s' % (value, s)
+    return "%sB" % n
+
+
+def print_(a, b):
+    if sys.stdout.isatty() and psutil.POSIX:
+        fmt = '\x1b[1;32m%-13s\x1b[0m %s' % (a, b)
+    else:
+        fmt = '%-11s %s' % (a, b)
+    print(fmt)
+
+
+def str_ntuple(nt, bytes2human=False):
+    if nt == ACCESS_DENIED:
+        return ""
+    if not bytes2human:
+        return ", ".join(["%s=%s" % (x, getattr(nt, x)) for x in nt._fields])
+    else:
+        return ", ".join(["%s=%s" % (x, convert_bytes(getattr(nt, x)))
+                          for x in nt._fields])
+
+
+def run(pid, verbose=False):
+    try:
+        proc = psutil.Process(pid)
+        pinfo = proc.as_dict(ad_value=ACCESS_DENIED)
+    except psutil.NoSuchProcess as err:
+        sys.exit(str(err))
+
+    # collect other proc info
+    with proc.oneshot():
+        try:
+            parent = proc.parent()
+            if parent:
+                parent = '(%s)' % parent.name()
+            else:
+                parent = ''
+        except psutil.Error:
+            parent = ''
+        try:
+            pinfo['children'] = proc.children()
+        except psutil.Error:
+            pinfo['children'] = []
+        if pinfo['create_time']:
+            started = datetime.datetime.fromtimestamp(
+                pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
+        else:
+            started = ACCESS_DENIED
+
+    # here we go
+    print_('pid', pinfo['pid'])
+    print_('name', pinfo['name'])
+    print_('parent', '%s %s' % (pinfo['ppid'], parent))
+    print_('exe', pinfo['exe'])
+    print_('cwd', pinfo['cwd'])
+    print_('cmdline', ' '.join(pinfo['cmdline']))
+    print_('started', started)
+
+    cpu_tot_time = datetime.timedelta(seconds=sum(pinfo['cpu_times']))
+    cpu_tot_time = "%s:%s.%s" % (
+        cpu_tot_time.seconds // 60 % 60,
+        str((cpu_tot_time.seconds % 60)).zfill(2),
+        str(cpu_tot_time.microseconds)[:2])
+    print_('cpu-tspent', cpu_tot_time)
+    print_('cpu-times', str_ntuple(pinfo['cpu_times']))
+    if hasattr(proc, "cpu_affinity"):
+        print_("cpu-affinity", pinfo["cpu_affinity"])
+    if hasattr(proc, "cpu_num"):
+        print_("cpu-num", pinfo["cpu_num"])
+
+    print_('memory', str_ntuple(pinfo['memory_info'], bytes2human=True))
+    print_('memory %', round(pinfo['memory_percent'], 2))
+    print_('user', pinfo['username'])
+    if psutil.POSIX:
+        print_('uids', str_ntuple(pinfo['uids']))
+    if psutil.POSIX:
+        print_('uids', str_ntuple(pinfo['uids']))
+    if psutil.POSIX:
+        print_('terminal', pinfo['terminal'] or '')
+
+    print_('status', pinfo['status'])
+    print_('nice', pinfo['nice'])
+    if hasattr(proc, "ionice"):
+        try:
+            ionice = proc.ionice()
+        except psutil.Error:
+            pass
+        else:
+            if psutil.WINDOWS:
+                print_("ionice", ionice)
+            else:
+                print_("ionice", "class=%s, value=%s" % (
+                    str(ionice.ioclass), ionice.value))
+
+    print_('num-threads', pinfo['num_threads'])
+    if psutil.POSIX:
+        print_('num-fds', pinfo['num_fds'])
+    if psutil.WINDOWS:
+        print_('num-handles', pinfo['num_handles'])
+
+    if 'io_counters' in pinfo:
+        print_('I/O', str_ntuple(pinfo['io_counters'], bytes2human=True))
+    if 'num_ctx_switches' in pinfo:
+        print_("ctx-switches", str_ntuple(pinfo['num_ctx_switches']))
+    if pinfo['children']:
+        template = "%-6s %s"
+        print_("children", template % ("PID", "NAME"))
+        for child in pinfo['children']:
+            try:
+                print_('', template % (child.pid, child.name()))
+            except psutil.AccessDenied:
+                print_('', template % (child.pid, ""))
+            except psutil.NoSuchProcess:
+                pass
+
+    if pinfo['open_files']:
+        print_('open-files', 'PATH')
+        for i, file in enumerate(pinfo['open_files']):
+            if not verbose and i >= NON_VERBOSE_ITERATIONS:
+                print_("", "[...]")
+                break
+            print_('', file.path)
+    else:
+        print_('open-files', '')
+
+    if pinfo['connections']:
+        template = '%-5s %-25s %-25s %s'
+        print_('connections',
+               template % ('PROTO', 'LOCAL ADDR', 'REMOTE ADDR', 'STATUS'))
+        for conn in pinfo['connections']:
+            if conn.type == socket.SOCK_STREAM:
+                type = 'TCP'
+            elif conn.type == socket.SOCK_DGRAM:
+                type = 'UDP'
+            else:
+                type = 'UNIX'
+            lip, lport = conn.laddr
+            if not conn.raddr:
+                rip, rport = '*', '*'
+            else:
+                rip, rport = conn.raddr
+            print_('', template % (
+                type,
+                "%s:%s" % (lip, lport),
+                "%s:%s" % (rip, rport),
+                conn.status))
+    else:
+        print_('connections', '')
+
+    if pinfo['threads'] and len(pinfo['threads']) > 1:
+        template = "%-5s %12s %12s"
+        print_('threads', template % ("TID", "USER", "SYSTEM"))
+        for i, thread in enumerate(pinfo['threads']):
+            if not verbose and i >= NON_VERBOSE_ITERATIONS:
+                print_("", "[...]")
+                break
+            print_('', template % thread)
+        print_('', "total=%s" % len(pinfo['threads']))
+    else:
+        print_('threads', '')
+
+    if hasattr(proc, "rlimit"):
+        res_names = [x for x in dir(psutil) if x.startswith("RLIMIT")]
+        resources = []
+        for res_name in res_names:
+            try:
+                soft, hard = proc.rlimit(getattr(psutil, res_name))
+            except psutil.AccessDenied:
+                pass
+            else:
+                resources.append((res_name, soft, hard))
+        if resources:
+            template = "%-12s %15s %15s"
+            print_("res-limits", template % ("RLIMIT", "SOFT", "HARD"))
+            for res_name, soft, hard in resources:
+                if soft == psutil.RLIM_INFINITY:
+                    soft = "infinity"
+                if hard == psutil.RLIM_INFINITY:
+                    hard = "infinity"
+                print_('', template % (
+                    RLIMITS_MAP.get(res_name, res_name), soft, hard))
+
+    if hasattr(proc, "environ") and pinfo['environ']:
+        template = "%-25s %s"
+        print_("environ", template % ("NAME", "VALUE"))
+        for i, k in enumerate(sorted(pinfo['environ'])):
+            if not verbose and i >= NON_VERBOSE_ITERATIONS:
+                print_("", "[...]")
+                break
+            print_("", template % (k, pinfo['environ'][k]))
+
+    if pinfo.get('memory_maps', None):
+        template = "%-8s %s"
+        print_("mem-maps", template % ("RSS", "PATH"))
+        maps = sorted(pinfo['memory_maps'], key=lambda x: x.rss, reverse=True)
+        for i, region in enumerate(maps):
+            if not verbose and i >= NON_VERBOSE_ITERATIONS:
+                print_("", "[...]")
+                break
+            print_("", template % (convert_bytes(region.rss), region.path))
+
+
+def main(argv=None):
+    parser = argparse.ArgumentParser(
+        description="print information about a process")
+    parser.add_argument("pid", type=int, help="process pid")
+    parser.add_argument('--verbose', '-v', action='store_true',
+                        help="print more info")
+    args = parser.parse_args()
+    run(args.pid, args.verbose)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/procsmem.py
@@ -0,0 +1,103 @@
+#!/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.
+
+"""
+Show detailed memory usage about all (querable) processes.
+
+Processes are sorted by their "USS" (Unique Set Size) memory, which is
+probably the most representative metric for determining how much memory
+is actually being used by a process.
+
+This is similar to "smem" cmdline utility on Linux:
+https://www.selenic.com/smem/
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+
+~/svn/psutil$ ./scripts/procsmem.py
+PID     User    Cmdline                            USS     PSS    Swap     RSS
+==============================================================================
+...
+3986    giampao /usr/bin/python3 /usr/bin/indi   15.3M   16.6M      0B   25.6M
+3906    giampao /usr/lib/ibus/ibus-ui-gtk3       17.6M   18.1M      0B   26.7M
+3991    giampao python /usr/bin/hp-systray -x    19.0M   23.3M      0B   40.7M
+3830    giampao /usr/bin/ibus-daemon --daemoni   19.0M   19.0M      0B   21.4M
+20529   giampao /opt/sublime_text/plugin_host    19.9M   20.1M      0B   22.0M
+3990    giampao nautilus -n                      20.6M   29.9M      0B   50.2M
+3898    giampao /usr/lib/unity/unity-panel-ser   27.1M   27.9M      0B   37.7M
+4176    giampao /usr/lib/evolution/evolution-c   35.7M   36.2M      0B   41.5M
+20712   giampao /usr/bin/python -B /home/giamp   45.6M   45.9M      0B   49.4M
+3880    giampao /usr/lib/x86_64-linux-gnu/hud/   51.6M   52.7M      0B   61.3M
+20513   giampao /opt/sublime_text/sublime_text   65.8M   73.0M      0B   87.9M
+3976    giampao compiz                          115.0M  117.0M      0B  130.9M
+32486   giampao skype                           145.1M  147.5M      0B  149.6M
+"""
+
+from __future__ import print_function
+import sys
+
+import psutil
+
+
+if not (psutil.LINUX or psutil.OSX or psutil.WINDOWS):
+    sys.exit("platform not supported")
+
+
+def convert_bytes(n):
+    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 '%.1f%s' % (value, s)
+    return "%sB" % n
+
+
+def main():
+    ad_pids = []
+    procs = []
+    for p in psutil.process_iter():
+        with p.oneshot():
+            try:
+                mem = p.memory_full_info()
+                info = p.as_dict(attrs=["cmdline", "username"])
+            except psutil.AccessDenied:
+                ad_pids.append(p.pid)
+            except psutil.NoSuchProcess:
+                pass
+            else:
+                p._uss = mem.uss
+                p._rss = mem.rss
+                if not p._uss:
+                    continue
+                p._pss = getattr(mem, "pss", "")
+                p._swap = getattr(mem, "swap", "")
+                p._info = info
+                procs.append(p)
+
+    procs.sort(key=lambda p: p._uss)
+    templ = "%-7s %-7s %-30s %7s %7s %7s %7s"
+    print(templ % ("PID", "User", "Cmdline", "USS", "PSS", "Swap", "RSS"))
+    print("=" * 78)
+    for p in procs[:86]:
+        line = templ % (
+            p.pid,
+            p._info["username"][:7],
+            " ".join(p._info["cmdline"])[:30],
+            convert_bytes(p._uss),
+            convert_bytes(p._pss) if p._pss != "" else "",
+            convert_bytes(p._swap) if p._swap != "" else "",
+            convert_bytes(p._rss),
+        )
+        print(line)
+    if ad_pids:
+        print("warning: access denied for %s pids" % (len(ad_pids)),
+              file=sys.stderr)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/ps.py
@@ -0,0 +1,104 @@
+#!/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.
+
+"""
+A clone of 'ps -aux' on UNIX.
+
+$ python scripts/ps.py
+...
+"""
+
+import datetime
+import os
+import time
+
+import psutil
+
+
+PROC_STATUSES_RAW = {
+    psutil.STATUS_RUNNING: "R",
+    psutil.STATUS_SLEEPING: "S",
+    psutil.STATUS_DISK_SLEEP: "D",
+    psutil.STATUS_STOPPED: "T",
+    psutil.STATUS_TRACING_STOP: "t",
+    psutil.STATUS_ZOMBIE: "Z",
+    psutil.STATUS_DEAD: "X",
+    psutil.STATUS_WAKING: "WA",
+    psutil.STATUS_IDLE: "I",
+    psutil.STATUS_LOCKED: "L",
+    psutil.STATUS_WAITING: "W",
+}
+
+if hasattr(psutil, 'STATUS_WAKE_KILL'):
+    PROC_STATUSES_RAW[psutil.STATUS_WAKE_KILL] = "WK"
+
+if hasattr(psutil, 'STATUS_SUSPENDED'):
+    PROC_STATUSES_RAW[psutil.STATUS_SUSPENDED] = "V"
+
+
+def main():
+    today_day = datetime.date.today()
+    templ = "%-10s %5s %4s %4s %7s %7s %-13s %-5s %5s %7s  %s"
+    attrs = ['pid', 'cpu_percent', 'memory_percent', 'name', 'cpu_times',
+             'create_time', 'memory_info', 'status']
+    if os.name == 'posix':
+        attrs.append('uids')
+        attrs.append('terminal')
+    print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
+                   "STAT", "START", "TIME", "COMMAND"))
+    for p in psutil.process_iter():
+        try:
+            pinfo = p.as_dict(attrs, ad_value='')
+        except psutil.NoSuchProcess:
+            pass
+        else:
+            if pinfo['create_time']:
+                ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
+                if ctime.date() == today_day:
+                    ctime = ctime.strftime("%H:%M")
+                else:
+                    ctime = ctime.strftime("%b%d")
+            else:
+                ctime = ''
+            cputime = time.strftime("%M:%S",
+                                    time.localtime(sum(pinfo['cpu_times'])))
+            try:
+                user = p.username()
+            except KeyError:
+                if os.name == 'posix':
+                    if pinfo['uids']:
+                        user = str(pinfo['uids'].real)
+                    else:
+                        user = ''
+                else:
+                    raise
+            except psutil.Error:
+                user = ''
+            if os.name == 'nt' and '\\' in user:
+                user = user.split('\\')[1]
+            vms = pinfo['memory_info'] and \
+                int(pinfo['memory_info'].vms / 1024) or '?'
+            rss = pinfo['memory_info'] and \
+                int(pinfo['memory_info'].rss / 1024) or '?'
+            memp = pinfo['memory_percent'] and \
+                round(pinfo['memory_percent'], 1) or '?'
+            status = PROC_STATUSES_RAW.get(pinfo['status'], pinfo['status'])
+            print(templ % (
+                user[:10],
+                pinfo['pid'],
+                pinfo['cpu_percent'],
+                memp,
+                vms,
+                rss,
+                pinfo.get('terminal', '') or '?',
+                status,
+                ctime,
+                cputime,
+                pinfo['name'].strip() or '?'))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/pstree.py
@@ -0,0 +1,71 @@
+#!/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.
+
+"""
+Similar to 'ps aux --forest' on Linux, prints the process list
+as a tree structure.
+
+$ python scripts/pstree.py
+0 ?
+|- 1 init
+| |- 289 cgmanager
+| |- 616 upstart-socket-bridge
+| |- 628 rpcbind
+| |- 892 upstart-file-bridge
+| |- 907 dbus-daemon
+| |- 978 avahi-daemon
+| | `_ 979 avahi-daemon
+| |- 987 NetworkManager
+| | |- 2242 dnsmasq
+| | `_ 10699 dhclient
+| |- 993 polkitd
+| |- 1061 getty
+| |- 1066 su
+| | `_ 1190 salt-minion...
+...
+"""
+
+from __future__ import print_function
+import collections
+import sys
+
+import psutil
+
+
+def print_tree(parent, tree, indent=''):
+    try:
+        name = psutil.Process(parent).name()
+    except psutil.Error:
+        name = "?"
+    print(parent, name)
+    if parent not in tree:
+        return
+    children = tree[parent][:-1]
+    for child in children:
+        sys.stdout.write(indent + "|- ")
+        print_tree(child, tree, indent + "| ")
+    child = tree[parent][-1]
+    sys.stdout.write(indent + "`_ ")
+    print_tree(child, tree, indent + "  ")
+
+
+def main():
+    # construct a dict where 'values' are all the processes
+    # having 'key' as their parent
+    tree = collections.defaultdict(list)
+    for p in psutil.process_iter():
+        try:
+            tree[p.ppid()].append(p.pid)
+        except (psutil.NoSuchProcess, psutil.ZombieProcess):
+            pass
+    # on systems supporting PID 0, PID 0's parent is usually 0
+    if 0 in tree and 0 in tree[0]:
+        tree[0].remove(0)
+    print_tree(min(tree), tree)
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/sensors.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# 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 clone of 'sensors' utility on Linux printing hardware temperatures,
+fans speed and battery info.
+
+$ python scripts/sensors.py
+asus
+    Temperatures:
+        asus                 57.0°C (high=None°C, critical=None°C)
+    Fans:
+        cpu_fan              3500 RPM
+acpitz
+    Temperatures:
+        acpitz               57.0°C (high=108.0°C, critical=108.0°C)
+coretemp
+    Temperatures:
+        Physical id 0        61.0°C (high=87.0°C, critical=105.0°C)
+        Core 0               61.0°C (high=87.0°C, critical=105.0°C)
+        Core 1               59.0°C (high=87.0°C, critical=105.0°C)
+Battery:
+    charge:     84.95%
+    status:     charging
+    plugged in: yes
+"""
+
+from __future__ import print_function
+
+import psutil
+
+
+def secs2hours(secs):
+    mm, ss = divmod(secs, 60)
+    hh, mm = divmod(mm, 60)
+    return "%d:%02d:%02d" % (hh, mm, ss)
+
+
+def main():
+    if hasattr(psutil, "sensors_temperatures"):
+        temps = psutil.sensors_temperatures()
+    else:
+        temps = {}
+    if hasattr(psutil, "sensors_fans"):
+        fans = psutil.sensors_fans()
+    else:
+        fans = {}
+    if hasattr(psutil, "sensors_battery"):
+        battery = psutil.sensors_battery()
+    else:
+        battery = None
+
+    if not any((temps, fans, battery)):
+        print("can't read any temperature, fans or battery info")
+        return
+
+    names = set(list(temps.keys()) + list(fans.keys()))
+    for name in names:
+        print(name)
+        # Temperatures.
+        if name in temps:
+            print("    Temperatures:")
+            for entry in temps[name]:
+                print("        %-20s %s°C (high=%s°C, critical=%s°C)" % (
+                    entry.label or name, entry.current, entry.high,
+                    entry.critical))
+        # Fans.
+        if name in fans:
+            print("    Fans:")
+            for entry in fans[name]:
+                print("        %-20s %s RPM" % (
+                    entry.label or name, entry.current))
+
+    # Battery.
+    if battery:
+        print("Battery:")
+        print("    charge:     %s%%" % round(battery.percent, 2))
+        if battery.power_plugged:
+            print("    status:     %s" % (
+                "charging" if battery.percent < 100 else "fully charged"))
+            print("    plugged in: yes")
+        else:
+            print("    left:       %s" % secs2hours(battery.secsleft))
+            print("    status:     %s" % "discharging")
+            print("    plugged in: no")
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/temperatures.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# 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 clone of 'sensors' utility on Linux printing hardware temperatures.
+
+$ python scripts/sensors.py
+asus
+    asus                 47.0 °C (high = None °C, critical = None °C)
+
+acpitz
+    acpitz               47.0 °C (high = 103.0 °C, critical = 103.0 °C)
+
+coretemp
+    Physical id 0        54.0 °C (high = 100.0 °C, critical = 100.0 °C)
+    Core 0               47.0 °C (high = 100.0 °C, critical = 100.0 °C)
+    Core 1               48.0 °C (high = 100.0 °C, critical = 100.0 °C)
+    Core 2               47.0 °C (high = 100.0 °C, critical = 100.0 °C)
+    Core 3               54.0 °C (high = 100.0 °C, critical = 100.0 °C)
+"""
+
+from __future__ import print_function
+import sys
+
+import psutil
+
+
+def main():
+    if not hasattr(psutil, "sensors_temperatures"):
+        sys.exit("platform not supported")
+    temps = psutil.sensors_temperatures()
+    if not temps:
+        sys.exit("can't read any temperature")
+    for name, entries in temps.items():
+        print(name)
+        for entry in entries:
+            print("    %-20s %s °C (high = %s °C, critical = %s °C)" % (
+                entry.label or name, entry.current, entry.high,
+                entry.critical))
+        print()
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/top.py
@@ -0,0 +1,237 @@
+#!/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.
+
+"""
+A clone of top / htop.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+
+$ python scripts/top.py
+ CPU0  [|                                       ]   4.9%
+ CPU1  [|||                                     ]   7.8%
+ CPU2  [                                        ]   2.0%
+ CPU3  [|||||                                   ]  13.9%
+ Mem   [|||||||||||||||||||                     ]  49.8%  4920M / 9888M
+ Swap  [                                        ]   0.0%     0M / 0M
+ Processes: 287 (running=1, sleeping=286, zombie=1)
+ Load average: 0.34 0.54 0.46  Uptime: 3 days, 10:16:37
+
+PID    USER       NI  VIRT   RES   CPU% MEM%     TIME+  NAME
+------------------------------------------------------------
+989    giampaol    0   66M   12M    7.4  0.1   0:00.61  python
+2083   root        0  506M  159M    6.5  1.6   0:29.26  Xorg
+4503   giampaol    0  599M   25M    6.5  0.3   3:32.60  gnome-terminal
+3868   giampaol    0  358M    8M    2.8  0.1  23:12.60  pulseaudio
+3936   giampaol    0    1G  111M    2.8  1.1  33:41.67  compiz
+4401   giampaol    0  536M  141M    2.8  1.4  35:42.73  skype
+4047   giampaol    0  743M   76M    1.8  0.8  42:03.33  unity-panel-service
+13155  giampaol    0    1G  280M    1.8  2.8  41:57.34  chrome
+10     root        0    0B    0B    0.9  0.0   4:01.81  rcu_sched
+339    giampaol    0    1G  113M    0.9  1.1   8:15.73  chrome
+...
+"""
+
+import atexit
+import datetime
+import os
+import sys
+import time
+try:
+    import curses
+except ImportError:
+    sys.exit('platform not supported')
+
+import psutil
+
+
+# --- curses stuff
+
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+
+# --- /curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9K'
+    >>> bytes2human(100001221)
+    '95M'
+    """
+    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 = int(float(n) / prefix[s])
+            return '%s%s' % (value, s)
+    return "%sB" % n
+
+
+def poll(interval):
+    # sleep some time
+    time.sleep(interval)
+    procs = []
+    procs_status = {}
+    for p in psutil.process_iter():
+        try:
+            p.dict = p.as_dict(['username', 'nice', 'memory_info',
+                                'memory_percent', 'cpu_percent',
+                                'cpu_times', 'name', 'status'])
+            try:
+                procs_status[p.dict['status']] += 1
+            except KeyError:
+                procs_status[p.dict['status']] = 1
+        except psutil.NoSuchProcess:
+            pass
+        else:
+            procs.append(p)
+
+    # return processes sorted by CPU percent usage
+    processes = sorted(procs, key=lambda p: p.dict['cpu_percent'],
+                       reverse=True)
+    return (processes, procs_status)
+
+
+def print_header(procs_status, num_procs):
+    """Print system-related info, above the process list."""
+
+    def get_dashes(perc):
+        dashes = "|" * int((float(perc) / 10 * 4))
+        empty_dashes = " " * (40 - len(dashes))
+        return dashes, empty_dashes
+
+    # cpu usage
+    percs = psutil.cpu_percent(interval=0, percpu=True)
+    for cpu_num, perc in enumerate(percs):
+        dashes, empty_dashes = get_dashes(perc)
+        print_line(" CPU%-2s [%s%s] %5s%%" % (cpu_num, dashes, empty_dashes,
+                                              perc))
+    mem = psutil.virtual_memory()
+    dashes, empty_dashes = get_dashes(mem.percent)
+    line = " Mem   [%s%s] %5s%% %6s / %s" % (
+        dashes, empty_dashes,
+        mem.percent,
+        str(int(mem.used / 1024 / 1024)) + "M",
+        str(int(mem.total / 1024 / 1024)) + "M"
+    )
+    print_line(line)
+
+    # swap usage
+    swap = psutil.swap_memory()
+    dashes, empty_dashes = get_dashes(swap.percent)
+    line = " Swap  [%s%s] %5s%% %6s / %s" % (
+        dashes, empty_dashes,
+        swap.percent,
+        str(int(swap.used / 1024 / 1024)) + "M",
+        str(int(swap.total / 1024 / 1024)) + "M"
+    )
+    print_line(line)
+
+    # processes number and status
+    st = []
+    for x, y in procs_status.items():
+        if y:
+            st.append("%s=%s" % (x, y))
+    st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1)
+    print_line(" Processes: %s (%s)" % (num_procs, ', '.join(st)))
+    # load average, uptime
+    uptime = datetime.datetime.now() - \
+        datetime.datetime.fromtimestamp(psutil.boot_time())
+    av1, av2, av3 = os.getloadavg()
+    line = " Load average: %.2f %.2f %.2f  Uptime: %s" \
+        % (av1, av2, av3, str(uptime).split('.')[0])
+    print_line(line)
+
+
+def refresh_window(procs, procs_status):
+    """Print results on screen by using curses."""
+    curses.endwin()
+    templ = "%-6s %-8s %4s %5s %5s %6s %4s %9s  %2s"
+    win.erase()
+    header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%",
+                      "TIME+", "NAME")
+    print_header(procs_status, len(procs))
+    print_line("")
+    print_line(header, highlight=True)
+    for p in procs:
+        # TIME+ column shows process CPU cumulative time and it
+        # is expressed as: "mm:ss.ms"
+        if p.dict['cpu_times'] is not None:
+            ctime = datetime.timedelta(seconds=sum(p.dict['cpu_times']))
+            ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60,
+                                  str((ctime.seconds % 60)).zfill(2),
+                                  str(ctime.microseconds)[:2])
+        else:
+            ctime = ''
+        if p.dict['memory_percent'] is not None:
+            p.dict['memory_percent'] = round(p.dict['memory_percent'], 1)
+        else:
+            p.dict['memory_percent'] = ''
+        if p.dict['cpu_percent'] is None:
+            p.dict['cpu_percent'] = ''
+        if p.dict['username']:
+            username = p.dict['username'][:8]
+        else:
+            username = ""
+        line = templ % (p.pid,
+                        username,
+                        p.dict['nice'],
+                        bytes2human(getattr(p.dict['memory_info'], 'vms', 0)),
+                        bytes2human(getattr(p.dict['memory_info'], 'rss', 0)),
+                        p.dict['cpu_percent'],
+                        p.dict['memory_percent'],
+                        ctime,
+                        p.dict['name'] or '',
+                        )
+        try:
+            print_line(line)
+        except curses.error:
+            break
+        win.refresh()
+
+
+def main():
+    try:
+        interval = 0
+        while True:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/who.py
@@ -0,0 +1,35 @@
+#!/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.
+
+"""
+A clone of 'who' command; print information about users who are
+currently logged in.
+
+$ python scripts/who.py
+giampaolo    console    2017-03-25 22:24                loginwindow
+giampaolo    ttys000    2017-03-25 23:28 (10.0.2.2)     sshd
+"""
+
+from datetime import datetime
+
+import psutil
+
+
+def main():
+    users = psutil.users()
+    for user in users:
+        proc_name = psutil.Process(user.pid).name() if user.pid else ""
+        print("%-12s %-10s %-10s %-14s %s" % (
+            user.name,
+            user.terminal or '-',
+            datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"),
+            "(%s)" % user.host if user.host else "",
+            proc_name
+        ))
+
+
+if __name__ == '__main__':
+    main()
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/winservices.py
@@ -0,0 +1,56 @@
+#!/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.
+
+"""
+List all Windows services installed.
+
+$ python scripts/winservices.py
+AeLookupSvc (Application Experience)
+status: stopped, start: manual, username: localSystem, pid: None
+binpath: C:\Windows\system32\svchost.exe -k netsvcs
+
+ALG (Application Layer Gateway Service)
+status: stopped, start: manual, username: NT AUTHORITY\LocalService, pid: None
+binpath: C:\Windows\System32\alg.exe
+
+APNMCP (Ask Update Service)
+status: running, start: automatic, username: LocalSystem, pid: 1108
+binpath: "C:\Program Files (x86)\AskPartnerNetwork\Toolbar\apnmcp.exe"
+
+AppIDSvc (Application Identity)
+status: stopped, start: manual, username: NT Authority\LocalService, pid: None
+binpath: C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation
+
+Appinfo (Application Information)
+status: stopped, start: manual, username: LocalSystem, pid: None
+binpath: C:\Windows\system32\svchost.exe -k netsvcs
+
+...
+"""
+
+
+import os
+import sys
+
+import psutil
+
+
+if os.name != 'nt':
+    sys.exit("platform not supported (Windows only)")
+
+
+def main():
+    for service in psutil.win_service_iter():
+        info = service.as_dict()
+        print("%r (%r)" % (info['name'], info['display_name']))
+        print("status: %s, start: %s, username: %s, pid: %s" % (
+            info['status'], info['start_type'], info['username'], info['pid']))
+        print("binpath: %s" % info['binpath'])
+        print("")
+
+
+if __name__ == '__main__':
+    sys.exit(main())
--- a/third_party/python/psutil/setup.cfg
+++ b/third_party/python/psutil/setup.cfg
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
old mode 100644
new mode 100755
--- a/third_party/python/psutil/setup.py
+++ b/third_party/python/psutil/setup.py
@@ -1,206 +1,350 @@
 #!/usr/bin/env python
 
 # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""psutil is a cross-platform library for retrieving information on
-running processes and system utilization (CPU, memory, disks, network)
-in Python.
-"""
+"""Cross-platform lib for process and system monitoring in Python."""
 
+import contextlib
+import io
 import os
+import platform
 import sys
-try:
-    from setuptools import setup, Extension
-except ImportError:
-    from distutils.core import setup, Extension
+import tempfile
+import warnings
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore")
+    try:
+        import setuptools
+        from setuptools import setup, Extension
+    except ImportError:
+        setuptools = None
+        from distutils.core import setup, Extension
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+# ...so we can import _common.py
+sys.path.insert(0, os.path.join(HERE, "psutil"))
+
+from _common import BSD  # NOQA
+from _common import FREEBSD  # NOQA
+from _common import LINUX  # NOQA
+from _common import NETBSD  # NOQA
+from _common import OPENBSD  # NOQA
+from _common import OSX  # NOQA
+from _common import POSIX  # NOQA
+from _common import SUNOS  # NOQA
+from _common import WINDOWS  # NOQA
+from _common import AIX  # NOQA
 
 
-HERE = os.path.abspath(os.path.dirname(__file__))
+macros = []
+if POSIX:
+    macros.append(("PSUTIL_POSIX", 1))
+if WINDOWS:
+    macros.append(("PSUTIL_WINDOWS", 1))
+if BSD:
+    macros.append(("PSUTIL_BSD", 1))
+
+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):
+    extras_require.update(dict(enum='enum34'))
 
 
 def get_version():
     INIT = os.path.join(HERE, 'psutil/__init__.py')
     with open(INIT, 'r') as f:
         for line in f:
             if line.startswith('__version__'):
                 ret = eval(line.strip().split(' = ')[1])
                 assert ret.count('.') == 2, ret
                 for num in ret.split('.'):
                     assert num.isdigit(), ret
                 return ret
-        else:
-            raise ValueError("couldn't find version string")
+        raise ValueError("couldn't find version string")
+
+
+VERSION = get_version()
+macros.append(('PSUTIL_VERSION', int(VERSION.replace('.', ''))))
 
 
 def get_description():
     README = os.path.join(HERE, 'README.rst')
     with open(README, 'r') as f:
         return f.read()
 
 
-VERSION = get_version()
-VERSION_MACRO = ('PSUTIL_VERSION', int(VERSION.replace('.', '')))
+@contextlib.contextmanager
+def silenced_output(stream_name):
+    class DummyFile(io.BytesIO):
+        # see: https://github.com/giampaolo/psutil/issues/678
+        errors = "ignore"
+
+        def write(self, s):
+            pass
+
+    orig = getattr(sys, stream_name)
+    try:
+        setattr(sys, stream_name, DummyFile())
+        yield
+    finally:
+        setattr(sys, stream_name, orig)
 
 
-# POSIX
-if os.name == 'posix':
-    libraries = []
-    if sys.platform.startswith("sunos"):
-        libraries.append('socket')
-
-    posix_extension = Extension(
-        'psutil._psutil_posix',
-        sources=['psutil/_psutil_posix.c'],
-        libraries=libraries,
-    )
-# Windows
-if sys.platform.startswith("win32"):
-
+if WINDOWS:
     def get_winver():
         maj, min = sys.getwindowsversion()[0:2]
         return '0x0%s' % ((maj * 100) + min)
 
-    extensions = [Extension(
+    if sys.getwindowsversion()[0] < 6:
+        msg = "warning: Windows versions < Vista are no longer supported or "
+        msg = "maintained; latest official supported version is psutil 3.4.2; "
+        msg += "psutil may still be installed from sources if you have "
+        msg += "Visual Studio and may also (kind of) work though"
+        warnings.warn(msg, UserWarning)
+
+    macros.extend([
+        # be nice to mingw, see:
+        # http://www.mingw.org/wiki/Use_more_recent_defined_functions
+        ('_WIN32_WINNT', get_winver()),
+        ('_AVAIL_WINVER_', get_winver()),
+        ('_CRT_SECURE_NO_WARNINGS', None),
+        # see: https://github.com/giampaolo/psutil/issues/348
+        ('PSAPI_VERSION', 1),
+    ])
+
+    ext = Extension(
         'psutil._psutil_windows',
-        sources=[
+        sources=sources + [
             'psutil/_psutil_windows.c',
-            'psutil/_psutil_common.c',
             'psutil/arch/windows/process_info.c',
             'psutil/arch/windows/process_handles.c',
             'psutil/arch/windows/security.c',
             'psutil/arch/windows/inet_ntop.c',
+            'psutil/arch/windows/services.c',
         ],
-        define_macros=[
-            VERSION_MACRO,
-            # be nice to mingw, see:
-            # http://www.mingw.org/wiki/Use_more_recent_defined_functions
-            ('_WIN32_WINNT', get_winver()),
-            ('_AVAIL_WINVER_', get_winver()),
-            ('_CRT_SECURE_NO_WARNINGS', None),
-            # see: https://github.com/giampaolo/psutil/issues/348
-            ('PSAPI_VERSION', 1),
-        ],
+        define_macros=macros,
         libraries=[
-            "psapi", "kernel32", "advapi32", "shell32", "netapi32", "iphlpapi",
-            "wtsapi32", "ws2_32",
+            "psapi", "kernel32", "advapi32", "shell32", "netapi32",
+            "iphlpapi", "wtsapi32", "ws2_32", "PowrProf",
         ],
         # extra_compile_args=["/Z7"],
         # extra_link_args=["/DEBUG"]
-    )]
-# OS X
-elif sys.platform.startswith("darwin"):
-    extensions = [Extension(
+    )
+
+elif OSX:
+    macros.append(("PSUTIL_OSX", 1))
+    ext = Extension(
         'psutil._psutil_osx',
-        sources=[
+        sources=sources + [
             'psutil/_psutil_osx.c',
-            'psutil/_psutil_common.c',
-            'psutil/arch/osx/process_info.c'
+            'psutil/arch/osx/process_info.c',
         ],
-        define_macros=[VERSION_MACRO],
+        define_macros=macros,
         extra_link_args=[
             '-framework', 'CoreFoundation', '-framework', 'IOKit'
+        ])
+
+elif FREEBSD:
+    macros.append(("PSUTIL_FREEBSD", 1))
+    ext = Extension(
+        'psutil._psutil_bsd',
+        sources=sources + [
+            'psutil/_psutil_bsd.c',
+            'psutil/arch/freebsd/specific.c',
+            'psutil/arch/freebsd/sys_socks.c',
+            'psutil/arch/freebsd/proc_socks.c',
         ],
-    ),
-        posix_extension,
-    ]
-# FreeBSD
-elif sys.platform.startswith("freebsd"):
-    extensions = [Extension(
+        define_macros=macros,
+        libraries=["devstat"])
+
+elif OPENBSD:
+    macros.append(("PSUTIL_OPENBSD", 1))
+    ext = Extension(
         'psutil._psutil_bsd',
-        sources=[
+        sources=sources + [
             'psutil/_psutil_bsd.c',
-            'psutil/_psutil_common.c',
-            'psutil/arch/bsd/process_info.c'
+            'psutil/arch/openbsd/specific.c',
+        ],
+        define_macros=macros,
+        libraries=["kvm"])
+
+elif NETBSD:
+    macros.append(("PSUTIL_NETBSD", 1))
+    ext = Extension(
+        'psutil._psutil_bsd',
+        sources=sources + [
+            'psutil/_psutil_bsd.c',
+            'psutil/arch/netbsd/specific.c',
+            'psutil/arch/netbsd/socks.c',
         ],
-        define_macros=[VERSION_MACRO],
-        libraries=["devstat"]),
-        posix_extension,
-    ]
-# Linux
-elif sys.platform.startswith("linux"):
-    extensions = [Extension(
+        define_macros=macros,
+        libraries=["kvm"])
+
+elif LINUX:
+    def get_ethtool_macro():
+        # see: https://github.com/giampaolo/psutil/issues/659
+        from distutils.unixccompiler import UnixCCompiler
+        from distutils.errors import CompileError
+
+        with tempfile.NamedTemporaryFile(
+                suffix='.c', delete=False, mode="wt") as f:
+            f.write("#include <linux/ethtool.h>")
+
+        try:
+            compiler = UnixCCompiler()
+            with silenced_output('stderr'):
+                with silenced_output('stdout'):
+                    compiler.compile([f.name])
+        except CompileError:
+            return ("PSUTIL_ETHTOOL_MISSING_TYPES", 1)
+        else:
+            return None
+        finally:
+            try:
+                os.remove(f.name)
+            except OSError:
+                pass
+
+    macros.append(("PSUTIL_LINUX", 1))
+    ETHTOOL_MACRO = get_ethtool_macro()
+    if ETHTOOL_MACRO is not None:
+        macros.append(ETHTOOL_MACRO)
+    ext = Extension(
         'psutil._psutil_linux',
-        sources=['psutil/_psutil_linux.c'],
-        define_macros=[VERSION_MACRO]),
-        posix_extension,
-    ]
-# Solaris
-elif sys.platform.lower().startswith('sunos'):
-    extensions = [Extension(
+        sources=sources + ['psutil/_psutil_linux.c'],
+        define_macros=macros)
+
+elif SUNOS:
+    macros.append(("PSUTIL_SUNOS", 1))
+    ext = Extension(
         'psutil._psutil_sunos',
-        sources=['psutil/_psutil_sunos.c'],
-        define_macros=[VERSION_MACRO],
-        libraries=['kstat', 'nsl', 'socket']),
-        posix_extension,
-    ]
+        sources=sources + [
+            'psutil/_psutil_sunos.c',
+            'psutil/arch/solaris/v10/ifaddrs.c',
+            'psutil/arch/solaris/environ.c'
+        ],
+        define_macros=macros,
+        libraries=['kstat', 'nsl', 'socket'])
+# AIX
+elif AIX:
+    macros.append(("PSUTIL_AIX", 1))
+    ext = Extension(
+        'psutil._psutil_aix',
+        sources=sources + [
+            'psutil/_psutil_aix.c',
+            'psutil/arch/aix/net_connections.c',
+            'psutil/arch/aix/common.c',
+            'psutil/arch/aix/ifaddrs.c'],
+        libraries=['perfstat'],
+        define_macros=macros)
 else:
     sys.exit('platform %s is not supported' % sys.platform)
 
 
+if POSIX:
+    posix_extension = Extension(
+        'psutil._psutil_posix',
+        define_macros=macros,
+        sources=sources)
+    if SUNOS:
+        posix_extension.libraries.append('socket')
+        if platform.release() == '5.10':
+            posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c')
+            posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1))
+    elif AIX:
+        posix_extension.sources.append('psutil/arch/aix/ifaddrs.c')
+
+    extensions = [ext, posix_extension]
+else:
+    extensions = [ext]
+
+
 def main():
-    setup_args = dict(
+    kwargs = dict(
         name='psutil',
         version=VERSION,
-        description=__doc__.replace('\n', '').strip(),
+        description=__doc__ .replace('\n', ' ').strip() if __doc__ else '',
         long_description=get_description(),
         keywords=[
             'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty',
             'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat',
             'ifconfig', 'taskset', 'who', 'pidof', 'pmap', 'smem', 'pstree',
-            'monitoring', 'ulimit', 'prlimit',
+            'monitoring', 'ulimit', 'prlimit', 'smem',
         ],
         author='Giampaolo Rodola',
-        author_email='g.rodola <at> gmail <dot> com',
+        author_email='g.rodola@gmail.com',
         url='https://github.com/giampaolo/psutil',
         platforms='Platform Independent',
         license='BSD',
-        packages=['psutil'],
+        packages=['psutil', 'psutil.tests'],
+        ext_modules=extensions,
         # see: python setup.py register --list-classifiers
         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.0',
-            'Programming Language :: Python :: 3.1',
-            'Programming Language :: Python :: 3.2',
-            'Programming Language :: Python :: 3.3',
             'Programming Language :: Python :: 3.4',
+            'Programming Language :: Python :: 3.5',
+            'Programming Language :: Python :: 3.6',
             'Programming Language :: Python :: Implementation :: CPython',
             'Programming Language :: Python :: Implementation :: PyPy',
             'Programming Language :: Python',
             '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',
         ],
     )
-    if extensions is not None:
-        setup_args["ext_modules"] = extensions
-    setup(**setup_args)
+    if setuptools is not None:
+        kwargs.update(
+            test_suite="psutil.tests.get_suite",
+            tests_require=tests_require,
+            extras_require=extras_require,
+            zip_safe=False,
+        )
+    setup(**kwargs)
+
 
 if __name__ == '__main__':
     main()
deleted file mode 100644
--- a/third_party/python/psutil/test/README.rst
+++ /dev/null
@@ -1,21 +0,0 @@
-- The recommended way to run tests (also on Windows) is to cd into parent
-  directory and run ``make test``
-
-- Dependencies for running tests:
-   -  python 2.6: ipaddress, mock, unittest2
-   -  python 2.7: ipaddress, mock
-   -  python 3.2: ipaddress, mock
-   -  python 3.3: ipaddress
-   -  python >= 3.4: no deps required
-
-- The main test script is ``test_psutil.py``, which also imports platform-specific
-  ``_*.py`` scripts (which should be ignored).
-
-- ``test_memory_leaks.py`` looks for memory leaks into C extension modules and must
-  be run separately with ``make test-memleaks``.
-
-- To run tests on all supported Python version install tox (pip install tox)
-  then run ``tox``.
-
-- Every time a commit is pushed tests are automatically run on Travis:
-  https://travis-ci.org/giampaolo/psutil/
deleted file mode 100644
--- a/third_party/python/psutil/test/_bsd.py
+++ /dev/null
@@ -1,252 +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.
-
-# TODO: add test for comparing connections with 'sockstat' cmd
-
-"""BSD specific tests.  These are implicitly run by test_psutil.py."""
-
-import os
-import subprocess
-import sys
-import time
-
-import psutil
-
-from psutil._compat import PY3
-from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which,
-                         retry_before_failing, reap_children, unittest)
-
-
-PAGESIZE = os.sysconf("SC_PAGE_SIZE")
-if os.getuid() == 0:  # muse requires root privileges
-    MUSE_AVAILABLE = which('muse')
-else:
-    MUSE_AVAILABLE = False
-
-
-def sysctl(cmdline):
-    """Expects a sysctl command with an argument and parse the result
-    returning only the value of interest.
-    """
-    result = sh("sysctl " + cmdline)
-    result = result[result.find(": ") + 2:]
-    try:
-        return int(result)
-    except ValueError:
-        return result
-
-
-def muse(field):
-    """Thin wrapper around 'muse' cmdline utility."""
-    out = sh('muse')
-    for line in out.split('\n'):
-        if line.startswith(field):
-            break
-    else:
-        raise ValueError("line not found")
-    return int(line.split()[1])
-
-
-@unittest.skipUnless(BSD, "not a BSD system")
-class BSDSpecificTestCase(unittest.TestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.pid = get_test_subprocess().pid
-
-    @classmethod
-    def tearDownClass(cls):
-        reap_children()
-
-    def test_boot_time(self):
-        s = sysctl('sysctl kern.boottime')
-        s = s[s.find(" sec = ") + 7:]
-        s = s[:s.find(',')]
-        btime = int(s)
-        self.assertEqual(btime, psutil.boot_time())
-
-    def test_process_create_time(self):
-        cmdline = "ps -o lstart -p %s" % self.pid
-        p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE)
-        output = p.communicate()[0]
-        if PY3:
-            output = str(output, sys.stdout.encoding)
-        start_ps = output.replace('STARTED', '').strip()
-        start_psutil = psutil.Process(self.pid).create_time()
-        start_psutil = time.strftime("%a %b %e %H:%M:%S %Y",
-                                     time.localtime(start_psutil))
-        self.assertEqual(start_ps, start_psutil)
-
-    def test_disks(self):
-        # test psutil.disk_usage() and psutil.disk_partitions()
-        # against "df -a"
-        def df(path):
-            out = sh('df -k "%s"' % path).strip()
-            lines = out.split('\n')
-            lines.pop(0)
-            line = lines.pop(0)
-            dev, total, used, free = line.split()[:4]
-            if dev == 'none':
-                dev = ''
-            total = int(total) * 1024
-            used = int(used) * 1024
-            free = int(free) * 1024
-            return dev, total, used, free
-
-        for part in psutil.disk_partitions(all=False):
-            usage = psutil.disk_usage(part.mountpoint)
-            dev, total, used, free = df(part.mountpoint)
-            self.assertEqual(part.device, dev)
-            self.assertEqual(usage.total, total)
-            # 10 MB tollerance
-            if abs(usage.free - free) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % (usage.free, free))
-            if abs(usage.used - used) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % (usage.used, used))
-
-    @retry_before_failing()
-    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:]
-        while lines:
-            line = lines.pop()
-            fields = line.split()
-            _, start, stop, perms, res = fields[:5]
-            map = maps.pop()
-            self.assertEqual("%s-%s" % (start, stop), map.addr)
-            self.assertEqual(int(res), map.rss)
-            if not map.path.startswith('['):
-                self.assertEqual(fields[10], map.path)
-
-    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_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_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)
-        uids = p.uids()
-        gids = p.gids()
-        self.assertEqual(uids.real, int(ruid))
-        self.assertEqual(uids.effective, int(euid))
-        self.assertEqual(uids.saved, int(suid))
-        self.assertEqual(gids.real, int(rgid))
-        self.assertEqual(gids.effective, int(egid))
-        self.assertEqual(gids.saved, int(sgid))
-
-    # --- virtual_memory(); tests against sysctl
-
-    def test_vmem_total(self):
-        syst = sysctl("sysctl vm.stats.vm.v_page_count") * PAGESIZE
-        self.assertEqual(psutil.virtual_memory().total, syst)
-
-    @retry_before_failing()
-    def test_vmem_active(self):
-        syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE
-        self.assertAlmostEqual(psutil.virtual_memory().active, syst,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_inactive(self):
-        syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE
-        self.assertAlmostEqual(psutil.virtual_memory().inactive, syst,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_wired(self):
-        syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE
-        self.assertAlmostEqual(psutil.virtual_memory().wired, syst,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_cached(self):
-        syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE
-        self.assertAlmostEqual(psutil.virtual_memory().cached, syst,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_free(self):
-        syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE
-        self.assertAlmostEqual(psutil.virtual_memory().free, syst,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_buffers(self):
-        syst = sysctl("vfs.bufspace")
-        self.assertAlmostEqual(psutil.virtual_memory().buffers, syst,
-                               delta=TOLERANCE)
-
-    def test_cpu_count_logical(self):
-        syst = sysctl("hw.ncpu")
-        self.assertEqual(psutil.cpu_count(logical=True), syst)
-
-    # --- virtual_memory(); tests against muse
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    def test_total(self):
-        num = muse('Total')
-        self.assertEqual(psutil.virtual_memory().total, num)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_active(self):
-        num = muse('Active')
-        self.assertAlmostEqual(psutil.virtual_memory().active, num,
-                               delta=TOLERANCE)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_inactive(self):
-        num = muse('Inactive')
-        self.assertAlmostEqual(psutil.virtual_memory().inactive, num,
-                               delta=TOLERANCE)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_wired(self):
-        num = muse('Wired')
-        self.assertAlmostEqual(psutil.virtual_memory().wired, num,
-                               delta=TOLERANCE)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_cached(self):
-        num = muse('Cache')
-        self.assertAlmostEqual(psutil.virtual_memory().cached, num,
-                               delta=TOLERANCE)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_free(self):
-        num = muse('Free')
-        self.assertAlmostEqual(psutil.virtual_memory().free, num,
-                               delta=TOLERANCE)
-
-    @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
-    @retry_before_failing()
-    def test_buffers(self):
-        num = muse('Buffer')
-        self.assertAlmostEqual(psutil.virtual_memory().buffers, num,
-                               delta=TOLERANCE)
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/_linux.py
+++ /dev/null
@@ -1,473 +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.
-
-"""Linux specific tests.  These are implicitly run by test_psutil.py."""
-
-from __future__ import division
-import contextlib
-import errno
-import fcntl
-import io
-import os
-import pprint
-import re
-import socket
-import struct
-import sys
-import tempfile
-import time
-import warnings
-
-try:
-    from unittest import mock  # py3
-except ImportError:
-    import mock  # requires "pip install mock"
-
-from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX
-from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess,
-                         retry_before_failing, get_kernel_version, unittest,
-                         which, call_until)
-
-import psutil
-import psutil._pslinux
-from psutil._compat import PY3, u
-
-
-SIOCGIFADDR = 0x8915
-SIOCGIFCONF = 0x8912
-SIOCGIFHWADDR = 0x8927
-
-
-def get_ipv4_address(ifname):
-    ifname = ifname[:15]
-    if PY3:
-        ifname = bytes(ifname, 'ascii')
-    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-    with contextlib.closing(s):
-        return socket.inet_ntoa(
-            fcntl.ioctl(s.fileno(),
-                        SIOCGIFADDR,
-                        struct.pack('256s', ifname))[20:24])
-
-
-def get_mac_address(ifname):
-    ifname = ifname[:15]
-    if PY3:
-        ifname = bytes(ifname, 'ascii')
-    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-    with contextlib.closing(s):
-        info = fcntl.ioctl(
-            s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname))
-        if PY3:
-            def ord(x):
-                return x
-        else:
-            import __builtin__
-            ord = __builtin__.ord
-        return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
-
-
-@unittest.skipUnless(LINUX, "not a Linux system")
-class LinuxSpecificTestCase(unittest.TestCase):
-
-    @unittest.skipIf(
-        POSIX and not hasattr(os, 'statvfs'),
-        reason="os.statvfs() function not available on this platform")
-    @skip_on_not_implemented()
-    def test_disks(self):
-        # test psutil.disk_usage() and psutil.disk_partitions()
-        # against "df -a"
-        def df(path):
-            out = sh('df -P -B 1 "%s"' % path).strip()
-            lines = out.split('\n')
-            lines.pop(0)
-            line = lines.pop(0)
-            dev, total, used, free = line.split()[:4]
-            if dev == 'none':
-                dev = ''
-            total, used, free = int(total), int(used), int(free)
-            return dev, total, used, free
-
-        for part in psutil.disk_partitions(all=False):
-            usage = psutil.disk_usage(part.mountpoint)
-            dev, total, used, free = df(part.mountpoint)
-            self.assertEqual(part.device, dev)
-            self.assertEqual(usage.total, total)
-            # 10 MB tollerance
-            if abs(usage.free - free) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % (usage.free, free))
-            if abs(usage.used - used) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % (usage.used, used))
-
-    def test_memory_maps(self):
-        sproc = get_test_subprocess()
-        time.sleep(1)
-        p = psutil.Process(sproc.pid)
-        maps = p.memory_maps(grouped=False)
-        pmap = sh('pmap -x %s' % p.pid).split('\n')
-        # get rid of header
-        del pmap[0]
-        del pmap[0]
-        while maps and pmap:
-            this = maps.pop(0)
-            other = pmap.pop(0)
-            addr, _, rss, dirty, mode, path = other.split(None, 5)
-            if not path.startswith('[') and not path.endswith(']'):
-                self.assertEqual(path, os.path.basename(this.path))
-            self.assertEqual(int(rss) * 1024, this.rss)
-            # test only rwx chars, ignore 's' and 'p'
-            self.assertEqual(mode[:3], this.perms[:3])
-
-    def test_vmem_total(self):
-        lines = sh('free').split('\n')[1:]
-        total = int(lines[0].split()[1]) * 1024
-        self.assertEqual(total, psutil.virtual_memory().total)
-
-    @retry_before_failing()
-    def test_vmem_used(self):
-        lines = sh('free').split('\n')[1:]
-        used = int(lines[0].split()[2]) * 1024
-        self.assertAlmostEqual(used, psutil.virtual_memory().used,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_free(self):
-        lines = sh('free').split('\n')[1:]
-        free = int(lines[0].split()[3]) * 1024
-        self.assertAlmostEqual(free, psutil.virtual_memory().free,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_buffers(self):
-        lines = sh('free').split('\n')[1:]
-        buffers = int(lines[0].split()[5]) * 1024
-        self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_cached(self):
-        lines = sh('free').split('\n')[1:]
-        cached = int(lines[0].split()[6]) * 1024
-        self.assertAlmostEqual(cached, psutil.virtual_memory().cached,
-                               delta=TOLERANCE)
-
-    def test_swapmem_total(self):
-        lines = sh('free').split('\n')[1:]
-        total = int(lines[2].split()[1]) * 1024
-        self.assertEqual(total, psutil.swap_memory().total)
-
-    @retry_before_failing()
-    def test_swapmem_used(self):
-        lines = sh('free').split('\n')[1:]
-        used = int(lines[2].split()[2]) * 1024
-        self.assertAlmostEqual(used, psutil.swap_memory().used,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_swapmem_free(self):
-        lines = sh('free').split('\n')[1:]
-        free = int(lines[2].split()[3]) * 1024
-        self.assertAlmostEqual(free, psutil.swap_memory().free,
-                               delta=TOLERANCE)
-
-    @unittest.skipIf(TRAVIS, "unknown failure on travis")
-    def test_cpu_times(self):
-        fields = psutil.cpu_times()._fields
-        kernel_ver = re.findall('\d+\.\d+\.\d+', os.uname()[2])[0]
-        kernel_ver_info = tuple(map(int, kernel_ver.split('.')))
-        if kernel_ver_info >= (2, 6, 11):
-            self.assertIn('steal', fields)
-        else:
-            self.assertNotIn('steal', fields)
-        if kernel_ver_info >= (2, 6, 24):
-            self.assertIn('guest', fields)
-        else:
-            self.assertNotIn('guest', fields)
-        if kernel_ver_info >= (3, 2, 0):
-            self.assertIn('guest_nice', fields)
-        else:
-            self.assertNotIn('guest_nice', fields)
-
-    def test_net_if_addrs_ips(self):
-        for name, addrs in psutil.net_if_addrs().items():
-            for addr in addrs:
-                if addr.family == psutil.AF_LINK:
-                    self.assertEqual(addr.address, get_mac_address(name))
-                elif addr.family == socket.AF_INET:
-                    self.assertEqual(addr.address, get_ipv4_address(name))
-                # TODO: test for AF_INET6 family
-
-    @unittest.skipUnless(which('ip'), "'ip' utility not available")
-    @unittest.skipIf(TRAVIS, "skipped on Travis")
-    def test_net_if_names(self):
-        out = sh("ip addr").strip()
-        nics = psutil.net_if_addrs()
-        found = 0
-        for line in out.split('\n'):
-            line = line.strip()
-            if re.search("^\d+:", line):
-                found += 1
-                name = line.split(':')[1].strip()
-                self.assertIn(name, nics.keys())
-        self.assertEqual(len(nics), found, msg="%s\n---\n%s" % (
-            pprint.pformat(nics), out))
-
-    @unittest.skipUnless(which("nproc"), "nproc utility not available")
-    def test_cpu_count_logical_w_nproc(self):
-        num = int(sh("nproc --all"))
-        self.assertEqual(psutil.cpu_count(logical=True), num)
-
-    @unittest.skipUnless(which("lscpu"), "lscpu utility not available")
-    def test_cpu_count_logical_w_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)
-
-    # --- mocked tests
-
-    def test_virtual_memory_mocked_warnings(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            with warnings.catch_warnings(record=True) as ws:
-                warnings.simplefilter("always")
-                ret = psutil._pslinux.virtual_memory()
-                assert m.called
-                self.assertEqual(len(ws), 1)
-                w = ws[0]
-                self.assertTrue(w.filename.endswith('psutil/_pslinux.py'))
-                self.assertIn(
-                    "'cached', 'active' and 'inactive' memory stats couldn't "
-                    "be determined", str(w.message))
-                self.assertEqual(ret.cached, 0)
-                self.assertEqual(ret.active, 0)
-                self.assertEqual(ret.inactive, 0)
-
-    def test_swap_memory_mocked_warnings(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            with warnings.catch_warnings(record=True) as ws:
-                warnings.simplefilter("always")
-                ret = psutil._pslinux.swap_memory()
-                assert m.called
-                self.assertEqual(len(ws), 1)
-                w = ws[0]
-                self.assertTrue(w.filename.endswith('psutil/_pslinux.py'))
-                self.assertIn(
-                    "'sin' and 'sout' swap memory stats couldn't "
-                    "be determined", str(w.message))
-                self.assertEqual(ret.sin, 0)
-                self.assertEqual(ret.sout, 0)
-
-    def test_cpu_count_logical_mocked(self):
-        import psutil._pslinux
-        original = psutil._pslinux.cpu_count_logical()
-        # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in
-        # order to cause the parsing of /proc/cpuinfo and /proc/stat.
-        with mock.patch(
-                'psutil._pslinux.os.sysconf', side_effect=ValueError) as m:
-            self.assertEqual(psutil._pslinux.cpu_count_logical(), original)
-            assert m.called
-
-            # 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:
-                self.assertIsNone(psutil._pslinux.cpu_count_logical())
-                self.assertEqual(m.call_count, 2)
-                # /proc/stat should be the last one
-                self.assertEqual(m.call_args[0][0], '/proc/stat')
-
-            # Let's push this a bit further and make sure /proc/cpuinfo
-            # parsing works as expected.
-            with open('/proc/cpuinfo', 'rb') as f:
-                cpuinfo_data = f.read()
-            fake_file = io.BytesIO(cpuinfo_data)
-            with mock.patch('psutil._pslinux.open',
-                            return_value=fake_file, create=True) as m:
-                self.assertEqual(psutil._pslinux.cpu_count_logical(), original)
-
-    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
-
-    def test_proc_open_files_file_gone(self):
-        # simulates a file which gets deleted during open_files()
-        # execution
-        p = psutil.Process()
-        files = p.open_files()
-        with tempfile.NamedTemporaryFile():
-            # give the kernel some time to see the new file
-            call_until(p.open_files, "len(ret) != %i" % len(files))
-            with mock.patch('psutil._pslinux.os.readlink',
-                            side_effect=OSError(errno.ENOENT, "")) as m:
-                files = p.open_files()
-                assert not files
-                assert m.called
-            # also simulate the case where os.readlink() returns EINVAL
-            # in which case psutil is supposed to 'continue'
-            with mock.patch('psutil._pslinux.os.readlink',
-                            side_effect=OSError(errno.EINVAL, "")) as m:
-                self.assertEqual(p.open_files(), [])
-                assert m.called
-
-    def test_proc_terminal_mocked(self):
-        with mock.patch('psutil._pslinux._psposix._get_terminal_map',
-                        return_value={}) as m:
-            self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal())
-            assert m.called
-
-    def test_proc_num_ctx_switches_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).num_ctx_switches)
-            assert m.called
-
-    def test_proc_num_threads_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).num_threads)
-            assert m.called
-
-    def test_proc_ppid_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).ppid)
-            assert m.called
-
-    def test_proc_uids_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).uids)
-            assert m.called
-
-    def test_proc_gids_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).gids)
-            assert m.called
-
-    def test_proc_cmdline_mocked(self):
-        # 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',
-                        return_value=fake_file, create=True) as m:
-            p.cmdline() == ['foo', 'bar']
-            assert m.called
-        fake_file = io.StringIO(u('foo\x00bar\x00\x00'))
-        with mock.patch('psutil._pslinux.open',
-                        return_value=fake_file, create=True) as m:
-            p.cmdline() == ['foo', 'bar', '']
-            assert m.called
-
-    def test_proc_io_counters_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                NotImplementedError,
-                psutil._pslinux.Process(os.getpid()).io_counters)
-            assert m.called
-
-    def test_boot_time_mocked(self):
-        with mock.patch('psutil._pslinux.open', create=True) as m:
-            self.assertRaises(
-                RuntimeError,
-                psutil._pslinux.boot_time)
-            assert m.called
-
-    def test_users_mocked(self):
-        # Make sure ':0' and ':0.0' (returned by C ext) are converted
-        # to 'localhost'.
-        with mock.patch('psutil._pslinux.cext.users',
-                        return_value=[('giampaolo', 'pts/2', ':0',
-                                      1436573184.0, True)]) as m:
-            self.assertEqual(psutil.users()[0].host, 'localhost')
-            assert m.called
-        with mock.patch('psutil._pslinux.cext.users',
-                        return_value=[('giampaolo', 'pts/2', ':0.0',
-                                      1436573184.0, True)]) as m:
-            self.assertEqual(psutil.users()[0].host, 'localhost')
-            assert m.called
-        # ...otherwise it should be returned as-is
-        with mock.patch('psutil._pslinux.cext.users',
-                        return_value=[('giampaolo', 'pts/2', 'foo',
-                                      1436573184.0, True)]) as m:
-            self.assertEqual(psutil.users()[0].host, 'foo')
-            assert m.called
-
-    def test_disk_partitions_mocked(self):
-        # Test that ZFS partitions are returned.
-        with open("/proc/filesystems", "r") as f:
-            data = f.read()
-        if 'zfs' in data:
-            for part in psutil.disk_partitions():
-                if part.fstype == 'zfs':
-                    break
-            else:
-                self.fail("couldn't find any ZFS partition")
-        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',
-                            return_value=fake_file, create=True) as m1:
-                with mock.patch(
-                        'psutil._pslinux.cext.disk_partitions',
-                        return_value=[('/dev/sdb3', '/', 'zfs', 'rw')]) as m2:
-                    ret = psutil.disk_partitions()
-                    assert m1.called
-                    assert m2.called
-                    assert ret
-                    self.assertEqual(ret[0].fstype, 'zfs')
-
-    # --- tests for specific kernel versions
-
-    @unittest.skipUnless(
-        get_kernel_version() >= (2, 6, 36),
-        "prlimit() not available on this Linux kernel version")
-    def test_prlimit_availability(self):
-        # prlimit() should be available starting from kernel 2.6.36
-        p = psutil.Process(os.getpid())
-        p.rlimit(psutil.RLIMIT_NOFILE)
-        # if prlimit() is supported *at least* these constants should
-        # be available
-        self.assertTrue(hasattr(psutil, "RLIM_INFINITY"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_AS"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_CORE"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_CPU"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_DATA"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_FSIZE"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_LOCKS"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_MEMLOCK"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_NOFILE"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_NPROC"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_RSS"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_STACK"))
-
-    @unittest.skipUnless(
-        get_kernel_version() >= (3, 0),
-        "prlimit constants not available on this Linux kernel version")
-    def test_resource_consts_kernel_v(self):
-        # more recent constants
-        self.assertTrue(hasattr(psutil, "RLIMIT_MSGQUEUE"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_NICE"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_RTPRIO"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_RTTIME"))
-        self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING"))
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/_osx.py
+++ /dev/null
@@ -1,160 +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.
-
-"""OSX specific tests.  These are implicitly run by test_psutil.py."""
-
-import os
-import re
-import subprocess
-import sys
-import time
-
-import psutil
-
-from psutil._compat import PY3
-from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess,
-                         reap_children, retry_before_failing, unittest)
-
-
-PAGESIZE = os.sysconf("SC_PAGE_SIZE")
-
-
-def sysctl(cmdline):
-    """Expects a sysctl command with an argument and parse the result
-    returning only the value of interest.
-    """
-    p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE)
-    result = p.communicate()[0].strip().split()[1]
-    if PY3:
-        result = str(result, sys.stdout.encoding)
-    try:
-        return int(result)
-    except ValueError:
-        return result
-
-
-def vm_stat(field):
-    """Wrapper around 'vm_stat' cmdline utility."""
-    out = sh('vm_stat')
-    for line in out.split('\n'):
-        if field in line:
-            break
-    else:
-        raise ValueError("line not found")
-    return int(re.search('\d+', line).group(0)) * PAGESIZE
-
-
-@unittest.skipUnless(OSX, "not an OSX system")
-class OSXSpecificTestCase(unittest.TestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.pid = get_test_subprocess().pid
-
-    @classmethod
-    def tearDownClass(cls):
-        reap_children()
-
-    def test_process_create_time(self):
-        cmdline = "ps -o lstart -p %s" % self.pid
-        p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE)
-        output = p.communicate()[0]
-        if PY3:
-            output = str(output, sys.stdout.encoding)
-        start_ps = output.replace('STARTED', '').strip()
-        start_psutil = psutil.Process(self.pid).create_time()
-        start_psutil = time.strftime("%a %b %e %H:%M:%S %Y",
-                                     time.localtime(start_psutil))
-        self.assertEqual(start_ps, start_psutil)
-
-    def test_disks(self):
-        # test psutil.disk_usage() and psutil.disk_partitions()
-        # against "df -a"
-        def df(path):
-            out = sh('df -k "%s"' % path).strip()
-            lines = out.split('\n')
-            lines.pop(0)
-            line = lines.pop(0)
-            dev, total, used, free = line.split()[:4]
-            if dev == 'none':
-                dev = ''
-            total = int(total) * 1024
-            used = int(used) * 1024
-            free = int(free) * 1024
-            return dev, total, used, free
-
-        for part in psutil.disk_partitions(all=False):
-            usage = psutil.disk_usage(part.mountpoint)
-            dev, total, used, free = df(part.mountpoint)
-            self.assertEqual(part.device, dev)
-            self.assertEqual(usage.total, total)
-            # 10 MB tollerance
-            if abs(usage.free - free) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % usage.free, free)
-            if abs(usage.used - used) > 10 * 1024 * 1024:
-                self.fail("psutil=%s, df=%s" % usage.used, used)
-
-    # --- virtual mem
-
-    def test_vmem_total(self):
-        sysctl_hwphymem = sysctl('sysctl hw.memsize')
-        self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total)
-
-    @retry_before_failing()
-    def test_vmem_free(self):
-        num = vm_stat("free")
-        self.assertAlmostEqual(psutil.virtual_memory().free, num,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_active(self):
-        num = vm_stat("active")
-        self.assertAlmostEqual(psutil.virtual_memory().active, num,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_inactive(self):
-        num = vm_stat("inactive")
-        self.assertAlmostEqual(psutil.virtual_memory().inactive, num,
-                               delta=TOLERANCE)
-
-    @retry_before_failing()
-    def test_vmem_wired(self):
-        num = vm_stat("wired")
-        self.assertAlmostEqual(psutil.virtual_memory().wired, num,
-                               delta=TOLERANCE)
-
-    # --- swap mem
-
-    def test_swapmem_sin(self):
-        num = vm_stat("Pageins")
-        self.assertEqual(psutil.swap_memory().sin, num)
-
-    def test_swapmem_sout(self):
-        num = vm_stat("Pageouts")
-        self.assertEqual(psutil.swap_memory().sout, num)
-
-    def test_swapmem_total(self):
-        tot1 = psutil.swap_memory().total
-        tot2 = 0
-        # OSX uses multiple cache files:
-        # http://en.wikipedia.org/wiki/Paging#OS_X
-        for name in os.listdir("/var/vm/"):
-            file = os.path.join("/var/vm", name)
-            if os.path.isfile(file):
-                tot2 += os.path.getsize(file)
-        self.assertEqual(tot1, tot2)
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/_posix.py
+++ /dev/null
@@ -1,258 +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.
-
-"""POSIX specific tests.  These are implicitly run by test_psutil.py."""
-
-import datetime
-import os
-import subprocess
-import sys
-import time
-
-import psutil
-
-from psutil._compat import PY3, callable
-from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX, TRAVIS
-from test_psutil import (get_test_subprocess, skip_on_access_denied,
-                         retry_before_failing, reap_children, sh, unittest,
-                         get_kernel_version, wait_for_pid)
-
-
-def ps(cmd):
-    """Expects a ps command with a -o argument and parse the result
-    returning only the value of interest.
-    """
-    if not LINUX:
-        cmd = cmd.replace(" --no-headers ", " ")
-    if SUNOS:
-        cmd = cmd.replace("-o command", "-o comm")
-        cmd = cmd.replace("-o start", "-o stime")
-    p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE)
-    output = p.communicate()[0].strip()
-    if PY3:
-        output = str(output, sys.stdout.encoding)
-    if not LINUX:
-        output = output.split('\n')[1].strip()
-    try:
-        return int(output)
-    except ValueError:
-        return output
-
-
-@unittest.skipUnless(POSIX, "not a POSIX system")
-class PosixSpecificTestCase(unittest.TestCase):
-    """Compare psutil results against 'ps' command line utility."""
-
-    @classmethod
-    def setUpClass(cls):
-        cls.pid = get_test_subprocess([PYTHON, "-E", "-O"],
-                                      stdin=subprocess.PIPE).pid
-        wait_for_pid(cls.pid)
-
-    @classmethod
-    def tearDownClass(cls):
-        reap_children()
-
-    # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps
-
-    def test_process_parent_pid(self):
-        ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid)
-        ppid_psutil = psutil.Process(self.pid).ppid()
-        self.assertEqual(ppid_ps, ppid_psutil)
-
-    def test_process_uid(self):
-        uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid)
-        uid_psutil = psutil.Process(self.pid).uids().real
-        self.assertEqual(uid_ps, uid_psutil)
-
-    def test_process_gid(self):
-        gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid)
-        gid_psutil = psutil.Process(self.pid).gids().real
-        self.assertEqual(gid_ps, gid_psutil)
-
-    def test_process_username(self):
-        username_ps = ps("ps --no-headers -o user -p %s" % self.pid)
-        username_psutil = psutil.Process(self.pid).username()
-        self.assertEqual(username_ps, username_psutil)
-
-    @skip_on_access_denied()
-    @retry_before_failing()
-    def test_process_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_psutil = psutil.Process(self.pid).memory_info()[0] / 1024
-        self.assertEqual(rss_ps, rss_psutil)
-
-    @skip_on_access_denied()
-    @retry_before_failing()
-    def test_process_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_psutil = psutil.Process(self.pid).memory_info()[1] / 1024
-        self.assertEqual(vsz_ps, vsz_psutil)
-
-    def test_process_name(self):
-        # use command + arg since "comm" keyword not supported on all platforms
-        name_ps = ps("ps --no-headers -o command -p %s" % (
-            self.pid)).split(' ')[0]
-        # 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()
-        self.assertEqual(name_ps, name_psutil)
-
-    @unittest.skipIf(OSX or BSD,
-                     'ps -o start not available')
-    def test_process_create_time(self):
-        time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0]
-        time_psutil = psutil.Process(self.pid).create_time()
-        time_psutil_tstamp = datetime.datetime.fromtimestamp(
-            time_psutil).strftime("%H:%M:%S")
-        # sometimes ps shows the time rounded up instead of down, so we check
-        # for both possible values
-        round_time_psutil = round(time_psutil)
-        round_time_psutil_tstamp = datetime.datetime.fromtimestamp(
-            round_time_psutil).strftime("%H:%M:%S")
-        self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp])
-
-    def test_process_exe(self):
-        ps_pathname = ps("ps --no-headers -o command -p %s" %
-                         self.pid).split(' ')[0]
-        psutil_pathname = psutil.Process(self.pid).exe()
-        try:
-            self.assertEqual(ps_pathname, psutil_pathname)
-        except AssertionError:
-            # certain platforms such as BSD are more accurate returning:
-            # "/usr/local/bin/python2.7"
-            # ...instead of:
-            # "/usr/local/bin/python"
-            # We do not want to consider this difference in accuracy
-            # an error.
-            adjusted_ps_pathname = ps_pathname[:len(ps_pathname)]
-            self.assertEqual(ps_pathname, adjusted_ps_pathname)
-
-    def test_process_cmdline(self):
-        ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid)
-        psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline())
-        if SUNOS:
-            # ps on Solaris only shows the first part of the cmdline
-            psutil_cmdline = psutil_cmdline.split(" ")[0]
-        self.assertEqual(ps_cmdline, psutil_cmdline)
-
-    @retry_before_failing()
-    def test_pids(self):
-        # Note: this test might fail if the OS is starting/killing
-        # other processes in the meantime
-        if SUNOS:
-            cmd = ["ps", "ax"]
-        else:
-            cmd = ["ps", "ax", "-o", "pid"]
-        p = get_test_subprocess(cmd, stdout=subprocess.PIPE)
-        output = p.communicate()[0].strip()
-        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_psutil = psutil.pids()
-        pids_ps.sort()
-        pids_psutil.sort()
-
-        # on OSX ps doesn't show pid 0
-        if OSX and 0 not in pids_ps:
-            pids_ps.insert(0, 0)
-
-        if pids_ps != pids_psutil:
-            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, "test not reliable on SUNOS")
-    @unittest.skipIf(TRAVIS, "test not reliable on Travis")
-    def test_nic_names(self):
-        p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE)
-        output = p.communicate()[0].strip()
-        if PY3:
-            output = str(output, sys.stdout.encoding)
-        for nic in psutil.net_io_counters(pernic=True).keys():
-            for line in output.split():
-                if line.startswith(nic):
-                    break
-            else:
-                self.fail(
-                    "couldn't find %s nic in 'ifconfig -a' output\n%s" % (
-                        nic, output))
-
-    @retry_before_failing()
-    def test_users(self):
-        out = sh("who")
-        lines = out.split('\n')
-        users = [x.split()[0] for x in lines]
-        self.assertEqual(len(users), len(psutil.users()))
-        terminals = [x.split()[1] for x in lines]
-        for u in psutil.users():
-            self.assertTrue(u.name in users, u.name)
-            self.assertTrue(u.terminal in terminals, u.terminal)
-
-    def test_fds_open(self):
-        # Note: this fails from time to time; I'm keen on thinking
-        # it doesn't mean something is broken
-        def call(p, attr):
-            args = ()
-            attr = getattr(p, name, None)
-            if attr is not None and callable(attr):
-                if name == 'rlimit':
-                    args = (psutil.RLIMIT_NOFILE,)
-                attr(*args)
-            else:
-                attr
-
-        p = psutil.Process(os.getpid())
-        failures = []
-        ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice',
-                         'send_signal', 'wait', 'children', 'as_dict']
-        if LINUX and get_kernel_version() < (2, 6, 36):
-            ignored_names.append('rlimit')
-        if LINUX and get_kernel_version() < (2, 6, 23):
-            ignored_names.append('num_ctx_switches')
-        for name in dir(psutil.Process):
-            if (name.startswith('_') or name in ignored_names):
-                continue
-            else:
-                try:
-                    num1 = p.num_fds()
-                    for x in range(2):
-                        call(p, name)
-                    num2 = p.num_fds()
-                except psutil.AccessDenied:
-                    pass
-                else:
-                    if abs(num2 - num1) > 1:
-                        fail = "failure while processing Process.%s method " \
-                               "(before=%s, after=%s)" % (name, num1, num2)
-                        failures.append(fail)
-        if failures:
-            self.fail('\n' + '\n'.join(failures))
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/_sunos.py
+++ /dev/null
@@ -1,48 +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.
-
-"""Sun OS specific tests.  These are implicitly run by test_psutil.py."""
-
-import sys
-import os
-
-from test_psutil import SUNOS, sh, unittest
-import psutil
-
-
-@unittest.skipUnless(SUNOS, "not a SunOS system")
-class SunOSSpecificTestCase(unittest.TestCase):
-
-    def test_swap_memory(self):
-        out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH'])
-        lines = out.strip().split('\n')[1:]
-        if not lines:
-            raise ValueError('no swap device(s) configured')
-        total = free = 0
-        for line in lines:
-            line = line.split()
-            t, f = line[-2:]
-            t = t.replace('K', '')
-            f = f.replace('K', '')
-            total += int(int(t) * 1024)
-            free += int(int(f) * 1024)
-        used = total - free
-
-        psutil_swap = psutil.swap_memory()
-        self.assertEqual(psutil_swap.total, total)
-        self.assertEqual(psutil_swap.used, used)
-        self.assertEqual(psutil_swap.free, free)
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/_windows.py
+++ /dev/null
@@ -1,464 +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.
-
-"""Windows specific tests.  These are implicitly run by test_psutil.py."""
-
-import errno
-import os
-import platform
-import signal
-import subprocess
-import sys
-import time
-import traceback
-
-from test_psutil import APPVEYOR, WINDOWS
-from test_psutil import get_test_subprocess, reap_children, unittest
-
-import mock
-try:
-    import wmi
-except ImportError:
-    wmi = None
-try:
-    import win32api
-    import win32con
-except ImportError:
-    win32api = win32con = None
-
-from psutil._compat import PY3, callable, long
-import psutil
-
-
-cext = psutil._psplatform.cext
-
-
-def wrap_exceptions(fun):
-    def wrapper(self, *args, **kwargs):
-        try:
-            return fun(self, *args, **kwargs)
-        except OSError as err:
-            from psutil._pswindows import ACCESS_DENIED_SET
-            if err.errno in ACCESS_DENIED_SET:
-                raise psutil.AccessDenied(None, None)
-            if err.errno == errno.ESRCH:
-                raise psutil.NoSuchProcess(None, None)
-            raise
-    return wrapper
-
-
-@unittest.skipUnless(WINDOWS, "not a Windows system")
-class WindowsSpecificTestCase(unittest.TestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.pid = get_test_subprocess().pid
-
-    @classmethod
-    def tearDownClass(cls):
-        reap_children()
-
-    def test_issue_24(self):
-        p = psutil.Process(0)
-        self.assertRaises(psutil.AccessDenied, p.kill)
-
-    def test_special_pid(self):
-        p = psutil.Process(4)
-        self.assertEqual(p.name(), 'System')
-        # use __str__ to access all common Process properties to check
-        # that nothing strange happens
-        str(p)
-        p.username()
-        self.assertTrue(p.create_time() >= 0.0)
-        try:
-            rss, vms = p.memory_info()
-        except psutil.AccessDenied:
-            # expected on Windows Vista and Windows 7
-            if not platform.uname()[1] in ('vista', 'win-7', 'win7'):
-                raise
-        else:
-            self.assertTrue(rss > 0)
-
-    def test_send_signal(self):
-        p = psutil.Process(self.pid)
-        self.assertRaises(ValueError, p.send_signal, signal.SIGINT)
-
-    def test_nic_names(self):
-        p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE)
-        out = p.communicate()[0]
-        if PY3:
-            out = str(out, sys.stdout.encoding)
-        nics = psutil.net_io_counters(pernic=True).keys()
-        for nic in nics:
-            if "pseudo-interface" in nic.replace(' ', '-').lower():
-                continue
-            if nic not in out:
-                self.fail(
-                    "%r nic wasn't found in 'ipconfig /all' output" % nic)
-
-    def test_exe(self):
-        for p in psutil.process_iter():
-            try:
-                self.assertEqual(os.path.basename(p.exe()), p.name())
-            except psutil.Error:
-                pass
-
-    # --- Process class tests
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_name(self):
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        self.assertEqual(p.name(), w.Caption)
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_exe(self):
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        # Note: wmi reports the exe as a lower case string.
-        # Being Windows paths case-insensitive we ignore that.
-        self.assertEqual(p.exe().lower(), w.ExecutablePath.lower())
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_cmdline(self):
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        self.assertEqual(' '.join(p.cmdline()),
-                         w.CommandLine.replace('"', ''))
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_username(self):
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        domain, _, username = w.GetOwner()
-        username = "%s\\%s" % (domain, username)
-        self.assertEqual(p.username(), username)
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_rss_memory(self):
-        time.sleep(0.1)
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        rss = p.memory_info().rss
-        self.assertEqual(rss, int(w.WorkingSetSize))
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_vms_memory(self):
-        time.sleep(0.1)
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        vms = p.memory_info().vms
-        # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx
-        # ...claims that PageFileUsage is represented in Kilo
-        # bytes but funnily enough on certain platforms bytes are
-        # returned instead.
-        wmi_usage = int(w.PageFileUsage)
-        if (vms != wmi_usage) and (vms != wmi_usage * 1024):
-            self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms))
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_process_create_time(self):
-        w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-        p = psutil.Process(self.pid)
-        wmic_create = str(w.CreationDate.split('.')[0])
-        psutil_create = time.strftime("%Y%m%d%H%M%S",
-                                      time.localtime(p.create_time()))
-        self.assertEqual(wmic_create, psutil_create)
-
-    # --- psutil namespace functions and constants tests
-
-    @unittest.skipUnless('NUMBER_OF_PROCESSORS' 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())
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_total_phymem(self):
-        w = wmi.WMI().Win32_ComputerSystem()[0]
-        self.assertEqual(int(w.TotalPhysicalMemory),
-                         psutil.virtual_memory().total)
-
-    # @unittest.skipIf(wmi is None, "wmi module is not installed")
-    # def test__UPTIME(self):
-    #     # _UPTIME constant is not public but it is used internally
-    #     # as value to return for pid 0 creation time.
-    #     # WMI behaves the same.
-    #     w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
-    #     p = psutil.Process(0)
-    #     wmic_create = str(w.CreationDate.split('.')[0])
-    #     psutil_create = time.strftime("%Y%m%d%H%M%S",
-    #                                   time.localtime(p.create_time()))
-    #
-
-    # Note: this test is not very reliable
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    @unittest.skipIf(APPVEYOR, "test not relieable on appveyor")
-    def test_pids(self):
-        # Note: this test might fail if the OS is starting/killing
-        # other processes in the meantime
-        w = wmi.WMI().Win32_Process()
-        wmi_pids = set([x.ProcessId for x in w])
-        psutil_pids = set(psutil.pids())
-        self.assertEqual(wmi_pids, psutil_pids)
-
-    @unittest.skipIf(wmi is None, "wmi module is not installed")
-    def test_disks(self):
-        ps_parts = psutil.disk_partitions(all=True)
-        wmi_parts = wmi.WMI().Win32_LogicalDisk()
-        for ps_part in ps_parts:
-            for wmi_part in wmi_parts:
-                if ps_part.device.replace('\\', '') == wmi_part.DeviceID:
-                    if not ps_part.mountpoint:
-                        # this is usually a CD-ROM with no disk inserted
-                        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
-                    self.assertEqual(usage.total, int(wmi_part.Size))
-                    wmi_free = int(wmi_part.FreeSpace)
-                    self.assertEqual(usage.free, wmi_free)
-                    # 10 MB tollerance
-                    if abs(usage.free - wmi_free) > 10 * 1024 * 1024:
-                        self.fail("psutil=%s, wmi=%s" % (
-                            usage.free, wmi_free))
-                    break
-            else:
-                self.fail("can't find partition %s" % repr(ps_part))
-
-    @unittest.skipIf(win32api is None, "pywin32 module is not installed")
-    def test_num_handles(self):
-        p = psutil.Process(os.getpid())
-        before = p.num_handles()
-        handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
-                                      win32con.FALSE, os.getpid())
-        after = p.num_handles()
-        self.assertEqual(after, before + 1)
-        win32api.CloseHandle(handle)
-        self.assertEqual(p.num_handles(), before)
-
-    @unittest.skipIf(win32api is None, "pywin32 module is not installed")
-    def test_num_handles_2(self):
-        # Note: this fails from time to time; I'm keen on thinking
-        # it doesn't mean something is broken
-        def call(p, attr):
-            attr = getattr(p, name, None)
-            if attr is not None and callable(attr):
-                attr()
-            else:
-                attr
-
-        p = psutil.Process(self.pid)
-        failures = []
-        for name in dir(psutil.Process):
-            if name.startswith('_') \
-                or name in ('terminate', 'kill', 'suspend', 'resume',
-                            'nice', 'send_signal', 'wait', 'children',
-                            'as_dict'):
-                continue
-            else:
-                try:
-                    call(p, name)
-                    num1 = p.num_handles()
-                    call(p, name)
-                    num2 = p.num_handles()
-                except (psutil.NoSuchProcess, psutil.AccessDenied):
-                    pass
-                else:
-                    if num2 > num1:
-                        fail = \
-                            "failure while processing Process.%s method " \
-                            "(before=%s, after=%s)" % (name, num1, num2)
-                        failures.append(fail)
-        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.skipUnless(WINDOWS, "not a Windows system")
-class TestDualProcessImplementation(unittest.TestCase):
-    """
-    Certain APIs on Windows have 2 internal implementations, one
-    based on documented Windows APIs, another one based
-    NtQuerySystemInformation() which gets called as fallback in
-    case the first fails because of limited permission error.
-    Here we test that the two methods return the exact same value,
-    see:
-    https://github.com/giampaolo/psutil/issues/304
-    """
-
-    fun_names = [
-        # function name, tolerance
-        ('proc_cpu_times', 0.2),
-        ('proc_create_time', 0.5),
-        ('proc_num_handles', 1),  # 1 because impl #1 opens a handle
-        ('proc_memory_info', 1024),  # KB
-        ('proc_io_counters', 0),
-    ]
-
-    def test_compare_values(self):
-        def assert_ge_0(obj):
-            if isinstance(obj, tuple):
-                for value in obj:
-                    self.assertGreaterEqual(value, 0, msg=obj)
-            elif isinstance(obj, (int, long, float)):
-                self.assertGreaterEqual(obj, 0)
-            else:
-                assert 0  # case not handled which needs to be fixed
-
-        def compare_with_tolerance(ret1, ret2, tolerance):
-            if ret1 == ret2:
-                return
-            else:
-                if isinstance(ret2, (int, long, float)):
-                    diff = abs(ret1 - ret2)
-                    self.assertLessEqual(diff, tolerance)
-                elif isinstance(ret2, tuple):
-                    for a, b in zip(ret1, ret2):
-                        diff = abs(a - b)
-                        self.assertLessEqual(diff, tolerance)
-
-        from psutil._pswindows import ntpinfo
-        failures = []
-        for p in psutil.process_iter():
-            try:
-                nt = ntpinfo(*cext.proc_info(p.pid))
-            except psutil.NoSuchProcess:
-                continue
-            assert_ge_0(nt)
-
-            for name, tolerance in self.fun_names:
-                if name == 'proc_memory_info' and p.pid == os.getpid():
-                    continue
-                if name == 'proc_create_time' and p.pid in (0, 4):
-                    continue
-                meth = wrap_exceptions(getattr(cext, name))
-                try:
-                    ret = meth(p.pid)
-                except (psutil.NoSuchProcess, psutil.AccessDenied):
-                    continue
-                # compare values
-                try:
-                    if name == 'proc_cpu_times':
-                        compare_with_tolerance(ret[0], nt.user_time, tolerance)
-                        compare_with_tolerance(ret[1],
-                                               nt.kernel_time, tolerance)
-                    elif name == 'proc_create_time':
-                        compare_with_tolerance(ret, nt.create_time, tolerance)
-                    elif name == 'proc_num_handles':
-                        compare_with_tolerance(ret, nt.num_handles, tolerance)
-                    elif name == 'proc_io_counters':
-                        compare_with_tolerance(ret[0], nt.io_rcount, tolerance)
-                        compare_with_tolerance(ret[1], nt.io_wcount, tolerance)
-                        compare_with_tolerance(ret[2], nt.io_rbytes, tolerance)
-                        compare_with_tolerance(ret[3], nt.io_wbytes, tolerance)
-                    elif name == 'proc_memory_info':
-                        try:
-                            rawtupl = cext.proc_memory_info_2(p.pid)
-                        except psutil.NoSuchProcess:
-                            continue
-                        compare_with_tolerance(ret, rawtupl, tolerance)
-                except AssertionError:
-                    trace = traceback.format_exc()
-                    msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % (
-                        trace, p.pid, name, ret, nt)
-                    failures.append(msg)
-                    break
-
-        if failures:
-            self.fail('\n\n'.join(failures))
-
-    # ---
-    # same tests as above but mimicks the AccessDenied failure of
-    # the first (fast) method failing with AD.
-    # TODO: currently does not take tolerance into account.
-
-    def test_name(self):
-        name = psutil.Process().name()
-        with mock.patch("psutil._psplatform.cext.proc_exe",
-                        side_effect=psutil.AccessDenied(os.getpid())) as fun:
-            psutil.Process().name() == name
-            assert fun.called
-
-    def test_memory_info(self):
-        mem = psutil.Process().memory_info()
-        with mock.patch("psutil._psplatform.cext.proc_memory_info",
-                        side_effect=OSError(errno.EPERM, "msg")) as fun:
-            psutil.Process().memory_info() == mem
-            assert fun.called
-
-    def test_create_time(self):
-        ctime = psutil.Process().create_time()
-        with mock.patch("psutil._psplatform.cext.proc_create_time",
-                        side_effect=OSError(errno.EPERM, "msg")) as fun:
-            psutil.Process().create_time() == ctime
-            assert fun.called
-
-    def test_cpu_times(self):
-        cpu_times = psutil.Process().cpu_times()
-        with mock.patch("psutil._psplatform.cext.proc_cpu_times",
-                        side_effect=OSError(errno.EPERM, "msg")) as fun:
-            psutil.Process().cpu_times() == cpu_times
-            assert fun.called
-
-    def test_io_counters(self):
-        io_counters = psutil.Process().io_counters()
-        with mock.patch("psutil._psplatform.cext.proc_io_counters",
-                        side_effect=OSError(errno.EPERM, "msg")) as fun:
-            psutil.Process().io_counters() == io_counters
-            assert fun.called
-
-    def test_num_handles(self):
-        io_counters = psutil.Process().io_counters()
-        with mock.patch("psutil._psplatform.cext.proc_io_counters",
-                        side_effect=OSError(errno.EPERM, "msg")) as fun:
-            psutil.Process().io_counters() == io_counters
-            assert fun.called
-
-    # --- other tests
-
-    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_zombies(self):
-        # test that NPS is raised by the 2nd implementation in case a
-        # process no longer exists
-        ZOMBIE_PID = max(psutil.pids()) + 5000
-        for name, _ in self.fun_names:
-            meth = wrap_exceptions(getattr(cext, name))
-            self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID)
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase))
-    test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/test_memory_leaks.py
+++ /dev/null
@@ -1,445 +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.
-
-"""
-A test script which attempts to detect memory leaks by calling C
-functions many times and compare process memory usage before and
-after the calls.  It might produce false positives.
-"""
-
-import functools
-import gc
-import os
-import socket
-import sys
-import threading
-import time
-
-import psutil
-import psutil._common
-
-from psutil._compat import xrange, callable
-from test_psutil import (WINDOWS, POSIX, OSX, LINUX, SUNOS, BSD, TESTFN,
-                         RLIMIT_SUPPORT, TRAVIS)
-from test_psutil import (reap_children, supports_ipv6, safe_remove,
-                         get_test_subprocess)
-
-if sys.version_info < (2, 7):
-    import unittest2 as unittest  # https://pypi.python.org/pypi/unittest2
-else:
-    import unittest
-
-
-LOOPS = 1000
-TOLERANCE = 4096
-SKIP_PYTHON_IMPL = True
-
-
-def skip_if_linux():
-    return unittest.skipIf(LINUX and SKIP_PYTHON_IMPL,
-                           "not worth being tested on LINUX (pure python)")
-
-
-class Base(unittest.TestCase):
-    proc = psutil.Process()
-
-    def execute(self, function, *args, **kwargs):
-        def call_many_times():
-            for x in xrange(LOOPS - 1):
-                self.call(function, *args, **kwargs)
-            del x
-            gc.collect()
-            return self.get_mem()
-
-        self.call(function, *args, **kwargs)
-        self.assertEqual(gc.garbage, [])
-        self.assertEqual(threading.active_count(), 1)
-
-        # RSS comparison
-        # step 1
-        rss1 = call_many_times()
-        # step 2
-        rss2 = call_many_times()
-
-        difference = rss2 - rss1
-        if difference > TOLERANCE:
-            # This doesn't necessarily mean we have a leak yet.
-            # At this point we assume that after having called the
-            # function so many times the memory usage is stabilized
-            # and if there are no leaks it should not increase any
-            # more.
-            # Let's keep calling fun for 3 more seconds and fail if
-            # we notice any difference.
-            stop_at = time.time() + 3
-            while True:
-                self.call(function, *args, **kwargs)
-                if time.time() >= stop_at:
-                    break
-            del stop_at
-            gc.collect()
-            rss3 = self.get_mem()
-            difference = rss3 - rss2
-            if rss3 > rss2:
-                self.fail("rss2=%s, rss3=%s, difference=%s"
-                          % (rss2, rss3, difference))
-
-    def execute_w_exc(self, exc, function, *args, **kwargs):
-        kwargs['_exc'] = exc
-        self.execute(function, *args, **kwargs)
-
-    def get_mem(self):
-        return psutil.Process().memory_info()[0]
-
-    def call(self, function, *args, **kwargs):
-        raise NotImplementedError("must be implemented in subclass")
-
-
-class TestProcessObjectLeaks(Base):
-    """Test leaks of Process class methods and properties"""
-
-    def setUp(self):
-        gc.collect()
-
-    def tearDown(self):
-        reap_children()
-
-    def call(self, function, *args, **kwargs):
-        if callable(function):
-            if '_exc' in kwargs:
-                exc = kwargs.pop('_exc')
-                self.assertRaises(exc, function, *args, **kwargs)
-            else:
-                try:
-                    function(*args, **kwargs)
-                except psutil.Error:
-                    pass
-        else:
-            meth = getattr(self.proc, function)
-            if '_exc' in kwargs:
-                exc = kwargs.pop('_exc')
-                self.assertRaises(exc, meth, *args, **kwargs)
-            else:
-                try:
-                    meth(*args, **kwargs)
-                except psutil.Error:
-                    pass
-
-    @skip_if_linux()
-    def test_name(self):
-        self.execute('name')
-
-    @skip_if_linux()
-    def test_cmdline(self):
-        self.execute('cmdline')
-
-    @skip_if_linux()
-    def test_exe(self):
-        self.execute('exe')
-
-    @skip_if_linux()
-    def test_ppid(self):
-        self.execute('ppid')
-
-    @unittest.skipUnless(POSIX, "POSIX only")
-    @skip_if_linux()
-    def test_uids(self):
-        self.execute('uids')
-
-    @unittest.skipUnless(POSIX, "POSIX only")
-    @skip_if_linux()
-    def test_gids(self):
-        self.execute('gids')
-
-    @skip_if_linux()
-    def test_status(self):
-        self.execute('status')
-
-    def test_nice_get(self):
-        self.execute('nice')
-
-    def test_nice_set(self):
-        niceness = psutil.Process().nice()
-        self.execute('nice', niceness)
-
-    @unittest.skipUnless(hasattr(psutil.Process, 'ionice'),
-                         "Linux and Windows Vista only")
-    def test_ionice_get(self):
-        self.execute('ionice')
-
-    @unittest.skipUnless(hasattr(psutil.Process, 'ionice'),
-                         "Linux and Windows Vista only")
-    def test_ionice_set(self):
-        if WINDOWS:
-            value = psutil.Process().ionice()
-            self.execute('ionice', value)
-        else:
-            from psutil._pslinux import cext
-            self.execute('ionice', psutil.IOPRIO_CLASS_NONE)
-            fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0)
-            self.execute_w_exc(OSError, fun)
-
-    @unittest.skipIf(OSX or SUNOS, "feature not supported on this platform")
-    @skip_if_linux()
-    def test_io_counters(self):
-        self.execute('io_counters')
-
-    @unittest.skipUnless(WINDOWS, "not worth being tested on posix")
-    def test_username(self):
-        self.execute('username')
-
-    @skip_if_linux()
-    def test_create_time(self):
-        self.execute('create_time')
-
-    @skip_if_linux()
-    def test_num_threads(self):
-        self.execute('num_threads')
-
-    @unittest.skipUnless(WINDOWS, "Windows only")
-    def test_num_handles(self):
-        self.execute('num_handles')
-
-    @unittest.skipUnless(POSIX, "POSIX only")
-    @skip_if_linux()
-    def test_num_fds(self):
-        self.execute('num_fds')
-
-    @skip_if_linux()
-    def test_threads(self):
-        self.execute('threads')
-
-    @skip_if_linux()
-    def test_cpu_times(self):
-        self.execute('cpu_times')
-
-    @skip_if_linux()
-    def test_memory_info(self):
-        self.execute('memory_info')
-
-    @skip_if_linux()
-    def test_memory_info_ex(self):
-        self.execute('memory_info_ex')
-
-    @unittest.skipUnless(POSIX, "POSIX only")
-    @skip_if_linux()
-    def test_terminal(self):
-        self.execute('terminal')
-
-    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
-                     "not worth being tested on POSIX (pure python)")
-    def test_resume(self):
-        self.execute('resume')
-
-    @skip_if_linux()
-    def test_cwd(self):
-        self.execute('cwd')
-
-    @unittest.skipUnless(WINDOWS or LINUX or BSD,
-                         "Windows or Linux or BSD only")
-    def test_cpu_affinity_get(self):
-        self.execute('cpu_affinity')
-
-    @unittest.skipUnless(WINDOWS or LINUX or BSD,
-                         "Windows or Linux or BSD only")
-    def test_cpu_affinity_set(self):
-        affinity = psutil.Process().cpu_affinity()
-        self.execute('cpu_affinity', affinity)
-        if not TRAVIS:
-            self.execute_w_exc(ValueError, 'cpu_affinity', [-1])
-
-    @skip_if_linux()
-    def test_open_files(self):
-        safe_remove(TESTFN)  # needed after UNIX socket test has run
-        with open(TESTFN, 'w'):
-            self.execute('open_files')
-
-    # OSX implementation is unbelievably slow
-    @unittest.skipIf(OSX, "OSX implementation is too slow")
-    @skip_if_linux()
-    def test_memory_maps(self):
-        self.execute('memory_maps')
-
-    @unittest.skipUnless(LINUX, "Linux only")
-    @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
-                         "only available on Linux >= 2.6.36")
-    def test_rlimit_get(self):
-        self.execute('rlimit', psutil.RLIMIT_NOFILE)
-
-    @unittest.skipUnless(LINUX, "Linux only")
-    @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
-                         "only available on Linux >= 2.6.36")
-    def test_rlimit_set(self):
-        limit = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)
-        self.execute('rlimit', psutil.RLIMIT_NOFILE, limit)
-        self.execute_w_exc(OSError, 'rlimit', -1)
-
-    @skip_if_linux()
-    # Windows implementation is based on a single system-wide function
-    @unittest.skipIf(WINDOWS, "tested later")
-    def test_connections(self):
-        def create_socket(family, type):
-            sock = socket.socket(family, type)
-            sock.bind(('', 0))
-            if type == socket.SOCK_STREAM:
-                sock.listen(1)
-            return sock
-
-        socks = []
-        socks.append(create_socket(socket.AF_INET, socket.SOCK_STREAM))
-        socks.append(create_socket(socket.AF_INET, socket.SOCK_DGRAM))
-        if supports_ipv6():
-            socks.append(create_socket(socket.AF_INET6, socket.SOCK_STREAM))
-            socks.append(create_socket(socket.AF_INET6, socket.SOCK_DGRAM))
-        if hasattr(socket, 'AF_UNIX'):
-            safe_remove(TESTFN)
-            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            s.bind(TESTFN)
-            s.listen(1)
-            socks.append(s)
-        kind = 'all'
-        # TODO: UNIX sockets are temporarily implemented by parsing
-        # 'pfiles' cmd  output; we don't want that part of the code to
-        # be executed.
-        if SUNOS:
-            kind = 'inet'
-        try:
-            self.execute('connections', kind=kind)
-        finally:
-            for s in socks:
-                s.close()
-
-
-p = get_test_subprocess()
-DEAD_PROC = psutil.Process(p.pid)
-DEAD_PROC.kill()
-DEAD_PROC.wait()
-del p
-
-
-class TestProcessObjectLeaksZombie(TestProcessObjectLeaks):
-    """Same as above but looks for leaks occurring when dealing with
-    zombie processes raising NoSuchProcess exception.
-    """
-    proc = DEAD_PROC
-
-    def call(self, *args, **kwargs):
-        try:
-            TestProcessObjectLeaks.call(self, *args, **kwargs)
-        except psutil.NoSuchProcess:
-            pass
-
-    if not POSIX:
-        def test_kill(self):
-            self.execute('kill')
-
-        def test_terminate(self):
-            self.execute('terminate')
-
-        def test_suspend(self):
-            self.execute('suspend')
-
-        def test_resume(self):
-            self.execute('resume')
-
-        def test_wait(self):
-            self.execute('wait')
-
-
-class TestModuleFunctionsLeaks(Base):
-    """Test leaks of psutil module functions."""
-
-    def setUp(self):
-        gc.collect()
-
-    def call(self, function, *args, **kwargs):
-        fun = getattr(psutil, function)
-        fun(*args, **kwargs)
-
-    @skip_if_linux()
-    def test_cpu_count_logical(self):
-        psutil.cpu_count = psutil._psplatform.cpu_count_logical
-        self.execute('cpu_count')
-
-    @skip_if_linux()
-    def test_cpu_count_physical(self):
-        psutil.cpu_count = psutil._psplatform.cpu_count_physical
-        self.execute('cpu_count')
-
-    @skip_if_linux()
-    def test_boot_time(self):
-        self.execute('boot_time')
-
-    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
-                     "not worth being tested on POSIX (pure python)")
-    def test_pid_exists(self):
-        self.execute('pid_exists', os.getpid())
-
-    def test_virtual_memory(self):
-        self.execute('virtual_memory')
-
-    # TODO: remove this skip when this gets fixed
-    @unittest.skipIf(SUNOS,
-                     "not worth being tested on SUNOS (uses a subprocess)")
-    def test_swap_memory(self):
-        self.execute('swap_memory')
-
-    @skip_if_linux()
-    def test_cpu_times(self):
-        self.execute('cpu_times')
-
-    @skip_if_linux()
-    def test_per_cpu_times(self):
-        self.execute('cpu_times', percpu=True)
-
-    @unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
-                     "not worth being tested on POSIX (pure python)")
-    def test_disk_usage(self):
-        self.execute('disk_usage', '.')
-
-    def test_disk_partitions(self):
-        self.execute('disk_partitions')
-
-    @skip_if_linux()
-    def test_net_io_counters(self):
-        self.execute('net_io_counters')
-
-    @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
-                     '/proc/diskstats not available on this Linux version')
-    @skip_if_linux()
-    def test_disk_io_counters(self):
-        self.execute('disk_io_counters')
-
-    # XXX - on Windows this produces a false positive
-    @unittest.skipIf(WINDOWS, "XXX produces a false positive on Windows")
-    def test_users(self):
-        self.execute('users')
-
-    @unittest.skipIf(LINUX,
-                     "not worth being tested on Linux (pure python)")
-    def test_net_connections(self):
-        self.execute('net_connections')
-
-    def test_net_if_addrs(self):
-        self.execute('net_if_addrs')
-
-    @unittest.skipIf(TRAVIS, "EPERM on travis")
-    def test_net_if_stats(self):
-        self.execute('net_if_stats')
-
-
-def main():
-    test_suite = unittest.TestSuite()
-    tests = [TestProcessObjectLeaksZombie,
-             TestProcessObjectLeaks,
-             TestModuleFunctionsLeaks]
-    for test in tests:
-        test_suite.addTest(unittest.makeSuite(test))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
deleted file mode 100644
--- a/third_party/python/psutil/test/test_psutil.py
+++ /dev/null
@@ -1,3013 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""
-psutil test suite. Run it with:
-$ make test
-
-If you're on Python < 2.7 unittest2 module must be installed first:
-https://pypi.python.org/pypi/unittest2
-"""
-
-from __future__ import division
-
-import ast
-import atexit
-import collections
-import contextlib
-import datetime
-import errno
-import functools
-import imp
-import json
-import os
-import pickle
-import pprint
-import re
-import select
-import shutil
-import signal
-import socket
-import stat
-import subprocess
-import sys
-import tempfile
-import textwrap
-import threading
-import time
-import traceback
-import types
-import warnings
-from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
-try:
-    import ipaddress  # python >= 3.3
-except ImportError:
-    ipaddress = None
-try:
-    from unittest import mock  # py3
-except ImportError:
-    import mock  # requires "pip install mock"
-
-import psutil
-from psutil._compat import PY3, callable, long, unicode
-
-if sys.version_info < (2, 7):
-    import unittest2 as unittest  # https://pypi.python.org/pypi/unittest2
-else:
-    import unittest
-if sys.version_info >= (3, 4):
-    import enum
-else:
-    enum = None
-
-
-# ===================================================================
-# --- Constants
-# ===================================================================
-
-# conf for retry_before_failing() decorator
-NO_RETRIES = 10
-# bytes tolerance for OS memory related tests
-TOLERANCE = 500 * 1024  # 500KB
-# the timeout used in functions which have to wait
-GLOBAL_TIMEOUT = 3
-
-AF_INET6 = getattr(socket, "AF_INET6")
-AF_UNIX = getattr(socket, "AF_UNIX", None)
-PYTHON = os.path.realpath(sys.executable)
-DEVNULL = open(os.devnull, 'r+')
-TESTFN = os.path.join(os.getcwd(), "$testfile")
-TESTFN_UNICODE = TESTFN + "ƒőő"
-TESTFILE_PREFIX = 'psutil-test-suite-'
-if not PY3:
-    try:
-        TESTFN_UNICODE = unicode(TESTFN_UNICODE, sys.getfilesystemencoding())
-    except UnicodeDecodeError:
-        TESTFN_UNICODE = TESTFN + "???"
-
-EXAMPLES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
-                               '..', 'examples'))
-
-POSIX = os.name == 'posix'
-WINDOWS = os.name == 'nt'
-if WINDOWS:
-    WIN_VISTA = (6, 0, 0)
-LINUX = sys.platform.startswith("linux")
-OSX = sys.platform.startswith("darwin")
-BSD = sys.platform.startswith("freebsd")
-SUNOS = sys.platform.startswith("sunos")
-VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil)
-                       if x.startswith('STATUS_')]
-# whether we're running this test suite on Travis (https://travis-ci.org/)
-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'))
-
-if TRAVIS or 'tox' in sys.argv[0]:
-    import ipaddress
-if TRAVIS or APPVEYOR:
-    GLOBAL_TIMEOUT = GLOBAL_TIMEOUT * 4
-
-
-# ===================================================================
-# --- Utility functions
-# ===================================================================
-
-def cleanup():
-    reap_children(search_all=True)
-    safe_remove(TESTFN)
-    try:
-        safe_rmdir(TESTFN_UNICODE)
-    except UnicodeEncodeError:
-        pass
-    for path in _testfiles:
-        safe_remove(path)
-
-atexit.register(cleanup)
-atexit.register(lambda: DEVNULL.close())
-
-
-_subprocesses_started = set()
-
-
-def get_test_subprocess(cmd=None, stdout=DEVNULL, stderr=DEVNULL,
-                        stdin=DEVNULL, wait=False):
-    """Return a subprocess.Popen object to use in tests.
-    By default stdout and stderr are redirected to /dev/null and the
-    python interpreter is used as test process.
-    If 'wait' is True attemps to make sure the process is in a
-    reasonably initialized state.
-    """
-    if cmd is None:
-        pyline = ""
-        if wait:
-            pyline += "open(r'%s', 'w'); " % TESTFN
-        pyline += "import time; time.sleep(60);"
-        cmd_ = [PYTHON, "-c", pyline]
-    else:
-        cmd_ = cmd
-    sproc = subprocess.Popen(cmd_, stdout=stdout, stderr=stderr, stdin=stdin)
-    if wait:
-        if cmd is None:
-            stop_at = time.time() + 3
-            while stop_at > time.time():
-                if os.path.exists(TESTFN):
-                    break
-                time.sleep(0.001)
-            else:
-                warn("couldn't make sure test file was actually created")
-        else:
-            wait_for_pid(sproc.pid)
-    _subprocesses_started.add(psutil.Process(sproc.pid))
-    return sproc
-
-
-_testfiles = []
-
-
-def pyrun(src):
-    """Run python code 'src' in a separate interpreter.
-    Return interpreter subprocess.
-    """
-    if PY3:
-        src = bytes(src, 'ascii')
-    with tempfile.NamedTemporaryFile(
-            prefix=TESTFILE_PREFIX, delete=False) as f:
-        _testfiles.append(f.name)
-        f.write(src)
-        f.flush()
-        subp = get_test_subprocess([PYTHON, f.name], stdout=None,
-                                   stderr=None)
-        wait_for_pid(subp.pid)
-        return subp
-
-
-def warn(msg):
-    """Raise a warning msg."""
-    warnings.warn(msg, UserWarning)
-
-
-def sh(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
-    """run cmd in a subprocess and return its output.
-    raises RuntimeError on error.
-    """
-    p = subprocess.Popen(cmdline, shell=True, stdout=stdout, stderr=stderr)
-    stdout, stderr = p.communicate()
-    if p.returncode != 0:
-        raise RuntimeError(stderr)
-    if stderr:
-        warn(stderr)
-    if PY3:
-        stdout = str(stdout, sys.stdout.encoding)
-    return stdout.strip()
-
-
-def which(program):
-    """Same as UNIX which command. Return None on command not found."""
-    def is_exe(fpath):
-        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-    fpath, fname = os.path.split(program)
-    if fpath:
-        if is_exe(program):
-            return program
-    else:
-        for path in os.environ["PATH"].split(os.pathsep):
-            exe_file = os.path.join(path, program)
-            if is_exe(exe_file):
-                return exe_file
-    return None
-
-
-if POSIX:
-    def get_kernel_version():
-        """Return a tuple such as (2, 6, 36)."""
-        s = ""
-        uname = os.uname()[2]
-        for c in uname:
-            if c.isdigit() or c == '.':
-                s += c
-            else:
-                break
-        if not s:
-            raise ValueError("can't parse %r" % uname)
-        minor = 0
-        micro = 0
-        nums = s.split('.')
-        major = int(nums[0])
-        if len(nums) >= 2:
-            minor = int(nums[1])
-        if len(nums) >= 3:
-            micro = int(nums[2])
-        return (major, minor, micro)
-
-
-if LINUX:
-    RLIMIT_SUPPORT = get_kernel_version() >= (2, 6, 36)
-else:
-    RLIMIT_SUPPORT = False
-
-
-def wait_for_pid(pid, timeout=GLOBAL_TIMEOUT):
-    """Wait for pid to show up in the process list then return.
-    Used in the test suite to give time the sub process to initialize.
-    """
-    raise_at = time.time() + timeout
-    while True:
-        if pid in psutil.pids():
-            # give it one more iteration to allow full initialization
-            time.sleep(0.01)
-            return
-        time.sleep(0.0001)
-        if time.time() >= raise_at:
-            raise RuntimeError("Timed out")
-
-
-def wait_for_file(fname, timeout=GLOBAL_TIMEOUT, delete_file=True):
-    """Wait for a file to be written on disk."""
-    stop_at = time.time() + 3
-    while time.time() < stop_at:
-        try:
-            with open(fname, "r") as f:
-                data = f.read()
-            if not data:
-                continue
-            if delete_file:
-                os.remove(fname)
-            return data
-        except IOError:
-            time.sleep(0.001)
-    raise RuntimeError("timed out (couldn't read file)")
-
-
-def reap_children(search_all=False):
-    """Kill any subprocess started by this test suite and ensure that
-    no zombies stick around to hog resources and create problems when
-    looking for refleaks.
-    """
-    global _subprocesses_started
-    procs = _subprocesses_started.copy()
-    if search_all:
-        this_process = psutil.Process()
-        for p in this_process.children(recursive=True):
-            procs.add(p)
-    for p in procs:
-        try:
-            p.terminate()
-        except psutil.NoSuchProcess:
-            pass
-    gone, alive = psutil.wait_procs(procs, timeout=GLOBAL_TIMEOUT)
-    for p in alive:
-        warn("couldn't terminate process %s" % p)
-        try:
-            p.kill()
-        except psutil.NoSuchProcess:
-            pass
-    _, alive = psutil.wait_procs(alive, timeout=GLOBAL_TIMEOUT)
-    if alive:
-        warn("couldn't not kill processes %s" % str(alive))
-    _subprocesses_started = set(alive)
-
-
-def check_ip_address(addr, family):
-    """Attempts to check IP address's validity."""
-    if enum and PY3:
-        assert isinstance(family, enum.IntEnum), family
-    if family == AF_INET:
-        octs = [int(x) for x in addr.split('.')]
-        assert len(octs) == 4, addr
-        for num in octs:
-            assert 0 <= num <= 255, addr
-        if ipaddress:
-            if not PY3:
-                addr = unicode(addr)
-            ipaddress.IPv4Address(addr)
-    elif family == AF_INET6:
-        assert isinstance(addr, str), addr
-        if ipaddress:
-            if not PY3:
-                addr = unicode(addr)
-            ipaddress.IPv6Address(addr)
-    elif family == psutil.AF_LINK:
-        assert re.match('([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr
-    else:
-        raise ValueError("unknown family %r", family)
-
-
-def check_connection_ntuple(conn):
-    """Check validity of a connection namedtuple."""
-    valid_conn_states = [getattr(psutil, x) for x in dir(psutil) if
-                         x.startswith('CONN_')]
-    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
-    assert conn.type in (SOCK_STREAM, SOCK_DGRAM), repr(conn.type)
-    assert conn.family in (AF_INET, AF_INET6, AF_UNIX), repr(conn.family)
-    assert conn.status in valid_conn_states, conn.status
-
-    # check IP address and port sanity
-    for addr in (conn.laddr, conn.raddr):
-        if not addr:
-            continue
-        if conn.family in (AF_INET, AF_INET6):
-            assert isinstance(addr, tuple), addr
-            ip, port = addr
-            assert isinstance(port, int), port
-            assert 0 <= port <= 65535, port
-            check_ip_address(ip, conn.family)
-        elif conn.family == AF_UNIX:
-            assert isinstance(addr, (str, None)), addr
-        else:
-            raise ValueError("unknown family %r", 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 not conn.raddr, repr(conn.raddr)
-        assert conn.status == psutil.CONN_NONE, conn.status
-
-    if getattr(conn, 'fd', -1) != -1:
-        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
-
-
-def safe_remove(file):
-    "Convenience function for removing temporary test files"
-    try:
-        os.remove(file)
-    except OSError as err:
-        if err.errno != errno.ENOENT:
-            # file is being used by another process
-            if WINDOWS and isinstance(err, WindowsError) and err.errno == 13:
-                return
-            raise
-
-
-def safe_rmdir(dir):
-    "Convenience function for removing temporary test directories"
-    try:
-        os.rmdir(dir)
-    except OSError as err:
-        if err.errno != errno.ENOENT:
-            raise
-
-
-def call_until(fun, expr, timeout=GLOBAL_TIMEOUT):
-    """Keep calling function for timeout secs and exit if eval()
-    expression is True.
-    """
-    stop_at = time.time() + timeout
-    while time.time() < stop_at:
-        ret = fun()
-        if eval(expr):
-            return ret
-        time.sleep(0.001)
-    raise RuntimeError('timed out (ret=%r)' % ret)
-
-
-def retry_before_failing(ntimes=None):
-    """Decorator which runs a test function and retries N times before
-    actually failing.
-    """
-    def decorator(fun):
-        @functools.wraps(fun)
-        def wrapper(*args, **kwargs):
-            for x in range(ntimes or NO_RETRIES):
-                try:
-                    return fun(*args, **kwargs)
-                except AssertionError:
-                    pass
-            raise
-        return wrapper
-    return decorator
-
-
-def skip_on_access_denied(only_if=None):
-    """Decorator to Ignore AccessDenied exceptions."""
-    def decorator(fun):
-        @functools.wraps(fun)
-        def wrapper(*args, **kwargs):
-            try:
-                return fun(*args, **kwargs)
-            except psutil.AccessDenied:
-                if only_if is not None:
-                    if not only_if:
-                        raise
-                msg = "%r was skipped because it raised AccessDenied" \
-                      % fun.__name__
-                raise unittest.SkipTest(msg)
-        return wrapper
-    return decorator
-
-
-def skip_on_not_implemented(only_if=None):
-    """Decorator to Ignore NotImplementedError exceptions."""
-    def decorator(fun):
-        @functools.wraps(fun)
-        def wrapper(*args, **kwargs):
-            try:
-                return fun(*args, **kwargs)
-            except NotImplementedError:
-                if only_if is not None:
-                    if not only_if:
-                        raise
-                msg = "%r was skipped because it raised NotImplementedError" \
-                      % fun.__name__
-                raise unittest.SkipTest(msg)
-        return wrapper
-    return decorator
-
-
-def supports_ipv6():
-    """Return True if IPv6 is supported on this platform."""
-    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
-        return False
-    sock = None
-    try:
-        sock = socket.socket(AF_INET6, SOCK_STREAM)
-        sock.bind(("::1", 0))
-    except (socket.error, socket.gaierror):
-        return False
-    else:
-        return True
-    finally:
-        if sock is not None:
-            sock.close()
-
-
-if WINDOWS:
-    def get_winver():
-        wv = sys.getwindowsversion()
-        if hasattr(wv, 'service_pack_major'):  # python >= 2.7
-            sp = wv.service_pack_major or 0
-        else:
-            r = re.search("\s\d$", wv[4])
-            if r:
-                sp = int(r.group(0))
-            else:
-                sp = 0
-        return (wv[0], wv[1], sp)
-
-
-class ThreadTask(threading.Thread):
-    """A thread object used for running process thread tests."""
-
-    def __init__(self):
-        threading.Thread.__init__(self)
-        self._running = False
-        self._interval = None
-        self._flag = threading.Event()
-
-    def __repr__(self):
-        name = self.__class__.__name__
-        return '<%s running=%s at %#x>' % (name, self._running, id(self))
-
-    def start(self, interval=0.001):
-        """Start thread and keep it running until an explicit
-        stop() request. Polls for shutdown every 'timeout' seconds.
-        """
-        if self._running:
-            raise ValueError("already started")
-        self._interval = interval
-        threading.Thread.start(self)
-        self._flag.wait()
-
-    def run(self):
-        self._running = True
-        self._flag.set()
-        while self._running:
-            time.sleep(self._interval)
-
-    def stop(self):
-        """Stop thread execution and and waits until it is stopped."""
-        if not self._running:
-            raise ValueError("already stopped")
-        self._running = False
-        self.join()
-
-
-# ===================================================================
-# --- System-related API tests
-# ===================================================================
-
-class TestSystemAPIs(unittest.TestCase):
-    """Tests for system-related APIs."""
-
-    def setUp(self):
-        safe_remove(TESTFN)
-
-    def tearDown(self):
-        reap_children()
-
-    def test_process_iter(self):
-        self.assertIn(os.getpid(), [x.pid for x in psutil.process_iter()])
-        sproc = get_test_subprocess()
-        self.assertIn(sproc.pid, [x.pid for x in psutil.process_iter()])
-        p = psutil.Process(sproc.pid)
-        p.kill()
-        p.wait()
-        self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()])
-
-    def test_wait_procs(self):
-        def callback(p):
-            l.append(p.pid)
-
-        l = []
-        sproc1 = get_test_subprocess()
-        sproc2 = get_test_subprocess()
-        sproc3 = get_test_subprocess()
-        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
-        self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1)
-        self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1)
-        t = time.time()
-        gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback)
-
-        self.assertLess(time.time() - t, 0.5)
-        self.assertEqual(gone, [])
-        self.assertEqual(len(alive), 3)
-        self.assertEqual(l, [])
-        for p in alive:
-            self.assertFalse(hasattr(p, 'returncode'))
-
-        @retry_before_failing(30)
-        def test(procs, callback):
-            gone, alive = psutil.wait_procs(procs, timeout=0.03,
-                                            callback=callback)
-            self.assertEqual(len(gone), 1)
-            self.assertEqual(len(alive), 2)
-            return gone, alive
-
-        sproc3.terminate()
-        gone, alive = test(procs, callback)
-        self.assertIn(sproc3.pid, [x.pid for x in gone])
-        if POSIX:
-            self.assertEqual(gone.pop().returncode, signal.SIGTERM)
-        else:
-            self.assertEqual(gone.pop().returncode, 1)
-        self.assertEqual(l, [sproc3.pid])
-        for p in alive:
-            self.assertFalse(hasattr(p, 'returncode'))
-
-        @retry_before_failing(30)
-        def test(procs, callback):
-            gone, alive = psutil.wait_procs(procs, timeout=0.03,
-                                            callback=callback)
-            self.assertEqual(len(gone), 3)
-            self.assertEqual(len(alive), 0)
-            return gone, alive
-
-        sproc1.terminate()
-        sproc2.terminate()
-        gone, alive = test(procs, callback)
-        self.assertEqual(set(l), set([sproc1.pid, sproc2.pid, sproc3.pid]))
-        for p in gone:
-            self.assertTrue(hasattr(p, 'returncode'))
-
-    def test_wait_procs_no_timeout(self):
-        sproc1 = get_test_subprocess()
-        sproc2 = get_test_subprocess()
-        sproc3 = get_test_subprocess()
-        procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
-        for p in procs:
-            p.terminate()
-        gone, alive = psutil.wait_procs(procs)
-
-    def test_boot_time(self):
-        bt = psutil.boot_time()
-        self.assertIsInstance(bt, float)
-        self.assertGreater(bt, 0)
-        self.assertLess(bt, time.time())
-
-    @unittest.skipUnless(POSIX, 'posix only')
-    def test_PAGESIZE(self):
-        # pagesize is used internally to perform different calculations
-        # and it's determined by using SC_PAGE_SIZE; make sure
-        # getpagesize() returns the same value.
-        import resource
-        self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize())
-
-    def test_virtual_memory(self):
-        mem = psutil.virtual_memory()
-        assert mem.total > 0, mem
-        assert mem.available > 0, mem
-        assert 0 <= mem.percent <= 100, mem
-        assert mem.used > 0, mem
-        assert mem.free >= 0, mem
-        for name in mem._fields:
-            value = getattr(mem, name)
-            if name != 'percent':
-                self.assertIsInstance(value, (int, long))
-            if name != 'total':
-                if not value >= 0:
-                    self.fail("%r < 0 (%s)" % (name, value))
-                if value > mem.total:
-                    self.fail("%r > total (total=%s, %s=%s)"
-                              % (name, mem.total, name, value))
-
-    def test_swap_memory(self):
-        mem = psutil.swap_memory()
-        assert mem.total >= 0, mem
-        assert mem.used >= 0, mem
-        if mem.total > 0:
-            # likely a system with no swap partition
-            assert mem.free > 0, mem
-        else:
-            assert mem.free == 0, mem
-        assert 0 <= mem.percent <= 100, mem
-        assert mem.sin >= 0, mem
-        assert mem.sout >= 0, mem
-
-    def test_pid_exists(self):
-        sproc = get_test_subprocess(wait=True)
-        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())
-        # pid 0
-        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):
-        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):
-        logical = psutil.cpu_count()
-        self.assertEqual(logical, len(psutil.cpu_times(percpu=True)))
-        self.assertGreaterEqual(logical, 1)
-        #
-        if LINUX:
-            with open("/proc/cpuinfo") as fd:
-                cpuinfo_data = fd.read()
-            if "physical id" not in cpuinfo_data:
-                raise unittest.SkipTest("cpuinfo doesn't include physical id")
-        physical = psutil.cpu_count(logical=False)
-        self.assertGreaterEqual(physical, 1)
-        self.assertGreaterEqual(logical, physical)
-
-    def test_sys_cpu_times(self):
-        total = 0
-        times = psutil.cpu_times()
-        sum(times)
-        for cp_time in times:
-            self.assertIsInstance(cp_time, float)
-            self.assertGreaterEqual(cp_time, 0.0)
-            total += cp_time
-        self.assertEqual(total, sum(times))
-        str(times)
-        if not WINDOWS:
-            # CPU times are always supposed to increase over time or
-            # remain the same but never go backwards, see:
-            # https://github.com/giampaolo/psutil/issues/392
-            last = psutil.cpu_times()
-            for x in range(100):
-                new = psutil.cpu_times()
-                for field in new._fields:
-                    new_t = getattr(new, field)
-                    last_t = getattr(last, field)
-                    self.assertGreaterEqual(new_t, last_t,
-                                            msg="%s %s" % (new_t, last_t))
-                last = new
-
-    def test_sys_cpu_times2(self):
-        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)
-
-    def test_sys_per_cpu_times(self):
-        for times in psutil.cpu_times(percpu=True):
-            total = 0
-            sum(times)
-            for cp_time in times:
-                self.assertIsInstance(cp_time, float)
-                self.assertGreaterEqual(cp_time, 0.0)
-                total += cp_time
-            self.assertEqual(total, sum(times))
-            str(times)
-        self.assertEqual(len(psutil.cpu_times(percpu=True)[0]),
-                         len(psutil.cpu_times(percpu=False)))
-
-        # Note: in theory CPU times are always supposed to increase over
-        # time or remain the same but never go backwards. In practice
-        # sometimes this is not the case.
-        # This issue seemd to be afflict Windows:
-        # https://github.com/giampaolo/psutil/issues/392
-        # ...but it turns out also Linux (rarely) behaves the same.
-        # last = psutil.cpu_times(percpu=True)
-        # for x in range(100):
-        #     new = psutil.cpu_times(percpu=True)
-        #     for index in range(len(new)):
-        #         newcpu = new[index]
-        #         lastcpu = last[index]
-        #         for field in newcpu._fields:
-        #             new_t = getattr(newcpu, field)
-        #             last_t = getattr(lastcpu, field)
-        #             self.assertGreaterEqual(
-        #                 new_t, last_t, msg="%s %s" % (lastcpu, newcpu))
-        #     last = new
-
-    def test_sys_per_cpu_times_2(self):
-        tot1 = psutil.cpu_times(percpu=True)
-        stop_at = time.time() + 0.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()
-
-    def _test_cpu_percent(self, percent, last_ret, new_ret):
-        try:
-            self.assertIsInstance(percent, float)
-            self.assertGreaterEqual(percent, 0.0)
-            self.assertIsNot(percent, -0.0)
-            self.assertLessEqual(percent, 100.0 * psutil.cpu_count())
-        except AssertionError as err:
-            raise AssertionError("\n%s\nlast=%s\nnew=%s" % (
-                err, pprint.pformat(last_ret), pprint.pformat(new_ret)))
-
-    def test_sys_cpu_percent(self):
-        last = psutil.cpu_percent(interval=0.001)
-        for x in range(100):
-            new = psutil.cpu_percent(interval=None)
-            self._test_cpu_percent(new, last, new)
-            last = new
-
-    def test_sys_per_cpu_percent(self):
-        last = psutil.cpu_percent(interval=0.001, percpu=True)
-        self.assertEqual(len(last), psutil.cpu_count())
-        for x in range(100):
-            new = psutil.cpu_percent(interval=None, percpu=True)
-            for percent in new:
-                self._test_cpu_percent(percent, last, new)
-            last = new
-
-    def test_sys_cpu_times_percent(self):
-        last = psutil.cpu_times_percent(interval=0.001)
-        for x in range(100):
-            new = psutil.cpu_times_percent(interval=None)
-            for percent in new:
-                self._test_cpu_percent(percent, last, new)
-            self._test_cpu_percent(sum(new), last, new)
-            last = new
-
-    def test_sys_per_cpu_times_percent(self):
-        last = psutil.cpu_times_percent(interval=0.001, percpu=True)
-        self.assertEqual(len(last), psutil.cpu_count())
-        for x in range(100):
-            new = psutil.cpu_times_percent(interval=None, percpu=True)
-            for cpu in new:
-                for percent in cpu:
-                    self._test_cpu_percent(percent, last, new)
-                self._test_cpu_percent(sum(cpu), last, new)
-            last = new
-
-    def test_sys_per_cpu_times_percent_negative(self):
-        # see: https://github.com/giampaolo/psutil/issues/645
-        psutil.cpu_times_percent(percpu=True)
-        zero_times = [x._make([0 for x in range(len(x._fields))])
-                      for x in psutil.cpu_times(percpu=True)]
-        with mock.patch('psutil.cpu_times', return_value=zero_times):
-            for cpu in psutil.cpu_times_percent(percpu=True):
-                for percent in cpu:
-                    self._test_cpu_percent(percent, None, None)
-
-    @unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
-                     "os.statvfs() function not available on this platform")
-    def test_disk_usage(self):
-        usage = psutil.disk_usage(os.getcwd())
-        assert usage.total > 0, usage
-        assert usage.used > 0, usage
-        assert usage.free > 0, usage
-        assert usage.total > usage.used, usage
-        assert usage.total > usage.free, usage
-        assert 0 <= usage.percent <= 100, usage.percent
-        if hasattr(shutil, 'disk_usage'):
-            # py >= 3.3, see: http://bugs.python.org/issue12442
-            shutil_usage = shutil.disk_usage(os.getcwd())
-            tolerance = 5 * 1024 * 1024  # 5MB
-            self.assertEqual(usage.total, shutil_usage.total)
-            self.assertAlmostEqual(usage.free, shutil_usage.free,
-                                   delta=tolerance)
-            self.assertAlmostEqual(usage.used, shutil_usage.used,
-                                   delta=tolerance)
-
-        # if path does not exist OSError ENOENT is expected across
-        # all platforms
-        fname = tempfile.mktemp()
-        try:
-            psutil.disk_usage(fname)
-        except OSError as err:
-            if err.args[0] != errno.ENOENT:
-                raise
-        else:
-            self.fail("OSError not raised")
-
-    @unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
-                     "os.statvfs() function not available on this platform")
-    def test_disk_usage_unicode(self):
-        # see: https://github.com/giampaolo/psutil/issues/416
-        # XXX this test is not really reliable as it always fails on
-        # Python 3.X (2.X is fine)
-        try:
-            safe_rmdir(TESTFN_UNICODE)
-            os.mkdir(TESTFN_UNICODE)
-            psutil.disk_usage(TESTFN_UNICODE)
-            safe_rmdir(TESTFN_UNICODE)
-        except UnicodeEncodeError:
-            pass
-
-    @unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
-                     "os.statvfs() function not available on this platform")
-    @unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
-    def test_disk_partitions(self):
-        # all = False
-        ls = psutil.disk_partitions(all=False)
-        # on travis we get:
-        #     self.assertEqual(p.cpu_affinity(), [n])
-        # AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7,... != [0]
-        self.assertTrue(ls, msg=ls)
-        for disk in ls:
-            if WINDOWS and 'cdrom' in disk.opts:
-                continue
-            if not POSIX:
-                assert os.path.exists(disk.device), disk
-            else:
-                # we cannot make any assumption about this, see:
-                # http://goo.gl/p9c43
-                disk.device
-            if SUNOS:
-                # 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 disk.fstype, disk
-            self.assertIsInstance(disk.opts, str)
-
-        # all = True
-        ls = psutil.disk_partitions(all=True)
-        self.assertTrue(ls, msg=ls)
-        for disk in psutil.disk_partitions(all=True):
-            if not WINDOWS:
-                try:
-                    os.stat(disk.mountpoint)
-                except OSError as err:
-                    # http://mail.python.org/pipermail/python-dev/
-                    #     2012-June/120787.html
-                    if err.errno not in (errno.EPERM, errno.EACCES):
-                        raise
-                else:
-                    if SUNOS:
-                        # on solaris apparently mount points can also be files
-                        assert os.path.exists(disk.mountpoint), disk
-                    else:
-                        assert os.path.isdir(disk.mountpoint), disk
-            self.assertIsInstance(disk.fstype, str)
-            self.assertIsInstance(disk.opts, str)
-
-        def find_mount_point(path):
-            path = os.path.abspath(path)
-            while not os.path.ismount(path):
-                path = os.path.dirname(path)
-            return path
-
-        mount = find_mount_point(__file__)
-        mounts = [x.mountpoint for x in psutil.disk_partitions(all=True)]
-        self.assertIn(mount, mounts)
-        psutil.disk_usage(mount)
-
-    @skip_on_access_denied()
-    def test_net_connections(self):
-        def check(cons, families, types_):
-            for conn in cons:
-                self.assertIn(conn.family, families, msg=conn)
-                if conn.family != getattr(socket, 'AF_UNIX', object()):
-                    self.assertIn(conn.type, types_, msg=conn)
-
-        from psutil._common import conn_tmap
-        for kind, groups in conn_tmap.items():
-            if SUNOS and kind == 'unix':
-                continue
-            families, types_ = groups
-            cons = psutil.net_connections(kind)
-            self.assertEqual(len(cons), len(set(cons)))
-            check(cons, families, types_)
-
-    def test_net_io_counters(self):
-        def check_ntuple(nt):
-            self.assertEqual(nt[0], nt.bytes_sent)
-            self.assertEqual(nt[1], nt.bytes_recv)
-            self.assertEqual(nt[2], nt.packets_sent)
-            self.assertEqual(nt[3], nt.packets_recv)
-            self.assertEqual(nt[4], nt.errin)
-            self.assertEqual(nt[5], nt.errout)
-            self.assertEqual(nt[6], nt.dropin)
-            self.assertEqual(nt[7], nt.dropout)
-            assert nt.bytes_sent >= 0, nt
-            assert nt.bytes_recv >= 0, nt
-            assert nt.packets_sent >= 0, nt
-            assert nt.packets_recv >= 0, nt
-            assert nt.errin >= 0, nt
-            assert nt.errout >= 0, nt
-            assert nt.dropin >= 0, nt
-            assert nt.dropout >= 0, nt
-
-        ret = psutil.net_io_counters(pernic=False)
-        check_ntuple(ret)
-        ret = psutil.net_io_counters(pernic=True)
-        self.assertNotEqual(ret, [])
-        for key in ret:
-            self.assertTrue(key)
-            check_ntuple(ret[key])
-
-    def test_net_if_addrs(self):
-        nics = psutil.net_if_addrs()
-        assert nics, nics
-
-        # Not reliable on all platforms (net_if_addrs() reports more
-        # interfaces).
-        # self.assertEqual(sorted(nics.keys()),
-        #                  sorted(psutil.net_io_counters(pernic=True).keys()))
-
-        families = set([socket.AF_INET, AF_INET6, psutil.AF_LINK])
-        for nic, addrs in nics.items():
-            self.assertEqual(len(set(addrs)), len(addrs))
-            for addr in addrs:
-                self.assertIsInstance(addr.family, int)
-                self.assertIsInstance(addr.address, str)
-                self.assertIsInstance(addr.netmask, (str, type(None)))
-                self.assertIsInstance(addr.broadcast, (str, type(None)))
-                self.assertIn(addr.family, families)
-                if sys.version_info >= (3, 4):
-                    self.assertIsInstance(addr.family, enum.IntEnum)
-                if addr.family == socket.AF_INET:
-                    s = socket.socket(addr.family)
-                    with contextlib.closing(s):
-                        s.bind((addr.address, 0))
-                elif addr.family == socket.AF_INET6:
-                    info = socket.getaddrinfo(
-                        addr.address, 0, socket.AF_INET6, socket.SOCK_STREAM,
-                        0, socket.AI_PASSIVE)[0]
-                    af, socktype, proto, canonname, sa = info
-                    s = socket.socket(af, socktype, proto)
-                    with contextlib.closing(s):
-                        s.bind(sa)
-                for ip in (addr.address, addr.netmask, addr.broadcast):
-                    if ip is not None:
-                        # TODO: skip AF_INET6 for now because I get:
-                        # AddressValueError: Only hex digits permitted in
-                        # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'
-                        if addr.family != AF_INET6:
-                            check_ip_address(ip, addr.family)
-
-        if BSD or OSX or SUNOS:
-            if hasattr(socket, "AF_LINK"):
-                self.assertEqual(psutil.AF_LINK, socket.AF_LINK)
-        elif LINUX:
-            self.assertEqual(psutil.AF_LINK, socket.AF_PACKET)
-        elif WINDOWS:
-            self.assertEqual(psutil.AF_LINK, -1)
-
-    @unittest.skipIf(TRAVIS, "EPERM on travis")
-    def test_net_if_stats(self):
-        nics = psutil.net_if_stats()
-        assert nics, nics
-        all_duplexes = (psutil.NIC_DUPLEX_FULL,
-                        psutil.NIC_DUPLEX_HALF,
-                        psutil.NIC_DUPLEX_UNKNOWN)
-        for nic, stats in nics.items():
-            isup, duplex, speed, mtu = stats
-            self.assertIsInstance(isup, bool)
-            self.assertIn(duplex, all_duplexes)
-            self.assertIn(duplex, all_duplexes)
-            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,
-                     "can't find any physical disk on Appveyor")
-    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)
-            self.assertEqual(nt[4], nt.read_time)
-            self.assertEqual(nt[5], nt.write_time)
-            assert nt.read_count >= 0, nt
-            assert nt.write_count >= 0, nt
-            assert nt.read_bytes >= 0, nt
-            assert nt.write_bytes >= 0, nt
-            assert nt.read_time >= 0, nt
-            assert nt.write_time >= 0, nt
-
-        ret = psutil.disk_io_counters(perdisk=False)
-        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_users(self):
-        users = psutil.users()
-        if not APPVEYOR:
-            self.assertNotEqual(users, [])
-        for user in users:
-            assert user.name, user
-            user.terminal
-            user.host
-            assert user.started > 0.0, user
-            datetime.datetime.fromtimestamp(user.started)
-
-
-# ===================================================================
-# --- psutil.Process class tests
-# ===================================================================
-
-class TestProcess(unittest.TestCase):
-    """Tests for psutil.Process class."""
-
-    def setUp(self):
-        safe_remove(TESTFN)
-
-    def tearDown(self):
-        reap_children()
-
-    def test_pid(self):
-        self.assertEqual(psutil.Process().pid, os.getpid())
-        sproc = get_test_subprocess()
-        self.assertEqual(psutil.Process(sproc.pid).pid, sproc.pid)
-
-    def test_kill(self):
-        sproc = get_test_subprocess(wait=True)
-        test_pid = sproc.pid
-        p = psutil.Process(test_pid)
-        p.kill()
-        sig = p.wait()
-        self.assertFalse(psutil.pid_exists(test_pid))
-        if POSIX:
-            self.assertEqual(sig, signal.SIGKILL)
-
-    def test_terminate(self):
-        sproc = get_test_subprocess(wait=True)
-        test_pid = sproc.pid
-        p = psutil.Process(test_pid)
-        p.terminate()
-        sig = p.wait()
-        self.assertFalse(psutil.pid_exists(test_pid))
-        if POSIX:
-            self.assertEqual(sig, signal.SIGTERM)
-
-    def test_send_signal(self):
-        sig = signal.SIGKILL if POSIX else signal.SIGTERM
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.send_signal(sig)
-        exit_sig = p.wait()
-        self.assertFalse(psutil.pid_exists(p.pid))
-        if POSIX:
-            self.assertEqual(exit_sig, sig)
-            #
-            sproc = get_test_subprocess()
-            p = psutil.Process(sproc.pid)
-            p.send_signal(sig)
-            with mock.patch('psutil.os.kill',
-                            side_effect=OSError(errno.ESRCH, "")) as fun:
-                with self.assertRaises(psutil.NoSuchProcess):
-                    p.send_signal(sig)
-                assert fun.called
-            #
-            sproc = get_test_subprocess()
-            p = psutil.Process(sproc.pid)
-            p.send_signal(sig)
-            with mock.patch('psutil.os.kill',
-                            side_effect=OSError(errno.EPERM, "")) as fun:
-                with self.assertRaises(psutil.AccessDenied):
-                    p.send_signal(sig)
-                assert fun.called
-
-    def test_wait(self):
-        # check exit code signal
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.kill()
-        code = p.wait()
-        if POSIX:
-            self.assertEqual(code, signal.SIGKILL)
-        else:
-            self.assertEqual(code, 0)
-        self.assertFalse(p.is_running())
-
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.terminate()
-        code = p.wait()
-        if POSIX:
-            self.assertEqual(code, signal.SIGTERM)
-        else:
-            self.assertEqual(code, 0)
-        self.assertFalse(p.is_running())
-
-        # check sys.exit() code
-        code = "import time, sys; time.sleep(0.01); sys.exit(5);"
-        sproc = get_test_subprocess([PYTHON, "-c", code])
-        p = psutil.Process(sproc.pid)
-        self.assertEqual(p.wait(), 5)
-        self.assertFalse(p.is_running())
-
-        # Test wait() issued twice.
-        # It is not supposed to raise NSP when the process is gone.
-        # On UNIX this should return None, on Windows it should keep
-        # returning the exit code.
-        sproc = get_test_subprocess([PYTHON, "-c", code])
-        p = psutil.Process(sproc.pid)
-        self.assertEqual(p.wait(), 5)
-        self.assertIn(p.wait(), (5, None))
-
-        # test timeout
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.name()
-        self.assertRaises(psutil.TimeoutExpired, p.wait, 0.01)
-
-        # timeout < 0 not allowed
-        self.assertRaises(ValueError, p.wait, -1)
-
-    # XXX why is this skipped on Windows?
-    @unittest.skipUnless(POSIX, 'skipped on Windows')
-    def test_wait_non_children(self):
-        # test wait() against processes which are not our children
-        code = "import sys;"
-        code += "from subprocess import Popen, PIPE;"
-        code += "cmd = ['%s', '-c', 'import time; time.sleep(60)'];" % PYTHON
-        code += "sp = Popen(cmd, stdout=PIPE);"
-        code += "sys.stdout.write(str(sp.pid));"
-        sproc = get_test_subprocess([PYTHON, "-c", code],
-                                    stdout=subprocess.PIPE)
-        grandson_pid = int(sproc.stdout.read())
-        grandson_proc = psutil.Process(grandson_pid)
-        try:
-            self.assertRaises(psutil.TimeoutExpired, grandson_proc.wait, 0.01)
-            grandson_proc.kill()
-            ret = grandson_proc.wait()
-            self.assertEqual(ret, None)
-        finally:
-            if grandson_proc.is_running():
-                grandson_proc.kill()
-                grandson_proc.wait()
-
-    def test_wait_timeout_0(self):
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        self.assertRaises(psutil.TimeoutExpired, p.wait, 0)
-        p.kill()
-        stop_at = time.time() + 2
-        while True:
-            try:
-                code = p.wait(0)
-            except psutil.TimeoutExpired:
-                if time.time() >= stop_at:
-                    raise
-            else:
-                break
-        if POSIX:
-            self.assertEqual(code, signal.SIGKILL)
-        else:
-            self.assertEqual(code, 0)
-        self.assertFalse(p.is_running())
-
-    def test_cpu_percent(self):
-        p = psutil.Process()
-        p.cpu_percent(interval=0.001)
-        p.cpu_percent(interval=0.001)
-        for x in range(100):
-            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)
-
-    def test_cpu_times(self):
-        times = psutil.Process().cpu_times()
-        assert (times.user > 0.0) or (times.system > 0.0), times
-        # make sure returned values can be pretty printed with strftime
-        time.strftime("%H:%M:%S", time.localtime(times.user))
-        time.strftime("%H:%M:%S", time.localtime(times.system))
-
-    # Test Process.cpu_times() against os.times()
-    # os.times() is broken on Python 2.6
-    # http://bugs.python.org/issue1040026
-    # XXX fails on OSX: not sure if it's for os.times(). We should
-    # try this with Python 2.7 and re-enable the test.
-
-    @unittest.skipUnless(sys.version_info > (2, 6, 1) and not OSX,
-                         'os.times() is not reliable on this Python version')
-    def test_cpu_times2(self):
-        user_time, kernel_time = psutil.Process().cpu_times()
-        utime, ktime = os.times()[:2]
-
-        # Use os.times()[:2] as base values to compare our results
-        # using a tolerance  of +/- 0.1 seconds.
-        # It will fail if the difference between the values is > 0.1s.
-        if (max([user_time, utime]) - min([user_time, utime])) > 0.1:
-            self.fail("expected: %s, found: %s" % (utime, user_time))
-
-        if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1:
-            self.fail("expected: %s, found: %s" % (ktime, kernel_time))
-
-    def test_create_time(self):
-        sproc = get_test_subprocess(wait=True)
-        now = time.time()
-        p = psutil.Process(sproc.pid)
-        create_time = p.create_time()
-
-        # Use time.time() as base value to compare our result using a
-        # tolerance of +/- 1 second.
-        # It will fail if the difference between the values is > 2s.
-        difference = abs(create_time - now)
-        if difference > 2:
-            self.fail("expected: %s, found: %s, difference: %s"
-                      % (now, create_time, difference))
-
-        # make sure returned value can be pretty printed with strftime
-        time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time()))
-
-    @unittest.skipIf(WINDOWS, 'Windows only')
-    def test_terminal(self):
-        terminal = psutil.Process().terminal()
-        if sys.stdin.isatty():
-            self.assertEqual(terminal, sh('tty'))
-        else:
-            assert terminal, repr(terminal)
-
-    @unittest.skipUnless(LINUX or BSD or WINDOWS,
-                         'not available on this platform')
-    @skip_on_not_implemented(only_if=LINUX)
-    def test_io_counters(self):
-        p = psutil.Process()
-        # test reads
-        io1 = p.io_counters()
-        with open(PYTHON, 'rb') as f:
-            f.read()
-        io2 = p.io_counters()
-        if not BSD:
-            assert io2.read_count > io1.read_count, (io1, io2)
-            self.assertEqual(io2.write_count, io1.write_count)
-        assert io2.read_bytes >= io1.read_bytes, (io1, io2)
-        assert io2.write_bytes >= io1.write_bytes, (io1, io2)
-        # test writes
-        io1 = p.io_counters()
-        with tempfile.TemporaryFile(prefix=TESTFILE_PREFIX) as f:
-            if PY3:
-                f.write(bytes("x" * 1000000, 'ascii'))
-            else:
-                f.write("x" * 1000000)
-        io2 = p.io_counters()
-        assert io2.write_count >= io1.write_count, (io1, io2)
-        assert io2.write_bytes >= io1.write_bytes, (io1, io2)
-        assert io2.read_count >= io1.read_count, (io1, io2)
-        assert io2.read_bytes >= io1.read_bytes, (io1, io2)
-
-    @unittest.skipUnless(LINUX or (WINDOWS and get_winver() >= WIN_VISTA),
-                         'Linux and Windows Vista only')
-    @unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
-    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)
-                #
-                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")
-                self.assertRaisesRegexp(
-                    ValueError, "can't specify value with IOPRIO_CLASS_NONE",
-                    p.ionice, psutil.IOPRIO_CLASS_NONE, 1)
-                self.assertRaisesRegexp(
-                    ValueError, "can't specify value with IOPRIO_CLASS_IDLE",
-                    p.ionice, psutil.IOPRIO_CLASS_IDLE, 1)
-                self.assertRaisesRegexp(
-                    ValueError, "'ioclass' argument must be specified",
-                    p.ionice, value=1)
-            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)
-            #
-            self.assertRaises(ValueError, p.ionice, 3)
-            self.assertRaises(TypeError, p.ionice, 2, 1)
-
-    @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
-                         "only available on Linux >= 2.6.36")
-    def test_rlimit_get(self):
-        import resource
-        p = psutil.Process(os.getpid())
-        names = [x for x in dir(psutil) if x.startswith('RLIMIT')]
-        assert names, names
-        for name in names:
-            value = getattr(psutil, name)
-            self.assertGreaterEqual(value, 0)
-            if name in dir(resource):
-                self.assertEqual(value, getattr(resource, name))
-                self.assertEqual(p.rlimit(value), resource.getrlimit(value))
-            else:
-                ret = p.rlimit(value)
-                self.assertEqual(len(ret), 2)
-                self.assertGreaterEqual(ret[0], -1)
-                self.assertGreaterEqual(ret[1], -1)
-
-    @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
-                         "only available on Linux >= 2.6.36")
-    def test_rlimit_set(self):
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))
-        self.assertEqual(p.rlimit(psutil.RLIMIT_NOFILE), (5, 5))
-        # If pid is 0 prlimit() applies to the calling process and
-        # we don't want that.
-        with self.assertRaises(ValueError):
-            psutil._psplatform.Process(0).rlimit(0)
-        with self.assertRaises(ValueError):
-            p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5))
-
-    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)
-        p = psutil.Process()
-        step1 = p.num_threads()
-
-        thread = ThreadTask()
-        thread.start()
-        try:
-            step2 = p.num_threads()
-            self.assertEqual(step2, step1 + 1)
-            thread.stop()
-        finally:
-            if thread._running:
-                thread.stop()
-
-    @unittest.skipUnless(WINDOWS, 'Windows only')
-    def test_num_handles(self):
-        # a better test is done later into test/_windows.py
-        p = psutil.Process()
-        self.assertGreater(p.num_handles(), 0)
-
-    def test_threads(self):
-        p = psutil.Process()
-        step1 = p.threads()
-
-        thread = ThreadTask()
-        thread.start()
-
-        try:
-            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])
-            # test num threads
-            thread.stop()
-        finally:
-            if thread._running:
-                thread.stop()
-
-    def test_memory_info(self):
-        p = psutil.Process()
-
-        # step 1 - get a base value to compare our results
-        rss1, vms1 = p.memory_info()
-        percent1 = p.memory_percent()
-        self.assertGreater(rss1, 0)
-        self.assertGreater(vms1, 0)
-
-        # step 2 - allocate some memory
-        memarr = [None] * 1500000
-
-        rss2, vms2 = p.memory_info()
-        percent2 = p.memory_percent()
-        # make sure that the memory usage bumped up
-        self.assertGreater(rss2, rss1)
-        self.assertGreaterEqual(vms2, vms1)  # vms might be equal
-        self.assertGreater(percent2, percent1)
-        del memarr
-
-    # def test_memory_info_ex(self):
-    # # tested later in fetch all test suite
-
-    def test_memory_maps(self):
-        p = psutil.Process()
-        maps = p.memory_maps()
-        paths = [x for x in maps]
-        self.assertEqual(len(paths), len(set(paths)))
-        ext_maps = p.memory_maps(grouped=False)
-
-        for nt in maps:
-            if not nt.path.startswith('['):
-                assert os.path.isabs(nt.path), nt.path
-                if POSIX:
-                    assert os.path.exists(nt.path), nt.path
-                else:
-                    # XXX - On Windows we have this strange behavior with
-                    # 64 bit dlls: they are visible via explorer but cannot
-                    # be accessed via os.stat() (wtf?).
-                    if '64' not in os.path.basename(nt.path):
-                        assert os.path.exists(nt.path), nt.path
-        for nt in ext_maps:
-            for fname in nt._fields:
-                value = getattr(nt, fname)
-                if fname == 'path':
-                    continue
-                elif fname in ('addr', 'perms'):
-                    assert value, value
-                else:
-                    self.assertIsInstance(value, (int, long))
-                    assert value >= 0, value
-
-    def test_memory_percent(self):
-        p = psutil.Process()
-        self.assertGreater(p.memory_percent(), 0.0)
-
-    def test_is_running(self):
-        sproc = get_test_subprocess(wait=True)
-        p = psutil.Process(sproc.pid)
-        assert p.is_running()
-        assert p.is_running()
-        p.kill()
-        p.wait()
-        assert not p.is_running()
-        assert not p.is_running()
-
-    def test_exe(self):
-        sproc = get_test_subprocess(wait=True)
-        exe = psutil.Process(sproc.pid).exe()
-        try:
-            self.assertEqual(exe, PYTHON)
-        except AssertionError:
-            if WINDOWS and len(exe) == len(PYTHON):
-                # on Windows we don't care about case sensitivity
-                self.assertEqual(exe.lower(), PYTHON.lower())
-            else:
-                # certain platforms such as BSD are more accurate returning:
-                # "/usr/local/bin/python2.7"
-                # ...instead of:
-                # "/usr/local/bin/python"
-                # We do not want to consider this difference in accuracy
-                # an error.
-                ver = "%s.%s" % (sys.version_info[0], sys.version_info[1])
-                self.assertEqual(exe.replace(ver, ''), PYTHON.replace(ver, ''))
-
-    def test_cmdline(self):
-        cmdline = [PYTHON, "-c", "import time; time.sleep(60)"]
-        sproc = get_test_subprocess(cmdline, wait=True)
-        self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()),
-                         ' '.join(cmdline))
-
-    def test_name(self):
-        sproc = get_test_subprocess(PYTHON, wait=True)
-        name = psutil.Process(sproc.pid).name().lower()
-        pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
-        assert pyexe.startswith(name), (pyexe, name)
-
-    @unittest.skipUnless(POSIX, "posix only")
-    # TODO: add support for other compilers
-    @unittest.skipUnless(which("gcc"), "gcc not available")
-    def test_prog_w_funky_name(self):
-        # Test that name(), exe() and cmdline() correctly handle programs
-        # with funky chars such as spaces and ")", see:
-        # https://github.com/giampaolo/psutil/issues/628
-        funky_name = "/tmp/foo bar )"
-        _, c_file = tempfile.mkstemp(prefix='psutil-', suffix='.c', dir="/tmp")
-        self.addCleanup(lambda: safe_remove(c_file))
-        self.addCleanup(lambda: safe_remove(funky_name))
-        with open(c_file, "w") as f:
-            f.write("void main() { pause(); }")
-        subprocess.check_call(["gcc", c_file, "-o", funky_name])
-        sproc = get_test_subprocess(
-            [funky_name, "arg1", "arg2", "", "arg3", ""])
-        p = psutil.Process(sproc.pid)
-        # ...in order to try to prevent occasional failures on travis
-        wait_for_pid(p.pid)
-        self.assertEqual(p.name(), "foo bar )")
-        self.assertEqual(p.exe(), "/tmp/foo bar )")
-        self.assertEqual(
-            p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2", "", "arg3", ""])
-
-    @unittest.skipUnless(POSIX, 'posix only')
-    def test_uids(self):
-        p = psutil.Process()
-        real, effective, saved = p.uids()
-        # os.getuid() refers to "real" uid
-        self.assertEqual(real, os.getuid())
-        # os.geteuid() refers to "effective" uid
-        self.assertEqual(effective, os.geteuid())
-        # no such thing as os.getsuid() ("saved" uid), but starting
-        # from python 2.7 we have os.getresuid()[2]
-        if hasattr(os, "getresuid"):
-            self.assertEqual(saved, os.getresuid()[2])
-
-    @unittest.skipUnless(POSIX, 'posix only')
-    def test_gids(self):
-        p = psutil.Process()
-        real, effective, saved = p.gids()
-        # os.getuid() refers to "real" uid
-        self.assertEqual(real, os.getgid())
-        # os.geteuid() refers to "effective" uid
-        self.assertEqual(effective, os.getegid())
-        # no such thing as os.getsuid() ("saved" uid), but starting
-        # from python 2.7 we have os.getresgid()[2]
-        if hasattr(os, "getresuid"):
-            self.assertEqual(saved, os.getresgid()[2])
-
-    def test_nice(self):
-        p = psutil.Process()
-        self.assertRaises(TypeError, p.nice, "str")
-        if WINDOWS:
-            try:
-                init = p.nice()
-                if sys.version_info > (3, 4):
-                    self.assertIsInstance(init, enum.IntEnum)
-                else:
-                    self.assertIsInstance(init, int)
-                self.assertEqual(init, psutil.NORMAL_PRIORITY_CLASS)
-                p.nice(psutil.HIGH_PRIORITY_CLASS)
-                self.assertEqual(p.nice(), psutil.HIGH_PRIORITY_CLASS)
-                p.nice(psutil.NORMAL_PRIORITY_CLASS)
-                self.assertEqual(p.nice(), psutil.NORMAL_PRIORITY_CLASS)
-            finally:
-                p.nice(psutil.NORMAL_PRIORITY_CLASS)
-        else:
-            try:
-                first_nice = p.nice()
-                p.nice(1)
-                self.assertEqual(p.nice(), 1)
-                # going back to previous nice value raises
-                # AccessDenied on OSX
-                if not OSX:
-                    p.nice(0)
-                    self.assertEqual(p.nice(), 0)
-            except psutil.AccessDenied:
-                pass
-            finally:
-                try:
-                    p.nice(first_nice)
-                except psutil.AccessDenied:
-                    pass
-
-    def test_status(self):
-        p = psutil.Process()
-        self.assertEqual(p.status(), psutil.STATUS_RUNNING)
-
-    def test_username(self):
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        if POSIX:
-            import pwd
-            self.assertEqual(p.username(), pwd.getpwuid(os.getuid()).pw_name)
-            with mock.patch("psutil.pwd.getpwuid",
-                            side_effect=KeyError) as fun:
-                p.username() == str(p.uids().real)
-                assert fun.called
-
-        elif WINDOWS and 'USERNAME' in os.environ:
-            expected_username = os.environ['USERNAME']
-            expected_domain = os.environ['USERDOMAIN']
-            domain, username = p.username().split('\\')
-            self.assertEqual(domain, expected_domain)
-            self.assertEqual(username, expected_username)
-        else:
-            p.username()
-
-    def test_cwd(self):
-        sproc = get_test_subprocess(wait=True)
-        p = psutil.Process(sproc.pid)
-        self.assertEqual(p.cwd(), os.getcwd())
-
-    def test_cwd_2(self):
-        cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(60)"]
-        sproc = get_test_subprocess(cmd, wait=True)
-        p = psutil.Process(sproc.pid)
-        call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
-
-    @unittest.skipUnless(WINDOWS or LINUX or BSD,
-                         'not available on this platform')
-    @unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
-    def test_cpu_affinity(self):
-        p = psutil.Process()
-        initial = p.cpu_affinity()
-        if hasattr(os, "sched_getaffinity"):
-            self.assertEqual(initial, list(os.sched_getaffinity(p.pid)))
-        self.assertEqual(len(initial), len(set(initial)))
-        all_cpus = list(range(len(psutil.cpu_percent(percpu=True))))
-        # setting on travis doesn't seem to work (always return all
-        # CPUs on get):
-        # AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, ... != [0]
-        for n in all_cpus:
-            p.cpu_affinity([n])
-            self.assertEqual(p.cpu_affinity(), [n])
-            if hasattr(os, "sched_getaffinity"):
-                self.assertEqual(p.cpu_affinity(),
-                                 list(os.sched_getaffinity(p.pid)))
-        #
-        p.cpu_affinity(all_cpus)
-        self.assertEqual(p.cpu_affinity(), all_cpus)
-        if hasattr(os, "sched_getaffinity"):
-            self.assertEqual(p.cpu_affinity(),
-                             list(os.sched_getaffinity(p.pid)))
-        #
-        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))
-        invalid_cpu = [len(psutil.cpu_times(percpu=True)) + 10]
-        self.assertRaises(ValueError, p.cpu_affinity, invalid_cpu)
-        self.assertRaises(ValueError, p.cpu_affinity, range(10000, 11000))
-        self.assertRaises(TypeError, p.cpu_affinity, [0, "1"])
-
-    # TODO
-    @unittest.skipIf(BSD, "broken on BSD, see #595")
-    @unittest.skipIf(APPVEYOR,
-                     "can't find any process file on Appveyor")
-    def test_open_files(self):
-        # current process
-        p = psutil.Process()
-        files = p.open_files()
-        self.assertFalse(TESTFN in files)
-        with open(TESTFN, 'w'):
-            # give the kernel some time to see the new file
-            call_until(p.open_files, "len(ret) != %i" % len(files))
-            filenames = [x.path for x in p.open_files()]
-            self.assertIn(TESTFN, filenames)
-        for file in filenames:
-            assert os.path.isfile(file), file
-
-        # another process
-        cmdline = "import time; f = open(r'%s', 'r'); time.sleep(60);" % TESTFN
-        sproc = get_test_subprocess([PYTHON, "-c", cmdline], wait=True)
-        p = psutil.Process(sproc.pid)
-
-        for x in range(100):
-            filenames = [x.path for x in p.open_files()]
-            if TESTFN in filenames:
-                break
-            time.sleep(.01)
-        else:
-            self.assertIn(TESTFN, filenames)
-        for file in filenames:
-            assert os.path.isfile(file), file
-
-    # TODO
-    @unittest.skipIf(BSD, "broken on BSD, see #595")
-    @unittest.skipIf(APPVEYOR,
-                     "can't find any process file on Appveyor")
-    def test_open_files2(self):
-        # test fd and path fields
-        with open(TESTFN, 'w') as fileobj:
-            p = psutil.Process()
-            for path, fd in p.open_files():
-                if path == fileobj.name or fd == fileobj.fileno():
-                    break
-            else:
-                self.fail("no file found; files=%s" % repr(p.open_files()))
-            self.assertEqual(path, fileobj.name)
-            if WINDOWS:
-                self.assertEqual(fd, -1)
-            else:
-                self.assertEqual(fd, fileobj.fileno())
-            # test positions
-            ntuple = p.open_files()[0]
-            self.assertEqual(ntuple[0], ntuple.path)
-            self.assertEqual(ntuple[1], ntuple.fd)
-            # test file is gone
-            self.assertTrue(fileobj.name not in p.open_files())
-
-    def compare_proc_sys_cons(self, pid, proc_cons):
-        from psutil._common import pconn
-        sys_cons = []
-        for c in psutil.net_connections(kind='all'):
-            if c.pid == pid:
-                sys_cons.append(pconn(*c[:-1]))
-        if BSD:
-            # on BSD all fds are set to -1
-            proc_cons = [pconn(*[-1] + list(x[1:])) for x in proc_cons]
-        self.assertEqual(sorted(proc_cons), sorted(sys_cons))
-
-    @skip_on_access_denied(only_if=OSX)
-    def test_connections(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.assertEqual(conn.family, family)
-            self.assertEqual(conn.type, type)
-            self.assertEqual(conn.laddr, laddr)
-            self.assertEqual(conn.raddr, raddr)
-            self.assertEqual(conn.status, status)
-            for kind in all_kinds:
-                cons = proc.connections(kind=kind)
-                if kind in kinds:
-                    self.assertNotEqual(cons, [])
-                else:
-                    self.assertEqual(cons, [])
-            # compare against system-wide connections
-            # XXX Solaris can't retrieve system-wide UNIX
-            # sockets.
-            if not SUNOS:
-                self.compare_proc_sys_cons(proc.pid, [conn])
-
-        tcp_template = textwrap.dedent("""
-            import socket, time
-            s = socket.socket($family, socket.SOCK_STREAM)
-            s.bind(('$addr', 0))
-            s.listen(1)
-            with open('$testfn', 'w') as f:
-                f.write(str(s.getsockname()[:2]))
-            time.sleep(60)
-        """)
-
-        udp_template = textwrap.dedent("""
-            import socket, time
-            s = socket.socket($family, socket.SOCK_DGRAM)
-            s.bind(('$addr', 0))
-            with open('$testfn', 'w') as f:
-                f.write(str(s.getsockname()[:2]))
-            time.sleep(60)
-        """)
-
-        from string import Template
-        testfile = os.path.basename(TESTFN)
-        tcp4_template = Template(tcp_template).substitute(
-            family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
-        udp4_template = Template(udp_template).substitute(
-            family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
-        tcp6_template = Template(tcp_template).substitute(
-            family=int(AF_INET6), addr="::1", testfn=testfile)
-        udp6_template = Template(udp_template).substitute(
-            family=int(AF_INET6), addr="::1", testfn=testfile)
-
-        # launch various subprocess instantiating a socket of various
-        # families and types to enrich psutil results
-        tcp4_proc = pyrun(tcp4_template)
-        tcp4_addr = eval(wait_for_file(testfile))
-        udp4_proc = pyrun(udp4_template)
-        udp4_addr = eval(wait_for_file(testfile))
-        if supports_ipv6():
-            tcp6_proc = pyrun(tcp6_template)
-            tcp6_addr = eval(wait_for_file(testfile))
-            udp6_proc = pyrun(udp6_template)
-            udp6_addr = eval(wait_for_file(testfile))
-        else:
-            tcp6_proc = None
-            udp6_proc = None
-            tcp6_addr = None
-            udp6_addr = None
-
-        for p in psutil.Process().children():
-            cons = p.connections()
-            self.assertEqual(len(cons), 1)
-            for conn in cons:
-                # TCP v4
-                if p.pid == tcp4_proc.pid:
-                    check_conn(p, conn, AF_INET, SOCK_STREAM, tcp4_addr, (),
-                               psutil.CONN_LISTEN,
-                               ("all", "inet", "inet4", "tcp", "tcp4"))
-                # UDP v4
-                elif p.pid == udp4_proc.pid:
-                    check_conn(p, conn, AF_INET, SOCK_DGRAM, udp4_addr, (),
-                               psutil.CONN_NONE,
-                               ("all", "inet", "inet4", "udp", "udp4"))
-                # TCP v6
-                elif p.pid == getattr(tcp6_proc, "pid", None):
-                    check_conn(p, conn, AF_INET6, SOCK_STREAM, tcp6_addr, (),
-                               psutil.CONN_LISTEN,
-                               ("all", "inet", "inet6", "tcp", "tcp6"))
-                # UDP v6
-                elif p.pid == getattr(udp6_proc, "pid", None):
-                    check_conn(p, conn, AF_INET6, SOCK_DGRAM, udp6_addr, (),
-                               psutil.CONN_NONE,
-                               ("all", "inet", "inet6", "udp", "udp6"))
-
-    @unittest.skipUnless(hasattr(socket, 'AF_UNIX'),
-                         'AF_UNIX is not supported')
-    @skip_on_access_denied(only_if=OSX)
-    def test_connections_unix(self):
-        def check(type):
-            safe_remove(TESTFN)
-            sock = socket.socket(AF_UNIX, type)
-            with contextlib.closing(sock):
-                sock.bind(TESTFN)
-                cons = psutil.Process().connections(kind='unix')
-                conn = cons[0]
-                check_connection_ntuple(conn)
-                if conn.fd != -1:  # != sunos and windows
-                    self.assertEqual(conn.fd, sock.fileno())
-                self.assertEqual(conn.family, AF_UNIX)
-                self.assertEqual(conn.type, type)
-                self.assertEqual(conn.laddr, TESTFN)
-                if not SUNOS:
-                    # XXX Solaris can't retrieve system-wide UNIX
-                    # sockets.
-                    self.compare_proc_sys_cons(os.getpid(), cons)
-
-        check(SOCK_STREAM)
-        check(SOCK_DGRAM)
-
-    @unittest.skipUnless(hasattr(socket, "fromfd"),
-                         'socket.fromfd() is not availble')
-    @unittest.skipIf(WINDOWS or SUNOS,
-                     'connection fd not available on this platform')
-    def test_connection_fromfd(self):
-        with contextlib.closing(socket.socket()) as sock:
-            sock.bind(('localhost', 0))
-            sock.listen(1)
-            p = psutil.Process()
-            for conn in p.connections():
-                if conn.fd == sock.fileno():
-                    break
-            else:
-                self.fail("couldn't find socket fd")
-            dupsock = socket.fromfd(conn.fd, conn.family, conn.type)
-            with contextlib.closing(dupsock):
-                self.assertEqual(dupsock.getsockname(), conn.laddr)
-                self.assertNotEqual(sock.fileno(), dupsock.fileno())
-
-    def test_connection_constants(self):
-        ints = []
-        strs = []
-        for name in dir(psutil):
-            if name.startswith('CONN_'):
-                num = getattr(psutil, name)
-                str_ = str(num)
-                assert str_.isupper(), str_
-                assert str_ not in strs, str_
-                assert num not in ints, num
-                ints.append(num)
-                strs.append(str_)
-        if SUNOS:
-            psutil.CONN_IDLE
-            psutil.CONN_BOUND
-        if WINDOWS:
-            psutil.CONN_DELETE_TCB
-
-    @unittest.skipUnless(POSIX, 'posix only')
-    def test_num_fds(self):
-        p = psutil.Process()
-        start = p.num_fds()
-        file = open(TESTFN, 'w')
-        self.addCleanup(file.close)
-        self.assertEqual(p.num_fds(), start + 1)
-        sock = socket.socket()
-        self.addCleanup(sock.close)
-        self.assertEqual(p.num_fds(), start + 2)
-        file.close()
-        sock.close()
-        self.assertEqual(p.num_fds(), start)
-
-    @skip_on_not_implemented(only_if=LINUX)
-    def test_num_ctx_switches(self):
-        p = psutil.Process()
-        before = sum(p.num_ctx_switches())
-        for x in range(500000):
-            after = sum(p.num_ctx_switches())
-            if after > before:
-                return
-        self.fail("num ctx switches still the same after 50.000 iterations")
-
-    def test_parent_ppid(self):
-        this_parent = os.getpid()
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        self.assertEqual(p.ppid(), this_parent)
-        self.assertEqual(p.parent().pid, this_parent)
-        # no other process is supposed to have us as parent
-        for p in psutil.process_iter():
-            if p.pid == sproc.pid:
-                continue
-            self.assertTrue(p.ppid() != this_parent)
-
-    def test_children(self):
-        p = psutil.Process()
-        self.assertEqual(p.children(), [])
-        self.assertEqual(p.children(recursive=True), [])
-        sproc = get_test_subprocess()
-        children1 = p.children()
-        children2 = p.children(recursive=True)
-        for children in (children1, children2):
-            self.assertEqual(len(children), 1)
-            self.assertEqual(children[0].pid, sproc.pid)
-            self.assertEqual(children[0].ppid(), os.getpid())
-
-    def test_children_recursive(self):
-        # here we create a subprocess which creates another one as in:
-        # A (parent) -> B (child) -> C (grandchild)
-        s = "import subprocess, os, sys, time;"
-        s += "PYTHON = os.path.realpath(sys.executable);"
-        s += "cmd = [PYTHON, '-c', 'import time; time.sleep(60);'];"
-        s += "subprocess.Popen(cmd);"
-        s += "time.sleep(60);"
-        get_test_subprocess(cmd=[PYTHON, "-c", s])
-        p = psutil.Process()
-        self.assertEqual(len(p.children(recursive=False)), 1)
-        # give the grandchild some time to start
-        stop_at = time.time() + GLOBAL_TIMEOUT
-        while time.time() < stop_at:
-            children = p.children(recursive=True)
-            if len(children) > 1:
-                break
-        self.assertEqual(len(children), 2)
-        self.assertEqual(children[0].ppid(), os.getpid())
-        self.assertEqual(children[1].ppid(), children[0].pid)
-
-    def test_children_duplicates(self):
-        # find the process which has the highest number of children
-        table = collections.defaultdict(int)
-        for p in psutil.process_iter():
-            try:
-                table[p.ppid()] += 1
-            except psutil.Error:
-                pass
-        # this is the one, now let's make sure there are no duplicates
-        pid = sorted(table.items(), key=lambda x: x[1])[-1][0]
-        p = psutil.Process(pid)
-        try:
-            c = p.children(recursive=True)
-        except psutil.AccessDenied:  # windows
-            pass
-        else:
-            self.assertEqual(len(c), len(set(c)))
-
-    def test_suspend_resume(self):
-        sproc = get_test_subprocess(wait=True)
-        p = psutil.Process(sproc.pid)
-        p.suspend()
-        for x in range(100):
-            if p.status() == psutil.STATUS_STOPPED:
-                break
-            time.sleep(0.01)
-        p.resume()
-        self.assertNotEqual(p.status(), psutil.STATUS_STOPPED)
-
-    def test_invalid_pid(self):
-        self.assertRaises(TypeError, psutil.Process, "1")
-        self.assertRaises(ValueError, psutil.Process, -1)
-
-    def test_as_dict(self):
-        p = psutil.Process()
-        d = p.as_dict(attrs=['exe', 'name'])
-        self.assertEqual(sorted(d.keys()), ['exe', 'name'])
-
-        p = psutil.Process(min(psutil.pids()))
-        d = p.as_dict(attrs=['connections'], ad_value='foo')
-        if not isinstance(d['connections'], list):
-            self.assertEqual(d['connections'], 'foo')
-
-    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)
-        # >>> time.sleep(2)  # time-consuming task, process dies in meantime
-        #  >>> proc.name()
-        # Refers to Issue #15
-        sproc = get_test_subprocess()
-        p = psutil.Process(sproc.pid)
-        p.terminate()
-        p.wait()
-        if WINDOWS:
-            wait_for_pid(p.pid)
-        self.assertFalse(p.is_running())
-        self.assertFalse(p.pid in psutil.pids())
-
-        excluded_names = ['pid', 'is_running', 'wait', 'create_time']
-        if LINUX and not RLIMIT_SUPPORT:
-            excluded_names.append('rlimit')
-        for name in dir(p):
-            if (name.startswith('_') or
-                    name in excluded_names):
-                continue
-            try:
-                meth = getattr(p, name)
-                # get/set methods
-                if name == 'nice':
-                    if POSIX:
-                        ret = meth(1)
-                    else:
-                        ret = meth(psutil.NORMAL_PRIORITY_CLASS)
-                elif name == 'ionice':
-                    ret = meth()
-                    ret = meth(2)
-                elif name == 'rlimit':
-                    ret = meth(psutil.RLIMIT_NOFILE)
-                    ret = meth(psutil.RLIMIT_NOFILE, (5, 5))
-                elif name == 'cpu_affinity':
-                    ret = meth()
-                    ret = meth([0])
-                elif name == 'send_signal':
-                    ret = meth(signal.SIGTERM)
-                else:
-                    ret = meth()
-            except psutil.ZombieProcess:
-                self.fail("ZombieProcess for %r was not supposed to happen" %
-                          name)
-            except psutil.NoSuchProcess:
-                pass
-            except NotImplementedError:
-                pass
-            else:
-                self.fail(
-                    "NoSuchProcess exception not raised for %r, retval=%s" % (
-                        name, ret))
-
-    @unittest.skipUnless(POSIX, 'posix only')
-    def test_zombie_process(self):
-        def succeed_or_zombie_p_exc(fun, *args, **kwargs):
-            try:
-                fun(*args, **kwargs)
-            except (psutil.ZombieProcess, psutil.AccessDenied):
-                pass
-
-        # Note: in this test we'll be creating two sub processes.
-        # Both of them are supposed to be freed / killed by
-        # reap_children() as they are attributable to 'us'
-        # (os.getpid()) via children(recursive=True).
-        src = textwrap.dedent("""\
-        import os, sys, time, socket, contextlib
-        child_pid = os.fork()
-        if child_pid > 0:
-            time.sleep(3000)
-        else:
-            # this is the zombie process
-            s = socket.socket(socket.AF_UNIX)
-            with contextlib.closing(s):
-                s.connect('%s')
-                if sys.version_info < (3, ):
-                    pid = str(os.getpid())
-                else:
-                    pid = bytes(str(os.getpid()), 'ascii')
-                s.sendall(pid)
-        """ % TESTFN)
-        with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
-            try:
-                sock.settimeout(GLOBAL_TIMEOUT)
-                sock.bind(TESTFN)
-                sock.listen(1)
-                pyrun(src)
-                conn, _ = sock.accept()
-                select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT)
-                zpid = int(conn.recv(1024))
-                zproc = psutil.Process(zpid)
-                call_until(lambda: zproc.status(),
-                           "ret == psutil.STATUS_ZOMBIE")
-                # A zombie process should always be instantiable
-                zproc = psutil.Process(zpid)
-                # ...and at least its status always be querable
-                self.assertEqual(zproc.status(), psutil.STATUS_ZOMBIE)
-                # ...and it should be considered 'running'
-                self.assertTrue(zproc.is_running())
-                # ...and as_dict() shouldn't crash
-                zproc.as_dict()
-                if hasattr(zproc, "rlimit"):
-                    succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE)
-                    succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE,
-                                            (5, 5))
-                # set methods
-                succeed_or_zombie_p_exc(zproc.parent)
-                if hasattr(zproc, 'cpu_affinity'):
-                    succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
-                succeed_or_zombie_p_exc(zproc.nice, 0)
-                if hasattr(zproc, 'ionice'):
-                    if LINUX:
-                        succeed_or_zombie_p_exc(zproc.ionice, 2, 0)
-                    else:
-                        succeed_or_zombie_p_exc(zproc.ionice, 0)  # Windows
-                if hasattr(zproc, 'rlimit'):
-                    succeed_or_zombie_p_exc(zproc.rlimit,
-                                            psutil.RLIMIT_NOFILE, (5, 5))
-                succeed_or_zombie_p_exc(zproc.suspend)
-                succeed_or_zombie_p_exc(zproc.resume)
-                succeed_or_zombie_p_exc(zproc.terminate)
-                succeed_or_zombie_p_exc(zproc.kill)
-
-                # ...its parent should 'see' it
-                # edit: not true on BSD and OSX
-                # descendants = [x.pid for x in psutil.Process().children(
-                #                recursive=True)]
-                # self.assertIn(zpid, descendants)
-                # XXX should we also assume ppid be usable?  Note: this
-                # would be an important use case as the only way to get
-                # rid of a zombie is to kill its parent.
-                # self.assertEqual(zpid.ppid(), os.getpid())
-                # ...and all other APIs should be able to deal with it
-                self.assertTrue(psutil.pid_exists(zpid))
-                self.assertIn(zpid, psutil.pids())
-                self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
-                psutil._pmap = {}
-                self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
-            finally:
-                reap_children(search_all=True)
-
-    def test_pid_0(self):
-        # Process(0) is supposed to work on all platforms except Linux
-        if 0 not in psutil.pids():
-            self.assertRaises(psutil.NoSuchProcess, psutil.Process, 0)
-            return
-
-        p = psutil.Process(0)
-        self.assertTrue(p.name())
-
-        if POSIX:
-            try:
-                self.assertEqual(p.uids().real, 0)
-                self.assertEqual(p.gids().real, 0)
-            except psutil.AccessDenied:
-                pass
-
-            self.assertRaisesRegexp(
-                ValueError, "preventing sending signal to process with PID 0",
-                p.send_signal, signal.SIGTERM)
-
-        self.assertIn(p.ppid(), (0, 1))
-        # self.assertEqual(p.exe(), "")
-        p.cmdline()
-        try:
-            p.num_threads()
-        except psutil.AccessDenied:
-            pass
-
-        try:
-            p.memory_info()
-        except psutil.AccessDenied:
-            pass
-
-        try:
-            if POSIX:
-                self.assertEqual(p.username(), 'root')
-            elif WINDOWS:
-                self.assertEqual(p.username(), 'NT AUTHORITY\\SYSTEM')
-            else:
-                p.username()
-        except psutil.AccessDenied:
-            pass
-
-        self.assertIn(0, psutil.pids())
-        self.assertTrue(psutil.pid_exists(0))
-
-    def test_Popen(self):
-        # Popen class test
-        # XXX this test causes a ResourceWarning on Python 3 because
-        # psutil.__subproc instance doesn't get propertly freed.
-        # Not sure what to do though.
-        cmd = [PYTHON, "-c", "import time; time.sleep(60);"]
-        proc = psutil.Popen(cmd, stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE)
-        try:
-            proc.name()
-            proc.stdin
-            self.assertTrue(hasattr(proc, 'name'))
-            self.assertTrue(hasattr(proc, 'stdin'))
-            self.assertTrue(dir(proc))
-            self.assertRaises(AttributeError, getattr, proc, 'foo')
-        finally:
-            proc.kill()
-            proc.wait()
-            self.assertIsNotNone(proc.returncode)
-
-
-# ===================================================================
-# --- Featch all processes test
-# ===================================================================
-
-class TestFetchAllProcesses(unittest.TestCase):
-    """Test which iterates over all running processes and performs
-    some sanity checks against Process API's returned values.
-    """
-
-    def setUp(self):
-        if POSIX:
-            import pwd
-            pall = pwd.getpwall()
-            self._uids = set([x.pw_uid for x in pall])
-            self._usernames = set([x.pw_name for x in pall])
-
-    def test_fetch_all(self):
-        valid_procs = 0
-        excluded_names = set([
-            'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
-            'as_dict', 'cpu_percent', 'parent', 'children', 'pid'])
-        if LINUX and not RLIMIT_SUPPORT:
-            excluded_names.add('rlimit')
-        attrs = []
-        for name in dir(psutil.Process):
-            if name.startswith("_"):
-                continue
-            if name in excluded_names:
-                continue
-            attrs.append(name)
-
-        default = object()
-        failures = []
-        for name in attrs:
-            for p in psutil.process_iter():
-                ret = default
-                try:
-                    try:
-                        args = ()
-                        attr = getattr(p, name, None)
-                        if attr is not None and callable(attr):
-                            if name == 'rlimit':
-                                args = (psutil.RLIMIT_NOFILE,)
-                            ret = attr(*args)
-                        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())
-                        self.assertTrue(str(err))
-                        self.assertTrue(err.msg)
-                    else:
-                        if ret not in (0, 0.0, [], None, ''):
-                            assert ret, ret
-                        meth = getattr(self, name)
-                        meth(ret)
-                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())
-                    failures.append(s)
-                    break
-
-        if failures:
-            self.fail(''.join(failures))
-
-        # we should always have a non-empty list, not including PID 0 etc.
-        # special cases.
-        self.assertTrue(valid_procs > 0)
-
-    def cmdline(self, ret):
-        pass
-
-    def exe(self, ret):
-        if not ret:
-            self.assertEqual(ret, '')
-        else:
-            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
-                    self.assertTrue(os.access(ret, os.X_OK))
-
-    def ppid(self, ret):
-        self.assertTrue(ret >= 0)
-
-    def name(self, ret):
-        self.assertIsInstance(ret, (str, unicode))
-        self.assertTrue(ret)
-
-    def create_time(self, ret):
-        self.assertTrue(ret > 0)
-        # this can't be taken for granted on all platforms
-        # self.assertGreaterEqual(ret, psutil.boot_time())
-        # make sure returned value can be pretty printed
-        # with strftime
-        time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret))
-
-    def uids(self, ret):
-        for uid in ret:
-            self.assertTrue(uid >= 0)
-            self.assertIn(uid, self._uids)
-
-    def gids(self, ret):
-        # note: testing all gids as above seems not to be reliable for
-        # gid == 30 (nodoby); not sure why.
-        for gid in ret:
-            self.assertTrue(gid >= 0)
-            # self.assertIn(uid, self.gids
-
-    def username(self, ret):
-        self.assertTrue(ret)
-        if POSIX:
-            self.assertIn(ret, self._usernames)
-
-    def status(self, ret):
-        self.assertTrue(ret != "")
-        self.assertTrue(ret != '?')
-        self.assertIn(ret, VALID_PROC_STATUSES)
-
-    def io_counters(self, ret):
-        for field in ret:
-            if field != -1:
-                self.assertTrue(field >= 0)
-
-    def ionice(self, ret):
-        if LINUX:
-            self.assertTrue(ret.ioclass >= 0)
-            self.assertTrue(ret.value >= 0)
-        else:
-            self.assertTrue(ret >= 0)
-            self.assertIn(ret, (0, 1, 2))
-
-    def num_threads(self, ret):
-        self.assertTrue(ret >= 1)
-
-    def threads(self, ret):
-        for t in ret:
-            self.assertTrue(t.id >= 0)
-            self.assertTrue(t.user_time >= 0)
-            self.assertTrue(t.system_time >= 0)
-
-    def cpu_times(self, ret):
-        self.assertTrue(ret.user >= 0)
-        self.assertTrue(ret.system >= 0)
-
-    def memory_info(self, ret):
-        self.assertTrue(ret.rss >= 0)
-        self.assertTrue(ret.vms >= 0)
-
-    def memory_info_ex(self, ret):
-        for name in ret._fields:
-            self.assertTrue(getattr(ret, name) >= 0)
-        if POSIX and ret.vms != 0:
-            # VMS is always supposed to be the highest
-            for name in ret._fields:
-                if name != 'vms':
-                    value = getattr(ret, name)
-                    assert ret.vms > value, ret
-        elif WINDOWS:
-            assert ret.peak_wset >= ret.wset, ret
-            assert ret.peak_paged_pool >= ret.paged_pool, ret
-            assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret
-            assert ret.peak_pagefile >= ret.pagefile, ret
-
-    def open_files(self, ret):
-        for f in ret:
-            if WINDOWS:
-                assert f.fd == -1, f
-            else:
-                self.assertIsInstance(f.fd, int)
-            assert os.path.isabs(f.path), f
-            assert os.path.isfile(f.path), f
-
-    def num_fds(self, ret):
-        self.assertTrue(ret >= 0)
-
-    def connections(self, ret):
-        self.assertEqual(len(ret), len(set(ret)))
-        for conn in ret:
-            check_connection_ntuple(conn)
-
-    def cwd(self, ret):
-        if ret is not None:  # BSD may return None
-            assert os.path.isabs(ret), ret
-            try:
-                st = os.stat(ret)
-            except OSError as err:
-                # directory has been removed in mean time
-                if err.errno != errno.ENOENT:
-                    raise
-            else:
-                self.assertTrue(stat.S_ISDIR(st.st_mode))
-
-    def memory_percent(self, ret):
-        assert 0 <= ret <= 100, ret
-
-    def is_running(self, ret):
-        self.assertTrue(ret)
-
-    def cpu_affinity(self, ret):
-        assert ret != [], ret
-
-    def terminal(self, ret):
-        if ret is not None:
-            assert os.path.isabs(ret), ret
-            assert os.path.exists(ret), ret
-
-    def memory_maps(self, ret):
-        for nt in ret:
-            for fname in nt._fields:
-                value = getattr(nt, fname)
-                if fname == 'path':
-                    if not value.startswith('['):
-                        assert os.path.isabs(nt.path), nt.path
-                        # commented as on Linux we might get
-                        # '/foo/bar (deleted)'
-                        # assert os.path.exists(nt.path), nt.path
-                elif fname in ('addr', 'perms'):
-                    self.assertTrue(value)
-                else:
-                    self.assertIsInstance(value, (int, long))
-                    assert value >= 0, value
-
-    def num_handles(self, ret):
-        if WINDOWS:
-            self.assertGreaterEqual(ret, 0)
-        else:
-            self.assertGreaterEqual(ret, 0)
-
-    def nice(self, ret):
-        if POSIX:
-            assert -20 <= ret <= 20, ret
-        else:
-            priorities = [getattr(psutil, x) for x in dir(psutil)
-                          if x.endswith('_PRIORITY_CLASS')]
-            self.assertIn(ret, priorities)
-
-    def num_ctx_switches(self, ret):
-        self.assertTrue(ret.voluntary >= 0)
-        self.assertTrue(ret.involuntary >= 0)
-
-    def rlimit(self, ret):
-        self.assertEqual(len(ret), 2)
-        self.assertGreaterEqual(ret[0], -1)
-        self.assertGreaterEqual(ret[1], -1)
-
-
-# ===================================================================
-# --- Limited user tests
-# ===================================================================
-
-@unittest.skipUnless(POSIX, "UNIX only")
-@unittest.skipUnless(hasattr(os, 'getuid') and os.getuid() == 0,
-                     "super user privileges are required")
-class LimitedUserTestCase(TestProcess):
-    """Repeat the previous tests by using a limited user.
-    Executed only on UNIX and only if the user who run the test script
-    is root.
-    """
-    # the uid/gid the test suite runs under
-    if hasattr(os, 'getuid'):
-        PROCESS_UID = os.getuid()
-        PROCESS_GID = os.getgid()
-
-    def __init__(self, *args, **kwargs):
-        TestProcess.__init__(self, *args, **kwargs)
-        # re-define all existent test methods in order to
-        # ignore AccessDenied exceptions
-        for attr in [x for x in dir(self) if x.startswith('test')]:
-            meth = getattr(self, attr)
-
-            def test_(self):
-                try:
-                    meth()
-                except psutil.AccessDenied:
-                    pass
-            setattr(self, attr, types.MethodType(test_, self))
-
-    def setUp(self):
-        safe_remove(TESTFN)
-        TestProcess.setUp(self)
-        os.setegid(1000)
-        os.seteuid(1000)
-
-    def tearDown(self):
-        os.setegid(self.PROCESS_UID)
-        os.seteuid(self.PROCESS_GID)
-        TestProcess.tearDown(self)
-
-    def test_nice(self):
-        try:
-            psutil.Process().nice(-1)
-        except psutil.AccessDenied:
-            pass
-        else:
-            self.fail("exception not raised")
-
-    def test_zombie_process(self):
-        # causes problems if test test suite is run as root
-        pass
-
-
-# ===================================================================
-# --- Misc tests
-# ===================================================================
-
-class TestMisc(unittest.TestCase):
-    """Misc / generic tests."""
-
-    def test_process__repr__(self, func=repr):
-        p = psutil.Process()
-        r = func(p)
-        self.assertIn("psutil.Process", r)
-        self.assertIn("pid=%s" % p.pid, r)
-        self.assertIn("name=", r)
-        self.assertIn(p.name(), r)
-        with mock.patch.object(psutil.Process, "name",
-                               side_effect=psutil.ZombieProcess(os.getpid())):
-            p = psutil.Process()
-            r = func(p)
-            self.assertIn("pid=%s" % p.pid, r)
-            self.assertIn("zombie", r)
-            self.assertNotIn("name=", r)
-        with mock.patch.object(psutil.Process, "name",
-                               side_effect=psutil.NoSuchProcess(os.getpid())):
-            p = psutil.Process()
-            r = func(p)
-            self.assertIn("pid=%s" % p.pid, r)
-            self.assertIn("terminated", r)
-            self.assertNotIn("name=", r)
-
-    def test_process__str__(self):
-        self.test_process__repr__(func=str)
-
-    def test_no_such_process__repr__(self, func=repr):
-        self.assertEqual(
-            repr(psutil.NoSuchProcess(321)),
-            "psutil.NoSuchProcess process no longer exists (pid=321)")
-        self.assertEqual(
-            repr(psutil.NoSuchProcess(321, name='foo')),
-            "psutil.NoSuchProcess process no longer exists (pid=321, "
-            "name='foo')")
-        self.assertEqual(
-            repr(psutil.NoSuchProcess(321, msg='foo')),
-            "psutil.NoSuchProcess foo")
-
-    def test_zombie_process__repr__(self, func=repr):
-        self.assertEqual(
-            repr(psutil.ZombieProcess(321)),
-            "psutil.ZombieProcess process still exists but it's a zombie "
-            "(pid=321)")
-        self.assertEqual(
-            repr(psutil.ZombieProcess(321, name='foo')),
-            "psutil.ZombieProcess process still exists but it's a zombie "
-            "(pid=321, name='foo')")
-        self.assertEqual(
-            repr(psutil.ZombieProcess(321, name='foo', ppid=1)),
-            "psutil.ZombieProcess process still exists but it's a zombie "
-            "(pid=321, name='foo', ppid=1)")
-        self.assertEqual(
-            repr(psutil.ZombieProcess(321, msg='foo')),
-            "psutil.ZombieProcess foo")
-
-    def test_access_denied__repr__(self, func=repr):
-        self.assertEqual(
-            repr(psutil.AccessDenied(321)),
-            "psutil.AccessDenied (pid=321)")
-        self.assertEqual(
-            repr(psutil.AccessDenied(321, name='foo')),
-            "psutil.AccessDenied (pid=321, name='foo')")
-        self.assertEqual(
-            repr(psutil.AccessDenied(321, msg='foo')),
-            "psutil.AccessDenied foo")
-
-    def test_timeout_expired__repr__(self, func=repr):
-        self.assertEqual(
-            repr(psutil.TimeoutExpired(321)),
-            "psutil.TimeoutExpired timeout after 321 seconds")
-        self.assertEqual(
-            repr(psutil.TimeoutExpired(321, pid=111)),
-            "psutil.TimeoutExpired timeout after 321 seconds (pid=111)")
-        self.assertEqual(
-            repr(psutil.TimeoutExpired(321, pid=111, name='foo')),
-            "psutil.TimeoutExpired timeout after 321 seconds "
-            "(pid=111, name='foo')")
-
-    def test_process__eq__(self):
-        p1 = psutil.Process()
-        p2 = psutil.Process()
-        self.assertEqual(p1, p2)
-        p2._ident = (0, 0)
-        self.assertNotEqual(p1, p2)
-        self.assertNotEqual(p1, 'foo')
-
-    def test_process__hash__(self):
-        s = set([psutil.Process(), psutil.Process()])
-        self.assertEqual(len(s), 1)
-
-    def test__all__(self):
-        dir_psutil = dir(psutil)
-        for name in dir_psutil:
-            if name in ('callable', 'error', 'namedtuple',
-                        'long', 'test', 'NUM_CPUS', 'BOOT_TIME',
-                        'TOTAL_PHYMEM'):
-                continue
-            if not name.startswith('_'):
-                try:
-                    __import__(name)
-                except ImportError:
-                    if name not in psutil.__all__:
-                        fun = getattr(psutil, name)
-                        if fun is None:
-                            continue
-                        if (fun.__doc__ is not None and
-                                'deprecated' not in fun.__doc__.lower()):
-                            self.fail('%r not in psutil.__all__' % name)
-
-        # Import 'star' will break if __all__ is inconsistent, see:
-        # https://github.com/giampaolo/psutil/issues/656
-        # Can't do `from psutil import *` as it won't work on python 3
-        # so we simply iterate over __all__.
-        for name in psutil.__all__:
-            self.assertIn(name, dir_psutil)
-
-    def test_version(self):
-        self.assertEqual('.'.join([str(x) for x in psutil.version_info]),
-                         psutil.__version__)
-
-    def test_memoize(self):
-        from psutil._common import memoize
-
-        @memoize
-        def foo(*args, **kwargs):
-            "foo docstring"
-            calls.append(None)
-            return (args, kwargs)
-
-        calls = []
-        # no args
-        for x in range(2):
-            ret = foo()
-            expected = ((), {})
-            self.assertEqual(ret, expected)
-            self.assertEqual(len(calls), 1)
-        # with args
-        for x in range(2):
-            ret = foo(1)
-            expected = ((1, ), {})
-            self.assertEqual(ret, expected)
-            self.assertEqual(len(calls), 2)
-        # with args + kwargs
-        for x in range(2):
-            ret = foo(1, bar=2)
-            expected = ((1, ), {'bar': 2})
-            self.assertEqual(ret, expected)
-            self.assertEqual(len(calls), 3)
-        # clear cache
-        foo.cache_clear()
-        ret = foo()
-        expected = ((), {})
-        self.assertEqual(ret, expected)
-        self.assertEqual(len(calls), 4)
-        # docstring
-        self.assertEqual(foo.__doc__, "foo docstring")
-
-    def test_isfile_strict(self):
-        from psutil._common import isfile_strict
-        this_file = os.path.abspath(__file__)
-        assert isfile_strict(this_file)
-        assert not isfile_strict(os.path.dirname(this_file))
-        with mock.patch('psutil._common.os.stat',
-                        side_effect=OSError(errno.EPERM, "foo")):
-            self.assertRaises(OSError, isfile_strict, this_file)
-        with mock.patch('psutil._common.os.stat',
-                        side_effect=OSError(errno.EACCES, "foo")):
-            self.assertRaises(OSError, isfile_strict, this_file)
-        with mock.patch('psutil._common.os.stat',
-                        side_effect=OSError(errno.EINVAL, "foo")):
-            assert not isfile_strict(this_file)
-        with mock.patch('psutil._common.stat.S_ISREG', return_value=False):
-            assert not isfile_strict(this_file)
-
-    def test_serialization(self):
-        def check(ret):
-            if json is not None:
-                json.loads(json.dumps(ret))
-            a = pickle.dumps(ret)
-            b = pickle.loads(a)
-            self.assertEqual(ret, b)
-
-        check(psutil.Process().as_dict())
-        check(psutil.virtual_memory())
-        check(psutil.swap_memory())
-        check(psutil.cpu_times())
-        check(psutil.cpu_times_percent(interval=0))
-        check(psutil.net_io_counters())
-        if LINUX and not os.path.exists('/proc/diskstats'):
-            pass
-        else:
-            if not APPVEYOR:
-                check(psutil.disk_io_counters())
-        check(psutil.disk_partitions())
-        check(psutil.disk_usage(os.getcwd()))
-        check(psutil.users())
-
-    def test_setup_script(self):
-        here = os.path.abspath(os.path.dirname(__file__))
-        setup_py = os.path.realpath(os.path.join(here, '..', 'setup.py'))
-        module = imp.load_source('setup', setup_py)
-        self.assertRaises(SystemExit, module.setup)
-        self.assertEqual(module.get_version(), psutil.__version__)
-
-    def test_ad_on_process_creation(self):
-        # We are supposed to be able to instantiate Process also in case
-        # of zombie processes or access denied.
-        with mock.patch.object(psutil.Process, 'create_time',
-                               side_effect=psutil.AccessDenied) as meth:
-            psutil.Process()
-            assert meth.called
-        with mock.patch.object(psutil.Process, 'create_time',
-                               side_effect=psutil.ZombieProcess(1)) as meth:
-            psutil.Process()
-            assert meth.called
-        with mock.patch.object(psutil.Process, 'create_time',
-                               side_effect=ValueError) as meth:
-            with self.assertRaises(ValueError):
-                psutil.Process()
-            assert meth.called
-
-
-# ===================================================================
-# --- Example script tests
-# ===================================================================
-
-class TestExampleScripts(unittest.TestCase):
-    """Tests for scripts in the examples directory."""
-
-    def assert_stdout(self, exe, args=None):
-        exe = os.path.join(EXAMPLES_DIR, exe)
-        if args:
-            exe = exe + ' ' + args
-        try:
-            out = sh(sys.executable + ' ' + exe).strip()
-        except RuntimeError as err:
-            if 'AccessDenied' in str(err):
-                return str(err)
-            else:
-                raise
-        assert out, out
-        return out
-
-    def assert_syntax(self, exe, args=None):
-        exe = os.path.join(EXAMPLES_DIR, exe)
-        with open(exe, 'r') as f:
-            src = f.read()
-        ast.parse(src)
-
-    def test_check_presence(self):
-        # make sure all example scripts have a test method defined
-        meths = dir(self)
-        for name in os.listdir(EXAMPLES_DIR):
-            if name.endswith('.py'):
-                if 'test_' + os.path.splitext(name)[0] not in meths:
-                    # self.assert_stdout(name)
-                    self.fail('no test defined for %r script'
-                              % os.path.join(EXAMPLES_DIR, name))
-
-    def test_disk_usage(self):
-        self.assert_stdout('disk_usage.py')
-
-    def test_free(self):
-        self.assert_stdout('free.py')
-
-    def test_meminfo(self):
-        self.assert_stdout('meminfo.py')
-
-    def test_process_detail(self):
-        self.assert_stdout('process_detail.py')
-
-    @unittest.skipIf(APPVEYOR, "can't find users on Appveyor")
-    def test_who(self):
-        self.assert_stdout('who.py')
-
-    def test_ps(self):
-        self.assert_stdout('ps.py')
-
-    def test_pstree(self):
-        self.assert_stdout('pstree.py')
-
-    def test_netstat(self):
-        self.assert_stdout('netstat.py')
-
-    @unittest.skipIf(TRAVIS, "permission denied on travis")
-    def test_ifconfig(self):
-        self.assert_stdout('ifconfig.py')
-
-    def test_pmap(self):
-        self.assert_stdout('pmap.py', args=str(os.getpid()))
-
-    @unittest.skipIf(ast is None,
-                     'ast module not available on this python version')
-    def test_killall(self):
-        self.assert_syntax('killall.py')
-
-    @unittest.skipIf(ast is None,
-                     'ast module not available on this python version')
-    def test_nettop(self):
-        self.assert_syntax('nettop.py')
-
-    @unittest.skipIf(ast is None,
-                     'ast module not available on this python version')
-    def test_top(self):
-        self.assert_syntax('top.py')
-
-    @unittest.skipIf(ast is None,
-                     'ast module not available on this python version')
-    def test_iotop(self):
-        self.assert_syntax('iotop.py')
-
-    def test_pidof(self):
-        output = self.assert_stdout('pidof.py %s' % psutil.Process().name())
-        self.assertIn(str(os.getpid()), output)
-
-
-def main():
-    tests = []
-    test_suite = unittest.TestSuite()
-    tests.append(TestSystemAPIs)
-    tests.append(TestProcess)
-    tests.append(TestFetchAllProcesses)
-    tests.append(TestMisc)
-    tests.append(TestExampleScripts)
-    tests.append(LimitedUserTestCase)
-
-    if POSIX:
-        from _posix import PosixSpecificTestCase
-        tests.append(PosixSpecificTestCase)
-
-    # import the specific platform test suite
-    stc = None
-    if LINUX:
-        from _linux import LinuxSpecificTestCase as stc
-    elif WINDOWS:
-        from _windows import WindowsSpecificTestCase as stc
-        from _windows import TestDualProcessImplementation
-        tests.append(TestDualProcessImplementation)
-    elif OSX:
-        from _osx import OSXSpecificTestCase as stc
-    elif BSD:
-        from _bsd import BSDSpecificTestCase as stc
-    elif SUNOS:
-        from _sunos import SunOSSpecificTestCase as stc
-    if stc is not None:
-        tests.append(stc)
-
-    for test_class in tests:
-        test_suite.addTest(unittest.makeSuite(test_class))
-    result = unittest.TextTestRunner(verbosity=2).run(test_suite)
-    return result.wasSuccessful()
-
-if __name__ == '__main__':
-    if not main():
-        sys.exit(1)
--- a/third_party/python/psutil/tox.ini
+++ b/third_party/python/psutil/tox.ini
@@ -1,32 +1,32 @@
 # Tox (http://tox.testrun.org/) is a tool for running tests
 # in multiple virtualenvs. This configuration file will run the
 # test suite on all supported python versions.
 # To use it run "pip install tox" and then run "tox" from this
 # directory.
 
 [tox]
-envlist = py26, py27, py32, py33, py34
+envlist = py26, py27, py33, py34, py35, py36
 
 [testenv]
 deps =
     flake8
-    pytest
     py26: ipaddress
     py26: mock==1.0.1
     py26: unittest2
     py27: ipaddress
     py27: mock
     py32: ipaddress
     py32: mock
     py33: ipaddress
 
 setenv =
     PYTHONPATH = {toxinidir}/test
+    TOX = 1
 
 commands =
-    py.test {posargs}
+    python psutil/tests/__main__.py
     git ls-files | grep \\.py$ | xargs flake8
 
 # suppress "WARNING: 'git' command found but not installed in testenv
 whitelist_externals = git
 usedevelop = True