зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1666345 - Un-vendor `psutil` r=firefox-build-system-reviewers,mhentges
After bug 1661624, it's no longer required. Differential Revision: https://phabricator.services.mozilla.com/D90916
This commit is contained in:
Родитель
d8119e6288
Коммит
0026a19ebc
|
@ -1,29 +0,0 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola'
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the psutil authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,582 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: psutil
|
||||
Version: 5.7.0
|
||||
Summary: Cross-platform lib for process and system monitoring in Python.
|
||||
Home-page: https://github.com/giampaolo/psutil
|
||||
Author: Giampaolo Rodola
|
||||
Author-email: g.rodola@gmail.com
|
||||
License: BSD
|
||||
Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability
|
||||
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 10
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 7
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 8
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Vista
|
||||
Classifier: Operating System :: Microsoft
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Operating System :: POSIX :: AIX
|
||||
Classifier: Operating System :: POSIX :: BSD :: FreeBSD
|
||||
Classifier: Operating System :: POSIX :: BSD :: NetBSD
|
||||
Classifier: Operating System :: POSIX :: BSD :: OpenBSD
|
||||
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 :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Software Development :: Libraries
|
||||
Classifier: Topic :: System :: Benchmark
|
||||
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
||||
Classifier: Topic :: System :: Hardware
|
||||
Classifier: Topic :: System :: Monitoring
|
||||
Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog
|
||||
Classifier: Topic :: System :: Networking :: Monitoring
|
||||
Classifier: Topic :: System :: Networking
|
||||
Classifier: Topic :: System :: Operating System
|
||||
Classifier: Topic :: System :: Systems Administration
|
||||
Classifier: Topic :: Utilities
|
||||
Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||||
Provides-Extra: enum
|
||||
Requires-Dist: enum34 ; extra == 'enum'
|
||||
|
||||
| |downloads| |stars| |forks| |contributors| |coverage| |quality|
|
||||
| |version| |py-versions| |packages| |license|
|
||||
| |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift|
|
||||
|
||||
.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/stargazers
|
||||
:alt: Github stars
|
||||
|
||||
.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/network/members
|
||||
:alt: Github forks
|
||||
|
||||
.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/graphs/contributors
|
||||
:alt: Contributors
|
||||
|
||||
.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg
|
||||
:target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade
|
||||
:alt: Code quality
|
||||
|
||||
.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy
|
||||
:target: https://travis-ci.org/giampaolo/psutil
|
||||
:alt: Linux tests (Travis)
|
||||
|
||||
.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
|
||||
:target: https://ci.appveyor.com/project/giampaolo/psutil
|
||||
:alt: Windows tests (Appveyor)
|
||||
|
||||
.. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD
|
||||
:target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci
|
||||
:alt: FreeBSD tests (Cirrus-Ci)
|
||||
|
||||
.. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage
|
||||
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
|
||||
:alt: Test coverage (coverall.io)
|
||||
|
||||
.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest
|
||||
:target: http://psutil.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Latest version
|
||||
|
||||
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg
|
||||
:target: https://repology.org/metapackage/python:psutil/versions
|
||||
:alt: Binary packages
|
||||
|
||||
.. |license| image:: https://img.shields.io/pypi/l/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/blob/master/LICENSE
|
||||
:alt: License
|
||||
|
||||
.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF
|
||||
:target: https://twitter.com/grodola
|
||||
:alt: Twitter Follow
|
||||
|
||||
.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
:alt: Tidelift
|
||||
|
||||
-----
|
||||
|
||||
Quick links
|
||||
===========
|
||||
|
||||
- `Home page <https://github.com/giampaolo/psutil>`_
|
||||
- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
|
||||
- `Documentation <http://psutil.readthedocs.io>`_
|
||||
- `Download <https://pypi.org/project/psutil/#files>`_
|
||||
- `Forum <http://groups.google.com/group/psutil/topics>`_
|
||||
- `StackOverflow <https://stackoverflow.com/questions/tagged/psutil>`_
|
||||
- `Blog <http://grodola.blogspot.com/search/label/psutil>`_
|
||||
- `Development guide <https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst>`_
|
||||
- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
psutil (process and system utilities) is a cross-platform library for
|
||||
retrieving information on **running processes** and **system utilization**
|
||||
(CPU, memory, disks, network, sensors) in Python.
|
||||
It is useful mainly for **system monitoring**, **profiling and limiting process
|
||||
resources** and **management of running processes**.
|
||||
It implements many functionalities offered by classic UNIX command line tools
|
||||
such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others.
|
||||
psutil currently supports the following platforms:
|
||||
|
||||
- **Linux**
|
||||
- **Windows**
|
||||
- **macOS**
|
||||
- **FreeBSD, OpenBSD**, **NetBSD**
|
||||
- **Sun Solaris**
|
||||
- **AIX**
|
||||
|
||||
...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 <http://pypy.org/>`__ is also known to work.
|
||||
|
||||
psutil for enterprise
|
||||
=====================
|
||||
|
||||
.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
|
||||
:width: 150
|
||||
:alt: Tidelift
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
.. list-table::
|
||||
:widths: 10 150
|
||||
|
||||
* - |tideliftlogo|
|
||||
- The maintainer of psutil and thousands of other packages are working
|
||||
with Tidelift to deliver commercial support and maintenance for the open
|
||||
source dependencies you use to build your applications. Save time,
|
||||
reduce risk, and improve code health, while paying the maintainers of
|
||||
the exact dependencies you use.
|
||||
`Learn more <https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`__.
|
||||
|
||||
By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support
|
||||
psutil future development. Alternatively consider making a small
|
||||
`donation`_.
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
To report a security vulnerability, please use the `Tidelift security
|
||||
contact`_. Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
Example applications
|
||||
====================
|
||||
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/top-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/top.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
|
||||
Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
|
||||
and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
|
||||
|
||||
Projects using psutil
|
||||
=====================
|
||||
|
||||
psutil has roughly the following monthly downloads:
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
There are over
|
||||
`10.000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
|
||||
on github which depend from psutil.
|
||||
Here's some I find particularly interesting:
|
||||
|
||||
- https://github.com/google/grr
|
||||
- https://github.com/facebook/osquery/
|
||||
- https://github.com/nicolargo/glances
|
||||
- https://github.com/Jahaja/psdash
|
||||
- https://github.com/ajenti/ajenti
|
||||
- https://github.com/home-assistant/home-assistant/
|
||||
|
||||
|
||||
Portings
|
||||
========
|
||||
|
||||
- Go: https://github.com/shirou/gopsutil
|
||||
- C: https://github.com/hamon-in/cpslib
|
||||
- Rust: https://github.com/borntyping/rust-psutil
|
||||
- Nim: https://github.com/johnscillieri/psutil-nim
|
||||
|
||||
|
||||
Example usages
|
||||
==============
|
||||
|
||||
This represents pretty much the whole psutil API.
|
||||
|
||||
CPU
|
||||
---
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>>
|
||||
>>> psutil.cpu_times()
|
||||
scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1)
|
||||
...
|
||||
4.0
|
||||
5.9
|
||||
3.8
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1, percpu=True)
|
||||
...
|
||||
[4.0, 6.9, 3.7, 9.2]
|
||||
[7.0, 8.5, 2.4, 2.1]
|
||||
[1.2, 9.0, 9.9, 7.2]
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_times_percent(interval=1, percpu=False)
|
||||
...
|
||||
scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
>>>
|
||||
>>> psutil.cpu_count()
|
||||
4
|
||||
>>> psutil.cpu_count(logical=False)
|
||||
2
|
||||
>>>
|
||||
>>> psutil.cpu_stats()
|
||||
scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
|
||||
>>>
|
||||
>>> psutil.cpu_freq()
|
||||
scpufreq(current=931.42925, min=800.0, max=3500.0)
|
||||
>>>
|
||||
>>> psutil.getloadavg() # also on Windows (emulated)
|
||||
(3.14, 3.89, 4.67)
|
||||
|
||||
Memory
|
||||
------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.virtual_memory()
|
||||
svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
|
||||
>>> psutil.swap_memory()
|
||||
sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
|
||||
>>>
|
||||
|
||||
Disks
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.disk_partitions()
|
||||
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
|
||||
sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
|
||||
>>>
|
||||
>>> psutil.disk_usage('/')
|
||||
sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
|
||||
>>>
|
||||
>>> psutil.disk_io_counters(perdisk=False)
|
||||
sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)
|
||||
>>>
|
||||
|
||||
Network
|
||||
-------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.net_io_counters(pernic=True)
|
||||
{'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
|
||||
'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
|
||||
>>>
|
||||
>>> psutil.net_connections()
|
||||
[sconn(fd=115, family=<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),
|
||||
...]
|
||||
>>>
|
||||
>>> psutil.net_if_addrs()
|
||||
{'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
|
||||
'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
|
||||
snicaddr(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()
|
||||
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
|
||||
'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
|
||||
>>>
|
||||
|
||||
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)]}
|
||||
>>>
|
||||
>>> psutil.sensors_fans()
|
||||
{'asus': [sfan(label='cpu_fan', current=3200)]}
|
||||
>>>
|
||||
>>> psutil.sensors_battery()
|
||||
sbattery(percent=93, secsleft=16628, power_plugged=False)
|
||||
>>>
|
||||
|
||||
Other system info
|
||||
-----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.users()
|
||||
[suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),
|
||||
suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]
|
||||
>>>
|
||||
>>> psutil.boot_time()
|
||||
1365519115.0
|
||||
>>>
|
||||
|
||||
Process management
|
||||
------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.pids()
|
||||
[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215, 1216, 1220, 1221, 1243, 1244,
|
||||
1301, 1601, 2237, 2355, 2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282,
|
||||
4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446,
|
||||
5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071]
|
||||
>>>
|
||||
>>> p = psutil.Process(7055)
|
||||
>>> p
|
||||
psutil.Process(pid=7055, name='python', started='09:04:44')
|
||||
>>> 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.children(recursive=True)
|
||||
[psutil.Process(pid=29835, name='python2.7', started='11:45:38'),
|
||||
psutil.Process(pid=29836, name='python2.7', started='11:43:39')]
|
||||
>>>
|
||||
>>> p.parent()
|
||||
psutil.Process(pid=4699, name='bash', started='09:06:44')
|
||||
>>> p.parents()
|
||||
[psutil.Process(pid=4699, name='bash', started='09:06:44'),
|
||||
psutil.Process(pid=4689, name='gnome-terminal-server', started='0:06:44'),
|
||||
psutil.Process(pid=1, name='systemd', started='05:56:55')]
|
||||
>>>
|
||||
>>> p.status()
|
||||
'running'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.create_time()
|
||||
1267551141.5019531
|
||||
>>> p.terminal()
|
||||
'/dev/pts/0'
|
||||
>>>
|
||||
>>> p.uids()
|
||||
puids(real=1000, effective=1000, saved=1000)
|
||||
>>> p.gids()
|
||||
pgids(real=1000, effective=1000, saved=1000)
|
||||
>>>
|
||||
>>> p.cpu_times()
|
||||
pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)
|
||||
>>> p.cpu_percent(interval=1.0)
|
||||
12.1
|
||||
>>> p.cpu_affinity()
|
||||
[0, 1, 2, 3]
|
||||
>>> p.cpu_affinity([0, 1]) # set
|
||||
>>> p.cpu_num()
|
||||
1
|
||||
>>>
|
||||
>>> p.memory_info()
|
||||
pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0)
|
||||
>>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only)
|
||||
pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
|
||||
>>> p.memory_percent()
|
||||
0.7823
|
||||
>>> p.memory_maps()
|
||||
[pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
|
||||
pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
|
||||
pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
|
||||
pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
|
||||
...]
|
||||
>>>
|
||||
>>> p.io_counters()
|
||||
pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)
|
||||
>>>
|
||||
>>> p.open_files()
|
||||
[popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),
|
||||
popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]
|
||||
>>>
|
||||
>>> p.connections()
|
||||
[pconn(fd=115, family=<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')]
|
||||
>>>
|
||||
>>> p.num_threads()
|
||||
4
|
||||
>>> p.num_fds()
|
||||
8
|
||||
>>> p.threads()
|
||||
[pthread(id=5234, user_time=22.5, system_time=9.2891),
|
||||
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.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',
|
||||
...}
|
||||
>>>
|
||||
>>> 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.kill()
|
||||
>>> 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
|
||||
...
|
||||
giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4
|
||||
giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome
|
||||
root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1
|
||||
>>>
|
||||
|
||||
Further process APIs
|
||||
--------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> for proc in psutil.process_iter(['pid', 'name']):
|
||||
... print(proc.info)
|
||||
...
|
||||
{'pid': 1, 'name': 'systemd'}
|
||||
{'pid': 2, 'name': 'kthreadd'}
|
||||
{'pid': 3, 'name': 'ksoftirqd/0'}
|
||||
...
|
||||
>>>
|
||||
>>> psutil.pid_exists(3)
|
||||
True
|
||||
>>>
|
||||
>>> def on_terminate(proc):
|
||||
... print("process {} terminated".format(proc))
|
||||
...
|
||||
>>> # waits for multiple processes to terminate
|
||||
>>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)
|
||||
>>>
|
||||
|
||||
Popen wrapper:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> from subprocess import PIPE
|
||||
>>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
|
||||
>>> p.name()
|
||||
'python'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.communicate()
|
||||
('hello\n', None)
|
||||
>>> p.wait(timeout=2)
|
||||
0
|
||||
>>>
|
||||
|
||||
Windows services
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> list(psutil.win_service_iter())
|
||||
[<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'}
|
||||
|
||||
|
||||
.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html
|
||||
.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
|
||||
.. _Tidelift security contact: https://tidelift.com/security
|
||||
.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
psutil/__init__.py,sha256=gkuk8_HMEBXTFl0y4l8A5QGNCjUH1T0tpF_Y8hPJCPA,87126
|
||||
psutil/_common.py,sha256=6kuDg4DKKv3OGndSXLQ3LftF_hPySIEOprC8HE49eag,26001
|
||||
psutil/_compat.py,sha256=lrMSZr32TO6y9FQw7C2xTNyKwAnPU2o11xQrA_fS_Qw,11607
|
||||
psutil/_psaix.py,sha256=u9M_jJqkiGqNgdhum9MZTbnS4pjIfdCHNr3HAYHA0GQ,18458
|
||||
psutil/_psbsd.py,sha256=LHfBJ-iKy4fuumIcSjO7b6VFOvU_d-Pzwp7Eg84rHSw,30534
|
||||
psutil/_pslinux.py,sha256=WtQtqOAhXd-zqtVPV-E0re91Vqj4DYrKQ3Mj9CfBWak,79806
|
||||
psutil/_psosx.py,sha256=kvG3GKxA-L581H_UgU_BYaZ3eMPX7qMtEBM7AGC-t10,17090
|
||||
psutil/_psposix.py,sha256=FtbNSosrJiiUb9l0gfFdTwNxUwQb6Icy4L3si0x6ZMA,6047
|
||||
psutil/_pssunos.py,sha256=c-0et4xslK83ZiTizQ9W3LH0pMZy11ATEIBbQBJ_v-g,25398
|
||||
psutil/_psutil_windows.pyd,sha256=jERIIv4AR_vfAvx8oOqkyv41JBPuMskRgnM1O8KdokE,62976
|
||||
psutil/_pswindows.py,sha256=iFcfbA5KZehjdZfe1b-cLGRdavGNryI4Zxpkqb12UjU,36839
|
||||
psutil/tests/__init__.py,sha256=uH3lPOnYc6179FxdSy4xfMeJOBvB9CZid268Pg_owC0,35141
|
||||
psutil/tests/__main__.py,sha256=bBfMPu_gPzyiA4gfAFSZLk7LyLSPTUIHQFU1kFcj_Ok,291
|
||||
psutil/tests/runner.py,sha256=l7BrTzIOdjPgNB3D3kiir8jBiK_QJKMlJSYu4Zr0_po,5188
|
||||
psutil/tests/test_aix.py,sha256=LA8PR-1vx5DN2OkjU57MsO8uzNOB0y1ScR1yx5bZJyc,4464
|
||||
psutil/tests/test_bsd.py,sha256=3zlUvefRm2fFTbyLrqM9u8NMK8CThLT7irioTGgnNts,20612
|
||||
psutil/tests/test_connections.py,sha256=U2KCl7eW6kRds8S7t8SmZ62OrVSPfdBuksHsDlk2C28,25293
|
||||
psutil/tests/test_contracts.py,sha256=BOKrRiZs625Xj3KZBzOZ8Kr6edAQgzHq0OlznoCxKzw,25355
|
||||
psutil/tests/test_linux.py,sha256=KvqJ-LiINvbQQ3y18uM2SZRKnpnH4PqlA7CqWLRIH5s,87573
|
||||
psutil/tests/test_memory_leaks.py,sha256=M5nWBv_PYfN7bIoaz6r0UqEYY79hO6wvvDfkKWFMETI,18499
|
||||
psutil/tests/test_misc.py,sha256=wqXpnXL2ycHyE1vu3GB43XTET5auTKz9VdBSjr-vdp0,38748
|
||||
psutil/tests/test_osx.py,sha256=oQIO0YReUtH0PFFAwyk8FpUqvjIiuhVPehMFS1AWJpM,9257
|
||||
psutil/tests/test_posix.py,sha256=fSi5J1LxPhA7ZocrnLnqCSBn6JUuOtBeAljBWughIf4,16537
|
||||
psutil/tests/test_process.py,sha256=JJB9VdAwKFYK-rlpvWn1NyeYasmr0nL_XetzFHHjaJU,63924
|
||||
psutil/tests/test_sunos.py,sha256=GwqWfAP46nrxWlJr-srg85cPNEU2DXvoMelAgqegfLA,1294
|
||||
psutil/tests/test_system.py,sha256=4nWe_fFRmnaB8epV7Eji_0JFnIJbvbykMgSi8LFzbKM,36162
|
||||
psutil/tests/test_unicode.py,sha256=5v1hFoOQoi9_2Wg4ZTIWY_YDd29G00-59ln9FOe8Tsg,13246
|
||||
psutil/tests/test_windows.py,sha256=cGjEspgL48Sb5BQeSG5kIU0DSeImPU1bL9cpFfNX2Tk,33666
|
||||
psutil-5.7.0.dist-info/LICENSE,sha256=JMEphFAMqgf_3OGe68BjlsXm0kS1c7xsQ49KbvjlbBs,1549
|
||||
psutil-5.7.0.dist-info/METADATA,sha256=RmX2_-V4HrxQ1p78U2Os4H5Q0ZFvF5oc0uUTUt9-sRo,25022
|
||||
psutil-5.7.0.dist-info/WHEEL,sha256=Yt_rpa18HI_NXs5dEYG1uWuZ-4QRLVJrN8TSetG3BOI,106
|
||||
psutil-5.7.0.dist-info/top_level.txt,sha256=gCNhn57wzksDjSAISmgMJ0aiXzQulk0GJhb2-BAyYgw,7
|
||||
psutil-5.7.0.dist-info/RECORD,,
|
|
@ -1,5 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.34.2)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp27-cp27m-win_amd64
|
||||
|
|
@ -1 +0,0 @@
|
|||
psutil
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,846 +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.
|
||||
|
||||
"""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, print_function
|
||||
|
||||
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
|
||||
from socket import SOCK_DGRAM
|
||||
from socket import SOCK_STREAM
|
||||
|
||||
try:
|
||||
from socket import AF_INET6
|
||||
except ImportError:
|
||||
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
|
||||
|
||||
|
||||
# can't take it from _common.py as this script is imported by setup.py
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
__all__ = [
|
||||
# OS constants
|
||||
'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
|
||||
'SUNOS', 'WINDOWS',
|
||||
# connection constants
|
||||
'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED',
|
||||
'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN',
|
||||
'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', 'STATUS_PARKED',
|
||||
# other constants
|
||||
'ENCODING', 'ENCODING_ERRS', 'AF_INET6',
|
||||
# named tuples
|
||||
'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile',
|
||||
'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart',
|
||||
'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser',
|
||||
# utility functions
|
||||
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
|
||||
'parse_environ_block', 'path_exists_strict', 'usage_percent',
|
||||
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
|
||||
'bytes2human', 'conn_to_ntuple', 'debug',
|
||||
# shell utils
|
||||
'hilite', 'term_supports_colors', 'print_color',
|
||||
]
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- OS constants
|
||||
# ===================================================================
|
||||
|
||||
|
||||
POSIX = os.name == "posix"
|
||||
WINDOWS = os.name == "nt"
|
||||
LINUX = sys.platform.startswith("linux")
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
OSX = MACOS # deprecated alias
|
||||
FREEBSD = sys.platform.startswith("freebsd")
|
||||
OPENBSD = sys.platform.startswith("openbsd")
|
||||
NETBSD = sys.platform.startswith("netbsd")
|
||||
BSD = FREEBSD or OPENBSD or NETBSD
|
||||
SUNOS = sys.platform.startswith(("sunos", "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" # Linux, macOS, FreeBSD
|
||||
STATUS_LOCKED = "locked" # FreeBSD
|
||||
STATUS_WAITING = "waiting" # FreeBSD
|
||||
STATUS_SUSPENDED = "suspended" # NetBSD
|
||||
STATUS_PARKED = "parked" # Linux
|
||||
|
||||
# Process.connections() and psutil.net_connections()
|
||||
CONN_ESTABLISHED = "ESTABLISHED"
|
||||
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__)
|
||||
|
||||
# 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
|
||||
|
||||
globals().update(BatteryTime.__members__)
|
||||
|
||||
# --- others
|
||||
|
||||
ENCODING = sys.getfilesystemencoding()
|
||||
if not PY3:
|
||||
ENCODING_ERRS = "replace"
|
||||
else:
|
||||
try:
|
||||
ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6
|
||||
except AttributeError:
|
||||
ENCODING_ERRS = "surrogateescape" if POSIX else "replace"
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- namedtuples
|
||||
# ===================================================================
|
||||
|
||||
# --- for system functions
|
||||
|
||||
# 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()
|
||||
snicaddr = namedtuple('snicaddr',
|
||||
['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_fans()
|
||||
sfan = namedtuple('sfan', ['label', 'current'])
|
||||
|
||||
# --- for Process methods
|
||||
|
||||
# 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'])
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- 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]),
|
||||
"inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
"inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
}
|
||||
|
||||
if AF_INET6 is not None:
|
||||
conn_tmap.update({
|
||||
"tcp6": ([AF_INET6], [SOCK_STREAM]),
|
||||
"udp6": ([AF_INET6], [SOCK_DGRAM]),
|
||||
})
|
||||
|
||||
if AF_UNIX is not None:
|
||||
conn_tmap.update({
|
||||
"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
})
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- Exceptions
|
||||
# =====================================================================
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base exception class. All other psutil exceptions inherit
|
||||
from this one.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, msg=""):
|
||||
Exception.__init__(self, msg)
|
||||
self.msg = msg
|
||||
|
||||
def __repr__(self):
|
||||
ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
|
||||
return ret.strip()
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
|
||||
class NoSuchProcess(Error):
|
||||
"""Exception raised when a process with a certain PID doesn't
|
||||
or no longer exists.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid, name=None, msg=None):
|
||||
Error.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
if name:
|
||||
details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
|
||||
else:
|
||||
details = "(pid=%s)" % self.pid
|
||||
self.msg = "process no longer exists " + details
|
||||
|
||||
def __path__(self):
|
||||
return 'xxx'
|
||||
|
||||
|
||||
class ZombieProcess(NoSuchProcess):
|
||||
"""Exception raised when querying a zombie process. This is
|
||||
raised on macOS, BSD and Solaris only, and not always: depending
|
||||
on the query the OS may be able to succeed anyway.
|
||||
On Linux all zombie processes are querable (hence this is never
|
||||
raised). Windows doesn't have zombie processes.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid, name=None, ppid=None, msg=None):
|
||||
NoSuchProcess.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.ppid = ppid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
args = ["pid=%s" % pid]
|
||||
if name:
|
||||
args.append("name=%s" % repr(self.name))
|
||||
if ppid:
|
||||
args.append("ppid=%s" % self.ppid)
|
||||
details = "(%s)" % ", ".join(args)
|
||||
self.msg = "process still exists but it's a zombie " + details
|
||||
|
||||
|
||||
class AccessDenied(Error):
|
||||
"""Exception raised when permission to perform an action is denied."""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid=None, name=None, msg=None):
|
||||
Error.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
if (pid is not None) and (name is not None):
|
||||
self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
|
||||
elif (pid is not None):
|
||||
self.msg = "(pid=%s)" % self.pid
|
||||
else:
|
||||
self.msg = ""
|
||||
|
||||
|
||||
class TimeoutExpired(Error):
|
||||
"""Raised on Process.wait(timeout) if timeout expires and process
|
||||
is still alive.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, seconds, pid=None, name=None):
|
||||
Error.__init__(self, "timeout after %s seconds" % seconds)
|
||||
self.seconds = seconds
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
if (pid is not None) and (name is not None):
|
||||
self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
|
||||
elif (pid is not None):
|
||||
self.msg += " (pid=%s)" % self.pid
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- utils
|
||||
# ===================================================================
|
||||
|
||||
|
||||
def usage_percent(used, total, round_=None):
|
||||
"""Calculate percentage usage of 'used' against 'total'."""
|
||||
try:
|
||||
ret = (float(used) / total) * 100
|
||||
except ZeroDivisionError:
|
||||
return 0.0
|
||||
else:
|
||||
if round_ is not None:
|
||||
ret = round(ret, round_)
|
||||
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(self)
|
||||
>>> foo()
|
||||
1
|
||||
>>> foo()
|
||||
>>> foo()
|
||||
>>>
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self):
|
||||
try:
|
||||
# case 1: we previously entered oneshot() ctx
|
||||
ret = self._cache[fun]
|
||||
except AttributeError:
|
||||
# case 2: we never entered oneshot() ctx
|
||||
return fun(self)
|
||||
except KeyError:
|
||||
# case 3: we entered oneshot() ctx but there's no cache
|
||||
# for this entry yet
|
||||
ret = self._cache[fun] = fun(self)
|
||||
return ret
|
||||
|
||||
def cache_activate(proc):
|
||||
"""Activate cache. Expects a Process instance. Cache will be
|
||||
stored as a "_cache" instance attribute."""
|
||||
proc._cache = {}
|
||||
|
||||
def cache_deactivate(proc):
|
||||
"""Deactivate and clear cache."""
|
||||
try:
|
||||
del proc._cache
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
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.SocketKind(num)
|
||||
except ValueError:
|
||||
return num
|
||||
|
||||
|
||||
def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
|
||||
"""Convert a raw connection tuple to a proper ntuple."""
|
||||
if fam in (socket.AF_INET, AF_INET6):
|
||||
if laddr:
|
||||
laddr = addr(*laddr)
|
||||
if raddr:
|
||||
raddr = addr(*raddr)
|
||||
if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6):
|
||||
status = status_map.get(status, CONN_NONE)
|
||||
else:
|
||||
status = CONN_NONE # ignore whatever C returned to us
|
||||
fam = sockfam_to_enum(fam)
|
||||
type_ = socktype_to_enum(type_)
|
||||
if pid is None:
|
||||
return pconn(fd, fam, type_, laddr, raddr, status)
|
||||
else:
|
||||
return sconn(fd, fam, type_, laddr, raddr, status, pid)
|
||||
|
||||
|
||||
def deprecated_method(replacement):
|
||||
"""A decorator which can be used to mark a method as deprecated
|
||||
'replcement' is the method name which will be called instead.
|
||||
"""
|
||||
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=DeprecationWarning, stacklevel=2)
|
||||
return getattr(self, replacement)(*args, **kwargs)
|
||||
return inner
|
||||
return outer
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def open_binary(fname, **kwargs):
|
||||
return open(fname, "rb", **kwargs)
|
||||
|
||||
|
||||
def open_text(fname, **kwargs):
|
||||
"""On Python 3 opens a file in text mode by using fs encoding and
|
||||
a proper en/decoding errors handler.
|
||||
On Python 2 this is just an alias for open(name, 'rt').
|
||||
"""
|
||||
if PY3:
|
||||
# See:
|
||||
# https://github.com/giampaolo/psutil/issues/675
|
||||
# https://github.com/giampaolo/psutil/pull/733
|
||||
kwargs.setdefault('encoding', ENCODING)
|
||||
kwargs.setdefault('errors', ENCODING_ERRS)
|
||||
return open(fname, "rt", **kwargs)
|
||||
|
||||
|
||||
def bytes2human(n, format="%(value).1f%(symbol)s"):
|
||||
"""Used by various scripts. See:
|
||||
http://goo.gl/zeJZl
|
||||
|
||||
>>> bytes2human(10000)
|
||||
'9.8K'
|
||||
>>> bytes2human(100001221)
|
||||
'95.4M'
|
||||
"""
|
||||
symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||
prefix = {}
|
||||
for i, s in enumerate(symbols[1:]):
|
||||
prefix[s] = 1 << (i + 1) * 10
|
||||
for symbol in reversed(symbols[1:]):
|
||||
if n >= prefix[symbol]:
|
||||
value = float(n) / prefix[symbol]
|
||||
return format % locals()
|
||||
return format % dict(symbol=symbols[0], value=n)
|
||||
|
||||
|
||||
def get_procfs_path():
|
||||
"""Return updated psutil.PROCFS_PATH constant."""
|
||||
return sys.modules['psutil'].PROCFS_PATH
|
||||
|
||||
|
||||
if PY3:
|
||||
def decode(s):
|
||||
return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
|
||||
else:
|
||||
def decode(s):
|
||||
return s
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- shell utils
|
||||
# =====================================================================
|
||||
|
||||
|
||||
@memoize
|
||||
def term_supports_colors(file=sys.stdout):
|
||||
if os.name == 'nt':
|
||||
return True
|
||||
try:
|
||||
import curses
|
||||
assert file.isatty()
|
||||
curses.setupterm()
|
||||
assert curses.tigetnum("colors") > 0
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def hilite(s, color="green", bold=False):
|
||||
"""Return an highlighted version of 'string'."""
|
||||
if not term_supports_colors():
|
||||
return s
|
||||
attr = []
|
||||
colors = dict(green='32', red='91', brown='33')
|
||||
colors[None] = '29'
|
||||
try:
|
||||
color = colors[color]
|
||||
except KeyError:
|
||||
raise ValueError("invalid color %r; choose between %r" % (
|
||||
list(colors.keys())))
|
||||
attr.append(color)
|
||||
if bold:
|
||||
attr.append('1')
|
||||
return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
|
||||
|
||||
|
||||
def print_color(s, color="green", bold=False, file=sys.stdout):
|
||||
"""Print a colorized version of string."""
|
||||
if not term_supports_colors():
|
||||
print(s, file=file)
|
||||
elif POSIX:
|
||||
print(hilite(s, color, bold), file=file)
|
||||
else:
|
||||
import ctypes
|
||||
|
||||
DEFAULT_COLOR = 7
|
||||
GetStdHandle = ctypes.windll.Kernel32.GetStdHandle
|
||||
SetConsoleTextAttribute = \
|
||||
ctypes.windll.Kernel32.SetConsoleTextAttribute
|
||||
|
||||
colors = dict(green=2, red=4, brown=6)
|
||||
colors[None] = DEFAULT_COLOR
|
||||
try:
|
||||
color = colors[color]
|
||||
except KeyError:
|
||||
raise ValueError("invalid color %r; choose between %r" % (
|
||||
color, list(colors.keys())))
|
||||
if bold and color <= 7:
|
||||
color += 8
|
||||
|
||||
handle_id = -12 if file is sys.stderr else -11
|
||||
GetStdHandle.restype = ctypes.c_ulong
|
||||
handle = GetStdHandle(handle_id)
|
||||
SetConsoleTextAttribute(handle, color)
|
||||
try:
|
||||
print(s, file=file)
|
||||
finally:
|
||||
SetConsoleTextAttribute(handle, DEFAULT_COLOR)
|
||||
|
||||
|
||||
if bool(os.getenv('PSUTIL_DEBUG', 0)):
|
||||
import inspect
|
||||
|
||||
def debug(msg):
|
||||
"""If PSUTIL_DEBUG env var is set, print a debug message to stderr."""
|
||||
fname, lineno, func_name, lines, index = inspect.getframeinfo(
|
||||
inspect.currentframe().f_back)
|
||||
print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg),
|
||||
file=sys.stderr)
|
||||
else:
|
||||
def debug(msg):
|
||||
pass
|
|
@ -1,345 +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.
|
||||
|
||||
"""Module which provides compatibility with older Python versions."""
|
||||
|
||||
import collections
|
||||
import errno
|
||||
import functools
|
||||
import os
|
||||
import sys
|
||||
|
||||
__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
|
||||
"lru_cache", "which", "get_terminal_size",
|
||||
"FileNotFoundError", "PermissionError", "ProcessLookupError",
|
||||
"InterruptedError", "ChildProcessError", "FileExistsError"]
|
||||
|
||||
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
|
||||
|
||||
|
||||
# --- exceptions
|
||||
|
||||
|
||||
if PY3:
|
||||
FileNotFoundError = FileNotFoundError # NOQA
|
||||
PermissionError = PermissionError # NOQA
|
||||
ProcessLookupError = ProcessLookupError # NOQA
|
||||
InterruptedError = InterruptedError # NOQA
|
||||
ChildProcessError = ChildProcessError # NOQA
|
||||
FileExistsError = FileExistsError # NOQA
|
||||
else:
|
||||
# https://github.com/PythonCharmers/python-future/blob/exceptions/
|
||||
# src/future/types/exceptions/pep3151.py
|
||||
import platform
|
||||
|
||||
_singleton = object()
|
||||
|
||||
def instance_checking_exception(base_exception=Exception):
|
||||
def wrapped(instance_checker):
|
||||
class TemporaryClass(base_exception):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(args) == 1 and isinstance(args[0], TemporaryClass):
|
||||
unwrap_me = args[0]
|
||||
for attr in dir(unwrap_me):
|
||||
if not attr.startswith('__'):
|
||||
setattr(self, attr, getattr(unwrap_me, attr))
|
||||
else:
|
||||
super(TemporaryClass, self).__init__(*args, **kwargs)
|
||||
|
||||
class __metaclass__(type):
|
||||
def __instancecheck__(cls, inst):
|
||||
return instance_checker(inst)
|
||||
|
||||
def __subclasscheck__(cls, classinfo):
|
||||
value = sys.exc_info()[1]
|
||||
return isinstance(value, cls)
|
||||
|
||||
TemporaryClass.__name__ = instance_checker.__name__
|
||||
TemporaryClass.__doc__ = instance_checker.__doc__
|
||||
return TemporaryClass
|
||||
|
||||
return wrapped
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def FileNotFoundError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ENOENT
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def ProcessLookupError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ESRCH
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def PermissionError(inst):
|
||||
return getattr(inst, 'errno', _singleton) in (
|
||||
errno.EACCES, errno.EPERM)
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def InterruptedError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.EINTR
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def ChildProcessError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ECHILD
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def FileExistsError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.EEXIST
|
||||
|
||||
if platform.python_implementation() != "CPython":
|
||||
try:
|
||||
raise OSError(errno.EEXIST, "perm")
|
||||
except FileExistsError:
|
||||
pass
|
||||
except OSError:
|
||||
raise RuntimeError(
|
||||
"broken / incompatible Python implementation, see: "
|
||||
"https://github.com/giampaolo/psutil/issues/1659")
|
||||
|
||||
|
||||
# --- stdlib additions
|
||||
|
||||
|
||||
# py 3.2 functools.lru_cache
|
||||
# Taken from: http://code.activestate.com/recipes/578078
|
||||
# Credit: Raymond Hettinger
|
||||
try:
|
||||
from functools import lru_cache
|
||||
except ImportError:
|
||||
try:
|
||||
from threading import RLock
|
||||
except ImportError:
|
||||
from dummy_threading import RLock
|
||||
|
||||
_CacheInfo = collections.namedtuple(
|
||||
"CacheInfo", ["hits", "misses", "maxsize", "currsize"])
|
||||
|
||||
class _HashedSeq(list):
|
||||
__slots__ = 'hashvalue'
|
||||
|
||||
def __init__(self, tup, hash=hash):
|
||||
self[:] = tup
|
||||
self.hashvalue = hash(tup)
|
||||
|
||||
def __hash__(self):
|
||||
return self.hashvalue
|
||||
|
||||
def _make_key(args, kwds, typed,
|
||||
kwd_mark=(object(), ),
|
||||
fasttypes=set((int, str, frozenset, type(None))),
|
||||
sorted=sorted, tuple=tuple, type=type, len=len):
|
||||
key = args
|
||||
if kwds:
|
||||
sorted_items = sorted(kwds.items())
|
||||
key += kwd_mark
|
||||
for item in sorted_items:
|
||||
key += item
|
||||
if typed:
|
||||
key += tuple(type(v) for v in args)
|
||||
if kwds:
|
||||
key += tuple(type(v) for k, v in sorted_items)
|
||||
elif len(key) == 1 and type(key[0]) in fasttypes:
|
||||
return key[0]
|
||||
return _HashedSeq(key)
|
||||
|
||||
def lru_cache(maxsize=100, typed=False):
|
||||
"""Least-recently-used cache decorator, see:
|
||||
http://docs.python.org/3/library/functools.html#functools.lru_cache
|
||||
"""
|
||||
def decorating_function(user_function):
|
||||
cache = dict()
|
||||
stats = [0, 0]
|
||||
HITS, MISSES = 0, 1
|
||||
make_key = _make_key
|
||||
cache_get = cache.get
|
||||
_len = len
|
||||
lock = RLock()
|
||||
root = []
|
||||
root[:] = [root, root, None, None]
|
||||
nonlocal_root = [root]
|
||||
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
|
||||
if maxsize == 0:
|
||||
def wrapper(*args, **kwds):
|
||||
result = user_function(*args, **kwds)
|
||||
stats[MISSES] += 1
|
||||
return result
|
||||
elif maxsize is None:
|
||||
def wrapper(*args, **kwds):
|
||||
key = make_key(args, kwds, typed)
|
||||
result = cache_get(key, root)
|
||||
if result is not root:
|
||||
stats[HITS] += 1
|
||||
return result
|
||||
result = user_function(*args, **kwds)
|
||||
cache[key] = result
|
||||
stats[MISSES] += 1
|
||||
return result
|
||||
else:
|
||||
def wrapper(*args, **kwds):
|
||||
if kwds or typed:
|
||||
key = make_key(args, kwds, typed)
|
||||
else:
|
||||
key = args
|
||||
lock.acquire()
|
||||
try:
|
||||
link = cache_get(key)
|
||||
if link is not None:
|
||||
root, = nonlocal_root
|
||||
link_prev, link_next, key, result = link
|
||||
link_prev[NEXT] = link_next
|
||||
link_next[PREV] = link_prev
|
||||
last = root[PREV]
|
||||
last[NEXT] = root[PREV] = link
|
||||
link[PREV] = last
|
||||
link[NEXT] = root
|
||||
stats[HITS] += 1
|
||||
return result
|
||||
finally:
|
||||
lock.release()
|
||||
result = user_function(*args, **kwds)
|
||||
lock.acquire()
|
||||
try:
|
||||
root, = nonlocal_root
|
||||
if key in cache:
|
||||
pass
|
||||
elif _len(cache) >= maxsize:
|
||||
oldroot = root
|
||||
oldroot[KEY] = key
|
||||
oldroot[RESULT] = result
|
||||
root = nonlocal_root[0] = oldroot[NEXT]
|
||||
oldkey = root[KEY]
|
||||
root[KEY] = root[RESULT] = None
|
||||
del cache[oldkey]
|
||||
cache[key] = oldroot
|
||||
else:
|
||||
last = root[PREV]
|
||||
link = [last, root, key, result]
|
||||
last[NEXT] = root[PREV] = cache[key] = link
|
||||
stats[MISSES] += 1
|
||||
finally:
|
||||
lock.release()
|
||||
return result
|
||||
|
||||
def cache_info():
|
||||
"""Report cache statistics"""
|
||||
lock.acquire()
|
||||
try:
|
||||
return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
|
||||
len(cache))
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def cache_clear():
|
||||
"""Clear the cache and cache statistics"""
|
||||
lock.acquire()
|
||||
try:
|
||||
cache.clear()
|
||||
root = nonlocal_root[0]
|
||||
root[:] = [root, root, None, None]
|
||||
stats[:] = [0, 0]
|
||||
finally:
|
||||
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
|
||||
|
||||
|
||||
# python 3.3
|
||||
try:
|
||||
from shutil import get_terminal_size
|
||||
except ImportError:
|
||||
def get_terminal_size(fallback=(80, 24)):
|
||||
try:
|
||||
import fcntl
|
||||
import termios
|
||||
import struct
|
||||
except ImportError:
|
||||
return fallback
|
||||
else:
|
||||
try:
|
||||
# This should work on Linux.
|
||||
res = struct.unpack(
|
||||
'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234'))
|
||||
return (res[1], res[0])
|
||||
except Exception:
|
||||
return fallback
|
|
@ -1,550 +0,0 @@
|
|||
# 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 functools
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from . import _common
|
||||
from . import _psposix
|
||||
from . import _psutil_aix as cext
|
||||
from . import _psutil_posix as cext_posix
|
||||
from ._common import AccessDenied
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import get_procfs_path
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NIC_DUPLEX_FULL
|
||||
from ._common import NIC_DUPLEX_HALF
|
||||
from ._common import NIC_DUPLEX_UNKNOWN
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
|
||||
|
||||
__extra__all__ = ["PROCFS_PATH"]
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
HAS_THREADS = hasattr(cext, "proc_threads")
|
||||
HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters")
|
||||
HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters")
|
||||
|
||||
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
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'])
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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
|
||||
|
||||
if HAS_NET_IO_COUNTERS:
|
||||
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 = []
|
||||
for item in rawlist:
|
||||
fd, fam, type_, laddr, raddr, status, pid = item
|
||||
if fam not in families:
|
||||
continue
|
||||
if type_ not in types:
|
||||
continue
|
||||
nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status,
|
||||
TCP_STATUSES, pid=pid if _pid == -1 else None)
|
||||
ret.append(nt)
|
||||
return ret
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""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(
|
||||
r"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.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except (FileNotFoundError, ProcessLookupError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
return wrapper
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||
|
||||
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_basic_info.cache_activate(self)
|
||||
self._proc_cred.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._proc_basic_info.cache_deactivate(self)
|
||||
self._proc_cred.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_basic_info(self):
|
||||
return cext.proc_basic_info(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_cred(self):
|
||||
return cext.proc_cred(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
if self.pid == 0:
|
||||
return "swapper"
|
||||
# note: max 16 characters
|
||||
return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00")
|
||||
|
||||
@wrap_exceptions
|
||||
def exe(self):
|
||||
# there is no way to get executable path in AIX other than to guess,
|
||||
# and guessing is more complex than what's in the wrapping class
|
||||
cmdline = self.cmdline()
|
||||
if not cmdline:
|
||||
return ''
|
||||
exe = cmdline[0]
|
||||
if os.path.sep in exe:
|
||||
# relative or absolute path
|
||||
if not os.path.isabs(exe):
|
||||
# if cwd has changed, we're out of luck - this may be wrong!
|
||||
exe = os.path.abspath(os.path.join(self.cwd(), exe))
|
||||
if (os.path.isabs(exe) and
|
||||
os.path.isfile(exe) and
|
||||
os.access(exe, os.X_OK)):
|
||||
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 cext.proc_args(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def environ(self):
|
||||
return cext.proc_environ(self.pid)
|
||||
|
||||
@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 FileNotFoundError:
|
||||
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
|
||||
return None
|
||||
|
||||
@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(r"(\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)
|
||||
|
||||
if HAS_PROC_IO_COUNTERS:
|
||||
@wrap_exceptions
|
||||
def io_counters(self):
|
||||
try:
|
||||
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
|
||||
except OSError:
|
||||
# if process is terminated, proc_io_counters returns OSError
|
||||
# instead of NSP
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
raise
|
||||
return _common.pio(rc, wc, rb, wb)
|
|
@ -1,903 +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.
|
||||
|
||||
"""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 collections import defaultdict
|
||||
|
||||
from . import _common
|
||||
from . import _psposix
|
||||
from . import _psutil_bsd as cext
|
||||
from . import _psutil_posix as cext_posix
|
||||
from ._common import AccessDenied
|
||||
from ._common import conn_tmap
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import FREEBSD
|
||||
from ._common import memoize
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NETBSD
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import OPENBSD
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import which
|
||||
|
||||
|
||||
__extra__all__ = []
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
if NETBSD:
|
||||
PAGESIZE = os.sysconf("SC_PAGESIZE")
|
||||
else:
|
||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
|
||||
HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads")
|
||||
HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files')
|
||||
HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds')
|
||||
|
||||
kinfo_proc_map = dict(
|
||||
ppid=0,
|
||||
status=1,
|
||||
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'])
|
||||
# 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'])
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 = 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 HAS_PER_CPU_TIMES:
|
||||
def per_cpu_times():
|
||||
"""Return system CPU times as a namedtuple"""
|
||||
ret = []
|
||||
for cpu_t in cext.per_cpu_times():
|
||||
user, nice, system, idle, irq = cpu_t
|
||||
item = scputimes(user, nice, system, idle, irq)
|
||||
ret.append(item)
|
||||
return ret
|
||||
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()
|
||||
|
||||
|
||||
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 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
|
||||
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
|
||||
retlist.append(ntuple)
|
||||
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:
|
||||
try:
|
||||
mtu = cext_posix.net_if_mtu(name)
|
||||
isup = cext_posix.net_if_flags(name)
|
||||
duplex, speed = cext_posix.net_if_duplex_speed(name)
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1279
|
||||
if err.errno != errno.ENODEV:
|
||||
raise
|
||||
else:
|
||||
if hasattr(_common, 'NicDuplex'):
|
||||
duplex = _common.NicDuplex(duplex)
|
||||
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
|
||||
return ret
|
||||
|
||||
|
||||
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()
|
||||
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:
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES, pid)
|
||||
ret.add(nt)
|
||||
return list(ret)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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)
|
||||
|
||||
def sensors_temperatures():
|
||||
"Return CPU cores temperatures if available, else an empty dict."
|
||||
ret = defaultdict(list)
|
||||
num_cpus = cpu_count_logical()
|
||||
for cpu in range(num_cpus):
|
||||
try:
|
||||
current, high = cext.sensors_cpu_temperature(cpu)
|
||||
if high <= 0:
|
||||
high = None
|
||||
name = "Core %s" % cpu
|
||||
ret["coretemp"].append(
|
||||
_common.shwtemp(name, current, high, high))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
return ret
|
||||
|
||||
def cpu_freq():
|
||||
"""Return frequency metrics for CPUs. As of Dec 2018 only
|
||||
CPU 0 appears to be supported by FreeBSD and all other cores
|
||||
match the frequency of CPU 0.
|
||||
"""
|
||||
ret = []
|
||||
num_cpus = cpu_count_logical()
|
||||
for cpu in range(num_cpus):
|
||||
try:
|
||||
current, available_freq = cext.cpu_frequency(cpu)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
if available_freq:
|
||||
try:
|
||||
min_freq = int(available_freq.split(" ")[-1].split("/")[0])
|
||||
except(IndexError, ValueError):
|
||||
min_freq = None
|
||||
try:
|
||||
max_freq = int(available_freq.split(" ")[0].split("/")[0])
|
||||
except(IndexError, ValueError):
|
||||
max_freq = None
|
||||
ret.append(_common.scpufreq(current, min_freq, max_freq))
|
||||
return ret
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- other system functions
|
||||
# =====================================================================
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 is_zombie(pid):
|
||||
try:
|
||||
st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']]
|
||||
return st == cext.SZOMB
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def wrap_exceptions(fun):
|
||||
"""Decorator which translates bare OSError exceptions into
|
||||
NoSuchProcess and AccessDenied.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except ProcessLookupError:
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except OSError:
|
||||
if self.pid == 0:
|
||||
if 0 in pids():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wrap_exceptions_procfs(inst):
|
||||
"""Same as above, for routines relying on reading /proc fs."""
|
||||
try:
|
||||
yield
|
||||
except (ProcessLookupError, FileNotFoundError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(inst.pid):
|
||||
raise NoSuchProcess(inst.pid, inst._name)
|
||||
else:
|
||||
raise ZombieProcess(inst.pid, inst._name, inst._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(inst.pid, inst._name)
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
|
||||
def _assert_alive(self):
|
||||
"""Raise NSP if the process disappeared on us."""
|
||||
# For those C function who do not raise NSP, possibly returning
|
||||
# incorrect or incomplete result.
|
||||
cext.proc_name(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def oneshot(self):
|
||||
"""Retrieves multiple process info in one shot as a raw tuple."""
|
||||
ret = cext.proc_oneshot_info(self.pid)
|
||||
assert len(ret) == len(kinfo_proc_map)
|
||||
return ret
|
||||
|
||||
def oneshot_enter(self):
|
||||
self.oneshot.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self.oneshot.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
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):
|
||||
if FREEBSD:
|
||||
if self.pid == 0:
|
||||
return '' # else NSP
|
||||
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]) or ""
|
||||
else:
|
||||
return ""
|
||||
|
||||
@wrap_exceptions
|
||||
def cmdline(self):
|
||||
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 is_zombie(self.pid):
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
elif not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name, self._ppid)
|
||||
else:
|
||||
# XXX: this happens with unicode tests. It means the C
|
||||
# routine is unable to decode invalid unicode chars.
|
||||
return []
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
return cext.proc_cmdline(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def terminal(self):
|
||||
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):
|
||||
self._ppid = self.oneshot()[kinfo_proc_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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']])
|
||||
|
||||
memory_full_info = memory_info
|
||||
|
||||
@wrap_exceptions
|
||||
def create_time(self):
|
||||
return self.oneshot()[kinfo_proc_map['create_time']]
|
||||
|
||||
@wrap_exceptions
|
||||
def num_threads(self):
|
||||
if HAS_PROC_NUM_THREADS:
|
||||
# FreeBSD
|
||||
return cext.proc_num_threads(self.pid)
|
||||
else:
|
||||
return len(self.threads())
|
||||
|
||||
@wrap_exceptions
|
||||
def num_ctx_switches(self):
|
||||
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:
|
||||
self._assert_alive()
|
||||
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 = []
|
||||
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:
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
self._assert_alive()
|
||||
return list(ret)
|
||||
|
||||
families, types = conn_tmap[kind]
|
||||
rawlist = cext.proc_connections(self.pid, families, types)
|
||||
ret = []
|
||||
for item in rawlist:
|
||||
fd, fam, type, laddr, raddr, status = item
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
|
||||
if OPENBSD:
|
||||
self._assert_alive()
|
||||
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
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 = 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):
|
||||
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 or HAS_PROC_OPEN_FILES:
|
||||
# FreeBSD < 8 does not support functions based on
|
||||
# kinfo_getfile() and kinfo_getvmmap()
|
||||
return cext.proc_cwd(self.pid) or None
|
||||
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 HAS_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 HAS_PROC_NUM_FDS:
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
"""Return the number of file descriptors opened by this process."""
|
||||
ret = cext.proc_num_fds(self.pid)
|
||||
if NETBSD:
|
||||
self._assert_alive()
|
||||
return ret
|
||||
else:
|
||||
num_fds = _not_implemented
|
||||
|
||||
# --- FreeBSD only APIs
|
||||
|
||||
if FREEBSD:
|
||||
|
||||
@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
|
||||
|
||||
@wrap_exceptions
|
||||
def memory_maps(self):
|
||||
return cext.proc_memory_maps(self.pid)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,564 +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.
|
||||
|
||||
"""macOS platform implementation."""
|
||||
|
||||
import contextlib
|
||||
import errno
|
||||
import functools
|
||||
import os
|
||||
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 AccessDenied
|
||||
from ._common import conn_tmap
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import isfile_strict
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import parse_environ_block
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
|
||||
|
||||
__extra__all__ = []
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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'])
|
||||
# psutil.Process.memory_info()
|
||||
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
|
||||
# psutil.Process.memory_full_info()
|
||||
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- memory
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def virtual_memory():
|
||||
"""System virtual memory as a namedtuple."""
|
||||
total, active, inactive, wired, free, speculative = cext.virtual_mem()
|
||||
# This is how Zabbix calculate avail and used mem:
|
||||
# https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
|
||||
# osx/memory.c
|
||||
# Also see: https://github.com/giampaolo/psutil/issues/1277
|
||||
avail = inactive + free
|
||||
used = active + wired
|
||||
# This is NOT how Zabbix calculates free mem but it matches "free"
|
||||
# cmdline utility.
|
||||
free -= speculative
|
||||
percent = usage_percent((total - avail), total, round_=1)
|
||||
return svmem(total, avail, percent, used, free,
|
||||
active, inactive, wired)
|
||||
|
||||
|
||||
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"""
|
||||
ret = []
|
||||
for cpu_t in cext.per_cpu_times():
|
||||
user, nice, system, idle = cpu_t
|
||||
item = scputimes(user, nice, system, idle)
|
||||
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 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 macOS 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
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 macOS 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
|
||||
else:
|
||||
if cons:
|
||||
for c in cons:
|
||||
c = list(c) + [pid]
|
||||
ret.append(_common.sconn(*c))
|
||||
return ret
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""Get NIC stats (isup, duplex, speed, mtu)."""
|
||||
names = net_io_counters().keys()
|
||||
ret = {}
|
||||
for name in names:
|
||||
try:
|
||||
mtu = cext_posix.net_if_mtu(name)
|
||||
isup = cext_posix.net_if_flags(name)
|
||||
duplex, speed = cext_posix.net_if_duplex_speed(name)
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1279
|
||||
if err.errno != errno.ENODEV:
|
||||
raise
|
||||
else:
|
||||
if hasattr(_common, 'NicDuplex'):
|
||||
duplex = _common.NicDuplex(duplex)
|
||||
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
|
||||
return ret
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 macOS versions pids() C doesn't return PID 0 but
|
||||
# "ps" does and the process is querable via sysctl():
|
||||
# https://travis-ci.org/giampaolo/psutil/jobs/309619941
|
||||
try:
|
||||
Process(0).create_time()
|
||||
ls.insert(0, 0)
|
||||
except NoSuchProcess:
|
||||
pass
|
||||
except AccessDenied:
|
||||
ls.insert(0, 0)
|
||||
return ls
|
||||
|
||||
|
||||
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 ProcessLookupError:
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except cext.ZombieProcessError:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
return wrapper
|
||||
|
||||
|
||||
@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", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _get_kinfo_proc(self):
|
||||
# Note: should work with all PIDs without permission issues.
|
||||
ret = cext.proc_kinfo_oneshot(self.pid)
|
||||
assert len(ret) == len(kinfo_proc_map)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _get_pidtaskinfo(self):
|
||||
# Note: should work for PIDs owned by user only.
|
||||
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)
|
||||
self._get_pidtaskinfo.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._get_kinfo_proc.cache_deactivate(self)
|
||||
self._get_pidtaskinfo.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
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):
|
||||
with catch_zombie(self):
|
||||
return cext.proc_exe(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def cmdline(self):
|
||||
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):
|
||||
self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def cwd(self):
|
||||
with catch_zombie(self):
|
||||
return cext.proc_cwd(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
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):
|
||||
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 = 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):
|
||||
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_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):
|
||||
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 self._get_kinfo_proc()[kinfo_proc_map['ctime']]
|
||||
|
||||
@wrap_exceptions
|
||||
def num_ctx_switches(self):
|
||||
# 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 self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
|
||||
|
||||
@wrap_exceptions
|
||||
def open_files(self):
|
||||
if self.pid == 0:
|
||||
return []
|
||||
files = []
|
||||
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]
|
||||
with catch_zombie(self):
|
||||
rawlist = cext.proc_connections(self.pid, families, types)
|
||||
ret = []
|
||||
for item in rawlist:
|
||||
fd, fam, type, laddr, raddr, status = item
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
if self.pid == 0:
|
||||
return 0
|
||||
with catch_zombie(self):
|
||||
return cext.proc_num_fds(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
return _psposix.wait_pid(self.pid, timeout, self._name)
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_get(self):
|
||||
with catch_zombie(self):
|
||||
return cext_posix.getpriority(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_set(self, value):
|
||||
with catch_zombie(self):
|
||||
return cext_posix.setpriority(self.pid, value)
|
||||
|
||||
@wrap_exceptions
|
||||
def status(self):
|
||||
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)
|
||||
retlist = []
|
||||
for thread_id, utime, stime in rawlist:
|
||||
ntuple = _common.pthread(thread_id, utime, stime)
|
||||
retlist.append(ntuple)
|
||||
return retlist
|
|
@ -1,175 +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.
|
||||
|
||||
"""Routines common to all posix systems."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from ._common import memoize
|
||||
from ._common import sdiskusage
|
||||
from ._common import TimeoutExpired
|
||||
from ._common import usage_percent
|
||||
from ._compat import ChildProcessError
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import InterruptedError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
from ._compat import unicode
|
||||
|
||||
|
||||
__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.
|
||||
# If we get here it means this UNIX platform *does* have
|
||||
# a process with id 0.
|
||||
return True
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except ProcessLookupError:
|
||||
return False
|
||||
except PermissionError:
|
||||
# EPERM clearly means there's a process to deny access to
|
||||
return True
|
||||
# According to "man 2 kill" possible error values are
|
||||
# (EINVAL, EPERM, ESRCH)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
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(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
|
||||
else:
|
||||
def waitcall():
|
||||
return os.waitpid(pid, 0)
|
||||
|
||||
delay = 0.0001
|
||||
while True:
|
||||
try:
|
||||
retpid, status = waitcall()
|
||||
except InterruptedError:
|
||||
delay = check_timeout(delay)
|
||||
except ChildProcessError:
|
||||
# This has two meanings:
|
||||
# - pid is not a child of os.getpid() in which case
|
||||
# we keep polling until it's gone
|
||||
# - pid never existed in the first place
|
||||
# In both cases we'll eventually return None as we
|
||||
# can't determine its exit status code.
|
||||
while True:
|
||||
if pid_exists(pid):
|
||||
delay = check_timeout(delay)
|
||||
else:
|
||||
return
|
||||
else:
|
||||
if retpid == 0:
|
||||
# WNOHANG was used, pid is still running
|
||||
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)
|
||||
# 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 ValueError("unknown process exit status %r" % status)
|
||||
|
||||
|
||||
def disk_usage(path):
|
||||
"""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)
|
||||
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)
|
||||
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)
|
||||
# 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:
|
||||
# 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():
|
||||
"""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, name
|
||||
try:
|
||||
ret[os.stat(name).st_rdev] = name
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return ret
|
|
@ -1,725 +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.
|
||||
|
||||
"""Sun OS Solaris platform implementation."""
|
||||
|
||||
import errno
|
||||
import functools
|
||||
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 AccessDenied
|
||||
from ._common import AF_INET6
|
||||
from ._common import debug
|
||||
from ._common import get_procfs_path
|
||||
from ._common import isfile_strict
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import sockfam_to_enum
|
||||
from ._common import socktype_to_enum
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import b
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
|
||||
|
||||
__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,
|
||||
cext.SSTOP: _common.STATUS_STOPPED,
|
||||
cext.SIDL: _common.STATUS_IDLE,
|
||||
cext.SONPROC: _common.STATUS_RUNNING, # same as run
|
||||
cext.SWAIT: _common.STATUS_WAITING,
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
uid=8,
|
||||
euid=9,
|
||||
gid=10,
|
||||
egid=11)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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'])
|
||||
# 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))
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- memory
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def virtual_memory():
|
||||
"""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'],
|
||||
stdout=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if PY3:
|
||||
stdout = stdout.decode(sys.stdout.encoding)
|
||||
if p.returncode != 0:
|
||||
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:]
|
||||
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)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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():
|
||||
"""Return the number of physical CPUs in the system."""
|
||||
return cext.cpu_count_phys()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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.
|
||||
try:
|
||||
if not disk_usage(mountpoint).total:
|
||||
continue
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1674
|
||||
debug("skipping %r: %r" % (mountpoint, err))
|
||||
continue
|
||||
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
|
||||
retlist.append(ntuple)
|
||||
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)
|
||||
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
|
||||
# TODO: refactor and use _common.conn_to_ntuple.
|
||||
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)
|
||||
return list(ret)
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""Get NIC stats (isup, duplex, speed, mtu)."""
|
||||
ret = cext.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.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except (FileNotFoundError, ProcessLookupError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except OSError:
|
||||
if self.pid == 0:
|
||||
if 0 in pids():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
self._procfs_path = get_procfs_path()
|
||||
|
||||
def _assert_alive(self):
|
||||
"""Raise NSP if the process disappeared on us."""
|
||||
# For those C function who do not raise NSP, possibly returning
|
||||
# incorrect or incomplete result.
|
||||
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
||||
|
||||
def oneshot_enter(self):
|
||||
self._proc_name_and_args.cache_activate(self)
|
||||
self._proc_basic_info.cache_activate(self)
|
||||
self._proc_cred.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._proc_name_and_args.cache_deactivate(self)
|
||||
self._proc_basic_info.cache_deactivate(self)
|
||||
self._proc_cred.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_name_and_args(self):
|
||||
return cext.proc_name_and_args(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_basic_info(self):
|
||||
if self.pid == 0 and not \
|
||||
os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
|
||||
raise AccessDenied(self.pid)
|
||||
ret = cext.proc_basic_info(self.pid, self._procfs_path)
|
||||
assert len(ret) == len(proc_info_map)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_cred(self):
|
||||
return cext.proc_cred(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
# note: max len == 15
|
||||
return self._proc_name_and_args()[0]
|
||||
|
||||
@wrap_exceptions
|
||||
def exe(self):
|
||||
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 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 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']]
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_get(self):
|
||||
# Note #1: getpriority(3) doesn't work for realtime processes.
|
||||
# Psinfo is what ps uses, see:
|
||||
# https://github.com/giampaolo/psutil/issues/1194
|
||||
return self._proc_basic_info()[proc_info_map['nice']]
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_set(self, value):
|
||||
if self.pid in (2, 3):
|
||||
# Special case PIDs: internally setpriority(3) return ESRCH
|
||||
# (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):
|
||||
self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
try:
|
||||
real, effective, saved, _, _, _ = self._proc_cred()
|
||||
except AccessDenied:
|
||||
real = self._proc_basic_info()[proc_info_map['uid']]
|
||||
effective = self._proc_basic_info()[proc_info_map['euid']]
|
||||
saved = None
|
||||
return _common.puids(real, effective, saved)
|
||||
|
||||
@wrap_exceptions
|
||||
def gids(self):
|
||||
try:
|
||||
_, _, _, real, effective, saved = self._proc_cred()
|
||||
except AccessDenied:
|
||||
real = self._proc_basic_info()[proc_info_map['gid']]
|
||||
effective = self._proc_basic_info()[proc_info_map['egid']]
|
||||
saved = None
|
||||
return _common.puids(real, effective, saved)
|
||||
|
||||
@wrap_exceptions
|
||||
def cpu_times(self):
|
||||
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(
|
||||
self._proc_basic_info()[proc_info_map['ttynr']])
|
||||
if tty != cext.PRNODEV:
|
||||
for x in (0, 1, 2, 255):
|
||||
try:
|
||||
return os.readlink(
|
||||
'%s/%d/path/%d' % (procfs_path, self.pid, x))
|
||||
except FileNotFoundError:
|
||||
hit_enoent = True
|
||||
continue
|
||||
if hit_enoent:
|
||||
self._assert_alive()
|
||||
|
||||
@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("%s/%s/path/cwd" % (procfs_path, self.pid))
|
||||
except FileNotFoundError:
|
||||
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
|
||||
return None
|
||||
|
||||
@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, '?')
|
||||
|
||||
@wrap_exceptions
|
||||
def threads(self):
|
||||
procfs_path = self._procfs_path
|
||||
ret = []
|
||||
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, 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:
|
||||
self._assert_alive()
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def open_files(self):
|
||||
retlist = []
|
||||
hit_enoent = False
|
||||
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 FileNotFoundError:
|
||||
hit_enoent = True
|
||||
continue
|
||||
else:
|
||||
if isfile_strict(file):
|
||||
retlist.append(_common.popenfile(file, int(fd)))
|
||||
if hit_enoent:
|
||||
self._assert_alive()
|
||||
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,
|
||||
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:
|
||||
if 'permission denied' in stderr.lower():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
if 'no such process' in stderr.lower():
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
raise RuntimeError("%r command error\n%s" % (cmd, stderr))
|
||||
|
||||
lines = stdout.split('\n')[2:]
|
||||
for i, line in enumerate(lines):
|
||||
line = line.lstrip()
|
||||
if line.startswith('sockname: AF_UNIX'):
|
||||
path = line.split(' ', 2)[2]
|
||||
type = lines[i - 2].strip()
|
||||
if type == 'SOCK_STREAM':
|
||||
type = socket.SOCK_STREAM
|
||||
elif type == 'SOCK_DGRAM':
|
||||
type = socket.SOCK_DGRAM
|
||||
else:
|
||||
type = -1
|
||||
yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
|
||||
|
||||
@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))
|
||||
|
||||
# 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 = []
|
||||
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(
|
||||
'%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 = '%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:
|
||||
self._assert_alive()
|
||||
return retlist
|
||||
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
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, self._procfs_path))
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
return _psposix.wait_pid(self.pid, timeout, self._name)
|
Двоичные данные
third_party/python/psutil-cp27-none-win_amd64/psutil/_psutil_windows.pyd
поставляемый
Двоичные данные
third_party/python/psutil-cp27-none-win_amd64/psutil/_psutil_windows.pyd
поставляемый
Двоичный файл не отображается.
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""
|
||||
Run unit tests. This is invoked by:
|
||||
$ python -m psutil.tests
|
||||
"""
|
||||
|
||||
from .runner import main
|
||||
main()
|
|
@ -1,162 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""
|
||||
Unit test runner, providing new features on top of unittest module:
|
||||
- colourized output (error, skip)
|
||||
- print failures/tracebacks on CTRL+C
|
||||
- re-run failed tests only (make test-failed)
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import TestResult
|
||||
from unittest import TextTestResult
|
||||
from unittest import TextTestRunner
|
||||
try:
|
||||
import ctypes
|
||||
except ImportError:
|
||||
ctypes = None
|
||||
|
||||
import psutil
|
||||
from psutil._common import hilite
|
||||
from psutil._common import print_color
|
||||
from psutil._common import term_supports_colors
|
||||
from psutil.tests import safe_rmpath
|
||||
from psutil.tests import TOX
|
||||
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
VERBOSITY = 1 if TOX else 2
|
||||
FAILED_TESTS_FNAME = '.failed-tests.txt'
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- unittest subclasses
|
||||
# =====================================================================
|
||||
|
||||
|
||||
class ColouredResult(TextTestResult):
|
||||
|
||||
def _print_color(self, s, color, bold=False):
|
||||
file = sys.stderr if color == "red" else sys.stdout
|
||||
print_color(s, color, bold=bold, file=file)
|
||||
|
||||
def addSuccess(self, test):
|
||||
TestResult.addSuccess(self, test)
|
||||
self._print_color("OK", "green")
|
||||
|
||||
def addError(self, test, err):
|
||||
TestResult.addError(self, test, err)
|
||||
self._print_color("ERROR", "red", bold=True)
|
||||
|
||||
def addFailure(self, test, err):
|
||||
TestResult.addFailure(self, test, err)
|
||||
self._print_color("FAIL", "red")
|
||||
|
||||
def addSkip(self, test, reason):
|
||||
TestResult.addSkip(self, test, reason)
|
||||
self._print_color("skipped: %s" % reason, "brown")
|
||||
|
||||
def printErrorList(self, flavour, errors):
|
||||
flavour = hilite(flavour, "red", bold=flavour == 'ERROR')
|
||||
TextTestResult.printErrorList(self, flavour, errors)
|
||||
|
||||
|
||||
class ColouredRunner(TextTestRunner):
|
||||
resultclass = ColouredResult if term_supports_colors() else TextTestResult
|
||||
|
||||
def _makeResult(self):
|
||||
# Store result instance so that it can be accessed on
|
||||
# KeyboardInterrupt.
|
||||
self.result = TextTestRunner._makeResult(self)
|
||||
return self.result
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- public API
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def setup_tests():
|
||||
if 'PSUTIL_TESTING' not in os.environ:
|
||||
# This won't work on Windows but set_testing() below will do it.
|
||||
os.environ['PSUTIL_TESTING'] = '1'
|
||||
psutil._psplatform.cext.set_testing()
|
||||
|
||||
|
||||
def get_suite(name=None):
|
||||
suite = unittest.TestSuite()
|
||||
if name is None:
|
||||
testmods = [os.path.splitext(x)[0] for x in os.listdir(HERE)
|
||||
if x.endswith('.py') and x.startswith('test_') and not
|
||||
x.startswith('test_memory_leaks')]
|
||||
if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ:
|
||||
testmods = [x for x in testmods if not x.endswith((
|
||||
"osx", "posix", "linux"))]
|
||||
for tm in testmods:
|
||||
# ...so that the full test paths are printed on screen
|
||||
tm = "psutil.tests.%s" % tm
|
||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
|
||||
else:
|
||||
name = os.path.splitext(os.path.basename(name))[0]
|
||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(name))
|
||||
return suite
|
||||
|
||||
|
||||
def get_suite_from_failed():
|
||||
# ...from previously failed test run
|
||||
suite = unittest.TestSuite()
|
||||
if not os.path.isfile(FAILED_TESTS_FNAME):
|
||||
return suite
|
||||
with open(FAILED_TESTS_FNAME, 'rt') as f:
|
||||
names = f.read().split()
|
||||
for n in names:
|
||||
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(n))
|
||||
return suite
|
||||
|
||||
|
||||
def save_failed_tests(result):
|
||||
if result.wasSuccessful():
|
||||
return safe_rmpath(FAILED_TESTS_FNAME)
|
||||
with open(FAILED_TESTS_FNAME, 'wt') as f:
|
||||
for t in result.errors + result.failures:
|
||||
tname = str(t[0])
|
||||
unittest.defaultTestLoader.loadTestsFromName(tname)
|
||||
f.write(tname + '\n')
|
||||
|
||||
|
||||
def run(name=None, last_failed=False):
|
||||
setup_tests()
|
||||
runner = ColouredRunner(verbosity=VERBOSITY)
|
||||
suite = get_suite_from_failed() if last_failed else get_suite(name)
|
||||
try:
|
||||
result = runner.run(suite)
|
||||
except (KeyboardInterrupt, SystemExit) as err:
|
||||
print("received %s" % err.__class__.__name__, file=sys.stderr)
|
||||
runner.result.printErrors()
|
||||
sys.exit(1)
|
||||
else:
|
||||
save_failed_tests(result)
|
||||
success = result.wasSuccessful()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
def main():
|
||||
usage = "python3 -m psutil.tests [opts]"
|
||||
parser = optparse.OptionParser(usage=usage, description="run unit tests")
|
||||
parser.add_option("--last-failed",
|
||||
action="store_true", default=False,
|
||||
help="only run last failed tests")
|
||||
opts, args = parser.parse_args()
|
||||
run(last_failed=opts.last_failed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,121 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# 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 specific tests."""
|
||||
|
||||
import re
|
||||
|
||||
from psutil import AIX
|
||||
from psutil.tests import sh
|
||||
from psutil.tests import unittest
|
||||
import psutil
|
||||
|
||||
|
||||
@unittest.skipIf(not AIX, "AIX only")
|
||||
class AIXSpecificTestCase(unittest.TestCase):
|
||||
|
||||
def test_virtual_memory(self):
|
||||
out = sh('/usr/bin/svmon -O unit=KB')
|
||||
re_pattern = r"memory\s*"
|
||||
for field in ("size inuse free pin virtual available mmode").split():
|
||||
re_pattern += r"(?P<%s>\S+)\s+" % (field,)
|
||||
matchobj = re.search(re_pattern, out)
|
||||
|
||||
self.assertIsNotNone(
|
||||
matchobj, "svmon command returned unexpected output")
|
||||
|
||||
KB = 1024
|
||||
total = int(matchobj.group("size")) * KB
|
||||
available = int(matchobj.group("available")) * KB
|
||||
used = int(matchobj.group("inuse")) * KB
|
||||
free = int(matchobj.group("free")) * KB
|
||||
|
||||
psutil_result = psutil.virtual_memory()
|
||||
|
||||
# MEMORY_TOLERANCE from psutil.tests is not enough. For some reason
|
||||
# we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance
|
||||
# when compared to GBs.
|
||||
MEMORY_TOLERANCE = 2 * KB * KB # 2 MB
|
||||
self.assertEqual(psutil_result.total, total)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.used, used, delta=MEMORY_TOLERANCE)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.available, available, delta=MEMORY_TOLERANCE)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.free, free, delta=MEMORY_TOLERANCE)
|
||||
|
||||
def test_swap_memory(self):
|
||||
out = sh('/usr/sbin/lsps -a')
|
||||
# From the man page, "The size is given in megabytes" so we assume
|
||||
# we'll always have 'MB' in the result
|
||||
# TODO maybe try to use "swap -l" to check "used" too, but its units
|
||||
# are not guaranteed to be "MB" so parsing may not be consistent
|
||||
matchobj = re.search(r"(?P<space>\S+)\s+"
|
||||
r"(?P<vol>\S+)\s+"
|
||||
r"(?P<vg>\S+)\s+"
|
||||
r"(?P<size>\d+)MB", out)
|
||||
|
||||
self.assertIsNotNone(
|
||||
matchobj, "lsps command returned unexpected output")
|
||||
|
||||
total_mb = int(matchobj.group("size"))
|
||||
MB = 1024 ** 2
|
||||
psutil_result = psutil.swap_memory()
|
||||
# we divide our result by MB instead of multiplying the lsps value by
|
||||
# MB because lsps may round down, so we round down too
|
||||
self.assertEqual(int(psutil_result.total / MB), total_mb)
|
||||
|
||||
def test_cpu_stats(self):
|
||||
out = sh('/usr/bin/mpstat -a')
|
||||
|
||||
re_pattern = r"ALL\s*"
|
||||
for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq "
|
||||
"push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd "
|
||||
"sysc").split():
|
||||
re_pattern += r"(?P<%s>\S+)\s+" % (field,)
|
||||
matchobj = re.search(re_pattern, out)
|
||||
|
||||
self.assertIsNotNone(
|
||||
matchobj, "mpstat command returned unexpected output")
|
||||
|
||||
# numbers are usually in the millions so 1000 is ok for tolerance
|
||||
CPU_STATS_TOLERANCE = 1000
|
||||
psutil_result = psutil.cpu_stats()
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.ctx_switches,
|
||||
int(matchobj.group("cs")),
|
||||
delta=CPU_STATS_TOLERANCE)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.syscalls,
|
||||
int(matchobj.group("sysc")),
|
||||
delta=CPU_STATS_TOLERANCE)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.interrupts,
|
||||
int(matchobj.group("dev")),
|
||||
delta=CPU_STATS_TOLERANCE)
|
||||
self.assertAlmostEqual(
|
||||
psutil_result.soft_interrupts,
|
||||
int(matchobj.group("soft")),
|
||||
delta=CPU_STATS_TOLERANCE)
|
||||
|
||||
def test_cpu_count_logical(self):
|
||||
out = sh('/usr/bin/mpstat -a')
|
||||
mpstat_lcpu = int(re.search(r"lcpu=(\d+)", out).group(1))
|
||||
psutil_lcpu = psutil.cpu_count(logical=True)
|
||||
self.assertEqual(mpstat_lcpu, psutil_lcpu)
|
||||
|
||||
def test_net_if_addrs_names(self):
|
||||
out = sh('/etc/ifconfig -l')
|
||||
ifconfig_names = set(out.split())
|
||||
psutil_names = set(psutil.net_if_addrs().keys())
|
||||
self.assertSetEqual(ifconfig_names, psutil_names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,565 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# 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_on_failure
|
||||
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 BSDTestCase(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 FreeBSDProcessTestCase(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.pid = get_test_subprocess().pid
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
reap_children()
|
||||
|
||||
@retry_on_failure()
|
||||
def test_memory_maps(self):
|
||||
out = sh('procstat -v %s' % self.pid)
|
||||
maps = psutil.Process(self.pid).memory_maps(grouped=False)
|
||||
lines = out.split('\n')[1:]
|
||||
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))
|
||||
|
||||
@retry_on_failure()
|
||||
def test_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_on_failure()
|
||||
def test_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")
|
||||
|
||||
|
||||
@unittest.skipIf(not FREEBSD, "FREEBSD only")
|
||||
class FreeBSDSystemTestCase(unittest.TestCase):
|
||||
|
||||
@staticmethod
|
||||
def parse_swapinfo():
|
||||
# the last line is always the total
|
||||
output = sh("swapinfo -k").splitlines()[-1]
|
||||
parts = re.split(r'\s+', output)
|
||||
|
||||
if not parts:
|
||||
raise ValueError("Can't parse swapinfo: %s" % output)
|
||||
|
||||
# the size is in 1k units, so multiply by 1024
|
||||
total, used, free = (int(p) * 1024 for p in parts[1:4])
|
||||
return total, used, free
|
||||
|
||||
def test_cpu_frequency_against_sysctl(self):
|
||||
# Currently only cpu 0 is frequency is supported in FreeBSD
|
||||
# All other cores use the same frequency.
|
||||
sensor = "dev.cpu.0.freq"
|
||||
try:
|
||||
sysctl_result = int(sysctl(sensor))
|
||||
except RuntimeError:
|
||||
self.skipTest("frequencies not supported by kernel")
|
||||
self.assertEqual(psutil.cpu_freq().current, sysctl_result)
|
||||
|
||||
sensor = "dev.cpu.0.freq_levels"
|
||||
sysctl_result = sysctl(sensor)
|
||||
# sysctl returns a string of the format:
|
||||
# <freq_level_1>/<voltage_level_1> <freq_level_2>/<voltage_level_2>...
|
||||
# Ordered highest available to lowest available.
|
||||
max_freq = int(sysctl_result.split()[0].split("/")[0])
|
||||
min_freq = int(sysctl_result.split()[-1].split("/")[0])
|
||||
self.assertEqual(psutil.cpu_freq().max, max_freq)
|
||||
self.assertEqual(psutil.cpu_freq().min, min_freq)
|
||||
|
||||
# --- virtual_memory(); tests against sysctl
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_active(self):
|
||||
syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE
|
||||
self.assertAlmostEqual(psutil.virtual_memory().active, syst,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_inactive(self):
|
||||
syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE
|
||||
self.assertAlmostEqual(psutil.virtual_memory().inactive, syst,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_wired(self):
|
||||
syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE
|
||||
self.assertAlmostEqual(psutil.virtual_memory().wired, syst,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_cached(self):
|
||||
syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE
|
||||
self.assertAlmostEqual(psutil.virtual_memory().cached, syst,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_free(self):
|
||||
syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE
|
||||
self.assertAlmostEqual(psutil.virtual_memory().free, syst,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
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_on_failure()
|
||||
def test_muse_vmem_active(self):
|
||||
num = muse('Active')
|
||||
self.assertAlmostEqual(psutil.virtual_memory().active, num,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
||||
@retry_on_failure()
|
||||
def test_muse_vmem_inactive(self):
|
||||
num = muse('Inactive')
|
||||
self.assertAlmostEqual(psutil.virtual_memory().inactive, num,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
||||
@retry_on_failure()
|
||||
def test_muse_vmem_wired(self):
|
||||
num = muse('Wired')
|
||||
self.assertAlmostEqual(psutil.virtual_memory().wired, num,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
||||
@retry_on_failure()
|
||||
def test_muse_vmem_cached(self):
|
||||
num = muse('Cache')
|
||||
self.assertAlmostEqual(psutil.virtual_memory().cached, num,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
||||
@retry_on_failure()
|
||||
def test_muse_vmem_free(self):
|
||||
num = muse('Free')
|
||||
self.assertAlmostEqual(psutil.virtual_memory().free, num,
|
||||
delta=MEMORY_TOLERANCE)
|
||||
|
||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
||||
@retry_on_failure()
|
||||
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)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_cpu_stats_syscalls(self):
|
||||
# pretty high tolerance but it looks like it's OK.
|
||||
self.assertAlmostEqual(psutil.cpu_stats().syscalls,
|
||||
sysctl('vm.stats.sys.v_syscall'), delta=200000)
|
||||
|
||||
# def test_cpu_stats_traps(self):
|
||||
# self.assertAlmostEqual(psutil.cpu_stats().traps,
|
||||
# sysctl('vm.stats.sys.v_trap'), delta=1000)
|
||||
|
||||
# --- swap memory
|
||||
|
||||
def test_swapmem_free(self):
|
||||
total, used, free = self.parse_swapinfo()
|
||||
self.assertAlmostEqual(
|
||||
psutil.swap_memory().free, free, delta=MEMORY_TOLERANCE)
|
||||
|
||||
def test_swapmem_used(self):
|
||||
total, used, free = self.parse_swapinfo()
|
||||
self.assertAlmostEqual(
|
||||
psutil.swap_memory().used, used, delta=MEMORY_TOLERANCE)
|
||||
|
||||
def test_swapmem_total(self):
|
||||
total, used, free = self.parse_swapinfo()
|
||||
self.assertAlmostEqual(
|
||||
psutil.swap_memory().total, total, delta=MEMORY_TOLERANCE)
|
||||
|
||||
# --- others
|
||||
|
||||
def test_boot_time(self):
|
||||
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())
|
||||
|
||||
# --- sensors_temperatures
|
||||
|
||||
def test_sensors_temperatures_against_sysctl(self):
|
||||
num_cpus = psutil.cpu_count(True)
|
||||
for cpu in range(num_cpus):
|
||||
sensor = "dev.cpu.%s.temperature" % cpu
|
||||
# sysctl returns a string in the format 46.0C
|
||||
try:
|
||||
sysctl_result = int(float(sysctl(sensor)[:-1]))
|
||||
except RuntimeError:
|
||||
self.skipTest("temperatures not supported by kernel")
|
||||
self.assertAlmostEqual(
|
||||
psutil.sensors_temperatures()["coretemp"][cpu].current,
|
||||
sysctl_result, delta=10)
|
||||
|
||||
sensor = "dev.cpu.%s.coretemp.tjmax" % cpu
|
||||
sysctl_result = int(float(sysctl(sensor)[:-1]))
|
||||
self.assertEqual(
|
||||
psutil.sensors_temperatures()["coretemp"][cpu].high,
|
||||
sysctl_result)
|
||||
|
||||
# =====================================================================
|
||||
# --- OpenBSD
|
||||
# =====================================================================
|
||||
|
||||
|
||||
@unittest.skipIf(not OPENBSD, "OPENBSD only")
|
||||
class OpenBSDTestCase(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 NetBSDTestCase(unittest.TestCase):
|
||||
|
||||
@staticmethod
|
||||
def parse_meminfo(look_for):
|
||||
with open('/proc/meminfo', 'rt') 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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,637 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Tests for net_connections() and Process.connections() APIs."""
|
||||
|
||||
import contextlib
|
||||
import errno
|
||||
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 MACOS
|
||||
from psutil import NETBSD
|
||||
from psutil import OPENBSD
|
||||
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_net_address
|
||||
from psutil.tests import CIRRUS
|
||||
from psutil.tests import create_sockets
|
||||
from psutil.tests import enum
|
||||
from psutil.tests import get_free_port
|
||||
from psutil.tests import HAS_CONNECTIONS_UNIX
|
||||
from psutil.tests import pyrun
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import safe_rmpath
|
||||
from psutil.tests import skip_on_access_denied
|
||||
from psutil.tests import SKIP_SYSCONS
|
||||
from psutil.tests import tcp_socketpair
|
||||
from psutil.tests import TESTFN
|
||||
from psutil.tests import TRAVIS
|
||||
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()
|
||||
SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object())
|
||||
|
||||
|
||||
class Base(object):
|
||||
|
||||
def setUp(self):
|
||||
safe_rmpath(TESTFN)
|
||||
if not (NETBSD or FREEBSD):
|
||||
# process opens a UNIX socket to /var/log/run.
|
||||
cons = thisproc.connections(kind='all')
|
||||
assert not cons, cons
|
||||
|
||||
def tearDown(self):
|
||||
safe_rmpath(TESTFN)
|
||||
reap_children()
|
||||
if not (FREEBSD or NETBSD):
|
||||
# Make sure we closed all resources.
|
||||
# NetBSD opens a UNIX socket to /var/log/run.
|
||||
cons = thisproc.connections(kind='all')
|
||||
assert not cons, cons
|
||||
|
||||
def compare_procsys_connections(self, pid, proc_cons, kind='all'):
|
||||
"""Given a process PID and its list of connections compare
|
||||
those against system-wide connections retrieved via
|
||||
psutil.net_connections.
|
||||
"""
|
||||
try:
|
||||
sys_cons = psutil.net_connections(kind=kind)
|
||||
except psutil.AccessDenied:
|
||||
# On MACOS, system-wide connections are retrieved by iterating
|
||||
# over all processes
|
||||
if MACOS:
|
||||
return
|
||||
else:
|
||||
raise
|
||||
# Filter for this proc PID and exlucde PIDs from the tuple.
|
||||
sys_cons = [c[:-1] for c in sys_cons if c.pid == pid]
|
||||
sys_cons.sort()
|
||||
proc_cons.sort()
|
||||
self.assertEqual(proc_cons, sys_cons)
|
||||
|
||||
def check_connection_ntuple(self, conn):
|
||||
"""Check validity of a connection namedtuple."""
|
||||
def check_ntuple(conn):
|
||||
has_pid = len(conn) == 7
|
||||
self.assertIn(len(conn), (6, 7))
|
||||
self.assertEqual(conn[0], conn.fd)
|
||||
self.assertEqual(conn[1], conn.family)
|
||||
self.assertEqual(conn[2], conn.type)
|
||||
self.assertEqual(conn[3], conn.laddr)
|
||||
self.assertEqual(conn[4], conn.raddr)
|
||||
self.assertEqual(conn[5], conn.status)
|
||||
if has_pid:
|
||||
self.assertEqual(conn[6], conn.pid)
|
||||
|
||||
def check_family(conn):
|
||||
self.assertIn(conn.family, (AF_INET, AF_INET6, AF_UNIX))
|
||||
if enum is not None:
|
||||
assert isinstance(conn.family, enum.IntEnum), conn
|
||||
else:
|
||||
assert isinstance(conn.family, int), conn
|
||||
if conn.family == AF_INET:
|
||||
# actually try to bind the local socket; ignore IPv6
|
||||
# sockets as their address might be represented as
|
||||
# an IPv4-mapped-address (e.g. "::127.0.0.1")
|
||||
# and that's rejected by bind()
|
||||
s = socket.socket(conn.family, conn.type)
|
||||
with contextlib.closing(s):
|
||||
try:
|
||||
s.bind((conn.laddr[0], 0))
|
||||
except socket.error as err:
|
||||
if err.errno != errno.EADDRNOTAVAIL:
|
||||
raise
|
||||
elif conn.family == AF_UNIX:
|
||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
||||
|
||||
def check_type(conn):
|
||||
# SOCK_SEQPACKET may happen in case of AF_UNIX socks
|
||||
self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET))
|
||||
if enum is not None:
|
||||
assert isinstance(conn.type, enum.IntEnum), conn
|
||||
else:
|
||||
assert isinstance(conn.type, int), conn
|
||||
if conn.type == SOCK_DGRAM:
|
||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
||||
|
||||
def check_addrs(conn):
|
||||
# check IP address and port sanity
|
||||
for addr in (conn.laddr, conn.raddr):
|
||||
if conn.family in (AF_INET, AF_INET6):
|
||||
self.assertIsInstance(addr, tuple)
|
||||
if not addr:
|
||||
continue
|
||||
self.assertIsInstance(addr.port, int)
|
||||
assert 0 <= addr.port <= 65535, addr.port
|
||||
check_net_address(addr.ip, conn.family)
|
||||
elif conn.family == AF_UNIX:
|
||||
self.assertIsInstance(addr, str)
|
||||
|
||||
def check_status(conn):
|
||||
self.assertIsInstance(conn.status, str)
|
||||
valids = [getattr(psutil, x) for x in dir(psutil)
|
||||
if x.startswith('CONN_')]
|
||||
self.assertIn(conn.status, valids)
|
||||
if conn.family in (AF_INET, AF_INET6) and conn.type == SOCK_STREAM:
|
||||
self.assertNotEqual(conn.status, psutil.CONN_NONE)
|
||||
else:
|
||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
||||
|
||||
check_ntuple(conn)
|
||||
check_family(conn)
|
||||
check_type(conn)
|
||||
check_addrs(conn)
|
||||
check_status(conn)
|
||||
|
||||
|
||||
class TestBase(Base, unittest.TestCase):
|
||||
|
||||
@unittest.skipIf(SKIP_SYSCONS, "requires root")
|
||||
def test_system(self):
|
||||
with create_sockets():
|
||||
for conn in psutil.net_connections(kind='all'):
|
||||
self.check_connection_ntuple(conn)
|
||||
|
||||
def test_process(self):
|
||||
with create_sockets():
|
||||
for conn in psutil.Process().connections(kind='all'):
|
||||
self.check_connection_ntuple(conn)
|
||||
|
||||
def test_invalid_kind(self):
|
||||
self.assertRaises(ValueError, thisproc.connections, kind='???')
|
||||
self.assertRaises(ValueError, psutil.net_connections, kind='???')
|
||||
|
||||
|
||||
class TestUnconnectedSockets(Base, unittest.TestCase):
|
||||
"""Tests sockets which are open but not connected to anything."""
|
||||
|
||||
def get_conn_from_sock(self, sock):
|
||||
cons = thisproc.connections(kind='all')
|
||||
smap = dict([(c.fd, c) for c in cons])
|
||||
if NETBSD or FREEBSD:
|
||||
# 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):
|
||||
"""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).
|
||||
"""
|
||||
conn = self.get_conn_from_sock(sock)
|
||||
self.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, kind='all')
|
||||
return conn
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TestConnectedSocket(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 or FREEBSD:
|
||||
# On NetBSD creating a UNIX socket will cause
|
||||
# a UNIX connection to /var/run/log.
|
||||
cons = [c for c in cons if c.raddr != '/var/run/log']
|
||||
if CIRRUS:
|
||||
cons = [c for c in cons if c.fd in
|
||||
(server.fileno(), client.fileno())]
|
||||
self.assertEqual(len(cons), 2, msg=cons)
|
||||
if LINUX or FREEBSD or SUNOS:
|
||||
# remote path is never set
|
||||
self.assertEqual(cons[0].raddr, "")
|
||||
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()
|
||||
|
||||
|
||||
class TestFilters(Base, unittest.TestCase):
|
||||
|
||||
def test_filters(self):
|
||||
def check(kind, families, types):
|
||||
for conn in thisproc.connections(kind=kind):
|
||||
self.assertIn(conn.family, families)
|
||||
self.assertIn(conn.type, types)
|
||||
if not SKIP_SYSCONS:
|
||||
for conn in psutil.net_connections(kind=kind):
|
||||
self.assertIn(conn.family, families)
|
||||
self.assertIn(conn.type, types)
|
||||
|
||||
with create_sockets():
|
||||
check('all',
|
||||
[AF_INET, AF_INET6, AF_UNIX],
|
||||
[SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET])
|
||||
check('inet',
|
||||
[AF_INET, AF_INET6],
|
||||
[SOCK_STREAM, SOCK_DGRAM])
|
||||
check('inet4',
|
||||
[AF_INET],
|
||||
[SOCK_STREAM, SOCK_DGRAM])
|
||||
check('tcp',
|
||||
[AF_INET, AF_INET6],
|
||||
[SOCK_STREAM])
|
||||
check('tcp4',
|
||||
[AF_INET],
|
||||
[SOCK_STREAM])
|
||||
check('tcp6',
|
||||
[AF_INET6],
|
||||
[SOCK_STREAM])
|
||||
check('udp',
|
||||
[AF_INET, AF_INET6],
|
||||
[SOCK_DGRAM])
|
||||
check('udp4',
|
||||
[AF_INET],
|
||||
[SOCK_DGRAM])
|
||||
check('udp6',
|
||||
[AF_INET6],
|
||||
[SOCK_DGRAM])
|
||||
if HAS_CONNECTIONS_UNIX:
|
||||
check('unix',
|
||||
[AF_UNIX],
|
||||
[SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET])
|
||||
|
||||
@skip_on_access_denied(only_if=MACOS)
|
||||
def test_combos(self):
|
||||
def check_conn(proc, conn, family, type, laddr, raddr, status, kinds):
|
||||
all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4",
|
||||
"tcp6", "udp", "udp4", "udp6")
|
||||
self.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(5)
|
||||
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"))
|
||||
|
||||
def test_count(self):
|
||||
with create_sockets():
|
||||
# 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))
|
||||
# Skipped on BSD becayse by default the Python process
|
||||
# creates a UNIX socket to '/var/run/log'.
|
||||
if HAS_CONNECTIONS_UNIX and not (FREEBSD or NETBSD):
|
||||
cons = thisproc.connections(kind='unix')
|
||||
self.assertEqual(len(cons), 3)
|
||||
for conn in cons:
|
||||
self.assertEqual(conn.family, AF_UNIX)
|
||||
self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
|
||||
|
||||
|
||||
@unittest.skipIf(SKIP_SYSCONS, "requires root")
|
||||
class TestSystemWideConnections(Base, unittest.TestCase):
|
||||
"""Tests for net_connections()."""
|
||||
|
||||
def test_it(self):
|
||||
def check(cons, families, types_):
|
||||
for conn in cons:
|
||||
self.assertIn(conn.family, families, msg=conn)
|
||||
if conn.family != AF_UNIX:
|
||||
self.assertIn(conn.type, types_, msg=conn)
|
||||
self.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_)
|
||||
|
||||
# See: https://travis-ci.org/giampaolo/psutil/jobs/237566297
|
||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on MACOS + TRAVIS")
|
||||
def test_multi_sockets_procs(self):
|
||||
# Creates multiple sub processes, each creating different
|
||||
# sockets. For each process check that proc.connections()
|
||||
# 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(r'%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)
|
||||
|
||||
|
||||
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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,690 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""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
|
||||
|
||||
from psutil import AIX
|
||||
from psutil import BSD
|
||||
from psutil import FREEBSD
|
||||
from psutil import LINUX
|
||||
from psutil import MACOS
|
||||
from psutil import NETBSD
|
||||
from psutil import OPENBSD
|
||||
from psutil import OSX
|
||||
from psutil import POSIX
|
||||
from psutil import SUNOS
|
||||
from psutil import WINDOWS
|
||||
from psutil._compat import long
|
||||
from psutil.tests import create_sockets
|
||||
from psutil.tests import enum
|
||||
from psutil.tests import get_kernel_version
|
||||
from psutil.tests import HAS_CPU_FREQ
|
||||
from psutil.tests import HAS_NET_IO_COUNTERS
|
||||
from psutil.tests import HAS_RLIMIT
|
||||
from psutil.tests import HAS_SENSORS_FANS
|
||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
||||
from psutil.tests import is_namedtuple
|
||||
from psutil.tests import safe_rmpath
|
||||
from psutil.tests import SKIP_SYSCONS
|
||||
from psutil.tests import TESTFN
|
||||
from psutil.tests import unittest
|
||||
from psutil.tests import VALID_PROC_STATUSES
|
||||
from psutil.tests import warn
|
||||
import psutil
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- APIs availability
|
||||
# ===================================================================
|
||||
|
||||
# Make sure code reflects what doc promises in terms of APIs
|
||||
# availability.
|
||||
|
||||
class TestAvailConstantsAPIs(unittest.TestCase):
|
||||
|
||||
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_linux(self):
|
||||
ae = self.assertEqual
|
||||
ae(hasattr(psutil, "IOPRIO_CLASS_NONE"), LINUX)
|
||||
ae(hasattr(psutil, "IOPRIO_CLASS_RT"), LINUX)
|
||||
ae(hasattr(psutil, "IOPRIO_CLASS_BE"), LINUX)
|
||||
ae(hasattr(psutil, "IOPRIO_CLASS_IDLE"), LINUX)
|
||||
|
||||
def test_linux_ioprio_windows(self):
|
||||
ae = self.assertEqual
|
||||
ae(hasattr(psutil, "IOPRIO_HIGH"), WINDOWS)
|
||||
ae(hasattr(psutil, "IOPRIO_NORMAL"), WINDOWS)
|
||||
ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS)
|
||||
ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS)
|
||||
|
||||
def test_linux_rlimit(self):
|
||||
ae = self.assertEqual
|
||||
hasit = LINUX and get_kernel_version() >= (2, 6, 36)
|
||||
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)
|
||||
|
||||
|
||||
class TestAvailSystemAPIs(unittest.TestCase):
|
||||
|
||||
def test_win_service_iter(self):
|
||||
self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS)
|
||||
|
||||
def test_win_service_get(self):
|
||||
self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS)
|
||||
|
||||
def test_cpu_freq(self):
|
||||
linux = (LINUX and
|
||||
(os.path.exists("/sys/devices/system/cpu/cpufreq") or
|
||||
os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq")))
|
||||
self.assertEqual(hasattr(psutil, "cpu_freq"),
|
||||
linux or MACOS or WINDOWS or FREEBSD)
|
||||
|
||||
def test_sensors_temperatures(self):
|
||||
self.assertEqual(
|
||||
hasattr(psutil, "sensors_temperatures"), LINUX or FREEBSD)
|
||||
|
||||
def test_sensors_fans(self):
|
||||
self.assertEqual(hasattr(psutil, "sensors_fans"), LINUX)
|
||||
|
||||
def test_battery(self):
|
||||
self.assertEqual(hasattr(psutil, "sensors_battery"),
|
||||
LINUX or WINDOWS or FREEBSD or MACOS)
|
||||
|
||||
|
||||
class TestAvailProcessAPIs(unittest.TestCase):
|
||||
|
||||
def test_environ(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "environ"),
|
||||
LINUX or MACOS or WINDOWS or AIX or SUNOS)
|
||||
|
||||
def test_uids(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
|
||||
|
||||
def test_gids(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
|
||||
|
||||
def test_terminal(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX)
|
||||
|
||||
def test_ionice(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS)
|
||||
|
||||
def test_rlimit(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX)
|
||||
|
||||
def test_io_counters(self):
|
||||
hasit = hasattr(psutil.Process, "io_counters")
|
||||
self.assertEqual(hasit, False if MACOS or SUNOS else True)
|
||||
|
||||
def test_num_fds(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX)
|
||||
|
||||
def test_num_handles(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS)
|
||||
|
||||
def test_cpu_affinity(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "cpu_affinity"),
|
||||
LINUX or WINDOWS or FREEBSD)
|
||||
|
||||
def test_cpu_num(self):
|
||||
self.assertEqual(hasattr(psutil.Process, "cpu_num"),
|
||||
LINUX or FREEBSD or SUNOS)
|
||||
|
||||
def test_memory_maps(self):
|
||||
hasit = hasattr(psutil.Process, "memory_maps")
|
||||
self.assertEqual(
|
||||
hasit, False if OPENBSD or NETBSD or AIX or MACOS else True)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- System API types
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestSystemAPITypes(unittest.TestCase):
|
||||
"""Check the return types of system related APIs.
|
||||
Mainly we want to test we never return unicode on Python 2, see:
|
||||
https://github.com/giampaolo/psutil/issues/1039
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.proc = psutil.Process()
|
||||
|
||||
def tearDown(self):
|
||||
safe_rmpath(TESTFN)
|
||||
|
||||
def assert_ntuple_of_nums(self, nt, type_=float, gezero=True):
|
||||
assert is_namedtuple(nt)
|
||||
for n in nt:
|
||||
self.assertIsInstance(n, type_)
|
||||
if gezero:
|
||||
self.assertGreaterEqual(n, 0)
|
||||
|
||||
def test_cpu_times(self):
|
||||
self.assert_ntuple_of_nums(psutil.cpu_times())
|
||||
for nt in psutil.cpu_times(percpu=True):
|
||||
self.assert_ntuple_of_nums(nt)
|
||||
|
||||
def test_cpu_percent(self):
|
||||
self.assertIsInstance(psutil.cpu_percent(interval=None), float)
|
||||
self.assertIsInstance(psutil.cpu_percent(interval=0.00001), float)
|
||||
|
||||
def test_cpu_times_percent(self):
|
||||
self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=None))
|
||||
self.assert_ntuple_of_nums(psutil.cpu_times_percent(interval=0.0001))
|
||||
|
||||
def test_cpu_count(self):
|
||||
self.assertIsInstance(psutil.cpu_count(), int)
|
||||
|
||||
@unittest.skipIf(not HAS_CPU_FREQ, "not supported")
|
||||
def test_cpu_freq(self):
|
||||
if psutil.cpu_freq() is None:
|
||||
raise self.skipTest("cpu_freq() returns None")
|
||||
self.assert_ntuple_of_nums(psutil.cpu_freq(), type_=(float, int, long))
|
||||
|
||||
def test_disk_io_counters(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
for k, v in psutil.disk_io_counters(perdisk=True).items():
|
||||
self.assertIsInstance(k, str)
|
||||
self.assert_ntuple_of_nums(v, type_=(int, long))
|
||||
|
||||
def test_disk_partitions(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
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(SKIP_SYSCONS, "requires root")
|
||||
def test_net_connections(self):
|
||||
with create_sockets():
|
||||
ret = psutil.net_connections('all')
|
||||
self.assertEqual(len(ret), len(set(ret)))
|
||||
for conn in ret:
|
||||
assert is_namedtuple(conn)
|
||||
|
||||
def test_net_if_addrs(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
for ifname, addrs in psutil.net_if_addrs().items():
|
||||
self.assertIsInstance(ifname, str)
|
||||
for addr in addrs:
|
||||
if enum is not None:
|
||||
assert isinstance(addr.family, enum.IntEnum), addr
|
||||
else:
|
||||
assert isinstance(addr.family, int), addr
|
||||
self.assertIsInstance(addr.address, str)
|
||||
self.assertIsInstance(addr.netmask, (str, type(None)))
|
||||
self.assertIsInstance(addr.broadcast, (str, type(None)))
|
||||
|
||||
def test_net_if_stats(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
for ifname, info in psutil.net_if_stats().items():
|
||||
self.assertIsInstance(ifname, str)
|
||||
self.assertIsInstance(info.isup, bool)
|
||||
if enum is not None:
|
||||
self.assertIsInstance(info.duplex, enum.IntEnum)
|
||||
else:
|
||||
self.assertIsInstance(info.duplex, int)
|
||||
self.assertIsInstance(info.speed, int)
|
||||
self.assertIsInstance(info.mtu, int)
|
||||
|
||||
@unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported')
|
||||
def test_net_io_counters(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
for ifname, _ in psutil.net_io_counters(pernic=True).items():
|
||||
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)
|
||||
self.assertIsInstance(unit.current, (float, int, type(None)))
|
||||
|
||||
@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)
|
||||
self.assertIsInstance(unit.current, (float, int, type(None)))
|
||||
self.assertIsInstance(unit.high, (float, int, type(None)))
|
||||
self.assertIsInstance(unit.critical, (float, int, type(None)))
|
||||
|
||||
def test_boot_time(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
self.assertIsInstance(psutil.boot_time(), float)
|
||||
|
||||
def test_users(self):
|
||||
# Duplicate of test_system.py. Keep it anyway.
|
||||
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 get_attr_names(self):
|
||||
excluded_names = set([
|
||||
'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
|
||||
'as_dict', 'parent', 'parents', '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)
|
||||
return attrs
|
||||
|
||||
def iter_procs(self):
|
||||
attrs = self.get_attr_names()
|
||||
for p in psutil.process_iter():
|
||||
with p.oneshot():
|
||||
for name in attrs:
|
||||
yield (p, name)
|
||||
|
||||
def call_meth(self, p, name):
|
||||
args = ()
|
||||
kwargs = {}
|
||||
attr = getattr(p, name, None)
|
||||
if attr is not None and callable(attr):
|
||||
if name == 'rlimit':
|
||||
args = (psutil.RLIMIT_NOFILE,)
|
||||
elif name == 'memory_maps':
|
||||
kwargs = {'grouped': False}
|
||||
return attr(*args, **kwargs)
|
||||
else:
|
||||
return attr
|
||||
|
||||
def test_fetch_all(self):
|
||||
valid_procs = 0
|
||||
default = object()
|
||||
failures = []
|
||||
for p, name in self.iter_procs():
|
||||
ret = default
|
||||
try:
|
||||
ret = self.call_meth(p, name)
|
||||
except NotImplementedError:
|
||||
msg = "%r was skipped because not implemented" % (
|
||||
self.__class__.__name__ + '.test_' + name)
|
||||
warn(msg)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied) as err:
|
||||
self.assertEqual(err.pid, p.pid)
|
||||
if err.name:
|
||||
# make sure exception's name attr is set
|
||||
# with the actual process name
|
||||
self.assertEqual(err.name, p.name())
|
||||
assert str(err)
|
||||
assert err.msg
|
||||
except Exception:
|
||||
s = '\n' + '=' * 70 + '\n'
|
||||
s += "FAIL: test_%s (proc=%s" % (name, p)
|
||||
if ret != default:
|
||||
s += ", ret=%s)" % repr(ret)
|
||||
s += ')\n'
|
||||
s += '-' * 70
|
||||
s += "\n%s" % traceback.format_exc()
|
||||
s = "\n".join((" " * 4) + i for i in s.splitlines())
|
||||
s += '\n'
|
||||
failures.append(s)
|
||||
break
|
||||
else:
|
||||
valid_procs += 1
|
||||
if ret not in (0, 0.0, [], None, '', {}):
|
||||
assert ret, ret
|
||||
meth = getattr(self, name)
|
||||
meth(ret, p)
|
||||
|
||||
if failures:
|
||||
self.fail(''.join(failures))
|
||||
|
||||
# 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:
|
||||
if WINDOWS and not ret.endswith('.exe'):
|
||||
return # May be "Registry", "MemCompression", ...
|
||||
assert os.path.isabs(ret), ret
|
||||
# Note: os.stat() may return False even if the file is there
|
||||
# hence we skip the test, see:
|
||||
# http://stackoverflow.com/questions/3112546/os-path-exists-lies
|
||||
if POSIX and os.path.isfile(ret):
|
||||
if hasattr(os, 'access') and hasattr(os, "X_OK"):
|
||||
# XXX may fail on MACOS
|
||||
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)
|
||||
|
||||
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 MACOS and not NETBSD:
|
||||
self.assertGreaterEqual(gid, 0)
|
||||
|
||||
def username(self, ret, proc):
|
||||
self.assertIsInstance(ret, str)
|
||||
assert ret
|
||||
|
||||
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 LINUX:
|
||||
self.assertIsInstance(ret.ioclass, int)
|
||||
self.assertIsInstance(ret.value, int)
|
||||
self.assertGreaterEqual(ret.ioclass, 0)
|
||||
self.assertGreaterEqual(ret.value, 0)
|
||||
else: # Windows, Cygwin
|
||||
choices = [
|
||||
psutil.IOPRIO_VERYLOW,
|
||||
psutil.IOPRIO_LOW,
|
||||
psutil.IOPRIO_NORMAL,
|
||||
psutil.IOPRIO_HIGH]
|
||||
self.assertIsInstance(ret, int)
|
||||
self.assertGreaterEqual(ret, 0)
|
||||
self.assertIn(ret, choices)
|
||||
|
||||
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 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))
|
||||
if LINUX or OSX and name in ('vms', 'data'):
|
||||
# On Linux there are processes (e.g. 'goa-daemon') whose
|
||||
# VMS is incredibly high for some reason.
|
||||
continue
|
||||
self.assertLessEqual(value, total, msg=(name, value, total))
|
||||
|
||||
if LINUX:
|
||||
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):
|
||||
with create_sockets():
|
||||
self.assertEqual(len(ret), len(set(ret)))
|
||||
for conn in ret:
|
||||
assert is_namedtuple(conn)
|
||||
|
||||
def cwd(self, ret, proc):
|
||||
if ret: # 'ret' can be None or empty
|
||||
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 == 'addr':
|
||||
assert value, repr(value)
|
||||
elif fname == 'perms':
|
||||
if not WINDOWS:
|
||||
assert value, repr(value)
|
||||
else:
|
||||
self.assertIsInstance(value, (int, long))
|
||||
self.assertGreaterEqual(value, 0)
|
||||
|
||||
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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,600 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""
|
||||
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).
|
||||
PyPy appears to be completely unstable for this framework, probably
|
||||
because of how its JIT handles memory, so tests are skipped.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import functools
|
||||
import gc
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import psutil
|
||||
import psutil._common
|
||||
from psutil import FREEBSD
|
||||
from psutil import LINUX
|
||||
from psutil import MACOS
|
||||
from psutil import OPENBSD
|
||||
from psutil import POSIX
|
||||
from psutil import SUNOS
|
||||
from psutil import WINDOWS
|
||||
from psutil._common import bytes2human
|
||||
from psutil._compat import ProcessLookupError
|
||||
from psutil._compat import xrange
|
||||
from psutil.tests import CIRRUS
|
||||
from psutil.tests import create_sockets
|
||||
from psutil.tests import get_test_subprocess
|
||||
from psutil.tests import HAS_CPU_AFFINITY
|
||||
from psutil.tests import HAS_CPU_FREQ
|
||||
from psutil.tests import HAS_ENVIRON
|
||||
from psutil.tests import HAS_IONICE
|
||||
from psutil.tests import HAS_MEMORY_MAPS
|
||||
from psutil.tests import HAS_NET_IO_COUNTERS
|
||||
from psutil.tests import HAS_PROC_CPU_NUM
|
||||
from psutil.tests import HAS_PROC_IO_COUNTERS
|
||||
from psutil.tests import HAS_RLIMIT
|
||||
from psutil.tests import HAS_SENSORS_BATTERY
|
||||
from psutil.tests import HAS_SENSORS_FANS
|
||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
||||
from psutil.tests import PYPY
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import 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
|
||||
|
||||
|
||||
# configurable opts
|
||||
LOOPS = 1000
|
||||
MEMORY_TOLERANCE = 4096
|
||||
RETRY_FOR = 3
|
||||
SKIP_PYTHON_IMPL = True
|
||||
|
||||
cext = psutil._psplatform.cext
|
||||
thisproc = psutil.Process()
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# utils
|
||||
# ===================================================================
|
||||
|
||||
|
||||
def skip_if_linux():
|
||||
return unittest.skipIf(LINUX and SKIP_PYTHON_IMPL,
|
||||
"worthless on LINUX (pure python)")
|
||||
|
||||
|
||||
@unittest.skipIf(PYPY, "unreliable on PYPY")
|
||||
class TestMemLeak(unittest.TestCase):
|
||||
"""Base framework class which calls a function many times and
|
||||
produces a failure if process memory usage keeps increasing
|
||||
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 MACOS:
|
||||
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", "parents", "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)
|
||||
|
||||
@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())
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class TestProcessDualImplementation(TestMemLeak):
|
||||
|
||||
def test_cmdline_peb_true(self):
|
||||
self.execute(cext.proc_cmdline, os.getpid(), use_peb=True)
|
||||
|
||||
def test_cmdline_peb_false(self):
|
||||
self.execute(cext.proc_cmdline, os.getpid(), use_peb=False)
|
||||
|
||||
|
||||
class TestTerminatedProcessLeaks(TestProcessObjectLeaks):
|
||||
"""Repeat the tests above looking for leaks occurring when dealing
|
||||
with terminated processes raising NoSuchProcess exception.
|
||||
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 ProcessLookupError:
|
||||
pass
|
||||
|
||||
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)
|
||||
|
||||
@skip_if_linux()
|
||||
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)
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
def test_getloadavg(self):
|
||||
self.execute(psutil.getloadavg)
|
||||
|
||||
# --- mem
|
||||
|
||||
def test_virtual_memory(self):
|
||||
self.execute(psutil.virtual_memory)
|
||||
|
||||
# TODO: remove this skip when this gets fixed
|
||||
@unittest.skipIf(SUNOS, "worthless on SUNOS (uses a subprocess)")
|
||||
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
|
||||
|
||||
@unittest.skipIf(TRAVIS and MACOS, "false positive on TRAVIS + MACOS")
|
||||
@unittest.skipIf(CIRRUS and FREEBSD, "false positive on CIRRUS + FREEBSD")
|
||||
@skip_if_linux()
|
||||
@unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported')
|
||||
def test_net_io_counters(self):
|
||||
self.execute(psutil.net_io_counters, nowrap=False)
|
||||
|
||||
@skip_if_linux()
|
||||
@unittest.skipIf(MACOS and os.getuid() != 0, "need root access")
|
||||
def test_net_connections(self):
|
||||
with create_sockets():
|
||||
self.execute(psutil.net_connections)
|
||||
|
||||
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)
|
||||
|
||||
@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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,294 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""MACOS specific tests."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import psutil
|
||||
from psutil import MACOS
|
||||
from psutil.tests import create_zombie_proc
|
||||
from psutil.tests import get_test_subprocess
|
||||
from psutil.tests import HAS_BATTERY
|
||||
from psutil.tests import MEMORY_TOLERANCE
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import retry_on_failure
|
||||
from psutil.tests import sh
|
||||
from psutil.tests import unittest
|
||||
|
||||
|
||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE") if MACOS 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 MACOS, "MACOS 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 MACOS, "MACOS 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)
|
||||
|
||||
|
||||
@unittest.skipIf(not MACOS, "MACOS 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_on_failure()
|
||||
def test_vmem_free(self):
|
||||
vmstat_val = vm_stat("free")
|
||||
psutil_val = psutil.virtual_memory().free
|
||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_active(self):
|
||||
vmstat_val = vm_stat("active")
|
||||
psutil_val = psutil.virtual_memory().active
|
||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
def test_vmem_inactive(self):
|
||||
vmstat_val = vm_stat("inactive")
|
||||
psutil_val = psutil.virtual_memory().inactive
|
||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
||||
|
||||
@retry_on_failure()
|
||||
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_on_failure()
|
||||
def test_swapmem_sin(self):
|
||||
vmstat_val = vm_stat("Pageins")
|
||||
psutil_val = psutil.swap_memory().sin
|
||||
self.assertEqual(psutil_val, vmstat_val)
|
||||
|
||||
@retry_on_failure()
|
||||
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(r"(\d+)%", out).group(1)
|
||||
drawing_from = re.search("Now drawing from '([^']+)'", out).group(1)
|
||||
power_plugged = drawing_from == "AC Power"
|
||||
psutil_result = psutil.sensors_battery()
|
||||
self.assertEqual(psutil_result.power_plugged, power_plugged)
|
||||
self.assertEqual(psutil_result.percent, int(percent))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,453 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- 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 time
|
||||
|
||||
import psutil
|
||||
from psutil import AIX
|
||||
from psutil import BSD
|
||||
from psutil import LINUX
|
||||
from psutil import MACOS
|
||||
from psutil import OPENBSD
|
||||
from psutil import POSIX
|
||||
from psutil import SUNOS
|
||||
from psutil.tests import CI_TESTING
|
||||
from psutil.tests import get_kernel_version
|
||||
from psutil.tests import get_test_subprocess
|
||||
from psutil.tests import HAS_NET_IO_COUNTERS
|
||||
from psutil.tests import mock
|
||||
from psutil.tests import PYTHON_EXE
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import retry_on_failure
|
||||
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(fmt, pid=None):
|
||||
"""
|
||||
Wrapper for calling the ps command with a little bit of cross-platform
|
||||
support for a narrow range of features.
|
||||
"""
|
||||
|
||||
cmd = ['ps']
|
||||
|
||||
if LINUX:
|
||||
cmd.append('--no-headers')
|
||||
|
||||
if pid is not None:
|
||||
cmd.extend(['-p', str(pid)])
|
||||
else:
|
||||
if SUNOS or AIX:
|
||||
cmd.append('-A')
|
||||
else:
|
||||
cmd.append('ax')
|
||||
|
||||
if SUNOS:
|
||||
fmt_map = set(('command', 'comm', 'start', 'stime'))
|
||||
fmt = fmt_map.get(fmt, fmt)
|
||||
|
||||
cmd.extend(['-o', fmt])
|
||||
|
||||
output = sh(cmd)
|
||||
|
||||
if LINUX:
|
||||
output = output.splitlines()
|
||||
else:
|
||||
output = output.splitlines()[1:]
|
||||
|
||||
all_output = []
|
||||
for line in output:
|
||||
line = line.strip()
|
||||
|
||||
try:
|
||||
line = int(line)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
all_output.append(line)
|
||||
|
||||
if pid is None:
|
||||
return all_output
|
||||
else:
|
||||
return all_output[0]
|
||||
|
||||
# ps "-o" field names differ wildly between platforms.
|
||||
# "comm" means "only executable name" but is not available on BSD platforms.
|
||||
# "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(field, pid).split()[0]
|
||||
|
||||
|
||||
def ps_args(pid):
|
||||
field = "command"
|
||||
if AIX or SUNOS:
|
||||
field = "args"
|
||||
return ps(field, pid)
|
||||
|
||||
|
||||
def ps_rss(pid):
|
||||
field = "rss"
|
||||
if AIX:
|
||||
field = "rssize"
|
||||
return ps(field, pid)
|
||||
|
||||
|
||||
def ps_vsz(pid):
|
||||
field = "vsz"
|
||||
if AIX:
|
||||
field = "vsize"
|
||||
return ps(field, pid)
|
||||
|
||||
|
||||
@unittest.skipIf(not POSIX, "POSIX only")
|
||||
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('ppid', self.pid)
|
||||
ppid_psutil = psutil.Process(self.pid).ppid()
|
||||
self.assertEqual(ppid_ps, ppid_psutil)
|
||||
|
||||
def test_uid(self):
|
||||
uid_ps = ps('uid', self.pid)
|
||||
uid_psutil = psutil.Process(self.pid).uids().real
|
||||
self.assertEqual(uid_ps, uid_psutil)
|
||||
|
||||
def test_gid(self):
|
||||
gid_ps = ps('rgid', self.pid)
|
||||
gid_psutil = psutil.Process(self.pid).gids().real
|
||||
self.assertEqual(gid_ps, gid_psutil)
|
||||
|
||||
def test_username(self):
|
||||
username_ps = ps('user', 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_on_failure()
|
||||
def test_rss_memory(self):
|
||||
# give python interpreter some time to properly initialize
|
||||
# so that the results are the same
|
||||
time.sleep(0.1)
|
||||
rss_ps = ps_rss(self.pid)
|
||||
rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024
|
||||
self.assertEqual(rss_ps, rss_psutil)
|
||||
|
||||
@skip_on_access_denied()
|
||||
@retry_on_failure()
|
||||
def test_vsz_memory(self):
|
||||
# give python interpreter some time to properly initialize
|
||||
# so that the results are the same
|
||||
time.sleep(0.1)
|
||||
vsz_ps = ps_vsz(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 MACOS this may
|
||||
# be "pythonX.Y".
|
||||
name_ps = re.sub(r"\d.\d", "", name_ps)
|
||||
name_psutil = re.sub(r"\d.\d", "", name_psutil)
|
||||
# ...may also be "python.X"
|
||||
name_ps = re.sub(r"\d", "", name_ps)
|
||||
name_psutil = re.sub(r"\d", "", name_psutil)
|
||||
self.assertEqual(name_ps, name_psutil)
|
||||
|
||||
def test_name_long(self):
|
||||
# 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(MACOS or BSD, 'ps -o start not available')
|
||||
def test_create_time(self):
|
||||
time_ps = ps('start', self.pid)
|
||||
time_psutil = psutil.Process(self.pid).create_time()
|
||||
time_psutil_tstamp = datetime.datetime.fromtimestamp(
|
||||
time_psutil).strftime("%H:%M:%S")
|
||||
# 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('nice', 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', 'parent', 'parents']
|
||||
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_on_failure()
|
||||
def test_pids(self):
|
||||
# Note: this test might fail if the OS is starting/killing
|
||||
# other processes in the meantime
|
||||
pids_ps = sorted(ps("pid"))
|
||||
pids_psutil = psutil.pids()
|
||||
|
||||
# on MACOS and OPENBSD ps doesn't show pid 0
|
||||
if MACOS or OPENBSD and 0 not in pids_ps:
|
||||
pids_ps.insert(0, 0)
|
||||
|
||||
# There will often be one more process in pids_ps for ps itself
|
||||
if len(pids_ps) - len(pids_psutil) > 1:
|
||||
difference = [x for x in pids_psutil if x not in pids_ps] + \
|
||||
[x for x in pids_ps if x not in pids_psutil]
|
||||
self.fail("difference: " + str(difference))
|
||||
|
||||
# for some reason ifconfig -a does not report all interfaces
|
||||
# returned by psutil
|
||||
@unittest.skipIf(SUNOS, "unreliable on SUNOS")
|
||||
@unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
|
||||
@unittest.skipIf(not which('ifconfig'), "no ifconfig cmd")
|
||||
@unittest.skipIf(not HAS_NET_IO_COUNTERS, "not supported")
|
||||
def test_nic_names(self):
|
||||
output = sh("ifconfig -a")
|
||||
for nic in psutil.net_io_counters(pernic=True).keys():
|
||||
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))
|
||||
|
||||
@unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI")
|
||||
@retry_on_failure()
|
||||
def test_users(self):
|
||||
out = sh("who")
|
||||
lines = out.split('\n')
|
||||
if not lines:
|
||||
raise self.skipTest("no users on this system")
|
||||
users = [x.split()[0] for x in lines]
|
||||
terminals = [x.split()[1] for x in lines]
|
||||
self.assertEqual(len(users), len(psutil.users()))
|
||||
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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""Sun OS specific tests."""
|
||||
|
||||
import os
|
||||
|
||||
import psutil
|
||||
from psutil import SUNOS
|
||||
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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,904 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
"""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 MACOS
|
||||
from psutil import NETBSD
|
||||
from psutil import OPENBSD
|
||||
from psutil import POSIX
|
||||
from psutil import SUNOS
|
||||
from psutil import WINDOWS
|
||||
from psutil._compat import FileNotFoundError
|
||||
from psutil._compat import long
|
||||
from psutil.tests import ASCII_FS
|
||||
from psutil.tests import check_net_address
|
||||
from psutil.tests import CI_TESTING
|
||||
from psutil.tests import DEVNULL
|
||||
from psutil.tests import enum
|
||||
from psutil.tests import get_test_subprocess
|
||||
from psutil.tests import HAS_BATTERY
|
||||
from psutil.tests import HAS_CPU_FREQ
|
||||
from psutil.tests import HAS_GETLOADAVG
|
||||
from psutil.tests import HAS_NET_IO_COUNTERS
|
||||
from psutil.tests import HAS_SENSORS_BATTERY
|
||||
from psutil.tests import HAS_SENSORS_FANS
|
||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
||||
from psutil.tests import mock
|
||||
from psutil.tests import PYPY
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import retry_on_failure
|
||||
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 TestProcessAPIs(unittest.TestCase):
|
||||
|
||||
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_attrs(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
|
||||
|
||||
@unittest.skipIf(PYPY and WINDOWS,
|
||||
"get_test_subprocess() unreliable on PYPY + WINDOWS")
|
||||
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_on_failure(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_on_failure(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'))
|
||||
|
||||
@unittest.skipIf(PYPY and WINDOWS,
|
||||
"get_test_subprocess() unreliable on PYPY + WINDOWS")
|
||||
def test_wait_procs_no_timeout(self):
|
||||
sproc1 = get_test_subprocess()
|
||||
sproc2 = get_test_subprocess()
|
||||
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_pid_exists(self):
|
||||
sproc = get_test_subprocess()
|
||||
self.assertTrue(psutil.pid_exists(sproc.pid))
|
||||
p = psutil.Process(sproc.pid)
|
||||
p.kill()
|
||||
p.wait()
|
||||
self.assertFalse(psutil.pid_exists(sproc.pid))
|
||||
self.assertFalse(psutil.pid_exists(-1))
|
||||
self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids())
|
||||
|
||||
def test_pid_exists_2(self):
|
||||
reap_children()
|
||||
pids = psutil.pids()
|
||||
for pid in pids:
|
||||
try:
|
||||
assert psutil.pid_exists(pid)
|
||||
except AssertionError:
|
||||
# in case the process disappeared in meantime fail only
|
||||
# if it is no longer in psutil.pids()
|
||||
time.sleep(.1)
|
||||
if pid in psutil.pids():
|
||||
self.fail(pid)
|
||||
pids = range(max(pids) + 5000, max(pids) + 6000)
|
||||
for pid in pids:
|
||||
self.assertFalse(psutil.pid_exists(pid), msg=pid)
|
||||
|
||||
def test_pids(self):
|
||||
pidslist = psutil.pids()
|
||||
procslist = [x.pid for x in psutil.process_iter()]
|
||||
# make sure every pid is unique
|
||||
self.assertEqual(sorted(set(pidslist)), pidslist)
|
||||
self.assertEqual(pidslist, procslist)
|
||||
|
||||
|
||||
class TestMiscAPIs(unittest.TestCase):
|
||||
|
||||
def test_boot_time(self):
|
||||
bt = psutil.boot_time()
|
||||
self.assertIsInstance(bt, float)
|
||||
self.assertGreater(bt, 0)
|
||||
self.assertLess(bt, time.time())
|
||||
|
||||
@unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI")
|
||||
def test_users(self):
|
||||
users = psutil.users()
|
||||
self.assertNotEqual(users, [])
|
||||
for user in users:
|
||||
assert user.name, user
|
||||
self.assertIsInstance(user.name, str)
|
||||
self.assertIsInstance(user.terminal, (str, type(None)))
|
||||
if user.host is not None:
|
||||
self.assertIsInstance(user.host, (str, type(None)))
|
||||
user.terminal
|
||||
user.host
|
||||
assert user.started > 0.0, user
|
||||
datetime.datetime.fromtimestamp(user.started)
|
||||
if WINDOWS or OPENBSD:
|
||||
self.assertIsNone(user.pid)
|
||||
else:
|
||||
psutil.Process(user.pid)
|
||||
|
||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
||||
def test_PAGESIZE(self):
|
||||
# pagesize is used internally to perform different calculations
|
||||
# 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_test(self):
|
||||
# test for psutil.test() function
|
||||
stdout = sys.stdout
|
||||
sys.stdout = DEVNULL
|
||||
try:
|
||||
psutil.test()
|
||||
finally:
|
||||
sys.stdout = stdout
|
||||
|
||||
def test_os_constants(self):
|
||||
names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD",
|
||||
"NETBSD", "BSD", "SUNOS"]
|
||||
for name in names:
|
||||
self.assertIsInstance(getattr(psutil, name), bool, msg=name)
|
||||
|
||||
if os.name == 'posix':
|
||||
assert psutil.POSIX
|
||||
assert not psutil.WINDOWS
|
||||
names.remove("POSIX")
|
||||
if "linux" in sys.platform.lower():
|
||||
assert psutil.LINUX
|
||||
names.remove("LINUX")
|
||||
elif "bsd" in sys.platform.lower():
|
||||
assert psutil.BSD
|
||||
self.assertEqual([psutil.FREEBSD, psutil.OPENBSD,
|
||||
psutil.NETBSD].count(True), 1)
|
||||
names.remove("BSD")
|
||||
names.remove("FREEBSD")
|
||||
names.remove("OPENBSD")
|
||||
names.remove("NETBSD")
|
||||
elif "sunos" in sys.platform.lower() or \
|
||||
"solaris" in sys.platform.lower():
|
||||
assert psutil.SUNOS
|
||||
names.remove("SUNOS")
|
||||
elif "darwin" in sys.platform.lower():
|
||||
assert psutil.MACOS
|
||||
names.remove("MACOS")
|
||||
else:
|
||||
assert psutil.WINDOWS
|
||||
assert not psutil.POSIX
|
||||
names.remove("WINDOWS")
|
||||
|
||||
# assert all other constants are set to False
|
||||
for name in names:
|
||||
self.assertIs(getattr(psutil, name), False, msg=name)
|
||||
|
||||
|
||||
class TestMemoryAPIs(unittest.TestCase):
|
||||
|
||||
def test_virtual_memory(self):
|
||||
mem = psutil.virtual_memory()
|
||||
assert mem.total > 0, mem
|
||||
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
|
||||
|
||||
|
||||
class TestCpuAPIs(unittest.TestCase):
|
||||
|
||||
def test_cpu_count_logical(self):
|
||||
logical = psutil.cpu_count()
|
||||
self.assertIsNotNone(logical)
|
||||
self.assertEqual(logical, len(psutil.cpu_times(percpu=True)))
|
||||
self.assertGreaterEqual(logical, 1)
|
||||
#
|
||||
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")
|
||||
|
||||
def test_cpu_count_physical(self):
|
||||
logical = psutil.cpu_count()
|
||||
physical = psutil.cpu_count(logical=False)
|
||||
if physical is None:
|
||||
raise self.skipTest("physical cpu_count() is None")
|
||||
if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista
|
||||
self.assertIsNone(physical)
|
||||
else:
|
||||
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())
|
||||
stop_at = time.time() + 1
|
||||
while time.time() < stop_at:
|
||||
t2 = sum(psutil.cpu_times())
|
||||
if t2 > t1:
|
||||
return
|
||||
self.fail("time remained the same")
|
||||
|
||||
def test_per_cpu_times(self):
|
||||
# Check type, value >= 0, str().
|
||||
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)
|
||||
giveup_at = time.time() + 1
|
||||
while True:
|
||||
if time.time() >= giveup_at:
|
||||
return self.fail("timeout")
|
||||
tot2 = psutil.cpu_times(percpu=True)
|
||||
for t1, t2 in zip(tot1, tot2):
|
||||
t1, t2 = psutil._cpu_busy_time(t1), psutil._cpu_busy_time(t2)
|
||||
difference = t2 - t1
|
||||
if difference >= 0.05:
|
||||
return
|
||||
|
||||
def test_cpu_times_comparison(self):
|
||||
# Make sure the sum of all per cpu times is almost equal to
|
||||
# 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_cpu_stats(self):
|
||||
# Tested more extensively in per-platform test modules.
|
||||
infos = psutil.cpu_stats()
|
||||
self.assertEqual(
|
||||
infos._fields,
|
||||
('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'))
|
||||
for name in infos._fields:
|
||||
value = getattr(infos, name)
|
||||
self.assertGreaterEqual(value, 0)
|
||||
# on AIX, ctx_switches is always 0
|
||||
if not AIX and name in ('ctx_switches', 'interrupts'):
|
||||
self.assertGreater(value, 0)
|
||||
|
||||
@unittest.skipIf(not HAS_CPU_FREQ, "not suported")
|
||||
def test_cpu_freq(self):
|
||||
def check_ls(ls):
|
||||
for nt in ls:
|
||||
self.assertEqual(nt._fields, ('current', 'min', 'max'))
|
||||
if nt.max != 0.0:
|
||||
self.assertLessEqual(nt.current, nt.max)
|
||||
for name in nt._fields:
|
||||
value = getattr(nt, name)
|
||||
self.assertIsInstance(value, (int, long, float))
|
||||
self.assertGreaterEqual(value, 0)
|
||||
|
||||
ls = psutil.cpu_freq(percpu=True)
|
||||
if TRAVIS and not ls:
|
||||
raise self.skipTest("skipped on Travis")
|
||||
if FREEBSD and not ls:
|
||||
raise self.skipTest("returns empty list on FreeBSD")
|
||||
|
||||
assert ls, ls
|
||||
check_ls([psutil.cpu_freq(percpu=False)])
|
||||
|
||||
if LINUX:
|
||||
self.assertEqual(len(ls), psutil.cpu_count())
|
||||
|
||||
@unittest.skipIf(not HAS_GETLOADAVG, "not supported")
|
||||
def test_getloadavg(self):
|
||||
loadavg = psutil.getloadavg()
|
||||
assert len(loadavg) == 3
|
||||
|
||||
for load in loadavg:
|
||||
self.assertIsInstance(load, float)
|
||||
self.assertGreaterEqual(load, 0.0)
|
||||
|
||||
|
||||
class TestDiskAPIs(unittest.TestCase):
|
||||
|
||||
def test_disk_usage(self):
|
||||
usage = psutil.disk_usage(os.getcwd())
|
||||
self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent'))
|
||||
|
||||
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(FileNotFoundError):
|
||||
psutil.disk_usage(fname)
|
||||
|
||||
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
|
||||
# on modern systems mount points can also be files
|
||||
assert os.path.exists(disk.mountpoint), disk
|
||||
assert disk.fstype, disk
|
||||
|
||||
# all = True
|
||||
ls = psutil.disk_partitions(all=True)
|
||||
self.assertTrue(ls, msg=ls)
|
||||
for disk in psutil.disk_partitions(all=True):
|
||||
if not WINDOWS and disk.mountpoint:
|
||||
try:
|
||||
os.stat(disk.mountpoint)
|
||||
except OSError as err:
|
||||
if TRAVIS and MACOS and err.errno == errno.EIO:
|
||||
continue
|
||||
# http://mail.python.org/pipermail/python-dev/
|
||||
# 2012-June/120787.html
|
||||
if err.errno not in (errno.EPERM, errno.EACCES):
|
||||
raise
|
||||
else:
|
||||
assert os.path.exists(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) if x.mountpoint]
|
||||
self.assertIn(mount, mounts)
|
||||
psutil.disk_usage(mount)
|
||||
|
||||
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
|
||||
'/proc/diskstats not available on this linux version')
|
||||
@unittest.skipIf(CI_TESTING and not psutil.disk_io_counters(),
|
||||
"unreliable on CI") # no visible disks
|
||||
def test_disk_io_counters(self):
|
||||
def check_ntuple(nt):
|
||||
self.assertEqual(nt[0], nt.read_count)
|
||||
self.assertEqual(nt[1], nt.write_count)
|
||||
self.assertEqual(nt[2], nt.read_bytes)
|
||||
self.assertEqual(nt[3], nt.write_bytes)
|
||||
if not (OPENBSD or NETBSD):
|
||||
self.assertEqual(nt[4], nt.read_time)
|
||||
self.assertEqual(nt[5], nt.write_time)
|
||||
if LINUX:
|
||||
self.assertEqual(nt[6], nt.read_merged_count)
|
||||
self.assertEqual(nt[7], nt.write_merged_count)
|
||||
self.assertEqual(nt[8], nt.busy_time)
|
||||
elif FREEBSD:
|
||||
self.assertEqual(nt[6], nt.busy_time)
|
||||
for name in nt._fields:
|
||||
assert getattr(nt, name) >= 0, nt
|
||||
|
||||
ret = psutil.disk_io_counters(perdisk=False)
|
||||
assert ret is not None, "no disks on this system?"
|
||||
check_ntuple(ret)
|
||||
ret = psutil.disk_io_counters(perdisk=True)
|
||||
# make sure there are no duplicates
|
||||
self.assertEqual(len(ret), len(set(ret)))
|
||||
for key in ret:
|
||||
assert key, key
|
||||
check_ntuple(ret[key])
|
||||
|
||||
def test_disk_io_counters_no_disks(self):
|
||||
# Emulate a case where no disks are installed, see:
|
||||
# https://github.com/giampaolo/psutil/issues/1062
|
||||
with mock.patch('psutil._psplatform.disk_io_counters',
|
||||
return_value={}) as m:
|
||||
self.assertIsNone(psutil.disk_io_counters(perdisk=False))
|
||||
self.assertEqual(psutil.disk_io_counters(perdisk=True), {})
|
||||
assert m.called
|
||||
|
||||
|
||||
class TestNetAPIs(unittest.TestCase):
|
||||
|
||||
@unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported')
|
||||
def test_net_io_counters(self):
|
||||
def check_ntuple(nt):
|
||||
self.assertEqual(nt[0], nt.bytes_sent)
|
||||
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])
|
||||
|
||||
@unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported')
|
||||
def test_net_io_counters_no_nics(self):
|
||||
# Emulate a case where no NICs are installed, see:
|
||||
# https://github.com/giampaolo/psutil/issues/1062
|
||||
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 MACOS 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(not (LINUX or BSD or MACOS),
|
||||
"LINUX or BSD or MACOS specific")
|
||||
def test_net_if_stats_enodev(self):
|
||||
# See: https://github.com/giampaolo/psutil/issues/1279
|
||||
with mock.patch('psutil._psutil_posix.net_if_mtu',
|
||||
side_effect=OSError(errno.ENODEV, "")) as m:
|
||||
ret = psutil.net_if_stats()
|
||||
self.assertEqual(ret, {})
|
||||
assert m.called
|
||||
|
||||
|
||||
class TestSensorsAPIs(unittest.TestCase):
|
||||
|
||||
@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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,372 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- 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
|
||||
======================================
|
||||
|
||||
Starting from version 5.3.0 psutil adds unicode support, see:
|
||||
https://github.com/giampaolo/psutil/issues/1040
|
||||
The notes below apply to *any* API returning a string such as
|
||||
process exe(), cwd() or username():
|
||||
|
||||
* all strings are encoded by using the OS filesystem encoding
|
||||
(sys.getfilesystemencoding()) which varies depending on the platform
|
||||
(e.g. "UTF-8" on macOS, "mbcs" on Win)
|
||||
* no API call is supposed to crash with UnicodeDecodeError
|
||||
* instead, in case of badly encoded data returned by the OS, the
|
||||
following error handlers are used to replace the corrupted characters in
|
||||
the string:
|
||||
* Python 3: sys.getfilesystemencodeerrors() (PY 3.6+) or
|
||||
"surrogatescape" on POSIX and "replace" on Windows
|
||||
* Python 2: "replace"
|
||||
* on Python 2 all APIs return bytes (str type), never unicode
|
||||
* on Python 2, you can go back to unicode by doing:
|
||||
|
||||
>>> unicode(p.exe(), sys.getdefaultencoding(), errors="replace")
|
||||
|
||||
For a detailed explanation of how psutil handles unicode see #1040.
|
||||
|
||||
Tests
|
||||
=====
|
||||
|
||||
List of APIs returning or dealing with a string:
|
||||
('not tested' means they are not tested to deal with non-ASCII strings):
|
||||
|
||||
* Process.cmdline()
|
||||
* 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
|
||||
"""
|
||||
|
||||
import os
|
||||
import traceback
|
||||
import warnings
|
||||
from contextlib import closing
|
||||
|
||||
from psutil import BSD
|
||||
from psutil import MACOS
|
||||
from psutil import OPENBSD
|
||||
from psutil import POSIX
|
||||
from psutil import WINDOWS
|
||||
from psutil._compat import PY3
|
||||
from psutil._compat import u
|
||||
from psutil.tests import APPVEYOR
|
||||
from psutil.tests import CIRRUS
|
||||
from psutil.tests import ASCII_FS
|
||||
from psutil.tests import bind_unix_socket
|
||||
from psutil.tests import chdir
|
||||
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 PYPY
|
||||
from psutil.tests import reap_children
|
||||
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
|
||||
|
||||
|
||||
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(os.path.normcase(exe),
|
||||
os.path.normcase(self.funky_name))
|
||||
|
||||
def test_proc_name(self):
|
||||
subp = get_test_subprocess(cmd=[self.funky_name])
|
||||
name = psutil.Process(subp.pid).name()
|
||||
self.assertIsInstance(name, str)
|
||||
if self.expect_exact_path_match():
|
||||
self.assertEqual(name, os.path.basename(self.funky_name))
|
||||
|
||||
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)
|
||||
|
||||
@unittest.skipIf(PYPY and WINDOWS, "fails on PYPY + WINDOWS")
|
||||
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 and not CIRRUS: # XXX
|
||||
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")
|
||||
@unittest.skipIf(PYPY and WINDOWS,
|
||||
"copyload_shared_lib() unsupported on PYPY + WINDOWS")
|
||||
def test_memory_maps(self):
|
||||
# XXX: on Python 2, using ctypes.CDLL with a unicode path
|
||||
# opens a message box which blocks the test run.
|
||||
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)
|
||||
|
||||
|
||||
# https://travis-ci.org/giampaolo/psutil/jobs/440073249
|
||||
@unittest.skipIf(PYPY and TRAVIS, "unreliable on PYPY + TRAVIS")
|
||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO
|
||||
@unittest.skipIf(ASCII_FS, "ASCII fs")
|
||||
@unittest.skipIf(not subprocess_supports_unicode(TESTFN_UNICODE),
|
||||
"subprocess can't deal with unicode")
|
||||
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.
|
||||
here = '.' if isinstance(cls.funky_name, str) else u('.')
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
return cls.funky_name in os.listdir(here)
|
||||
|
||||
|
||||
@unittest.skipIf(PYPY and TRAVIS, "unreliable on PYPY + TRAVIS")
|
||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO
|
||||
@unittest.skipIf(PYPY, "unreliable on PYPY")
|
||||
@unittest.skipIf(not subprocess_supports_unicode(INVALID_NAME),
|
||||
"subprocess can't deal with invalid unicode")
|
||||
class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase):
|
||||
"""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
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 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")
|
||||
@unittest.skipIf(PYPY and WINDOWS, "segfaults on PYPY + WINDOWS")
|
||||
def test_proc_environ(self):
|
||||
# Note: differently from others, this test does not deal
|
||||
# with fs paths. On Python 2 subprocess module is broken as
|
||||
# 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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,870 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- 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 FileNotFoundError
|
||||
from psutil.tests import APPVEYOR
|
||||
from psutil.tests import get_test_subprocess
|
||||
from psutil.tests import HAS_BATTERY
|
||||
from psutil.tests import mock
|
||||
from psutil.tests import PY3
|
||||
from psutil.tests import PYPY
|
||||
from psutil.tests import reap_children
|
||||
from psutil.tests import retry_on_failure
|
||||
from psutil.tests import sh
|
||||
from psutil.tests import unittest
|
||||
|
||||
|
||||
if WINDOWS and not PYPY:
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
import win32api # requires "pip install pypiwin32"
|
||||
import win32con
|
||||
import win32process
|
||||
import wmi # requires "pip install wmi" / "make setup-dev-env"
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@unittest.skipIf(PYPY, "pywin32 not available on PYPY") # skip whole module
|
||||
class TestCase(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# System APIs
|
||||
# ===================================================================
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class TestCpuAPIs(TestCase):
|
||||
|
||||
@unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ,
|
||||
'NUMBER_OF_PROCESSORS env var is not available')
|
||||
def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self):
|
||||
# Will likely fail on many-cores systems:
|
||||
# https://stackoverflow.com/questions/31209256
|
||||
num_cpus = int(os.environ['NUMBER_OF_PROCESSORS'])
|
||||
self.assertEqual(num_cpus, psutil.cpu_count())
|
||||
|
||||
def test_cpu_count_vs_GetSystemInfo(self):
|
||||
# Will likely fail on many-cores systems:
|
||||
# https://stackoverflow.com/questions/31209256
|
||||
sys_value = win32api.GetSystemInfo()[5]
|
||||
psutil_value = psutil.cpu_count()
|
||||
self.assertEqual(sys_value, psutil_value)
|
||||
|
||||
def test_cpu_count_logical_vs_wmi(self):
|
||||
w = wmi.WMI()
|
||||
proc = w.Win32_Processor()[0]
|
||||
self.assertEqual(psutil.cpu_count(), proc.NumberOfLogicalProcessors)
|
||||
|
||||
def test_cpu_count_phys_vs_wmi(self):
|
||||
w = wmi.WMI()
|
||||
proc = w.Win32_Processor()[0]
|
||||
self.assertEqual(psutil.cpu_count(logical=False), proc.NumberOfCores)
|
||||
|
||||
def test_cpu_count_vs_cpu_times(self):
|
||||
self.assertEqual(psutil.cpu_count(),
|
||||
len(psutil.cpu_times(percpu=True)))
|
||||
|
||||
def test_cpu_freq(self):
|
||||
w = wmi.WMI()
|
||||
proc = w.Win32_Processor()[0]
|
||||
self.assertEqual(proc.CurrentClockSpeed, psutil.cpu_freq().current)
|
||||
self.assertEqual(proc.MaxClockSpeed, psutil.cpu_freq().max)
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class TestSystemAPIs(TestCase):
|
||||
|
||||
def test_nic_names(self):
|
||||
out = sh('ipconfig /all')
|
||||
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_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_on_failure()
|
||||
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_on_failure()
|
||||
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
|
||||
if 'cdrom' in ps_part.opts:
|
||||
break
|
||||
try:
|
||||
usage = psutil.disk_usage(ps_part.mountpoint)
|
||||
except FileNotFoundError:
|
||||
# usually this is the floppy
|
||||
break
|
||||
self.assertEqual(usage.total, int(wmi_part.Size))
|
||||
wmi_free = int(wmi_part.FreeSpace)
|
||||
self.assertEqual(usage.free, wmi_free)
|
||||
# 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():
|
||||
if 'cdrom' in disk.opts:
|
||||
continue
|
||||
sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint)
|
||||
psutil_value = psutil.disk_usage(disk.mountpoint)
|
||||
self.assertAlmostEqual(sys_value[0], psutil_value.free,
|
||||
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())
|
||||
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(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(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_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))
|
||||
|
||||
@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_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, self.pid)
|
||||
self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle)
|
||||
|
||||
hndcnt = ctypes.wintypes.DWORD()
|
||||
ctypes.windll.kernel32.GetProcessHandleCount(
|
||||
handle, ctypes.byref(hndcnt))
|
||||
sys_value = hndcnt.value
|
||||
psutil_value = psutil.Process(self.pid).num_handles()
|
||||
self.assertEqual(psutil_value, sys_value)
|
||||
|
||||
def test_error_partial_copy(self):
|
||||
# https://github.com/giampaolo/psutil/issues/875
|
||||
exc = WindowsError()
|
||||
exc.winerror = 299
|
||||
with mock.patch("psutil._psplatform.cext.proc_cwd", side_effect=exc):
|
||||
with mock.patch("time.sleep") as m:
|
||||
p = psutil.Process()
|
||||
self.assertRaises(psutil.AccessDenied, p.cwd)
|
||||
self.assertGreaterEqual(m.call_count, 5)
|
||||
|
||||
def test_exe(self):
|
||||
# NtQuerySystemInformation succeeds if process is gone. Make sure
|
||||
# it raises NSP for a non existent pid.
|
||||
pid = psutil.pids()[-1] + 99999
|
||||
proc = psutil._psplatform.Process(pid)
|
||||
self.assertRaises(psutil.NoSuchProcess, proc.exe)
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class TestProcessWMI(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(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()
|
||||
|
||||
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_times",
|
||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
||||
self.assertEqual(psutil.Process(self.pid).create_time(), ctime)
|
||||
assert fun.called
|
||||
|
||||
def test_cpu_times(self):
|
||||
cpu_times_1 = psutil.Process(self.pid).cpu_times()
|
||||
with mock.patch("psutil._psplatform.cext.proc_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
|
||||
|
||||
def test_cmdline(self):
|
||||
from psutil._pswindows import convert_oserror
|
||||
for pid in psutil.pids():
|
||||
try:
|
||||
a = cext.proc_cmdline(pid, use_peb=True)
|
||||
b = cext.proc_cmdline(pid, use_peb=False)
|
||||
except OSError as err:
|
||||
err = convert_oserror(err)
|
||||
if not isinstance(err, (psutil.AccessDenied,
|
||||
psutil.NoSuchProcess)):
|
||||
raise
|
||||
else:
|
||||
self.assertEqual(a, b)
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class RemoteProcessTestCase(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)
|
||||
try:
|
||||
p.environ()
|
||||
except psutil.AccessDenied:
|
||||
pass
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# Windows services
|
||||
# ===================================================================
|
||||
|
||||
|
||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
||||
class TestServices(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):
|
||||
ERROR_SERVICE_DOES_NOT_EXIST = \
|
||||
psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST
|
||||
ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED
|
||||
|
||||
name = next(psutil.win_service_iter()).name()
|
||||
with self.assertRaises(psutil.NoSuchProcess) as cm:
|
||||
psutil.win_service_get(name + '???')
|
||||
self.assertEqual(cm.exception.name, name + '???')
|
||||
|
||||
# test NoSuchProcess
|
||||
service = psutil.win_service_get(name)
|
||||
if PY3:
|
||||
args = (0, "msg", 0, ERROR_SERVICE_DOES_NOT_EXIST)
|
||||
else:
|
||||
args = (ERROR_SERVICE_DOES_NOT_EXIST, "msg")
|
||||
exc = WindowsError(*args)
|
||||
with mock.patch("psutil._psplatform.cext.winservice_query_status",
|
||||
side_effect=exc):
|
||||
self.assertRaises(psutil.NoSuchProcess, service.status)
|
||||
with mock.patch("psutil._psplatform.cext.winservice_query_config",
|
||||
side_effect=exc):
|
||||
self.assertRaises(psutil.NoSuchProcess, service.username)
|
||||
|
||||
# test AccessDenied
|
||||
if PY3:
|
||||
args = (0, "msg", 0, ERROR_ACCESS_DENIED)
|
||||
else:
|
||||
args = (ERROR_ACCESS_DENIED, "msg")
|
||||
exc = WindowsError(*args)
|
||||
with mock.patch("psutil._psplatform.cext.winservice_query_status",
|
||||
side_effect=exc):
|
||||
self.assertRaises(psutil.AccessDenied, service.status)
|
||||
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__':
|
||||
from psutil.tests.runner import run
|
||||
run(__file__)
|
|
@ -1,31 +0,0 @@
|
|||
freebsd_12_1_py3_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-1-release-amd64
|
||||
env:
|
||||
CIRRUS: 1
|
||||
install_script:
|
||||
- pkg install -y python3 gcc py37-pip
|
||||
script:
|
||||
- python3 -m pip install --user setuptools
|
||||
- make clean
|
||||
- make install
|
||||
- make test
|
||||
- make test-memleaks
|
||||
- PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_access_denied.py
|
||||
- PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_api_speed.py
|
||||
|
||||
freebsd_12_1_py2_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-1-release-amd64
|
||||
env:
|
||||
CIRRUS: 1
|
||||
install_script:
|
||||
- pkg install -y python gcc py27-pip
|
||||
script:
|
||||
- python2.7 -m pip install --user setuptools ipaddress mock
|
||||
- make clean
|
||||
- make install
|
||||
- make test
|
||||
- make test-memleaks
|
||||
- PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_access_denied.py
|
||||
- PSUTIL_TESTING=1 python3 -Wa scripts/internal/print_api_speed.py
|
|
@ -1,32 +0,0 @@
|
|||
[report]
|
||||
|
||||
include =
|
||||
*psutil*
|
||||
omit =
|
||||
psutil/_compat.py
|
||||
psutil/tests/*
|
||||
setup.py
|
||||
exclude_lines =
|
||||
enum.IntEnum
|
||||
except ImportError:
|
||||
globals().update
|
||||
if __name__ == .__main__.:
|
||||
if _WINDOWS:
|
||||
if BSD
|
||||
if enum is None:
|
||||
if enum is not None:
|
||||
if FREEBSD
|
||||
if has_enums:
|
||||
if LINUX
|
||||
if LITTLE_ENDIAN:
|
||||
if NETBSD
|
||||
if OPENBSD
|
||||
if MACOS
|
||||
if ppid_map is None:
|
||||
if PY3:
|
||||
if SUNOS
|
||||
if sys.platform.startswith
|
||||
if WINDOWS
|
||||
import enum
|
||||
pragma: no cover
|
||||
raise NotImplementedError
|
|
@ -1,19 +0,0 @@
|
|||
syntax: glob
|
||||
*.al
|
||||
*.bak
|
||||
*.egg-info
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
*.orig
|
||||
*.pyc
|
||||
*.pyd
|
||||
*.rej
|
||||
*.so
|
||||
*.swp
|
||||
.failed-tests.txt
|
||||
.cache/
|
||||
.idea/
|
||||
.tox/
|
||||
build/
|
||||
dist/
|
|
@ -1,666 +0,0 @@
|
|||
Intro
|
||||
=====
|
||||
|
||||
I would like to recognize some of the people who have been instrumental in the
|
||||
development of psutil. I'm sure I'm forgetting somebody (feel free to email me)
|
||||
but here is a short list. It's modeled after the Linux CREDITS file where the
|
||||
fields are: name (N), e-mail (E), website (W), country (C), description (D),
|
||||
(I) issues. Issue tracker is at https://github.com/giampaolo/psutil/issues).
|
||||
A big thanks to all of you.
|
||||
|
||||
- Giampaolo
|
||||
|
||||
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)
|
||||
- macOS:
|
||||
- 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)
|
||||
|
||||
Top 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
|
||||
D: AIX implementation, expert on multiple fronts
|
||||
I: 517, 607, 610, 1131, 1123, 1130, 1154, 1164, 1174, 1177, 1210, 1214, 1408,
|
||||
1329, 1276, 1494, 1528.
|
||||
|
||||
N: Alex Manuskin
|
||||
W: https://github.com/amanusk
|
||||
D: FreeBSD cpu_freq(), OSX temperatures, support for Linux temperatures.
|
||||
I: 1284, 1345, 1350, 1352, 1472, 1481, 1487.
|
||||
|
||||
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 macOS C development.
|
||||
I: 125, 150, 174, 206
|
||||
|
||||
N: Landry Breuil
|
||||
W: https://github.com/landryb
|
||||
D: OpenBSD implementation.
|
||||
I: 615
|
||||
|
||||
N: Justin Venus
|
||||
E: justin.venus@gmail.com
|
||||
D: Solaris support
|
||||
I: 18
|
||||
|
||||
N: Thomas Klausner
|
||||
W: https://github.com/0-wiz-0
|
||||
D: NetBSD implementation (co-author).
|
||||
I: 557
|
||||
|
||||
N: Ryo Onodera
|
||||
W: https://github.com/ryoon
|
||||
D: NetBSD implementation (co-author).
|
||||
I: 557
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
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
|
||||
D: help on Windows development (initial version of Process.username())
|
||||
|
||||
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: cjgohlke
|
||||
E: cjgohlke@gmail.com
|
||||
D: Windows 64 bit support
|
||||
I: 107
|
||||
|
||||
N: Frank Benkstein
|
||||
D: process environ()
|
||||
W: https://github.com/fbenkstein
|
||||
I: 732, 733
|
||||
|
||||
N: Mozilla Foundation
|
||||
D: sample code for process USS memory.
|
||||
|
||||
N: EccoTheFlintstone
|
||||
W: https://github.com/EccoTheFlintstone
|
||||
I: 1368, 1348
|
||||
|
||||
----
|
||||
|
||||
N: Jeffery Kline
|
||||
E: jeffery.kline@gmail.com
|
||||
I: 130
|
||||
|
||||
N: Grabriel Monnerat
|
||||
E: gabrielmonnerat@gmail.com
|
||||
I: 146
|
||||
|
||||
N: Philip Roberts
|
||||
E: philip.roberts@gmail.com
|
||||
I: 168
|
||||
|
||||
N: jcscoobyrs
|
||||
E: jcscoobyrs@gmail.com
|
||||
I: 125
|
||||
|
||||
N: Sandro Tosi
|
||||
E: sandro.tosi@gmail.com
|
||||
I: 200, 201
|
||||
|
||||
N: Andrew Colin
|
||||
E: andrew.colin@gmail.com
|
||||
I: 248
|
||||
|
||||
N: Amoser
|
||||
E: amoser@google.com
|
||||
I: 266, 267, 340
|
||||
|
||||
N: Matthew Grant
|
||||
E: matthewgrant5@gmail.com
|
||||
I: 271
|
||||
|
||||
N: oweidner
|
||||
E: oweidner@cct.lsu.edu
|
||||
I: 275
|
||||
|
||||
N: Tarek Ziade
|
||||
E: ziade.tarek
|
||||
I: 281
|
||||
|
||||
N: Luca Cipriani
|
||||
C: Turin, Italy
|
||||
E: luca.opensource@gmail.com
|
||||
I: 278
|
||||
|
||||
N: Maciej Lach,
|
||||
E: maciej.lach@gmail.com
|
||||
I: 294
|
||||
|
||||
N: James Pye
|
||||
E: james.pye@gmail.com
|
||||
I: 305, 306
|
||||
|
||||
N: Stanchev Emil
|
||||
E: stanchev.emil
|
||||
I: 314
|
||||
|
||||
N: Kim Gräsman
|
||||
E: kim.grasman@gmail.com
|
||||
D: ...also kindly donated some money.
|
||||
I: 316
|
||||
|
||||
N: Riccardo Murri
|
||||
C: Italy
|
||||
I: 318
|
||||
|
||||
N: Florent Xicluna
|
||||
E: florent.xicluna@gmail.com
|
||||
I: 319
|
||||
|
||||
N: Michal Spondr
|
||||
E: michal.spondr
|
||||
I: 313
|
||||
|
||||
N: Jean Sebastien
|
||||
E: dumbboules@gmail.com
|
||||
I: 344
|
||||
|
||||
N: Rob Smith
|
||||
W: http://www.kormoc.com/
|
||||
I: 341
|
||||
|
||||
N: Youngsik Kim
|
||||
W: https://plus.google.com/101320747613749824490/
|
||||
I: 317
|
||||
|
||||
N: Gregory Szorc
|
||||
W: https://plus.google.com/116873264322260110710/posts
|
||||
I: 323
|
||||
|
||||
N: André Oriani
|
||||
E: aoriani@gmail.com
|
||||
I: 361
|
||||
|
||||
N: clackwell
|
||||
E: clackwell@gmail.com
|
||||
I: 356
|
||||
|
||||
N: m.malycha
|
||||
E: m.malycha@gmail.com
|
||||
I: 351
|
||||
|
||||
N: John Baldwin
|
||||
E: jhb@FreeBSD.org
|
||||
I: 370
|
||||
|
||||
N: Jan Beich
|
||||
E: jbeich@tormail.org
|
||||
I: 325
|
||||
|
||||
N: floppymaster
|
||||
E: floppymaster@gmail.com
|
||||
I: 380
|
||||
|
||||
N: Arfrever.FTA
|
||||
E: Arfrever.FTA@gmail.com
|
||||
I: 369, 404
|
||||
|
||||
N: danudey
|
||||
E: danudey@gmail.com
|
||||
I: 386
|
||||
|
||||
N: Adrien Fallou
|
||||
I: 224
|
||||
|
||||
N: Gisle Vanem
|
||||
E: gisle.vanem@gmail.com
|
||||
I: 411
|
||||
|
||||
N: thepyr0
|
||||
E: thepyr0@gmail.com
|
||||
I: 414
|
||||
|
||||
N: John Pankov
|
||||
E: john.pankov@gmail.com
|
||||
I: 435
|
||||
|
||||
N: Matt Good
|
||||
W: http://matt-good.net/
|
||||
I: 438
|
||||
|
||||
N: Ulrich Klank
|
||||
E: ulrich.klank@scitics.de
|
||||
I: 448
|
||||
|
||||
N: Josiah Carlson
|
||||
E: josiah.carlson@gmail.com
|
||||
I: 451, 452
|
||||
|
||||
N: Raymond Hettinger
|
||||
D: namedtuple and lru_cache backward compatible implementations.
|
||||
|
||||
N: Jason Kirtland
|
||||
D: backward compatible implementation of collections.defaultdict.
|
||||
|
||||
M: Ken Seeho
|
||||
D: @cached_property decorator
|
||||
|
||||
N: crusaderky
|
||||
E: crusaderky@gmail.com
|
||||
I: 470, 477
|
||||
|
||||
E: alex@mroja.net
|
||||
I: 471
|
||||
|
||||
N: Gautam Singh
|
||||
E: gautam.singh@gmail.com
|
||||
I: 466
|
||||
|
||||
E: lhn@hupfeldtit.dk
|
||||
I: 476, 479
|
||||
|
||||
N: Francois Charron
|
||||
E: francois.charron.1@gmail.com
|
||||
I: 474
|
||||
|
||||
N: Naveed Roudsari
|
||||
E: naveed.roudsari@gmail.com
|
||||
I: 421
|
||||
|
||||
N: Alexander Grothe
|
||||
E: Alexander.Grothe@gmail.com
|
||||
I: 497
|
||||
|
||||
N: Szigeti Gabor Niif
|
||||
E: szigeti.gabor.niif@gmail.com
|
||||
I: 446
|
||||
|
||||
N: msabramo
|
||||
E: msabramo@gmail.com
|
||||
I: 492
|
||||
|
||||
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, 603
|
||||
|
||||
N: Sylvain Mouquet
|
||||
E: sylvain.mouquet@gmail.com
|
||||
I: 565
|
||||
|
||||
N: karthikrev
|
||||
I: 568
|
||||
|
||||
N: Bruno Binet
|
||||
E: bruno.binet@gmail.com
|
||||
I: 572
|
||||
|
||||
N: Gabi Davar
|
||||
C: Israel
|
||||
W: https://github.com/mindw
|
||||
I: 578, 581, 587
|
||||
|
||||
N: spacewanderlzx
|
||||
C: Guangzhou,China
|
||||
E: spacewanderlzx@gmail.com
|
||||
I: 555
|
||||
|
||||
N: Fabian Groffen
|
||||
I: 611, 618
|
||||
|
||||
N: desbma
|
||||
W: https://github.com/desbma
|
||||
C: France
|
||||
I: 628
|
||||
|
||||
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: 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: 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
|
||||
D: good help with FreeBSD
|
||||
I: 1042, 1079, 1070
|
||||
|
||||
N: Oleksii Shevchuk
|
||||
W: https://github.com/alxchk
|
||||
I: 1077, 1093, 1091, 1220, 1346
|
||||
|
||||
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
|
||||
|
||||
N: Dan Vinakovsky
|
||||
W: https://github.com/hexaclock
|
||||
I: 1216
|
||||
|
||||
N: stswandering
|
||||
W: https://github.com/stswandering
|
||||
I: 1243
|
||||
|
||||
N: Georg Sauthoff
|
||||
W: https://github.com/gsauthof
|
||||
I: 1193, 1194
|
||||
|
||||
N: Maxime Mouial
|
||||
W: https://github.com/hush-hush
|
||||
I: 1239
|
||||
|
||||
N: Denis Krienbühl
|
||||
W: https://github.com/href
|
||||
I: 1260
|
||||
|
||||
N: Jean-Luc Migot
|
||||
W: https://github.com/jmigot-tehtris
|
||||
I: 1258, 1289
|
||||
|
||||
N: Nikhil Marathe
|
||||
W: https://github.com/nikhilm
|
||||
I: 1278
|
||||
|
||||
N: Sylvain Duchesne
|
||||
W: https://github.com/sylvainduchesne
|
||||
I: 1294
|
||||
|
||||
N: Lawrence Ye
|
||||
W: https://github.com/LEAFERx
|
||||
I: 1321
|
||||
|
||||
N: Ilya Yanok
|
||||
W: https://github.com/yanok
|
||||
I: 1332
|
||||
|
||||
N: Jaime Fullaondo
|
||||
W: https://github.com/truthbk
|
||||
D: AIX support
|
||||
I: 1320
|
||||
|
||||
N: Koen Kooi
|
||||
W: https://github.com/koenkooi
|
||||
I: 1360
|
||||
|
||||
N: Ghislain Le Meur
|
||||
W: https://github.com/gigi206
|
||||
D: idea for Process.parents()
|
||||
I: 1379
|
||||
|
||||
N: Benjamin Drung
|
||||
D: make tests invariant to LANG setting
|
||||
W: https://github.com/bdrung
|
||||
I: 1462
|
||||
|
||||
N: Xiaoling Bao
|
||||
I: 1223
|
||||
|
||||
N: Cedric Lamoriniere
|
||||
W: https://github.com/clamoriniere
|
||||
I: 1470
|
||||
|
||||
N: Daniel Beer
|
||||
W: https://github.com/dbeer1
|
||||
I: 1471
|
||||
|
||||
N: Samer Masterson
|
||||
W: https://github.com/samertm
|
||||
I: 1480
|
||||
|
||||
N: Ammar Askar
|
||||
E: ammar@ammaraskar.com
|
||||
W: http://ammaraskar.com/
|
||||
I: 604, 1484
|
||||
|
||||
N: agnewee
|
||||
W: https://github.com/Agnewee
|
||||
C: China
|
||||
I: 1491
|
||||
|
||||
N: Kamil Rytarowski
|
||||
W: https://github.com/krytarowski
|
||||
C: Poland
|
||||
I: 1526, 1530
|
||||
|
||||
N: Athos Ribeiro
|
||||
W: https://github.com/athos-ribeiro
|
||||
I: 1585
|
||||
|
||||
N: Erwan Le Pape
|
||||
W: https://github.com/erwan-le-pape
|
||||
I: 1570
|
||||
|
||||
N: Étienne Servais
|
||||
W: https://github.com/vser1
|
||||
I: 1607, 1637
|
||||
|
||||
N: Bernát Gábor
|
||||
W: https://github.com/gaborbernat
|
||||
I: 1565
|
||||
|
||||
N: Nathan Houghton
|
||||
W: https://github.com/n1000
|
||||
I: 1619
|
||||
|
||||
N: Riccardo Schirone
|
||||
W: https://github.com/ret2libc
|
||||
C: Milano, Italy
|
||||
I: 1616
|
||||
|
||||
N: Po-Chuan Hsieh
|
||||
W: https://github.com/sunpoet
|
||||
C: Taiwan
|
||||
I: 1646
|
||||
|
||||
N: Javad Karabi
|
||||
W: https://github.com/karabijavad
|
||||
I: 1648
|
||||
|
||||
N: Mike Hommey
|
||||
W: https://github.com/glandium
|
||||
I: 1665
|
||||
|
||||
N: Anselm Kruis
|
||||
W: https://github.com/akruis
|
||||
I: 1695
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,140 +0,0 @@
|
|||
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::
|
||||
|
||||
wget https://bootstrap.pypa.io/get-pip.py -O - | python
|
||||
|
||||
On macOS or via curl::
|
||||
|
||||
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::
|
||||
|
||||
C:\Python27\python.exe get-pip.py
|
||||
|
||||
Permission issues (UNIX)
|
||||
========================
|
||||
|
||||
The commands below assume you're running as root.
|
||||
If you aren't or you bump into permission errors you can either install psutil
|
||||
for your user only::
|
||||
|
||||
pip3 install --user psutil
|
||||
|
||||
...or prepend ``sudo`` and install it globally, e.g.::
|
||||
|
||||
sudo pip3 install psutil
|
||||
|
||||
Linux
|
||||
=====
|
||||
|
||||
Ubuntu / Debian::
|
||||
|
||||
sudo apt-get install gcc python3-dev
|
||||
pip3 install psutil
|
||||
|
||||
RedHat / CentOS::
|
||||
|
||||
sudo yum install gcc python3-devel
|
||||
pip3 install psutil
|
||||
|
||||
If you're on Python 2 use ``python-dev`` instead.
|
||||
|
||||
macOS
|
||||
=====
|
||||
|
||||
Install `Xcode <https://developer.apple.com/downloads/?name=Xcode>`__ then run::
|
||||
|
||||
pip3 install psutil
|
||||
|
||||
Windows
|
||||
=======
|
||||
|
||||
Open a cmd.exe shell and run::
|
||||
|
||||
python3 -m pip install psutil
|
||||
|
||||
This assumes "python" is in your PATH. If not, specify the full python.exe
|
||||
path.
|
||||
|
||||
In order to compile psutil from sources you'll need **Visual Studio** (Mingw32
|
||||
is not supported).
|
||||
This `blog post <https://blog.ionelmc.ro/2014/12/21/compiling-python-extensions-on-windows/>`__
|
||||
provides numerous info on how to properly set up VS. The needed VS versions are:
|
||||
|
||||
* Python 2.6, 2.7: `VS-2008 <http://www.microsoft.com/en-us/download/details.aspx?id=44266>`__
|
||||
* Python 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 <https://www.microsoft.com/en-us/download/details.aspx?id=3138>`__.
|
||||
Once installed run `vcvars64.bat`
|
||||
(see `here <http://stackoverflow.com/questions/11072521/>`__).
|
||||
Once VS is setup open a cmd.exe shell, cd into psutil directory and run::
|
||||
|
||||
python3 setup.py build
|
||||
python3 setup.py install
|
||||
|
||||
FreeBSD
|
||||
=======
|
||||
|
||||
::
|
||||
|
||||
pkg install python3 gcc
|
||||
python -m pip3 install psutil
|
||||
|
||||
OpenBSD
|
||||
=======
|
||||
|
||||
::
|
||||
|
||||
export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/
|
||||
pkg_add -v python gcc
|
||||
python3 -m pip install psutil
|
||||
|
||||
NetBSD
|
||||
======
|
||||
|
||||
::
|
||||
|
||||
export PKG_PATH="ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All"
|
||||
pkg_add -v pkgin
|
||||
pkgin install python3 gcc
|
||||
python3 -m pip install psutil
|
||||
|
||||
Solaris
|
||||
=======
|
||||
|
||||
If ``cc`` compiler is not installed create a symlink to ``gcc``::
|
||||
|
||||
sudo ln -s /usr/bin/gcc /usr/local/bin/cc
|
||||
|
||||
Install::
|
||||
|
||||
pkg install gcc
|
||||
python3 -m pip install psutil
|
||||
|
||||
Install from sources
|
||||
====================
|
||||
|
||||
::
|
||||
|
||||
git clone https://github.com/giampaolo/psutil.git
|
||||
cd psutil
|
||||
python3 setup.py install
|
||||
|
||||
Testing installation
|
||||
====================
|
||||
|
||||
::
|
||||
|
||||
python3 -m psutil.tests
|
||||
|
||||
Dev Guide
|
||||
=========
|
||||
|
||||
See: `dev guide <https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst>`__.
|
|
@ -1,29 +0,0 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola'
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the psutil authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,145 +0,0 @@
|
|||
include .cirrus.yml
|
||||
include .coveragerc
|
||||
include .gitignore
|
||||
include CREDITS
|
||||
include HISTORY.rst
|
||||
include INSTALL.rst
|
||||
include LICENSE
|
||||
include MANIFEST.in
|
||||
include Makefile
|
||||
include README.rst
|
||||
include docs/DEVGUIDE.rst
|
||||
include docs/DEVNOTES
|
||||
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/__init__.py
|
||||
include psutil/_common.py
|
||||
include psutil/_compat.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/cpu.c
|
||||
include psutil/arch/windows/cpu.h
|
||||
include psutil/arch/windows/disk.c
|
||||
include psutil/arch/windows/disk.h
|
||||
include psutil/arch/windows/net.c
|
||||
include psutil/arch/windows/net.h
|
||||
include psutil/arch/windows/ntextapi.h
|
||||
include psutil/arch/windows/process_handles.c
|
||||
include psutil/arch/windows/process_handles.h
|
||||
include psutil/arch/windows/process_info.c
|
||||
include psutil/arch/windows/process_info.h
|
||||
include psutil/arch/windows/process_utils.c
|
||||
include psutil/arch/windows/process_utils.h
|
||||
include psutil/arch/windows/security.c
|
||||
include psutil/arch/windows/security.h
|
||||
include psutil/arch/windows/services.c
|
||||
include psutil/arch/windows/services.h
|
||||
include psutil/arch/windows/socks.c
|
||||
include psutil/arch/windows/socks.h
|
||||
include psutil/arch/windows/wmi.c
|
||||
include psutil/arch/windows/wmi.h
|
||||
include psutil/tests/README.rst
|
||||
include psutil/tests/__init__.py
|
||||
include psutil/tests/__main__.py
|
||||
include psutil/tests/runner.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/.git-pre-commit
|
||||
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/clinter.py
|
||||
include scripts/internal/fix_flake8.py
|
||||
include scripts/internal/generate_manifest.py
|
||||
include scripts/internal/print_access_denied.py
|
||||
include scripts/internal/print_announce.py
|
||||
include scripts/internal/print_api_speed.py
|
||||
include scripts/internal/print_timeline.py
|
||||
include scripts/internal/purge_installation.py
|
||||
include scripts/internal/win_download_wheels.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 tox.ini
|
|
@ -1,292 +0,0 @@
|
|||
# Shortcuts for various tasks (UNIX only).
|
||||
# To use a specific Python version run: "make install PYTHON=python3.3"
|
||||
# You can set the variables below from the command line.
|
||||
|
||||
PYTHON = python3
|
||||
TSCRIPT = psutil/tests/runner.py
|
||||
ARGS =
|
||||
# List of nice-to-have dev libs.
|
||||
DEPS = \
|
||||
argparse \
|
||||
check-manifest \
|
||||
coverage \
|
||||
flake8 \
|
||||
pyperf \
|
||||
requests \
|
||||
setuptools \
|
||||
twine \
|
||||
virtualenv \
|
||||
wheel
|
||||
PY2_DEPS = \
|
||||
futures \
|
||||
ipaddress \
|
||||
mock==1.0.1 \
|
||||
unittest2
|
||||
DEPS += `$(PYTHON) -c \
|
||||
"import sys; print('$(PY2_DEPS)' if sys.version_info[0] == 2 else '')"`
|
||||
# In not in a virtualenv, add --user options for install commands.
|
||||
INSTALL_OPTS = `$(PYTHON) -c \
|
||||
"import sys; print('' if hasattr(sys, 'real_prefix') else '--user')"`
|
||||
TEST_PREFIX = PYTHONWARNINGS=all PSUTIL_TESTING=1 PSUTIL_DEBUG=1
|
||||
|
||||
all: test
|
||||
|
||||
# ===================================================================
|
||||
# Install
|
||||
# ===================================================================
|
||||
|
||||
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 \
|
||||
.failed-tests.txt \
|
||||
.tox \
|
||||
build/ \
|
||||
dist/ \
|
||||
docs/_build/ \
|
||||
htmlcov/
|
||||
|
||||
_:
|
||||
|
||||
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.
|
||||
PYTHONWARNINGS=all $(PYTHON) setup.py build_ext -i
|
||||
$(PYTHON) -c "import psutil" # make sure it actually worked
|
||||
|
||||
install: ## Install this package as current user in "edit" mode.
|
||||
${MAKE} build
|
||||
PYTHONWARNINGS=all $(PYTHON) setup.py develop $(INSTALL_OPTS)
|
||||
|
||||
uninstall: ## Uninstall this package via pip.
|
||||
cd ..; $(PYTHON) -m pip uninstall -y -v psutil || true
|
||||
$(PYTHON) scripts/internal/purge_installation.py
|
||||
|
||||
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);"
|
||||
|
||||
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 --trusted-host files.pythonhosted.org pip
|
||||
$(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade --trusted-host files.pythonhosted.org $(DEPS)
|
||||
|
||||
# ===================================================================
|
||||
# Tests
|
||||
# ===================================================================
|
||||
|
||||
test: ## Run all tests.
|
||||
${MAKE} install
|
||||
$(TEST_PREFIX) $(PYTHON) $(TSCRIPT)
|
||||
|
||||
test-process: ## Run process-related API tests.
|
||||
${MAKE} install
|
||||
$(TEST_PREFIX) $(PYTHON) psutil/tests/test_process.py
|
||||
|
||||
test-system: ## Run system-related API tests.
|
||||
${MAKE} install
|
||||
$(TEST_PREFIX) $(PYTHON) psutil/tests/test_system.py
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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-failed: ## Re-run tests which failed on last run
|
||||
${MAKE} install
|
||||
$(TEST_PREFIX) $(PYTHON) $(TSCRIPT) --last-failed
|
||||
|
||||
test-coverage: ## Run test coverage.
|
||||
${MAKE} install
|
||||
# Note: coverage options are controlled by .coveragerc file
|
||||
rm -rf .coverage htmlcov
|
||||
$(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
|
||||
|
||||
# ===================================================================
|
||||
# Linters
|
||||
# ===================================================================
|
||||
|
||||
lint-py: ## Run Python (flake8) linter.
|
||||
@git ls-files '*.py' | xargs $(PYTHON) -m flake8
|
||||
|
||||
lint-c: ## Run C linter.
|
||||
@git ls-files '*.c' '*.h' | xargs $(PYTHON) scripts/internal/clinter.py
|
||||
|
||||
lint: ## Run Python (flake8) and C linters.
|
||||
${MAKE} lint-py
|
||||
${MAKE} lint-c
|
||||
|
||||
fix-lint: ## Attempt to automatically fix some Python lint issues.
|
||||
@git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 --exit-zero | $(PYTHON) scripts/internal/fix_flake8.py
|
||||
|
||||
# ===================================================================
|
||||
# GIT
|
||||
# ===================================================================
|
||||
|
||||
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 ../../scripts/internal/.git-pre-commit .git/hooks/pre-commit
|
||||
chmod +x .git/hooks/pre-commit
|
||||
|
||||
# ===================================================================
|
||||
# Distribution
|
||||
# ===================================================================
|
||||
|
||||
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/win_download_wheels.py --user giampaolo --project psutil
|
||||
|
||||
upload-src: ## Upload source tarball on https://pypi.org/project/psutil/
|
||||
${MAKE} sdist
|
||||
$(PYTHON) setup.py sdist upload
|
||||
|
||||
upload-win-wheels: ## Upload wheels in dist/* directory on PyPI.
|
||||
$(PYTHON) -m twine upload dist/*.whl
|
||||
|
||||
# --- others
|
||||
|
||||
check-sdist: ## Create source distribution and checks its sanity (MANIFEST)
|
||||
rm -rf dist
|
||||
${MAKE} clean
|
||||
$(PYTHON) -m virtualenv --clear --no-wheel --quiet build/venv
|
||||
PYTHONWARNINGS=all $(PYTHON) setup.py sdist
|
||||
build/venv/bin/python -m pip install -v --isolated --quiet dist/*.tar.gz
|
||||
build/venv/bin/python -c "import os; os.chdir('build/venv'); import psutil"
|
||||
|
||||
pre-release: ## Check if we're ready to produce a new release.
|
||||
${MAKE} check-sdist
|
||||
${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
|
||||
|
||||
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
|
||||
|
||||
# ===================================================================
|
||||
# Printers
|
||||
# ===================================================================
|
||||
|
||||
print-announce: ## Print announce of new release.
|
||||
@$(PYTHON) scripts/internal/print_announce.py
|
||||
|
||||
print-timeline: ## Print releases' timeline.
|
||||
@$(PYTHON) scripts/internal/print_timeline.py
|
||||
|
||||
print-access-denied: ## Print AD exceptions
|
||||
${MAKE} install
|
||||
@$(TEST_PREFIX) $(PYTHON) scripts/internal/print_access_denied.py
|
||||
|
||||
print-api-speed: ## Benchmark all API calls
|
||||
${MAKE} install
|
||||
@$(TEST_PREFIX) $(PYTHON) scripts/internal/print_api_speed.py $(ARGS)
|
||||
|
||||
# ===================================================================
|
||||
# Misc
|
||||
# ===================================================================
|
||||
|
||||
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}'
|
|
@ -1,578 +0,0 @@
|
|||
Metadata-Version: 1.2
|
||||
Name: psutil
|
||||
Version: 5.7.0
|
||||
Summary: Cross-platform lib for process and system monitoring in Python.
|
||||
Home-page: https://github.com/giampaolo/psutil
|
||||
Author: Giampaolo Rodola
|
||||
Author-email: g.rodola@gmail.com
|
||||
License: BSD
|
||||
Description: | |downloads| |stars| |forks| |contributors| |coverage| |quality|
|
||||
| |version| |py-versions| |packages| |license|
|
||||
| |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift|
|
||||
|
||||
.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/stargazers
|
||||
:alt: Github stars
|
||||
|
||||
.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/network/members
|
||||
:alt: Github forks
|
||||
|
||||
.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/graphs/contributors
|
||||
:alt: Contributors
|
||||
|
||||
.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg
|
||||
:target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade
|
||||
:alt: Code quality
|
||||
|
||||
.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy
|
||||
:target: https://travis-ci.org/giampaolo/psutil
|
||||
:alt: Linux tests (Travis)
|
||||
|
||||
.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
|
||||
:target: https://ci.appveyor.com/project/giampaolo/psutil
|
||||
:alt: Windows tests (Appveyor)
|
||||
|
||||
.. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD
|
||||
:target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci
|
||||
:alt: FreeBSD tests (Cirrus-Ci)
|
||||
|
||||
.. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage
|
||||
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
|
||||
:alt: Test coverage (coverall.io)
|
||||
|
||||
.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest
|
||||
:target: http://psutil.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Latest version
|
||||
|
||||
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg
|
||||
:target: https://repology.org/metapackage/python:psutil/versions
|
||||
:alt: Binary packages
|
||||
|
||||
.. |license| image:: https://img.shields.io/pypi/l/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/blob/master/LICENSE
|
||||
:alt: License
|
||||
|
||||
.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF
|
||||
:target: https://twitter.com/grodola
|
||||
:alt: Twitter Follow
|
||||
|
||||
.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
:alt: Tidelift
|
||||
|
||||
-----
|
||||
|
||||
Quick links
|
||||
===========
|
||||
|
||||
- `Home page <https://github.com/giampaolo/psutil>`_
|
||||
- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
|
||||
- `Documentation <http://psutil.readthedocs.io>`_
|
||||
- `Download <https://pypi.org/project/psutil/#files>`_
|
||||
- `Forum <http://groups.google.com/group/psutil/topics>`_
|
||||
- `StackOverflow <https://stackoverflow.com/questions/tagged/psutil>`_
|
||||
- `Blog <http://grodola.blogspot.com/search/label/psutil>`_
|
||||
- `Development guide <https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst>`_
|
||||
- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
psutil (process and system utilities) is a cross-platform library for
|
||||
retrieving information on **running processes** and **system utilization**
|
||||
(CPU, memory, disks, network, sensors) in Python.
|
||||
It is useful mainly for **system monitoring**, **profiling and limiting process
|
||||
resources** and **management of running processes**.
|
||||
It implements many functionalities offered by classic UNIX command line tools
|
||||
such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others.
|
||||
psutil currently supports the following platforms:
|
||||
|
||||
- **Linux**
|
||||
- **Windows**
|
||||
- **macOS**
|
||||
- **FreeBSD, OpenBSD**, **NetBSD**
|
||||
- **Sun Solaris**
|
||||
- **AIX**
|
||||
|
||||
...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 <http://pypy.org/>`__ is also known to work.
|
||||
|
||||
psutil for enterprise
|
||||
=====================
|
||||
|
||||
.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
|
||||
:width: 150
|
||||
:alt: Tidelift
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
.. list-table::
|
||||
:widths: 10 150
|
||||
|
||||
* - |tideliftlogo|
|
||||
- The maintainer of psutil and thousands of other packages are working
|
||||
with Tidelift to deliver commercial support and maintenance for the open
|
||||
source dependencies you use to build your applications. Save time,
|
||||
reduce risk, and improve code health, while paying the maintainers of
|
||||
the exact dependencies you use.
|
||||
`Learn more <https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`__.
|
||||
|
||||
By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support
|
||||
psutil future development. Alternatively consider making a small
|
||||
`donation`_.
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
To report a security vulnerability, please use the `Tidelift security
|
||||
contact`_. Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
Example applications
|
||||
====================
|
||||
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/top-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/top.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
|
||||
Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
|
||||
and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
|
||||
|
||||
Projects using psutil
|
||||
=====================
|
||||
|
||||
psutil has roughly the following monthly downloads:
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
There are over
|
||||
`10.000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
|
||||
on github which depend from psutil.
|
||||
Here's some I find particularly interesting:
|
||||
|
||||
- https://github.com/google/grr
|
||||
- https://github.com/facebook/osquery/
|
||||
- https://github.com/nicolargo/glances
|
||||
- https://github.com/Jahaja/psdash
|
||||
- https://github.com/ajenti/ajenti
|
||||
- https://github.com/home-assistant/home-assistant/
|
||||
|
||||
|
||||
Portings
|
||||
========
|
||||
|
||||
- Go: https://github.com/shirou/gopsutil
|
||||
- C: https://github.com/hamon-in/cpslib
|
||||
- Rust: https://github.com/borntyping/rust-psutil
|
||||
- Nim: https://github.com/johnscillieri/psutil-nim
|
||||
|
||||
|
||||
Example usages
|
||||
==============
|
||||
|
||||
This represents pretty much the whole psutil API.
|
||||
|
||||
CPU
|
||||
---
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>>
|
||||
>>> psutil.cpu_times()
|
||||
scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1)
|
||||
...
|
||||
4.0
|
||||
5.9
|
||||
3.8
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1, percpu=True)
|
||||
...
|
||||
[4.0, 6.9, 3.7, 9.2]
|
||||
[7.0, 8.5, 2.4, 2.1]
|
||||
[1.2, 9.0, 9.9, 7.2]
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_times_percent(interval=1, percpu=False)
|
||||
...
|
||||
scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
>>>
|
||||
>>> psutil.cpu_count()
|
||||
4
|
||||
>>> psutil.cpu_count(logical=False)
|
||||
2
|
||||
>>>
|
||||
>>> psutil.cpu_stats()
|
||||
scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
|
||||
>>>
|
||||
>>> psutil.cpu_freq()
|
||||
scpufreq(current=931.42925, min=800.0, max=3500.0)
|
||||
>>>
|
||||
>>> psutil.getloadavg() # also on Windows (emulated)
|
||||
(3.14, 3.89, 4.67)
|
||||
|
||||
Memory
|
||||
------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.virtual_memory()
|
||||
svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
|
||||
>>> psutil.swap_memory()
|
||||
sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
|
||||
>>>
|
||||
|
||||
Disks
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.disk_partitions()
|
||||
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
|
||||
sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
|
||||
>>>
|
||||
>>> psutil.disk_usage('/')
|
||||
sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
|
||||
>>>
|
||||
>>> psutil.disk_io_counters(perdisk=False)
|
||||
sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)
|
||||
>>>
|
||||
|
||||
Network
|
||||
-------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.net_io_counters(pernic=True)
|
||||
{'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
|
||||
'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
|
||||
>>>
|
||||
>>> psutil.net_connections()
|
||||
[sconn(fd=115, family=<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),
|
||||
...]
|
||||
>>>
|
||||
>>> psutil.net_if_addrs()
|
||||
{'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
|
||||
'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
|
||||
snicaddr(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()
|
||||
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
|
||||
'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
|
||||
>>>
|
||||
|
||||
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)]}
|
||||
>>>
|
||||
>>> psutil.sensors_fans()
|
||||
{'asus': [sfan(label='cpu_fan', current=3200)]}
|
||||
>>>
|
||||
>>> psutil.sensors_battery()
|
||||
sbattery(percent=93, secsleft=16628, power_plugged=False)
|
||||
>>>
|
||||
|
||||
Other system info
|
||||
-----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.users()
|
||||
[suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),
|
||||
suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]
|
||||
>>>
|
||||
>>> psutil.boot_time()
|
||||
1365519115.0
|
||||
>>>
|
||||
|
||||
Process management
|
||||
------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.pids()
|
||||
[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215, 1216, 1220, 1221, 1243, 1244,
|
||||
1301, 1601, 2237, 2355, 2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282,
|
||||
4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446,
|
||||
5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071]
|
||||
>>>
|
||||
>>> p = psutil.Process(7055)
|
||||
>>> p
|
||||
psutil.Process(pid=7055, name='python', started='09:04:44')
|
||||
>>> 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.children(recursive=True)
|
||||
[psutil.Process(pid=29835, name='python2.7', started='11:45:38'),
|
||||
psutil.Process(pid=29836, name='python2.7', started='11:43:39')]
|
||||
>>>
|
||||
>>> p.parent()
|
||||
psutil.Process(pid=4699, name='bash', started='09:06:44')
|
||||
>>> p.parents()
|
||||
[psutil.Process(pid=4699, name='bash', started='09:06:44'),
|
||||
psutil.Process(pid=4689, name='gnome-terminal-server', started='0:06:44'),
|
||||
psutil.Process(pid=1, name='systemd', started='05:56:55')]
|
||||
>>>
|
||||
>>> p.status()
|
||||
'running'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.create_time()
|
||||
1267551141.5019531
|
||||
>>> p.terminal()
|
||||
'/dev/pts/0'
|
||||
>>>
|
||||
>>> p.uids()
|
||||
puids(real=1000, effective=1000, saved=1000)
|
||||
>>> p.gids()
|
||||
pgids(real=1000, effective=1000, saved=1000)
|
||||
>>>
|
||||
>>> p.cpu_times()
|
||||
pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)
|
||||
>>> p.cpu_percent(interval=1.0)
|
||||
12.1
|
||||
>>> p.cpu_affinity()
|
||||
[0, 1, 2, 3]
|
||||
>>> p.cpu_affinity([0, 1]) # set
|
||||
>>> p.cpu_num()
|
||||
1
|
||||
>>>
|
||||
>>> p.memory_info()
|
||||
pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0)
|
||||
>>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only)
|
||||
pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
|
||||
>>> p.memory_percent()
|
||||
0.7823
|
||||
>>> p.memory_maps()
|
||||
[pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
|
||||
pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
|
||||
pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
|
||||
pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
|
||||
...]
|
||||
>>>
|
||||
>>> p.io_counters()
|
||||
pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)
|
||||
>>>
|
||||
>>> p.open_files()
|
||||
[popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),
|
||||
popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]
|
||||
>>>
|
||||
>>> p.connections()
|
||||
[pconn(fd=115, family=<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')]
|
||||
>>>
|
||||
>>> p.num_threads()
|
||||
4
|
||||
>>> p.num_fds()
|
||||
8
|
||||
>>> p.threads()
|
||||
[pthread(id=5234, user_time=22.5, system_time=9.2891),
|
||||
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.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',
|
||||
...}
|
||||
>>>
|
||||
>>> 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.kill()
|
||||
>>> 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
|
||||
...
|
||||
giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4
|
||||
giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome
|
||||
root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1
|
||||
>>>
|
||||
|
||||
Further process APIs
|
||||
--------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> for proc in psutil.process_iter(['pid', 'name']):
|
||||
... print(proc.info)
|
||||
...
|
||||
{'pid': 1, 'name': 'systemd'}
|
||||
{'pid': 2, 'name': 'kthreadd'}
|
||||
{'pid': 3, 'name': 'ksoftirqd/0'}
|
||||
...
|
||||
>>>
|
||||
>>> psutil.pid_exists(3)
|
||||
True
|
||||
>>>
|
||||
>>> def on_terminate(proc):
|
||||
... print("process {} terminated".format(proc))
|
||||
...
|
||||
>>> # waits for multiple processes to terminate
|
||||
>>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)
|
||||
>>>
|
||||
|
||||
Popen wrapper:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> from subprocess import PIPE
|
||||
>>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
|
||||
>>> p.name()
|
||||
'python'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.communicate()
|
||||
('hello\n', None)
|
||||
>>> p.wait(timeout=2)
|
||||
0
|
||||
>>>
|
||||
|
||||
Windows services
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> list(psutil.win_service_iter())
|
||||
[<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'}
|
||||
|
||||
|
||||
.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html
|
||||
.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
|
||||
.. _Tidelift security contact: https://tidelift.com/security
|
||||
.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
|
||||
Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability
|
||||
Platform: Platform Independent
|
||||
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 10
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 7
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 8
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008
|
||||
Classifier: Operating System :: Microsoft :: Windows :: Windows Vista
|
||||
Classifier: Operating System :: Microsoft
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Operating System :: POSIX :: AIX
|
||||
Classifier: Operating System :: POSIX :: BSD :: FreeBSD
|
||||
Classifier: Operating System :: POSIX :: BSD :: NetBSD
|
||||
Classifier: Operating System :: POSIX :: BSD :: OpenBSD
|
||||
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 :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Software Development :: Libraries
|
||||
Classifier: Topic :: System :: Benchmark
|
||||
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
||||
Classifier: Topic :: System :: Hardware
|
||||
Classifier: Topic :: System :: Monitoring
|
||||
Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog
|
||||
Classifier: Topic :: System :: Networking :: Monitoring
|
||||
Classifier: Topic :: System :: Networking
|
||||
Classifier: Topic :: System :: Operating System
|
||||
Classifier: Topic :: System :: Systems Administration
|
||||
Classifier: Topic :: Utilities
|
||||
Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
|
@ -1,521 +0,0 @@
|
|||
| |downloads| |stars| |forks| |contributors| |coverage| |quality|
|
||||
| |version| |py-versions| |packages| |license|
|
||||
| |travis| |appveyor| |cirrus| |doc| |twitter| |tidelift|
|
||||
|
||||
.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/stargazers
|
||||
:alt: Github stars
|
||||
|
||||
.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/network/members
|
||||
:alt: Github forks
|
||||
|
||||
.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/graphs/contributors
|
||||
:alt: Contributors
|
||||
|
||||
.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg
|
||||
:target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&utm_medium=referral&utm_content=giampaolo/psutil&utm_campaign=Badge_Grade
|
||||
:alt: Code quality
|
||||
|
||||
.. |travis| image:: https://img.shields.io/travis/giampaolo/psutil/master.svg?maxAge=3600&label=Linux,%20OSX,%20PyPy
|
||||
:target: https://travis-ci.org/giampaolo/psutil
|
||||
:alt: Linux tests (Travis)
|
||||
|
||||
.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
|
||||
:target: https://ci.appveyor.com/project/giampaolo/psutil
|
||||
:alt: Windows tests (Appveyor)
|
||||
|
||||
.. |cirrus| image:: https://img.shields.io/cirrus/github/giampaolo/psutil?label=FreeBSD
|
||||
:target: https://cirrus-ci.com/github/giampaolo/psutil-cirrus-ci
|
||||
:alt: FreeBSD tests (Cirrus-Ci)
|
||||
|
||||
.. |coverage| image:: https://img.shields.io/coveralls/github/giampaolo/psutil.svg?label=test%20coverage
|
||||
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
|
||||
:alt: Test coverage (coverall.io)
|
||||
|
||||
.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest
|
||||
:target: http://psutil.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Latest version
|
||||
|
||||
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg
|
||||
:target: https://pypi.org/project/psutil
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg
|
||||
:target: https://repology.org/metapackage/python:psutil/versions
|
||||
:alt: Binary packages
|
||||
|
||||
.. |license| image:: https://img.shields.io/pypi/l/psutil.svg
|
||||
:target: https://github.com/giampaolo/psutil/blob/master/LICENSE
|
||||
:alt: License
|
||||
|
||||
.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF
|
||||
:target: https://twitter.com/grodola
|
||||
:alt: Twitter Follow
|
||||
|
||||
.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
:alt: Tidelift
|
||||
|
||||
-----
|
||||
|
||||
Quick links
|
||||
===========
|
||||
|
||||
- `Home page <https://github.com/giampaolo/psutil>`_
|
||||
- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
|
||||
- `Documentation <http://psutil.readthedocs.io>`_
|
||||
- `Download <https://pypi.org/project/psutil/#files>`_
|
||||
- `Forum <http://groups.google.com/group/psutil/topics>`_
|
||||
- `StackOverflow <https://stackoverflow.com/questions/tagged/psutil>`_
|
||||
- `Blog <http://grodola.blogspot.com/search/label/psutil>`_
|
||||
- `Development guide <https://github.com/giampaolo/psutil/blob/master/docs/DEVGUIDE.rst>`_
|
||||
- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
psutil (process and system utilities) is a cross-platform library for
|
||||
retrieving information on **running processes** and **system utilization**
|
||||
(CPU, memory, disks, network, sensors) in Python.
|
||||
It is useful mainly for **system monitoring**, **profiling and limiting process
|
||||
resources** and **management of running processes**.
|
||||
It implements many functionalities offered by classic UNIX command line tools
|
||||
such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others.
|
||||
psutil currently supports the following platforms:
|
||||
|
||||
- **Linux**
|
||||
- **Windows**
|
||||
- **macOS**
|
||||
- **FreeBSD, OpenBSD**, **NetBSD**
|
||||
- **Sun Solaris**
|
||||
- **AIX**
|
||||
|
||||
...both **32-bit** and **64-bit** architectures. Supported Python versions are **2.6**, **2.7** and **3.4+**. `PyPy3 <http://pypy.org/>`__ is also known to work.
|
||||
|
||||
psutil for enterprise
|
||||
=====================
|
||||
|
||||
.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
|
||||
:width: 150
|
||||
:alt: Tidelift
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
.. list-table::
|
||||
:widths: 10 150
|
||||
|
||||
* - |tideliftlogo|
|
||||
- The maintainer of psutil and thousands of other packages are working
|
||||
with Tidelift to deliver commercial support and maintenance for the open
|
||||
source dependencies you use to build your applications. Save time,
|
||||
reduce risk, and improve code health, while paying the maintainers of
|
||||
the exact dependencies you use.
|
||||
`Learn more <https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=enterprise&utm_term=repo>`__.
|
||||
|
||||
By subscribing to Tidelift you will help me (`Giampaolo Rodola`_) support
|
||||
psutil future development. Alternatively consider making a small
|
||||
`donation`_.
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
To report a security vulnerability, please use the `Tidelift security
|
||||
contact`_. Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
Example applications
|
||||
====================
|
||||
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/top-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procinfo.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/top.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem-small.png | .. image:: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap-small.png |
|
||||
| :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/procsmem.png | :target: https://github.com/giampaolo/psutil/blob/master/docs/_static/pmap.png |
|
||||
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
|
||||
|
||||
Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
|
||||
and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
|
||||
|
||||
Projects using psutil
|
||||
=====================
|
||||
|
||||
psutil has roughly the following monthly downloads:
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/psutil.svg
|
||||
:target: https://pepy.tech/project/psutil
|
||||
:alt: Downloads
|
||||
|
||||
There are over
|
||||
`10.000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
|
||||
on github which depend from psutil.
|
||||
Here's some I find particularly interesting:
|
||||
|
||||
- https://github.com/google/grr
|
||||
- https://github.com/facebook/osquery/
|
||||
- https://github.com/nicolargo/glances
|
||||
- https://github.com/Jahaja/psdash
|
||||
- https://github.com/ajenti/ajenti
|
||||
- https://github.com/home-assistant/home-assistant/
|
||||
|
||||
|
||||
Portings
|
||||
========
|
||||
|
||||
- Go: https://github.com/shirou/gopsutil
|
||||
- C: https://github.com/hamon-in/cpslib
|
||||
- Rust: https://github.com/borntyping/rust-psutil
|
||||
- Nim: https://github.com/johnscillieri/psutil-nim
|
||||
|
||||
|
||||
Example usages
|
||||
==============
|
||||
|
||||
This represents pretty much the whole psutil API.
|
||||
|
||||
CPU
|
||||
---
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>>
|
||||
>>> psutil.cpu_times()
|
||||
scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1)
|
||||
...
|
||||
4.0
|
||||
5.9
|
||||
3.8
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_percent(interval=1, percpu=True)
|
||||
...
|
||||
[4.0, 6.9, 3.7, 9.2]
|
||||
[7.0, 8.5, 2.4, 2.1]
|
||||
[1.2, 9.0, 9.9, 7.2]
|
||||
>>>
|
||||
>>> for x in range(3):
|
||||
... psutil.cpu_times_percent(interval=1, percpu=False)
|
||||
...
|
||||
scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
|
||||
>>>
|
||||
>>> psutil.cpu_count()
|
||||
4
|
||||
>>> psutil.cpu_count(logical=False)
|
||||
2
|
||||
>>>
|
||||
>>> psutil.cpu_stats()
|
||||
scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
|
||||
>>>
|
||||
>>> psutil.cpu_freq()
|
||||
scpufreq(current=931.42925, min=800.0, max=3500.0)
|
||||
>>>
|
||||
>>> psutil.getloadavg() # also on Windows (emulated)
|
||||
(3.14, 3.89, 4.67)
|
||||
|
||||
Memory
|
||||
------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.virtual_memory()
|
||||
svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
|
||||
>>> psutil.swap_memory()
|
||||
sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
|
||||
>>>
|
||||
|
||||
Disks
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.disk_partitions()
|
||||
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
|
||||
sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
|
||||
>>>
|
||||
>>> psutil.disk_usage('/')
|
||||
sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
|
||||
>>>
|
||||
>>> psutil.disk_io_counters(perdisk=False)
|
||||
sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)
|
||||
>>>
|
||||
|
||||
Network
|
||||
-------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> psutil.net_io_counters(pernic=True)
|
||||
{'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
|
||||
'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
|
||||
>>>
|
||||
>>> psutil.net_connections()
|
||||
[sconn(fd=115, family=<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),
|
||||
...]
|
||||
>>>
|
||||
>>> psutil.net_if_addrs()
|
||||
{'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
|
||||
'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
|
||||
snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
|
||||
snicaddr(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()
|
||||
{'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
|
||||
'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
|
||||
>>>
|
||||
|
||||
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)]}
|
||||
>>>
|
||||
>>> psutil.sensors_fans()
|
||||
{'asus': [sfan(label='cpu_fan', current=3200)]}
|
||||
>>>
|
||||
>>> psutil.sensors_battery()
|
||||
sbattery(percent=93, secsleft=16628, power_plugged=False)
|
||||
>>>
|
||||
|
||||
Other system info
|
||||
-----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.users()
|
||||
[suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),
|
||||
suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]
|
||||
>>>
|
||||
>>> psutil.boot_time()
|
||||
1365519115.0
|
||||
>>>
|
||||
|
||||
Process management
|
||||
------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> psutil.pids()
|
||||
[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215, 1216, 1220, 1221, 1243, 1244,
|
||||
1301, 1601, 2237, 2355, 2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282,
|
||||
4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446,
|
||||
5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071]
|
||||
>>>
|
||||
>>> p = psutil.Process(7055)
|
||||
>>> p
|
||||
psutil.Process(pid=7055, name='python', started='09:04:44')
|
||||
>>> 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.children(recursive=True)
|
||||
[psutil.Process(pid=29835, name='python2.7', started='11:45:38'),
|
||||
psutil.Process(pid=29836, name='python2.7', started='11:43:39')]
|
||||
>>>
|
||||
>>> p.parent()
|
||||
psutil.Process(pid=4699, name='bash', started='09:06:44')
|
||||
>>> p.parents()
|
||||
[psutil.Process(pid=4699, name='bash', started='09:06:44'),
|
||||
psutil.Process(pid=4689, name='gnome-terminal-server', started='0:06:44'),
|
||||
psutil.Process(pid=1, name='systemd', started='05:56:55')]
|
||||
>>>
|
||||
>>> p.status()
|
||||
'running'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.create_time()
|
||||
1267551141.5019531
|
||||
>>> p.terminal()
|
||||
'/dev/pts/0'
|
||||
>>>
|
||||
>>> p.uids()
|
||||
puids(real=1000, effective=1000, saved=1000)
|
||||
>>> p.gids()
|
||||
pgids(real=1000, effective=1000, saved=1000)
|
||||
>>>
|
||||
>>> p.cpu_times()
|
||||
pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)
|
||||
>>> p.cpu_percent(interval=1.0)
|
||||
12.1
|
||||
>>> p.cpu_affinity()
|
||||
[0, 1, 2, 3]
|
||||
>>> p.cpu_affinity([0, 1]) # set
|
||||
>>> p.cpu_num()
|
||||
1
|
||||
>>>
|
||||
>>> p.memory_info()
|
||||
pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0)
|
||||
>>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only)
|
||||
pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
|
||||
>>> p.memory_percent()
|
||||
0.7823
|
||||
>>> p.memory_maps()
|
||||
[pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
|
||||
pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
|
||||
pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
|
||||
pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
|
||||
...]
|
||||
>>>
|
||||
>>> p.io_counters()
|
||||
pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)
|
||||
>>>
|
||||
>>> p.open_files()
|
||||
[popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),
|
||||
popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]
|
||||
>>>
|
||||
>>> p.connections()
|
||||
[pconn(fd=115, family=<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')]
|
||||
>>>
|
||||
>>> p.num_threads()
|
||||
4
|
||||
>>> p.num_fds()
|
||||
8
|
||||
>>> p.threads()
|
||||
[pthread(id=5234, user_time=22.5, system_time=9.2891),
|
||||
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.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',
|
||||
...}
|
||||
>>>
|
||||
>>> 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.kill()
|
||||
>>> 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
|
||||
...
|
||||
giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4
|
||||
giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome
|
||||
root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1
|
||||
>>>
|
||||
|
||||
Further process APIs
|
||||
--------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> for proc in psutil.process_iter(['pid', 'name']):
|
||||
... print(proc.info)
|
||||
...
|
||||
{'pid': 1, 'name': 'systemd'}
|
||||
{'pid': 2, 'name': 'kthreadd'}
|
||||
{'pid': 3, 'name': 'ksoftirqd/0'}
|
||||
...
|
||||
>>>
|
||||
>>> psutil.pid_exists(3)
|
||||
True
|
||||
>>>
|
||||
>>> def on_terminate(proc):
|
||||
... print("process {} terminated".format(proc))
|
||||
...
|
||||
>>> # waits for multiple processes to terminate
|
||||
>>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)
|
||||
>>>
|
||||
|
||||
Popen wrapper:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import psutil
|
||||
>>> from subprocess import PIPE
|
||||
>>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
|
||||
>>> p.name()
|
||||
'python'
|
||||
>>> p.username()
|
||||
'giampaolo'
|
||||
>>> p.communicate()
|
||||
('hello\n', None)
|
||||
>>> p.wait(timeout=2)
|
||||
0
|
||||
>>>
|
||||
|
||||
Windows services
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> list(psutil.win_service_iter())
|
||||
[<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'}
|
||||
|
||||
|
||||
.. _`Giampaolo Rodola`: http://grodola.blogspot.com/p/about.html
|
||||
.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
|
||||
.. _Tidelift security contact: https://tidelift.com/security
|
||||
.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
@echo off
|
||||
|
||||
rem ==========================================================================
|
||||
rem Shortcuts for various tasks, emulating UNIX "make" on Windows.
|
||||
rem It is primarly intended as a shortcut for compiling / installing
|
||||
rem psutil ("make.bat build", "make.bat install") and running tests
|
||||
rem ("make.bat test").
|
||||
rem
|
||||
rem This script is modeled after my Windows installation which uses:
|
||||
rem - Visual studio 2008 for Python 2.6, 2.7
|
||||
rem - Visual studio 2010 for Python 3.4+
|
||||
rem ...therefore it might not work on your Windows installation.
|
||||
rem
|
||||
rem By default C:\Python27\python.exe is used.
|
||||
rem To compile for a specific Python version run:
|
||||
rem set PYTHON=C:\Python34\python.exe & make.bat build
|
||||
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%" == "" (
|
||||
if exist "C:\Python38-64\python.exe" (
|
||||
set PYTHON=C:\Python38-64\python.exe
|
||||
) else (
|
||||
set PYTHON=C:\Python27\python.exe
|
||||
)
|
||||
)
|
||||
|
||||
if "%TSCRIPT%" == "" (
|
||||
set TSCRIPT=psutil\tests\runner.py
|
||||
)
|
||||
|
||||
rem Needed to locate the .pypirc file and upload exes on PyPI.
|
||||
set HOME=%USERPROFILE%
|
||||
|
||||
%PYTHON% scripts\internal\winmake.py %1 %2 %3 %4 %5 %6
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,846 +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.
|
||||
|
||||
"""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, print_function
|
||||
|
||||
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
|
||||
from socket import SOCK_DGRAM
|
||||
from socket import SOCK_STREAM
|
||||
|
||||
try:
|
||||
from socket import AF_INET6
|
||||
except ImportError:
|
||||
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
|
||||
|
||||
|
||||
# can't take it from _common.py as this script is imported by setup.py
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
__all__ = [
|
||||
# OS constants
|
||||
'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
|
||||
'SUNOS', 'WINDOWS',
|
||||
# connection constants
|
||||
'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED',
|
||||
'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN',
|
||||
'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', 'STATUS_PARKED',
|
||||
# other constants
|
||||
'ENCODING', 'ENCODING_ERRS', 'AF_INET6',
|
||||
# named tuples
|
||||
'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile',
|
||||
'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart',
|
||||
'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser',
|
||||
# utility functions
|
||||
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
|
||||
'parse_environ_block', 'path_exists_strict', 'usage_percent',
|
||||
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
|
||||
'bytes2human', 'conn_to_ntuple', 'debug',
|
||||
# shell utils
|
||||
'hilite', 'term_supports_colors', 'print_color',
|
||||
]
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- OS constants
|
||||
# ===================================================================
|
||||
|
||||
|
||||
POSIX = os.name == "posix"
|
||||
WINDOWS = os.name == "nt"
|
||||
LINUX = sys.platform.startswith("linux")
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
OSX = MACOS # deprecated alias
|
||||
FREEBSD = sys.platform.startswith("freebsd")
|
||||
OPENBSD = sys.platform.startswith("openbsd")
|
||||
NETBSD = sys.platform.startswith("netbsd")
|
||||
BSD = FREEBSD or OPENBSD or NETBSD
|
||||
SUNOS = sys.platform.startswith(("sunos", "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" # Linux, macOS, FreeBSD
|
||||
STATUS_LOCKED = "locked" # FreeBSD
|
||||
STATUS_WAITING = "waiting" # FreeBSD
|
||||
STATUS_SUSPENDED = "suspended" # NetBSD
|
||||
STATUS_PARKED = "parked" # Linux
|
||||
|
||||
# Process.connections() and psutil.net_connections()
|
||||
CONN_ESTABLISHED = "ESTABLISHED"
|
||||
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__)
|
||||
|
||||
# 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
|
||||
|
||||
globals().update(BatteryTime.__members__)
|
||||
|
||||
# --- others
|
||||
|
||||
ENCODING = sys.getfilesystemencoding()
|
||||
if not PY3:
|
||||
ENCODING_ERRS = "replace"
|
||||
else:
|
||||
try:
|
||||
ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6
|
||||
except AttributeError:
|
||||
ENCODING_ERRS = "surrogateescape" if POSIX else "replace"
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- namedtuples
|
||||
# ===================================================================
|
||||
|
||||
# --- for system functions
|
||||
|
||||
# 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()
|
||||
snicaddr = namedtuple('snicaddr',
|
||||
['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_fans()
|
||||
sfan = namedtuple('sfan', ['label', 'current'])
|
||||
|
||||
# --- for Process methods
|
||||
|
||||
# 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'])
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- 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]),
|
||||
"inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
"inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
}
|
||||
|
||||
if AF_INET6 is not None:
|
||||
conn_tmap.update({
|
||||
"tcp6": ([AF_INET6], [SOCK_STREAM]),
|
||||
"udp6": ([AF_INET6], [SOCK_DGRAM]),
|
||||
})
|
||||
|
||||
if AF_UNIX is not None:
|
||||
conn_tmap.update({
|
||||
"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
|
||||
})
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- Exceptions
|
||||
# =====================================================================
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base exception class. All other psutil exceptions inherit
|
||||
from this one.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, msg=""):
|
||||
Exception.__init__(self, msg)
|
||||
self.msg = msg
|
||||
|
||||
def __repr__(self):
|
||||
ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
|
||||
return ret.strip()
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
|
||||
class NoSuchProcess(Error):
|
||||
"""Exception raised when a process with a certain PID doesn't
|
||||
or no longer exists.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid, name=None, msg=None):
|
||||
Error.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
if name:
|
||||
details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
|
||||
else:
|
||||
details = "(pid=%s)" % self.pid
|
||||
self.msg = "process no longer exists " + details
|
||||
|
||||
def __path__(self):
|
||||
return 'xxx'
|
||||
|
||||
|
||||
class ZombieProcess(NoSuchProcess):
|
||||
"""Exception raised when querying a zombie process. This is
|
||||
raised on macOS, BSD and Solaris only, and not always: depending
|
||||
on the query the OS may be able to succeed anyway.
|
||||
On Linux all zombie processes are querable (hence this is never
|
||||
raised). Windows doesn't have zombie processes.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid, name=None, ppid=None, msg=None):
|
||||
NoSuchProcess.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.ppid = ppid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
args = ["pid=%s" % pid]
|
||||
if name:
|
||||
args.append("name=%s" % repr(self.name))
|
||||
if ppid:
|
||||
args.append("ppid=%s" % self.ppid)
|
||||
details = "(%s)" % ", ".join(args)
|
||||
self.msg = "process still exists but it's a zombie " + details
|
||||
|
||||
|
||||
class AccessDenied(Error):
|
||||
"""Exception raised when permission to perform an action is denied."""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, pid=None, name=None, msg=None):
|
||||
Error.__init__(self, msg)
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.msg = msg
|
||||
if msg is None:
|
||||
if (pid is not None) and (name is not None):
|
||||
self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
|
||||
elif (pid is not None):
|
||||
self.msg = "(pid=%s)" % self.pid
|
||||
else:
|
||||
self.msg = ""
|
||||
|
||||
|
||||
class TimeoutExpired(Error):
|
||||
"""Raised on Process.wait(timeout) if timeout expires and process
|
||||
is still alive.
|
||||
"""
|
||||
__module__ = 'psutil'
|
||||
|
||||
def __init__(self, seconds, pid=None, name=None):
|
||||
Error.__init__(self, "timeout after %s seconds" % seconds)
|
||||
self.seconds = seconds
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
if (pid is not None) and (name is not None):
|
||||
self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
|
||||
elif (pid is not None):
|
||||
self.msg += " (pid=%s)" % self.pid
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# --- utils
|
||||
# ===================================================================
|
||||
|
||||
|
||||
def usage_percent(used, total, round_=None):
|
||||
"""Calculate percentage usage of 'used' against 'total'."""
|
||||
try:
|
||||
ret = (float(used) / total) * 100
|
||||
except ZeroDivisionError:
|
||||
return 0.0
|
||||
else:
|
||||
if round_ is not None:
|
||||
ret = round(ret, round_)
|
||||
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(self)
|
||||
>>> foo()
|
||||
1
|
||||
>>> foo()
|
||||
>>> foo()
|
||||
>>>
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self):
|
||||
try:
|
||||
# case 1: we previously entered oneshot() ctx
|
||||
ret = self._cache[fun]
|
||||
except AttributeError:
|
||||
# case 2: we never entered oneshot() ctx
|
||||
return fun(self)
|
||||
except KeyError:
|
||||
# case 3: we entered oneshot() ctx but there's no cache
|
||||
# for this entry yet
|
||||
ret = self._cache[fun] = fun(self)
|
||||
return ret
|
||||
|
||||
def cache_activate(proc):
|
||||
"""Activate cache. Expects a Process instance. Cache will be
|
||||
stored as a "_cache" instance attribute."""
|
||||
proc._cache = {}
|
||||
|
||||
def cache_deactivate(proc):
|
||||
"""Deactivate and clear cache."""
|
||||
try:
|
||||
del proc._cache
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
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.SocketKind(num)
|
||||
except ValueError:
|
||||
return num
|
||||
|
||||
|
||||
def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
|
||||
"""Convert a raw connection tuple to a proper ntuple."""
|
||||
if fam in (socket.AF_INET, AF_INET6):
|
||||
if laddr:
|
||||
laddr = addr(*laddr)
|
||||
if raddr:
|
||||
raddr = addr(*raddr)
|
||||
if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6):
|
||||
status = status_map.get(status, CONN_NONE)
|
||||
else:
|
||||
status = CONN_NONE # ignore whatever C returned to us
|
||||
fam = sockfam_to_enum(fam)
|
||||
type_ = socktype_to_enum(type_)
|
||||
if pid is None:
|
||||
return pconn(fd, fam, type_, laddr, raddr, status)
|
||||
else:
|
||||
return sconn(fd, fam, type_, laddr, raddr, status, pid)
|
||||
|
||||
|
||||
def deprecated_method(replacement):
|
||||
"""A decorator which can be used to mark a method as deprecated
|
||||
'replcement' is the method name which will be called instead.
|
||||
"""
|
||||
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=DeprecationWarning, stacklevel=2)
|
||||
return getattr(self, replacement)(*args, **kwargs)
|
||||
return inner
|
||||
return outer
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def open_binary(fname, **kwargs):
|
||||
return open(fname, "rb", **kwargs)
|
||||
|
||||
|
||||
def open_text(fname, **kwargs):
|
||||
"""On Python 3 opens a file in text mode by using fs encoding and
|
||||
a proper en/decoding errors handler.
|
||||
On Python 2 this is just an alias for open(name, 'rt').
|
||||
"""
|
||||
if PY3:
|
||||
# See:
|
||||
# https://github.com/giampaolo/psutil/issues/675
|
||||
# https://github.com/giampaolo/psutil/pull/733
|
||||
kwargs.setdefault('encoding', ENCODING)
|
||||
kwargs.setdefault('errors', ENCODING_ERRS)
|
||||
return open(fname, "rt", **kwargs)
|
||||
|
||||
|
||||
def bytes2human(n, format="%(value).1f%(symbol)s"):
|
||||
"""Used by various scripts. See:
|
||||
http://goo.gl/zeJZl
|
||||
|
||||
>>> bytes2human(10000)
|
||||
'9.8K'
|
||||
>>> bytes2human(100001221)
|
||||
'95.4M'
|
||||
"""
|
||||
symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||
prefix = {}
|
||||
for i, s in enumerate(symbols[1:]):
|
||||
prefix[s] = 1 << (i + 1) * 10
|
||||
for symbol in reversed(symbols[1:]):
|
||||
if n >= prefix[symbol]:
|
||||
value = float(n) / prefix[symbol]
|
||||
return format % locals()
|
||||
return format % dict(symbol=symbols[0], value=n)
|
||||
|
||||
|
||||
def get_procfs_path():
|
||||
"""Return updated psutil.PROCFS_PATH constant."""
|
||||
return sys.modules['psutil'].PROCFS_PATH
|
||||
|
||||
|
||||
if PY3:
|
||||
def decode(s):
|
||||
return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
|
||||
else:
|
||||
def decode(s):
|
||||
return s
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- shell utils
|
||||
# =====================================================================
|
||||
|
||||
|
||||
@memoize
|
||||
def term_supports_colors(file=sys.stdout):
|
||||
if os.name == 'nt':
|
||||
return True
|
||||
try:
|
||||
import curses
|
||||
assert file.isatty()
|
||||
curses.setupterm()
|
||||
assert curses.tigetnum("colors") > 0
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def hilite(s, color="green", bold=False):
|
||||
"""Return an highlighted version of 'string'."""
|
||||
if not term_supports_colors():
|
||||
return s
|
||||
attr = []
|
||||
colors = dict(green='32', red='91', brown='33')
|
||||
colors[None] = '29'
|
||||
try:
|
||||
color = colors[color]
|
||||
except KeyError:
|
||||
raise ValueError("invalid color %r; choose between %s" % (
|
||||
list(colors.keys())))
|
||||
attr.append(color)
|
||||
if bold:
|
||||
attr.append('1')
|
||||
return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
|
||||
|
||||
|
||||
def print_color(s, color="green", bold=False, file=sys.stdout):
|
||||
"""Print a colorized version of string."""
|
||||
if not term_supports_colors():
|
||||
print(s, file=file)
|
||||
elif POSIX:
|
||||
print(hilite(s, color, bold), file=file)
|
||||
else:
|
||||
import ctypes
|
||||
|
||||
DEFAULT_COLOR = 7
|
||||
GetStdHandle = ctypes.windll.Kernel32.GetStdHandle
|
||||
SetConsoleTextAttribute = \
|
||||
ctypes.windll.Kernel32.SetConsoleTextAttribute
|
||||
|
||||
colors = dict(green=2, red=4, brown=6)
|
||||
colors[None] = DEFAULT_COLOR
|
||||
try:
|
||||
color = colors[color]
|
||||
except KeyError:
|
||||
raise ValueError("invalid color %r; choose between %r" % (
|
||||
color, list(colors.keys())))
|
||||
if bold and color <= 7:
|
||||
color += 8
|
||||
|
||||
handle_id = -12 if file is sys.stderr else -11
|
||||
GetStdHandle.restype = ctypes.c_ulong
|
||||
handle = GetStdHandle(handle_id)
|
||||
SetConsoleTextAttribute(handle, color)
|
||||
try:
|
||||
print(s, file=file)
|
||||
finally:
|
||||
SetConsoleTextAttribute(handle, DEFAULT_COLOR)
|
||||
|
||||
|
||||
if bool(os.getenv('PSUTIL_DEBUG', 0)):
|
||||
import inspect
|
||||
|
||||
def debug(msg):
|
||||
"""If PSUTIL_DEBUG env var is set, print a debug message to stderr."""
|
||||
fname, lineno, func_name, lines, index = inspect.getframeinfo(
|
||||
inspect.currentframe().f_back)
|
||||
print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg),
|
||||
file=sys.stderr)
|
||||
else:
|
||||
def debug(msg):
|
||||
pass
|
|
@ -1,345 +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.
|
||||
|
||||
"""Module which provides compatibility with older Python versions."""
|
||||
|
||||
import collections
|
||||
import errno
|
||||
import functools
|
||||
import os
|
||||
import sys
|
||||
|
||||
__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
|
||||
"lru_cache", "which", "get_terminal_size",
|
||||
"FileNotFoundError", "PermissionError", "ProcessLookupError",
|
||||
"InterruptedError", "ChildProcessError", "FileExistsError"]
|
||||
|
||||
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
|
||||
|
||||
|
||||
# --- exceptions
|
||||
|
||||
|
||||
if PY3:
|
||||
FileNotFoundError = FileNotFoundError # NOQA
|
||||
PermissionError = PermissionError # NOQA
|
||||
ProcessLookupError = ProcessLookupError # NOQA
|
||||
InterruptedError = InterruptedError # NOQA
|
||||
ChildProcessError = ChildProcessError # NOQA
|
||||
FileExistsError = FileExistsError # NOQA
|
||||
else:
|
||||
# https://github.com/PythonCharmers/python-future/blob/exceptions/
|
||||
# src/future/types/exceptions/pep3151.py
|
||||
import platform
|
||||
|
||||
_singleton = object()
|
||||
|
||||
def instance_checking_exception(base_exception=Exception):
|
||||
def wrapped(instance_checker):
|
||||
class TemporaryClass(base_exception):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(args) == 1 and isinstance(args[0], TemporaryClass):
|
||||
unwrap_me = args[0]
|
||||
for attr in dir(unwrap_me):
|
||||
if not attr.startswith('__'):
|
||||
setattr(self, attr, getattr(unwrap_me, attr))
|
||||
else:
|
||||
super(TemporaryClass, self).__init__(*args, **kwargs)
|
||||
|
||||
class __metaclass__(type):
|
||||
def __instancecheck__(cls, inst):
|
||||
return instance_checker(inst)
|
||||
|
||||
def __subclasscheck__(cls, classinfo):
|
||||
value = sys.exc_info()[1]
|
||||
return isinstance(value, cls)
|
||||
|
||||
TemporaryClass.__name__ = instance_checker.__name__
|
||||
TemporaryClass.__doc__ = instance_checker.__doc__
|
||||
return TemporaryClass
|
||||
|
||||
return wrapped
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def FileNotFoundError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ENOENT
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def ProcessLookupError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ESRCH
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def PermissionError(inst):
|
||||
return getattr(inst, 'errno', _singleton) in (
|
||||
errno.EACCES, errno.EPERM)
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def InterruptedError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.EINTR
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def ChildProcessError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.ECHILD
|
||||
|
||||
@instance_checking_exception(EnvironmentError)
|
||||
def FileExistsError(inst):
|
||||
return getattr(inst, 'errno', _singleton) == errno.EEXIST
|
||||
|
||||
if platform.python_implementation() != "CPython":
|
||||
try:
|
||||
raise OSError(errno.EEXIST, "perm")
|
||||
except FileExistsError:
|
||||
pass
|
||||
except OSError:
|
||||
raise RuntimeError(
|
||||
"broken / incompatible Python implementation, see: "
|
||||
"https://github.com/giampaolo/psutil/issues/1659")
|
||||
|
||||
|
||||
# --- stdlib additions
|
||||
|
||||
|
||||
# py 3.2 functools.lru_cache
|
||||
# Taken from: http://code.activestate.com/recipes/578078
|
||||
# Credit: Raymond Hettinger
|
||||
try:
|
||||
from functools import lru_cache
|
||||
except ImportError:
|
||||
try:
|
||||
from threading import RLock
|
||||
except ImportError:
|
||||
from dummy_threading import RLock
|
||||
|
||||
_CacheInfo = collections.namedtuple(
|
||||
"CacheInfo", ["hits", "misses", "maxsize", "currsize"])
|
||||
|
||||
class _HashedSeq(list):
|
||||
__slots__ = 'hashvalue'
|
||||
|
||||
def __init__(self, tup, hash=hash):
|
||||
self[:] = tup
|
||||
self.hashvalue = hash(tup)
|
||||
|
||||
def __hash__(self):
|
||||
return self.hashvalue
|
||||
|
||||
def _make_key(args, kwds, typed,
|
||||
kwd_mark=(object(), ),
|
||||
fasttypes=set((int, str, frozenset, type(None))),
|
||||
sorted=sorted, tuple=tuple, type=type, len=len):
|
||||
key = args
|
||||
if kwds:
|
||||
sorted_items = sorted(kwds.items())
|
||||
key += kwd_mark
|
||||
for item in sorted_items:
|
||||
key += item
|
||||
if typed:
|
||||
key += tuple(type(v) for v in args)
|
||||
if kwds:
|
||||
key += tuple(type(v) for k, v in sorted_items)
|
||||
elif len(key) == 1 and type(key[0]) in fasttypes:
|
||||
return key[0]
|
||||
return _HashedSeq(key)
|
||||
|
||||
def lru_cache(maxsize=100, typed=False):
|
||||
"""Least-recently-used cache decorator, see:
|
||||
http://docs.python.org/3/library/functools.html#functools.lru_cache
|
||||
"""
|
||||
def decorating_function(user_function):
|
||||
cache = dict()
|
||||
stats = [0, 0]
|
||||
HITS, MISSES = 0, 1
|
||||
make_key = _make_key
|
||||
cache_get = cache.get
|
||||
_len = len
|
||||
lock = RLock()
|
||||
root = []
|
||||
root[:] = [root, root, None, None]
|
||||
nonlocal_root = [root]
|
||||
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
|
||||
if maxsize == 0:
|
||||
def wrapper(*args, **kwds):
|
||||
result = user_function(*args, **kwds)
|
||||
stats[MISSES] += 1
|
||||
return result
|
||||
elif maxsize is None:
|
||||
def wrapper(*args, **kwds):
|
||||
key = make_key(args, kwds, typed)
|
||||
result = cache_get(key, root)
|
||||
if result is not root:
|
||||
stats[HITS] += 1
|
||||
return result
|
||||
result = user_function(*args, **kwds)
|
||||
cache[key] = result
|
||||
stats[MISSES] += 1
|
||||
return result
|
||||
else:
|
||||
def wrapper(*args, **kwds):
|
||||
if kwds or typed:
|
||||
key = make_key(args, kwds, typed)
|
||||
else:
|
||||
key = args
|
||||
lock.acquire()
|
||||
try:
|
||||
link = cache_get(key)
|
||||
if link is not None:
|
||||
root, = nonlocal_root
|
||||
link_prev, link_next, key, result = link
|
||||
link_prev[NEXT] = link_next
|
||||
link_next[PREV] = link_prev
|
||||
last = root[PREV]
|
||||
last[NEXT] = root[PREV] = link
|
||||
link[PREV] = last
|
||||
link[NEXT] = root
|
||||
stats[HITS] += 1
|
||||
return result
|
||||
finally:
|
||||
lock.release()
|
||||
result = user_function(*args, **kwds)
|
||||
lock.acquire()
|
||||
try:
|
||||
root, = nonlocal_root
|
||||
if key in cache:
|
||||
pass
|
||||
elif _len(cache) >= maxsize:
|
||||
oldroot = root
|
||||
oldroot[KEY] = key
|
||||
oldroot[RESULT] = result
|
||||
root = nonlocal_root[0] = oldroot[NEXT]
|
||||
oldkey = root[KEY]
|
||||
root[KEY] = root[RESULT] = None
|
||||
del cache[oldkey]
|
||||
cache[key] = oldroot
|
||||
else:
|
||||
last = root[PREV]
|
||||
link = [last, root, key, result]
|
||||
last[NEXT] = root[PREV] = cache[key] = link
|
||||
stats[MISSES] += 1
|
||||
finally:
|
||||
lock.release()
|
||||
return result
|
||||
|
||||
def cache_info():
|
||||
"""Report cache statistics"""
|
||||
lock.acquire()
|
||||
try:
|
||||
return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
|
||||
len(cache))
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def cache_clear():
|
||||
"""Clear the cache and cache statistics"""
|
||||
lock.acquire()
|
||||
try:
|
||||
cache.clear()
|
||||
root = nonlocal_root[0]
|
||||
root[:] = [root, root, None, None]
|
||||
stats[:] = [0, 0]
|
||||
finally:
|
||||
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
|
||||
|
||||
|
||||
# python 3.3
|
||||
try:
|
||||
from shutil import get_terminal_size
|
||||
except ImportError:
|
||||
def get_terminal_size(fallback=(80, 24)):
|
||||
try:
|
||||
import fcntl
|
||||
import termios
|
||||
import struct
|
||||
except ImportError:
|
||||
return fallback
|
||||
else:
|
||||
try:
|
||||
# This should work on Linux.
|
||||
res = struct.unpack(
|
||||
'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234'))
|
||||
return (res[1], res[0])
|
||||
except Exception:
|
||||
return fallback
|
|
@ -1,550 +0,0 @@
|
|||
# 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 functools
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from . import _common
|
||||
from . import _psposix
|
||||
from . import _psutil_aix as cext
|
||||
from . import _psutil_posix as cext_posix
|
||||
from ._common import AccessDenied
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import get_procfs_path
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NIC_DUPLEX_FULL
|
||||
from ._common import NIC_DUPLEX_HALF
|
||||
from ._common import NIC_DUPLEX_UNKNOWN
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
|
||||
|
||||
__extra__all__ = ["PROCFS_PATH"]
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
HAS_THREADS = hasattr(cext, "proc_threads")
|
||||
HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters")
|
||||
HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters")
|
||||
|
||||
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
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'])
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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
|
||||
|
||||
if HAS_NET_IO_COUNTERS:
|
||||
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 = []
|
||||
for item in rawlist:
|
||||
fd, fam, type_, laddr, raddr, status, pid = item
|
||||
if fam not in families:
|
||||
continue
|
||||
if type_ not in types:
|
||||
continue
|
||||
nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status,
|
||||
TCP_STATUSES, pid=pid if _pid == -1 else None)
|
||||
ret.append(nt)
|
||||
return ret
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""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(
|
||||
r"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.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except (FileNotFoundError, ProcessLookupError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
return wrapper
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||
|
||||
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_basic_info.cache_activate(self)
|
||||
self._proc_cred.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._proc_basic_info.cache_deactivate(self)
|
||||
self._proc_cred.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_basic_info(self):
|
||||
return cext.proc_basic_info(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_cred(self):
|
||||
return cext.proc_cred(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
if self.pid == 0:
|
||||
return "swapper"
|
||||
# note: max 16 characters
|
||||
return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00")
|
||||
|
||||
@wrap_exceptions
|
||||
def exe(self):
|
||||
# there is no way to get executable path in AIX other than to guess,
|
||||
# and guessing is more complex than what's in the wrapping class
|
||||
cmdline = self.cmdline()
|
||||
if not cmdline:
|
||||
return ''
|
||||
exe = cmdline[0]
|
||||
if os.path.sep in exe:
|
||||
# relative or absolute path
|
||||
if not os.path.isabs(exe):
|
||||
# if cwd has changed, we're out of luck - this may be wrong!
|
||||
exe = os.path.abspath(os.path.join(self.cwd(), exe))
|
||||
if (os.path.isabs(exe) and
|
||||
os.path.isfile(exe) and
|
||||
os.access(exe, os.X_OK)):
|
||||
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 cext.proc_args(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def environ(self):
|
||||
return cext.proc_environ(self.pid)
|
||||
|
||||
@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 FileNotFoundError:
|
||||
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
|
||||
return None
|
||||
|
||||
@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(r"(\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)
|
||||
|
||||
if HAS_PROC_IO_COUNTERS:
|
||||
@wrap_exceptions
|
||||
def io_counters(self):
|
||||
try:
|
||||
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
|
||||
except OSError:
|
||||
# if process is terminated, proc_io_counters returns OSError
|
||||
# instead of NSP
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
raise
|
||||
return _common.pio(rc, wc, rb, wb)
|
|
@ -1,903 +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.
|
||||
|
||||
"""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 collections import defaultdict
|
||||
|
||||
from . import _common
|
||||
from . import _psposix
|
||||
from . import _psutil_bsd as cext
|
||||
from . import _psutil_posix as cext_posix
|
||||
from ._common import AccessDenied
|
||||
from ._common import conn_tmap
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import FREEBSD
|
||||
from ._common import memoize
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NETBSD
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import OPENBSD
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import which
|
||||
|
||||
|
||||
__extra__all__ = []
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
if NETBSD:
|
||||
PAGESIZE = os.sysconf("SC_PAGESIZE")
|
||||
else:
|
||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
|
||||
HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads")
|
||||
HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files')
|
||||
HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds')
|
||||
|
||||
kinfo_proc_map = dict(
|
||||
ppid=0,
|
||||
status=1,
|
||||
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'])
|
||||
# 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'])
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 = 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 HAS_PER_CPU_TIMES:
|
||||
def per_cpu_times():
|
||||
"""Return system CPU times as a namedtuple"""
|
||||
ret = []
|
||||
for cpu_t in cext.per_cpu_times():
|
||||
user, nice, system, idle, irq = cpu_t
|
||||
item = scputimes(user, nice, system, idle, irq)
|
||||
ret.append(item)
|
||||
return ret
|
||||
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()
|
||||
|
||||
|
||||
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 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
|
||||
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
|
||||
retlist.append(ntuple)
|
||||
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:
|
||||
try:
|
||||
mtu = cext_posix.net_if_mtu(name)
|
||||
isup = cext_posix.net_if_flags(name)
|
||||
duplex, speed = cext_posix.net_if_duplex_speed(name)
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1279
|
||||
if err.errno != errno.ENODEV:
|
||||
raise
|
||||
else:
|
||||
if hasattr(_common, 'NicDuplex'):
|
||||
duplex = _common.NicDuplex(duplex)
|
||||
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
|
||||
return ret
|
||||
|
||||
|
||||
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()
|
||||
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:
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES, pid)
|
||||
ret.add(nt)
|
||||
return list(ret)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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)
|
||||
|
||||
def sensors_temperatures():
|
||||
"Return CPU cores temperatures if available, else an empty dict."
|
||||
ret = defaultdict(list)
|
||||
num_cpus = cpu_count_logical()
|
||||
for cpu in range(num_cpus):
|
||||
try:
|
||||
current, high = cext.sensors_cpu_temperature(cpu)
|
||||
if high <= 0:
|
||||
high = None
|
||||
name = "Core %s" % cpu
|
||||
ret["coretemp"].append(
|
||||
_common.shwtemp(name, current, high, high))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
return ret
|
||||
|
||||
def cpu_freq():
|
||||
"""Return frequency metrics for CPUs. As of Dec 2018 only
|
||||
CPU 0 appears to be supported by FreeBSD and all other cores
|
||||
match the frequency of CPU 0.
|
||||
"""
|
||||
ret = []
|
||||
num_cpus = cpu_count_logical()
|
||||
for cpu in range(num_cpus):
|
||||
try:
|
||||
current, available_freq = cext.cpu_frequency(cpu)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
if available_freq:
|
||||
try:
|
||||
min_freq = int(available_freq.split(" ")[-1].split("/")[0])
|
||||
except(IndexError, ValueError):
|
||||
min_freq = None
|
||||
try:
|
||||
max_freq = int(available_freq.split(" ")[0].split("/")[0])
|
||||
except(IndexError, ValueError):
|
||||
max_freq = None
|
||||
ret.append(_common.scpufreq(current, min_freq, max_freq))
|
||||
return ret
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- other system functions
|
||||
# =====================================================================
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 is_zombie(pid):
|
||||
try:
|
||||
st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']]
|
||||
return st == cext.SZOMB
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def wrap_exceptions(fun):
|
||||
"""Decorator which translates bare OSError exceptions into
|
||||
NoSuchProcess and AccessDenied.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except ProcessLookupError:
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except OSError:
|
||||
if self.pid == 0:
|
||||
if 0 in pids():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wrap_exceptions_procfs(inst):
|
||||
"""Same as above, for routines relying on reading /proc fs."""
|
||||
try:
|
||||
yield
|
||||
except (ProcessLookupError, FileNotFoundError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(inst.pid):
|
||||
raise NoSuchProcess(inst.pid, inst._name)
|
||||
else:
|
||||
raise ZombieProcess(inst.pid, inst._name, inst._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(inst.pid, inst._name)
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
|
||||
def _assert_alive(self):
|
||||
"""Raise NSP if the process disappeared on us."""
|
||||
# For those C function who do not raise NSP, possibly returning
|
||||
# incorrect or incomplete result.
|
||||
cext.proc_name(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def oneshot(self):
|
||||
"""Retrieves multiple process info in one shot as a raw tuple."""
|
||||
ret = cext.proc_oneshot_info(self.pid)
|
||||
assert len(ret) == len(kinfo_proc_map)
|
||||
return ret
|
||||
|
||||
def oneshot_enter(self):
|
||||
self.oneshot.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self.oneshot.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
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):
|
||||
if FREEBSD:
|
||||
if self.pid == 0:
|
||||
return '' # else NSP
|
||||
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]) or ""
|
||||
else:
|
||||
return ""
|
||||
|
||||
@wrap_exceptions
|
||||
def cmdline(self):
|
||||
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 is_zombie(self.pid):
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
elif not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name, self._ppid)
|
||||
else:
|
||||
# XXX: this happens with unicode tests. It means the C
|
||||
# routine is unable to decode invalid unicode chars.
|
||||
return []
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
return cext.proc_cmdline(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def terminal(self):
|
||||
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):
|
||||
self._ppid = self.oneshot()[kinfo_proc_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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']])
|
||||
|
||||
memory_full_info = memory_info
|
||||
|
||||
@wrap_exceptions
|
||||
def create_time(self):
|
||||
return self.oneshot()[kinfo_proc_map['create_time']]
|
||||
|
||||
@wrap_exceptions
|
||||
def num_threads(self):
|
||||
if HAS_PROC_NUM_THREADS:
|
||||
# FreeBSD
|
||||
return cext.proc_num_threads(self.pid)
|
||||
else:
|
||||
return len(self.threads())
|
||||
|
||||
@wrap_exceptions
|
||||
def num_ctx_switches(self):
|
||||
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:
|
||||
self._assert_alive()
|
||||
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 = []
|
||||
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:
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
self._assert_alive()
|
||||
return list(ret)
|
||||
|
||||
families, types = conn_tmap[kind]
|
||||
rawlist = cext.proc_connections(self.pid, families, types)
|
||||
ret = []
|
||||
for item in rawlist:
|
||||
fd, fam, type, laddr, raddr, status = item
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
|
||||
if OPENBSD:
|
||||
self._assert_alive()
|
||||
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
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 = 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):
|
||||
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 or HAS_PROC_OPEN_FILES:
|
||||
# FreeBSD < 8 does not support functions based on
|
||||
# kinfo_getfile() and kinfo_getvmmap()
|
||||
return cext.proc_cwd(self.pid) or None
|
||||
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 HAS_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 HAS_PROC_NUM_FDS:
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
"""Return the number of file descriptors opened by this process."""
|
||||
ret = cext.proc_num_fds(self.pid)
|
||||
if NETBSD:
|
||||
self._assert_alive()
|
||||
return ret
|
||||
else:
|
||||
num_fds = _not_implemented
|
||||
|
||||
# --- FreeBSD only APIs
|
||||
|
||||
if FREEBSD:
|
||||
|
||||
@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
|
||||
|
||||
@wrap_exceptions
|
||||
def memory_maps(self):
|
||||
return cext.proc_memory_maps(self.pid)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,564 +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.
|
||||
|
||||
"""macOS platform implementation."""
|
||||
|
||||
import contextlib
|
||||
import errno
|
||||
import functools
|
||||
import os
|
||||
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 AccessDenied
|
||||
from ._common import conn_tmap
|
||||
from ._common import conn_to_ntuple
|
||||
from ._common import isfile_strict
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import parse_environ_block
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
|
||||
|
||||
__extra__all__ = []
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- globals
|
||||
# =====================================================================
|
||||
|
||||
|
||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
AF_LINK = cext_posix.AF_LINK
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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'])
|
||||
# psutil.Process.memory_info()
|
||||
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
|
||||
# psutil.Process.memory_full_info()
|
||||
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- memory
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def virtual_memory():
|
||||
"""System virtual memory as a namedtuple."""
|
||||
total, active, inactive, wired, free, speculative = cext.virtual_mem()
|
||||
# This is how Zabbix calculate avail and used mem:
|
||||
# https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
|
||||
# osx/memory.c
|
||||
# Also see: https://github.com/giampaolo/psutil/issues/1277
|
||||
avail = inactive + free
|
||||
used = active + wired
|
||||
# This is NOT how Zabbix calculates free mem but it matches "free"
|
||||
# cmdline utility.
|
||||
free -= speculative
|
||||
percent = usage_percent((total - avail), total, round_=1)
|
||||
return svmem(total, avail, percent, used, free,
|
||||
active, inactive, wired)
|
||||
|
||||
|
||||
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"""
|
||||
ret = []
|
||||
for cpu_t in cext.per_cpu_times():
|
||||
user, nice, system, idle = cpu_t
|
||||
item = scputimes(user, nice, system, idle)
|
||||
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 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 macOS 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
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 macOS 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
|
||||
else:
|
||||
if cons:
|
||||
for c in cons:
|
||||
c = list(c) + [pid]
|
||||
ret.append(_common.sconn(*c))
|
||||
return ret
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""Get NIC stats (isup, duplex, speed, mtu)."""
|
||||
names = net_io_counters().keys()
|
||||
ret = {}
|
||||
for name in names:
|
||||
try:
|
||||
mtu = cext_posix.net_if_mtu(name)
|
||||
isup = cext_posix.net_if_flags(name)
|
||||
duplex, speed = cext_posix.net_if_duplex_speed(name)
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1279
|
||||
if err.errno != errno.ENODEV:
|
||||
raise
|
||||
else:
|
||||
if hasattr(_common, 'NicDuplex'):
|
||||
duplex = _common.NicDuplex(duplex)
|
||||
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
|
||||
return ret
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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 macOS versions pids() C doesn't return PID 0 but
|
||||
# "ps" does and the process is querable via sysctl():
|
||||
# https://travis-ci.org/giampaolo/psutil/jobs/309619941
|
||||
try:
|
||||
Process(0).create_time()
|
||||
ls.insert(0, 0)
|
||||
except NoSuchProcess:
|
||||
pass
|
||||
except AccessDenied:
|
||||
ls.insert(0, 0)
|
||||
return ls
|
||||
|
||||
|
||||
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 ProcessLookupError:
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except cext.ZombieProcessError:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
return wrapper
|
||||
|
||||
|
||||
@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", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _get_kinfo_proc(self):
|
||||
# Note: should work with all PIDs without permission issues.
|
||||
ret = cext.proc_kinfo_oneshot(self.pid)
|
||||
assert len(ret) == len(kinfo_proc_map)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _get_pidtaskinfo(self):
|
||||
# Note: should work for PIDs owned by user only.
|
||||
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)
|
||||
self._get_pidtaskinfo.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._get_kinfo_proc.cache_deactivate(self)
|
||||
self._get_pidtaskinfo.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
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):
|
||||
with catch_zombie(self):
|
||||
return cext.proc_exe(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def cmdline(self):
|
||||
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):
|
||||
self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def cwd(self):
|
||||
with catch_zombie(self):
|
||||
return cext.proc_cwd(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
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):
|
||||
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 = 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):
|
||||
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_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):
|
||||
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 self._get_kinfo_proc()[kinfo_proc_map['ctime']]
|
||||
|
||||
@wrap_exceptions
|
||||
def num_ctx_switches(self):
|
||||
# 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 self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
|
||||
|
||||
@wrap_exceptions
|
||||
def open_files(self):
|
||||
if self.pid == 0:
|
||||
return []
|
||||
files = []
|
||||
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]
|
||||
with catch_zombie(self):
|
||||
rawlist = cext.proc_connections(self.pid, families, types)
|
||||
ret = []
|
||||
for item in rawlist:
|
||||
fd, fam, type, laddr, raddr, status = item
|
||||
nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
|
||||
TCP_STATUSES)
|
||||
ret.append(nt)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
if self.pid == 0:
|
||||
return 0
|
||||
with catch_zombie(self):
|
||||
return cext.proc_num_fds(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
return _psposix.wait_pid(self.pid, timeout, self._name)
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_get(self):
|
||||
with catch_zombie(self):
|
||||
return cext_posix.getpriority(self.pid)
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_set(self, value):
|
||||
with catch_zombie(self):
|
||||
return cext_posix.setpriority(self.pid, value)
|
||||
|
||||
@wrap_exceptions
|
||||
def status(self):
|
||||
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)
|
||||
retlist = []
|
||||
for thread_id, utime, stime in rawlist:
|
||||
ntuple = _common.pthread(thread_id, utime, stime)
|
||||
retlist.append(ntuple)
|
||||
return retlist
|
|
@ -1,175 +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.
|
||||
|
||||
"""Routines common to all posix systems."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from ._common import memoize
|
||||
from ._common import sdiskusage
|
||||
from ._common import TimeoutExpired
|
||||
from ._common import usage_percent
|
||||
from ._compat import ChildProcessError
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import InterruptedError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
from ._compat import unicode
|
||||
|
||||
|
||||
__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.
|
||||
# If we get here it means this UNIX platform *does* have
|
||||
# a process with id 0.
|
||||
return True
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except ProcessLookupError:
|
||||
return False
|
||||
except PermissionError:
|
||||
# EPERM clearly means there's a process to deny access to
|
||||
return True
|
||||
# According to "man 2 kill" possible error values are
|
||||
# (EINVAL, EPERM, ESRCH)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
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(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
|
||||
else:
|
||||
def waitcall():
|
||||
return os.waitpid(pid, 0)
|
||||
|
||||
delay = 0.0001
|
||||
while True:
|
||||
try:
|
||||
retpid, status = waitcall()
|
||||
except InterruptedError:
|
||||
delay = check_timeout(delay)
|
||||
except ChildProcessError:
|
||||
# This has two meanings:
|
||||
# - pid is not a child of os.getpid() in which case
|
||||
# we keep polling until it's gone
|
||||
# - pid never existed in the first place
|
||||
# In both cases we'll eventually return None as we
|
||||
# can't determine its exit status code.
|
||||
while True:
|
||||
if pid_exists(pid):
|
||||
delay = check_timeout(delay)
|
||||
else:
|
||||
return
|
||||
else:
|
||||
if retpid == 0:
|
||||
# WNOHANG was used, pid is still running
|
||||
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)
|
||||
# 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 ValueError("unknown process exit status %r" % status)
|
||||
|
||||
|
||||
def disk_usage(path):
|
||||
"""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)
|
||||
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)
|
||||
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)
|
||||
# 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:
|
||||
# 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():
|
||||
"""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, name
|
||||
try:
|
||||
ret[os.stat(name).st_rdev] = name
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return ret
|
|
@ -1,725 +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.
|
||||
|
||||
"""Sun OS Solaris platform implementation."""
|
||||
|
||||
import errno
|
||||
import functools
|
||||
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 AccessDenied
|
||||
from ._common import AF_INET6
|
||||
from ._common import debug
|
||||
from ._common import get_procfs_path
|
||||
from ._common import isfile_strict
|
||||
from ._common import memoize_when_activated
|
||||
from ._common import NoSuchProcess
|
||||
from ._common import sockfam_to_enum
|
||||
from ._common import socktype_to_enum
|
||||
from ._common import usage_percent
|
||||
from ._common import ZombieProcess
|
||||
from ._compat import b
|
||||
from ._compat import FileNotFoundError
|
||||
from ._compat import PermissionError
|
||||
from ._compat import ProcessLookupError
|
||||
from ._compat import PY3
|
||||
|
||||
|
||||
__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,
|
||||
cext.SSTOP: _common.STATUS_STOPPED,
|
||||
cext.SIDL: _common.STATUS_IDLE,
|
||||
cext.SONPROC: _common.STATUS_RUNNING, # same as run
|
||||
cext.SWAIT: _common.STATUS_WAITING,
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
uid=8,
|
||||
euid=9,
|
||||
gid=10,
|
||||
egid=11)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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'])
|
||||
# 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))
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- memory
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def virtual_memory():
|
||||
"""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'],
|
||||
stdout=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if PY3:
|
||||
stdout = stdout.decode(sys.stdout.encoding)
|
||||
if p.returncode != 0:
|
||||
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:]
|
||||
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)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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():
|
||||
"""Return the number of physical CPUs in the system."""
|
||||
return cext.cpu_count_phys()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# --- 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.
|
||||
try:
|
||||
if not disk_usage(mountpoint).total:
|
||||
continue
|
||||
except OSError as err:
|
||||
# https://github.com/giampaolo/psutil/issues/1674
|
||||
debug("skipping %r: %r" % (mountpoint, err))
|
||||
continue
|
||||
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
|
||||
retlist.append(ntuple)
|
||||
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)
|
||||
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
|
||||
# TODO: refactor and use _common.conn_to_ntuple.
|
||||
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)
|
||||
return list(ret)
|
||||
|
||||
|
||||
def net_if_stats():
|
||||
"""Get NIC stats (isup, duplex, speed, mtu)."""
|
||||
ret = cext.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.
|
||||
"""
|
||||
@functools.wraps(fun)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return fun(self, *args, **kwargs)
|
||||
except (FileNotFoundError, ProcessLookupError):
|
||||
# ENOENT (no such file or directory) gets raised on open().
|
||||
# ESRCH (no such process) can get raised on read() if
|
||||
# process is gone in meantime.
|
||||
if not pid_exists(self.pid):
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
else:
|
||||
raise ZombieProcess(self.pid, self._name, self._ppid)
|
||||
except PermissionError:
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
except OSError:
|
||||
if self.pid == 0:
|
||||
if 0 in pids():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
else:
|
||||
raise
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
|
||||
class Process(object):
|
||||
"""Wrapper class around underlying C implementation."""
|
||||
|
||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||
|
||||
def __init__(self, pid):
|
||||
self.pid = pid
|
||||
self._name = None
|
||||
self._ppid = None
|
||||
self._procfs_path = get_procfs_path()
|
||||
|
||||
def _assert_alive(self):
|
||||
"""Raise NSP if the process disappeared on us."""
|
||||
# For those C function who do not raise NSP, possibly returning
|
||||
# incorrect or incomplete result.
|
||||
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
||||
|
||||
def oneshot_enter(self):
|
||||
self._proc_name_and_args.cache_activate(self)
|
||||
self._proc_basic_info.cache_activate(self)
|
||||
self._proc_cred.cache_activate(self)
|
||||
|
||||
def oneshot_exit(self):
|
||||
self._proc_name_and_args.cache_deactivate(self)
|
||||
self._proc_basic_info.cache_deactivate(self)
|
||||
self._proc_cred.cache_deactivate(self)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_name_and_args(self):
|
||||
return cext.proc_name_and_args(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_basic_info(self):
|
||||
if self.pid == 0 and not \
|
||||
os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
|
||||
raise AccessDenied(self.pid)
|
||||
ret = cext.proc_basic_info(self.pid, self._procfs_path)
|
||||
assert len(ret) == len(proc_info_map)
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
@memoize_when_activated
|
||||
def _proc_cred(self):
|
||||
return cext.proc_cred(self.pid, self._procfs_path)
|
||||
|
||||
@wrap_exceptions
|
||||
def name(self):
|
||||
# note: max len == 15
|
||||
return self._proc_name_and_args()[0]
|
||||
|
||||
@wrap_exceptions
|
||||
def exe(self):
|
||||
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 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 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']]
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_get(self):
|
||||
# Note #1: getpriority(3) doesn't work for realtime processes.
|
||||
# Psinfo is what ps uses, see:
|
||||
# https://github.com/giampaolo/psutil/issues/1194
|
||||
return self._proc_basic_info()[proc_info_map['nice']]
|
||||
|
||||
@wrap_exceptions
|
||||
def nice_set(self, value):
|
||||
if self.pid in (2, 3):
|
||||
# Special case PIDs: internally setpriority(3) return ESRCH
|
||||
# (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):
|
||||
self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
|
||||
return self._ppid
|
||||
|
||||
@wrap_exceptions
|
||||
def uids(self):
|
||||
try:
|
||||
real, effective, saved, _, _, _ = self._proc_cred()
|
||||
except AccessDenied:
|
||||
real = self._proc_basic_info()[proc_info_map['uid']]
|
||||
effective = self._proc_basic_info()[proc_info_map['euid']]
|
||||
saved = None
|
||||
return _common.puids(real, effective, saved)
|
||||
|
||||
@wrap_exceptions
|
||||
def gids(self):
|
||||
try:
|
||||
_, _, _, real, effective, saved = self._proc_cred()
|
||||
except AccessDenied:
|
||||
real = self._proc_basic_info()[proc_info_map['gid']]
|
||||
effective = self._proc_basic_info()[proc_info_map['egid']]
|
||||
saved = None
|
||||
return _common.puids(real, effective, saved)
|
||||
|
||||
@wrap_exceptions
|
||||
def cpu_times(self):
|
||||
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(
|
||||
self._proc_basic_info()[proc_info_map['ttynr']])
|
||||
if tty != cext.PRNODEV:
|
||||
for x in (0, 1, 2, 255):
|
||||
try:
|
||||
return os.readlink(
|
||||
'%s/%d/path/%d' % (procfs_path, self.pid, x))
|
||||
except FileNotFoundError:
|
||||
hit_enoent = True
|
||||
continue
|
||||
if hit_enoent:
|
||||
self._assert_alive()
|
||||
|
||||
@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("%s/%s/path/cwd" % (procfs_path, self.pid))
|
||||
except FileNotFoundError:
|
||||
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
|
||||
return None
|
||||
|
||||
@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, '?')
|
||||
|
||||
@wrap_exceptions
|
||||
def threads(self):
|
||||
procfs_path = self._procfs_path
|
||||
ret = []
|
||||
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, 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:
|
||||
self._assert_alive()
|
||||
return ret
|
||||
|
||||
@wrap_exceptions
|
||||
def open_files(self):
|
||||
retlist = []
|
||||
hit_enoent = False
|
||||
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 FileNotFoundError:
|
||||
hit_enoent = True
|
||||
continue
|
||||
else:
|
||||
if isfile_strict(file):
|
||||
retlist.append(_common.popenfile(file, int(fd)))
|
||||
if hit_enoent:
|
||||
self._assert_alive()
|
||||
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,
|
||||
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:
|
||||
if 'permission denied' in stderr.lower():
|
||||
raise AccessDenied(self.pid, self._name)
|
||||
if 'no such process' in stderr.lower():
|
||||
raise NoSuchProcess(self.pid, self._name)
|
||||
raise RuntimeError("%r command error\n%s" % (cmd, stderr))
|
||||
|
||||
lines = stdout.split('\n')[2:]
|
||||
for i, line in enumerate(lines):
|
||||
line = line.lstrip()
|
||||
if line.startswith('sockname: AF_UNIX'):
|
||||
path = line.split(' ', 2)[2]
|
||||
type = lines[i - 2].strip()
|
||||
if type == 'SOCK_STREAM':
|
||||
type = socket.SOCK_STREAM
|
||||
elif type == 'SOCK_DGRAM':
|
||||
type = socket.SOCK_DGRAM
|
||||
else:
|
||||
type = -1
|
||||
yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
|
||||
|
||||
@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))
|
||||
|
||||
# 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 = []
|
||||
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(
|
||||
'%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 = '%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:
|
||||
self._assert_alive()
|
||||
return retlist
|
||||
|
||||
@wrap_exceptions
|
||||
def num_fds(self):
|
||||
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, self._procfs_path))
|
||||
|
||||
@wrap_exceptions
|
||||
def wait(self, timeout=None):
|
||||
return _psposix.wait_pid(self.pid, timeout, self._name)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,403 +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.
|
||||
*
|
||||
* Routines common to all platforms.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include "_psutil_common.h"
|
||||
|
||||
// ====================================================================
|
||||
// --- Global vars
|
||||
// ====================================================================
|
||||
|
||||
int PSUTIL_DEBUG = 0;
|
||||
int PSUTIL_TESTING = 0;
|
||||
// PSUTIL_CONN_NONE
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// --- Backward compatibility with missing Python.h APIs
|
||||
// ====================================================================
|
||||
|
||||
// PyPy on Windows
|
||||
#if defined(PSUTIL_WINDOWS) && \
|
||||
defined(PYPY_VERSION) && \
|
||||
!defined(PyErr_SetFromWindowsErrWithFilename)
|
||||
PyObject *
|
||||
PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) {
|
||||
PyObject *py_exc = NULL;
|
||||
PyObject *py_winerr = NULL;
|
||||
|
||||
if (winerr == 0)
|
||||
winerr = GetLastError();
|
||||
if (filename == NULL) {
|
||||
py_exc = PyObject_CallFunction(PyExc_OSError, "(is)", winerr,
|
||||
strerror(winerr));
|
||||
}
|
||||
else {
|
||||
py_exc = PyObject_CallFunction(PyExc_OSError, "(iss)", winerr,
|
||||
strerror(winerr), filename);
|
||||
}
|
||||
if (py_exc == NULL)
|
||||
return NULL;
|
||||
|
||||
py_winerr = Py_BuildValue("i", winerr);
|
||||
if (py_winerr == NULL)
|
||||
goto error;
|
||||
if (PyObject_SetAttrString(py_exc, "winerror", py_winerr) != 0)
|
||||
goto error;
|
||||
PyErr_SetObject(PyExc_OSError, py_exc);
|
||||
Py_XDECREF(py_exc);
|
||||
return NULL;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_exc);
|
||||
Py_XDECREF(py_winerr);
|
||||
return NULL;
|
||||
}
|
||||
#endif // PYPY on Windows
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// --- Custom exceptions
|
||||
// ====================================================================
|
||||
|
||||
/*
|
||||
* Same as PyErr_SetFromErrno(0) but adds the syscall to the exception
|
||||
* message.
|
||||
*/
|
||||
PyObject *
|
||||
PyErr_SetFromOSErrnoWithSyscall(const char *syscall) {
|
||||
char fullmsg[1024];
|
||||
|
||||
#ifdef PSUTIL_WINDOWS
|
||||
sprintf(fullmsg, "(originated from %s)", syscall);
|
||||
PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg);
|
||||
#else
|
||||
PyObject *exc;
|
||||
sprintf(fullmsg, "%s (originated from %s)", strerror(errno), syscall);
|
||||
exc = PyObject_CallFunction(PyExc_OSError, "(is)", errno, fullmsg);
|
||||
PyErr_SetObject(PyExc_OSError, exc);
|
||||
Py_XDECREF(exc);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set OSError(errno=ESRCH, strerror="No such process (originated from")
|
||||
* Python exception.
|
||||
*/
|
||||
PyObject *
|
||||
NoSuchProcess(const char *syscall) {
|
||||
PyObject *exc;
|
||||
char msg[1024];
|
||||
|
||||
sprintf(msg, "No such process (originated from %s)", syscall);
|
||||
exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
|
||||
PyErr_SetObject(PyExc_OSError, exc);
|
||||
Py_XDECREF(exc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set OSError(errno=EACCES, strerror="Permission denied" (originated from ...)
|
||||
* Python exception.
|
||||
*/
|
||||
PyObject *
|
||||
AccessDenied(const char *syscall) {
|
||||
PyObject *exc;
|
||||
char msg[1024];
|
||||
|
||||
sprintf(msg, "Access denied (originated from %s)", syscall);
|
||||
exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
|
||||
PyErr_SetObject(PyExc_OSError, exc);
|
||||
Py_XDECREF(exc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// --- Global utils
|
||||
// ====================================================================
|
||||
|
||||
/*
|
||||
* Enable testing mode. This has the same effect as setting PSUTIL_TESTING
|
||||
* env var. This dual method exists because updating os.environ on
|
||||
* 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;
|
||||
if (PSUTIL_DEBUG) {
|
||||
va_start(argptr, format);
|
||||
fprintf(stderr, "psutil-debug> ");
|
||||
vfprintf(stderr, format, argptr);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(argptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called on module import on all platforms.
|
||||
*/
|
||||
int
|
||||
psutil_setup(void) {
|
||||
if (getenv("PSUTIL_DEBUG") != NULL)
|
||||
PSUTIL_DEBUG = 1;
|
||||
if (getenv("PSUTIL_TESTING") != NULL)
|
||||
PSUTIL_TESTING = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// --- Windows
|
||||
// ====================================================================
|
||||
|
||||
#ifdef PSUTIL_WINDOWS
|
||||
#include <windows.h>
|
||||
|
||||
// Needed to make these globally visible.
|
||||
int PSUTIL_WINVER;
|
||||
SYSTEM_INFO PSUTIL_SYSTEM_INFO;
|
||||
CRITICAL_SECTION PSUTIL_CRITICAL_SECTION;
|
||||
|
||||
#define NT_FACILITY_MASK 0xfff
|
||||
#define NT_FACILITY_SHIFT 16
|
||||
#define NT_FACILITY(Status) \
|
||||
((((ULONG)(Status)) >> NT_FACILITY_SHIFT) & NT_FACILITY_MASK)
|
||||
#define NT_NTWIN32(status) (NT_FACILITY(Status) == FACILITY_WIN32)
|
||||
#define WIN32_FROM_NTSTATUS(Status) (((ULONG)(Status)) & 0xffff)
|
||||
|
||||
|
||||
// A wrapper around GetModuleHandle and GetProcAddress.
|
||||
PVOID
|
||||
psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) {
|
||||
HMODULE mod;
|
||||
FARPROC addr;
|
||||
|
||||
if ((mod = GetModuleHandleA(libname)) == NULL) {
|
||||
PyErr_SetFromWindowsErrWithFilename(0, libname);
|
||||
return NULL;
|
||||
}
|
||||
if ((addr = GetProcAddress(mod, procname)) == NULL) {
|
||||
PyErr_SetFromWindowsErrWithFilename(0, procname);
|
||||
return NULL;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
// A wrapper around LoadLibrary and GetProcAddress.
|
||||
PVOID
|
||||
psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) {
|
||||
HMODULE mod;
|
||||
FARPROC addr;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
mod = LoadLibraryA(libname);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (mod == NULL) {
|
||||
PyErr_SetFromWindowsErrWithFilename(0, libname);
|
||||
return NULL;
|
||||
}
|
||||
if ((addr = GetProcAddress(mod, procname)) == NULL) {
|
||||
PyErr_SetFromWindowsErrWithFilename(0, procname);
|
||||
FreeLibrary(mod);
|
||||
return NULL;
|
||||
}
|
||||
// Causes crash.
|
||||
// FreeLibrary(mod);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a NTSTATUS value to a Win32 error code and set the proper
|
||||
* Python exception.
|
||||
*/
|
||||
PVOID
|
||||
psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) {
|
||||
ULONG err;
|
||||
char fullmsg[1024];
|
||||
|
||||
if (NT_NTWIN32(Status))
|
||||
err = WIN32_FROM_NTSTATUS(Status);
|
||||
else
|
||||
err = RtlNtStatusToDosErrorNoTeb(Status);
|
||||
// if (GetLastError() != 0)
|
||||
// err = GetLastError();
|
||||
sprintf(fullmsg, "(originated from %s)", syscall);
|
||||
return PyErr_SetFromWindowsErrWithFilename(err, fullmsg);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
psutil_loadlibs() {
|
||||
// --- Mandatory
|
||||
NtQuerySystemInformation = psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "NtQuerySystemInformation");
|
||||
if (! NtQuerySystemInformation)
|
||||
return 1;
|
||||
NtQueryInformationProcess = psutil_GetProcAddress(
|
||||
"ntdll.dll", "NtQueryInformationProcess");
|
||||
if (! NtQueryInformationProcess)
|
||||
return 1;
|
||||
NtSetInformationProcess = psutil_GetProcAddress(
|
||||
"ntdll.dll", "NtSetInformationProcess");
|
||||
if (! NtSetInformationProcess)
|
||||
return 1;
|
||||
WinStationQueryInformationW = psutil_GetProcAddressFromLib(
|
||||
"winsta.dll", "WinStationQueryInformationW");
|
||||
if (! WinStationQueryInformationW)
|
||||
return 1;
|
||||
NtQueryObject = psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "NtQueryObject");
|
||||
if (! NtQueryObject)
|
||||
return 1;
|
||||
RtlIpv4AddressToStringA = psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "RtlIpv4AddressToStringA");
|
||||
if (! RtlIpv4AddressToStringA)
|
||||
return 1;
|
||||
GetExtendedTcpTable = psutil_GetProcAddressFromLib(
|
||||
"iphlpapi.dll", "GetExtendedTcpTable");
|
||||
if (! GetExtendedTcpTable)
|
||||
return 1;
|
||||
GetExtendedUdpTable = psutil_GetProcAddressFromLib(
|
||||
"iphlpapi.dll", "GetExtendedUdpTable");
|
||||
if (! GetExtendedUdpTable)
|
||||
return 1;
|
||||
RtlGetVersion = psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "RtlGetVersion");
|
||||
if (! RtlGetVersion)
|
||||
return 1;
|
||||
NtSuspendProcess = psutil_GetProcAddressFromLib(
|
||||
"ntdll", "NtSuspendProcess");
|
||||
if (! NtSuspendProcess)
|
||||
return 1;
|
||||
NtResumeProcess = psutil_GetProcAddressFromLib(
|
||||
"ntdll", "NtResumeProcess");
|
||||
if (! NtResumeProcess)
|
||||
return 1;
|
||||
NtQueryVirtualMemory = psutil_GetProcAddressFromLib(
|
||||
"ntdll", "NtQueryVirtualMemory");
|
||||
if (! NtQueryVirtualMemory)
|
||||
return 1;
|
||||
RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib(
|
||||
"ntdll", "RtlNtStatusToDosErrorNoTeb");
|
||||
if (! RtlNtStatusToDosErrorNoTeb)
|
||||
return 1;
|
||||
GetTickCount64 = psutil_GetProcAddress(
|
||||
"kernel32", "GetTickCount64");
|
||||
if (! GetTickCount64)
|
||||
return 1;
|
||||
RtlIpv6AddressToStringA = psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "RtlIpv6AddressToStringA");
|
||||
if (! RtlIpv6AddressToStringA)
|
||||
return 1;
|
||||
|
||||
// --- Optional
|
||||
// minimum requirement: Win 7
|
||||
GetActiveProcessorCount = psutil_GetProcAddress(
|
||||
"kernel32", "GetActiveProcessorCount");
|
||||
// minumum requirement: Win 7
|
||||
GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib(
|
||||
"kernel32", "GetLogicalProcessorInformationEx");
|
||||
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
psutil_set_winver() {
|
||||
RTL_OSVERSIONINFOEXW versionInfo;
|
||||
ULONG maj;
|
||||
ULONG min;
|
||||
|
||||
versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
|
||||
memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW));
|
||||
RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo);
|
||||
maj = versionInfo.dwMajorVersion;
|
||||
min = versionInfo.dwMinorVersion;
|
||||
if (maj == 6 && min == 0)
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA; // or Server 2008
|
||||
else if (maj == 6 && min == 1)
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_7;
|
||||
else if (maj == 6 && min == 2)
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_8;
|
||||
else if (maj == 6 && min == 3)
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_8_1;
|
||||
else if (maj == 10 && min == 0)
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_10;
|
||||
else
|
||||
PSUTIL_WINVER = PSUTIL_WINDOWS_NEW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
psutil_load_globals() {
|
||||
if (psutil_loadlibs() != 0)
|
||||
return 1;
|
||||
if (psutil_set_winver() != 0)
|
||||
return 1;
|
||||
GetSystemInfo(&PSUTIL_SYSTEM_INFO);
|
||||
InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert the hi and lo parts of a FILETIME structure or a LARGE_INTEGER
|
||||
* to a UNIX time.
|
||||
* A FILETIME contains a 64-bit value representing the number of
|
||||
* 100-nanosecond intervals since January 1, 1601 (UTC).
|
||||
* A UNIX time is the number of seconds that have elapsed since the
|
||||
* UNIX epoch, that is the time 00:00:00 UTC on 1 January 1970.
|
||||
*/
|
||||
static double
|
||||
_to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) {
|
||||
ULONGLONG ret;
|
||||
|
||||
// 100 nanosecond intervals since January 1, 1601.
|
||||
ret = hiPart << 32;
|
||||
ret += loPart;
|
||||
// Change starting time to the Epoch (00:00:00 UTC, January 1, 1970).
|
||||
ret -= 116444736000000000ull;
|
||||
// Convert nano secs to secs.
|
||||
return (double) ret / 10000000ull;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
psutil_FiletimeToUnixTime(FILETIME ft) {
|
||||
return _to_unix_time((ULONGLONG)ft.dwHighDateTime,
|
||||
(ULONGLONG)ft.dwLowDateTime);
|
||||
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {
|
||||
return _to_unix_time((ULONGLONG)li.HighPart,
|
||||
(ULONGLONG)li.LowPart);
|
||||
}
|
||||
#endif // PSUTIL_WINDOWS
|
|
@ -1,139 +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>
|
||||
|
||||
// ====================================================================
|
||||
// --- Global vars / constants
|
||||
// ====================================================================
|
||||
|
||||
extern int PSUTIL_TESTING;
|
||||
extern int PSUTIL_DEBUG;
|
||||
// a signaler for connections without an actual status
|
||||
static const int PSUTIL_CONN_NONE = 128;
|
||||
|
||||
// ====================================================================
|
||||
// --- Backward compatibility with missing Python.h APIs
|
||||
// ====================================================================
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
// On Python 2 we just return a plain byte string, which is never
|
||||
// supposed to raise decoding errors, see:
|
||||
// https://github.com/giampaolo/psutil/issues/1040
|
||||
#define PyUnicode_DecodeFSDefault PyString_FromString
|
||||
#define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize
|
||||
#endif
|
||||
|
||||
#if defined(PSUTIL_WINDOWS) && \
|
||||
defined(PYPY_VERSION) && \
|
||||
!defined(PyErr_SetFromWindowsErrWithFilename)
|
||||
PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr,
|
||||
const char *filename);
|
||||
#endif
|
||||
|
||||
// --- _Py_PARSE_PID
|
||||
|
||||
// SIZEOF_INT|LONG is missing on Linux + PyPy (only?).
|
||||
// SIZEOF_PID_T is missing on Windows + Python2.
|
||||
// In this case we guess it from setup.py. It's not 100% bullet proof,
|
||||
// If wrong we'll probably get compiler warnings.
|
||||
// FWIW on all UNIX platforms I've seen pid_t is defined as an int.
|
||||
// _getpid() on Windows also returns an int.
|
||||
#if !defined(SIZEOF_INT)
|
||||
#define SIZEOF_INT 4
|
||||
#endif
|
||||
#if !defined(SIZEOF_LONG)
|
||||
#define SIZEOF_LONG 8
|
||||
#endif
|
||||
#if !defined(SIZEOF_PID_T)
|
||||
#define SIZEOF_PID_T PSUTIL_SIZEOF_PID_T // set as a macro in setup.py
|
||||
#endif
|
||||
|
||||
// _Py_PARSE_PID is Python 3 only, but since it's private make sure it's
|
||||
// always present.
|
||||
#ifndef _Py_PARSE_PID
|
||||
#if SIZEOF_PID_T == SIZEOF_INT
|
||||
#define _Py_PARSE_PID "i"
|
||||
#elif SIZEOF_PID_T == SIZEOF_LONG
|
||||
#define _Py_PARSE_PID "l"
|
||||
#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG
|
||||
#define _Py_PARSE_PID "L"
|
||||
#else
|
||||
#error "_Py_PARSE_PID: sizeof(pid_t) is neither sizeof(int), "
|
||||
"sizeof(long) or sizeof(long long)"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Python 2 or PyPy on Windows
|
||||
#ifndef PyLong_FromPid
|
||||
#if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG))
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define PyLong_FromPid PyLong_FromLong
|
||||
#else
|
||||
#define PyLong_FromPid PyInt_FromLong
|
||||
#endif
|
||||
#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG
|
||||
#define PyLong_FromPid PyLong_FromLongLong
|
||||
#else
|
||||
#error "PyLong_FromPid: sizeof(pid_t) is neither sizeof(int), "
|
||||
"sizeof(long) or sizeof(long long)"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ====================================================================
|
||||
// --- Custom exceptions
|
||||
// ====================================================================
|
||||
|
||||
PyObject* AccessDenied(const char *msg);
|
||||
PyObject* NoSuchProcess(const char *msg);
|
||||
PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall);
|
||||
|
||||
// ====================================================================
|
||||
// --- Global utils
|
||||
// ====================================================================
|
||||
|
||||
PyObject* psutil_set_testing(PyObject *self, PyObject *args);
|
||||
void psutil_debug(const char* format, ...);
|
||||
int psutil_setup(void);
|
||||
|
||||
// ====================================================================
|
||||
// --- Windows
|
||||
// ====================================================================
|
||||
|
||||
#ifdef PSUTIL_WINDOWS
|
||||
#include <windows.h>
|
||||
// make it available to any file which includes this module
|
||||
#include "arch/windows/ntextapi.h"
|
||||
|
||||
extern int PSUTIL_WINVER;
|
||||
extern SYSTEM_INFO PSUTIL_SYSTEM_INFO;
|
||||
extern CRITICAL_SECTION PSUTIL_CRITICAL_SECTION;
|
||||
|
||||
#define PSUTIL_WINDOWS_VISTA 60
|
||||
#define PSUTIL_WINDOWS_7 61
|
||||
#define PSUTIL_WINDOWS_8 62
|
||||
#define PSUTIL_WINDOWS_8_1 63
|
||||
#define PSUTIL_WINDOWS_10 100
|
||||
#define PSUTIL_WINDOWS_NEW MAXLONG
|
||||
|
||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||
#define MALLOC_ZERO(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x))
|
||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||
|
||||
#define LO_T 1e-7
|
||||
#define HI_T 429.4967296
|
||||
|
||||
#ifndef AF_INET6
|
||||
#define AF_INET6 23
|
||||
#endif
|
||||
|
||||
int psutil_load_globals();
|
||||
PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname);
|
||||
PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname);
|
||||
PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall);
|
||||
double psutil_FiletimeToUnixTime(FILETIME ft);
|
||||
double psutil_LargeIntegerToUnixTime(LARGE_INTEGER li);
|
||||
#endif
|
|
@ -1,673 +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.
|
||||
*
|
||||
* Linux-specific functions.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
#include <Python.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <mntent.h>
|
||||
#include <features.h>
|
||||
#include <utmp.h>
|
||||
#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>
|
||||
|
||||
/* 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 \
|
||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) && \
|
||||
(__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 13) && \
|
||||
defined(__NR_prlimit64)
|
||||
|
||||
#if PSUTIL_HAVE_PRLIMIT
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#include <time.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
// Should exist starting from CentOS 6 (year 2011).
|
||||
#ifdef CPU_ALLOC
|
||||
#define PSUTIL_HAVE_CPU_AFFINITY
|
||||
#endif
|
||||
|
||||
#include "_psutil_common.h"
|
||||
#include "_psutil_posix.h"
|
||||
|
||||
// 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) {
|
||||
return syscall(__NR_ioprio_get, which, who);
|
||||
}
|
||||
|
||||
static inline int
|
||||
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) {
|
||||
pid_t pid;
|
||||
int ioprio, ioclass, iodata;
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
|
||||
return NULL;
|
||||
ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
|
||||
if (ioprio == -1)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
ioclass = IOPRIO_PRIO_CLASS(ioprio);
|
||||
iodata = IOPRIO_PRIO_DATA(ioprio);
|
||||
return Py_BuildValue("ii", ioclass, iodata);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
pid_t pid;
|
||||
int ioprio, ioclass, iodata;
|
||||
int retval;
|
||||
|
||||
if (! PyArg_ParseTuple(
|
||||
args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) {
|
||||
return NULL;
|
||||
}
|
||||
ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
|
||||
retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
|
||||
if (retval == -1)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#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) {
|
||||
pid_t pid;
|
||||
int ret, resource;
|
||||
struct rlimit old, new;
|
||||
struct rlimit *newp = NULL;
|
||||
PyObject *py_soft = NULL;
|
||||
PyObject *py_hard = NULL;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i|OO", &pid, &resource,
|
||||
&py_soft, &py_hard)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get
|
||||
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(py_soft);
|
||||
if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
new.rlim_max = PyLong_AsLongLong(py_hard);
|
||||
if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
#else
|
||||
new.rlim_cur = PyLong_AsLong(py_soft);
|
||||
if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#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) {
|
||||
FILE *file = NULL;
|
||||
struct mntent *entry;
|
||||
char *mtab_path;
|
||||
PyObject *py_dev = NULL;
|
||||
PyObject *py_mountp = NULL;
|
||||
PyObject *py_tuple = NULL;
|
||||
PyObject *py_retlist = PyList_New(0);
|
||||
|
||||
if (py_retlist == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &mtab_path))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
file = setmntent(mtab_path, "r");
|
||||
Py_END_ALLOW_THREADS
|
||||
if ((file == 0) || (file == NULL)) {
|
||||
psutil_debug("setmntent() failed");
|
||||
PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
while ((entry = getmntent(file))) {
|
||||
if (entry == NULL) {
|
||||
PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
|
||||
goto error;
|
||||
}
|
||||
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_CLEAR(py_dev);
|
||||
Py_CLEAR(py_mountp);
|
||||
Py_CLEAR(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) {
|
||||
struct sysinfo info;
|
||||
|
||||
if (sysinfo(&info) != 0)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
// note: boot time might also be determined from here
|
||||
return Py_BuildValue(
|
||||
"(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
|
||||
*/
|
||||
#ifdef PSUTIL_HAVE_CPU_AFFINITY
|
||||
|
||||
static PyObject *
|
||||
psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
|
||||
int cpu, ncpus, count, cpucount_s;
|
||||
pid_t pid;
|
||||
size_t setsize;
|
||||
cpu_set_t *mask = NULL;
|
||||
PyObject *py_list = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
|
||||
return NULL;
|
||||
ncpus = NCPUS_START;
|
||||
while (1) {
|
||||
setsize = CPU_ALLOC_SIZE(ncpus);
|
||||
mask = CPU_ALLOC(ncpus);
|
||||
if (mask == NULL) {
|
||||
psutil_debug("CPU_ALLOC() failed");
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
if (sched_getaffinity(pid, setsize, mask) == 0)
|
||||
break;
|
||||
CPU_FREE(mask);
|
||||
if (errno != EINVAL)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (ncpus > INT_MAX / 2) {
|
||||
PyErr_SetString(PyExc_OverflowError, "could not allocate "
|
||||
"a large enough CPU set");
|
||||
return NULL;
|
||||
}
|
||||
ncpus = ncpus * 2;
|
||||
}
|
||||
|
||||
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(py_list, cpu_num)) {
|
||||
Py_DECREF(cpu_num);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(cpu_num);
|
||||
--count;
|
||||
}
|
||||
}
|
||||
CPU_FREE(mask);
|
||||
return py_list;
|
||||
|
||||
error:
|
||||
if (mask)
|
||||
CPU_FREE(mask);
|
||||
Py_XDECREF(py_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set process CPU affinity; expects a bitmask
|
||||
*/
|
||||
static PyObject *
|
||||
psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
|
||||
cpu_set_t cpu_set;
|
||||
size_t len;
|
||||
pid_t pid;
|
||||
int i, seq_len;
|
||||
PyObject *py_cpu_set;
|
||||
PyObject *py_cpu_seq = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set))
|
||||
return NULL;
|
||||
|
||||
if (!PySequence_Check(py_cpu_set)) {
|
||||
PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s",
|
||||
Py_TYPE(py_cpu_set)->tp_name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
|
||||
if (!py_cpu_seq)
|
||||
goto error;
|
||||
seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
|
||||
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 (!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;
|
||||
|
||||
error:
|
||||
if (py_cpu_seq != NULL)
|
||||
Py_DECREF(py_cpu_seq);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* PSUTIL_HAVE_CPU_AFFINITY */
|
||||
|
||||
|
||||
/*
|
||||
* Return currently connected users as a list of tuples.
|
||||
*/
|
||||
static PyObject *
|
||||
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 (py_retlist == NULL)
|
||||
return NULL;
|
||||
setutent();
|
||||
while (NULL != (ut = getutent())) {
|
||||
py_tuple = NULL;
|
||||
py_user_proc = NULL;
|
||||
if (ut->ut_type == USER_PROCESS)
|
||||
py_user_proc = Py_True;
|
||||
else
|
||||
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(
|
||||
"OOOfO" _Py_PARSE_PID,
|
||||
py_username, // username
|
||||
py_tty, // tty
|
||||
py_hostname, // hostname
|
||||
(float)ut->ut_tv.tv_sec, // tstamp
|
||||
py_user_proc, // (bool) user process
|
||||
ut->ut_pid // process id
|
||||
);
|
||||
if (! py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_username);
|
||||
Py_CLEAR(py_tty);
|
||||
Py_CLEAR(py_hostname);
|
||||
Py_CLEAR(py_tuple);
|
||||
}
|
||||
endutent();
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
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_duplex_speed(PyObject* self, PyObject* args) {
|
||||
char *nic_name;
|
||||
int sock = 0;
|
||||
int ret;
|
||||
int duplex;
|
||||
int speed;
|
||||
struct ifreq ifr;
|
||||
struct ethtool_cmd ethcmd;
|
||||
PyObject *py_retlist = NULL;
|
||||
|
||||
if (! PyArg_ParseTuple(args, "s", &nic_name))
|
||||
return NULL;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == -1)
|
||||
return PyErr_SetFromOSErrnoWithSyscall("socket()");
|
||||
strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
|
||||
|
||||
// duplex and speed
|
||||
memset(ðcmd, 0, sizeof ethcmd);
|
||||
ethcmd.cmd = ETHTOOL_GSET;
|
||||
ifr.ifr_data = (void *)ðcmd;
|
||||
ret = ioctl(sock, SIOCETHTOOL, &ifr);
|
||||
|
||||
if (ret != -1) {
|
||||
duplex = ethcmd.duplex;
|
||||
speed = ethcmd.speed;
|
||||
}
|
||||
else {
|
||||
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 {
|
||||
PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
py_retlist = Py_BuildValue("[ii]", duplex, speed);
|
||||
if (!py_retlist)
|
||||
goto error;
|
||||
close(sock);
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
if (sock != -1)
|
||||
close(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Module init.
|
||||
*/
|
||||
|
||||
static PyMethodDef mod_methods[] = {
|
||||
// --- 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
|
||||
#ifdef PSUTIL_HAVE_CPU_AFFINITY
|
||||
{"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
|
||||
"Return process CPU affinity as a Python long (the bitmask)."},
|
||||
{"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
|
||||
"Set process CPU affinity; expects a bitmask."},
|
||||
#endif
|
||||
|
||||
// --- system related functions
|
||||
|
||||
{"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_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}
|
||||
};
|
||||
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define INITERR return NULL
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_psutil_linux",
|
||||
NULL,
|
||||
-1,
|
||||
mod_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyObject *PyInit__psutil_linux(void)
|
||||
#else /* PY_MAJOR_VERSION */
|
||||
#define INITERR return
|
||||
|
||||
void init_psutil_linux(void)
|
||||
#endif /* PY_MAJOR_VERSION */
|
||||
{
|
||||
PyObject *v;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *mod = PyModule_Create(&moduledef);
|
||||
#else
|
||||
PyObject *mod = Py_InitModule("_psutil_linux", mod_methods);
|
||||
#endif
|
||||
if (mod == NULL)
|
||||
INITERR;
|
||||
|
||||
if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR;
|
||||
#if PSUTIL_HAVE_PRLIMIT
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_AS", RLIMIT_AS)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_CORE", RLIMIT_CORE)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_CPU", RLIMIT_CPU)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_DATA", RLIMIT_DATA)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_FSIZE", RLIMIT_FSIZE)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", RLIMIT_LOCKS)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", RLIMIT_MEMLOCK)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", RLIMIT_NOFILE)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_NPROC", RLIMIT_NPROC)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_RSS", RLIMIT_RSS)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_STACK", RLIMIT_STACK)) INITERR;
|
||||
|
||||
#if defined(HAVE_LONG_LONG)
|
||||
if (sizeof(RLIM_INFINITY) > sizeof(long)) {
|
||||
v = PyLong_FromLongLong((PY_LONG_LONG) RLIM_INFINITY);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
v = PyLong_FromLong((long) RLIM_INFINITY);
|
||||
}
|
||||
if (v) {
|
||||
PyModule_AddObject(mod, "RLIM_INFINITY", v);
|
||||
}
|
||||
|
||||
#ifdef RLIMIT_MSGQUEUE
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", RLIMIT_MSGQUEUE)) INITERR;
|
||||
#endif
|
||||
#ifdef RLIMIT_NICE
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_NICE", RLIMIT_NICE)) INITERR;
|
||||
#endif
|
||||
#ifdef RLIMIT_RTPRIO
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_RTPRIO", RLIMIT_RTPRIO)) INITERR;
|
||||
#endif
|
||||
#ifdef RLIMIT_RTTIME
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_RTTIME", RLIMIT_RTTIME)) INITERR;
|
||||
#endif
|
||||
#ifdef RLIMIT_SIGPENDING
|
||||
if (PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING))
|
||||
INITERR;
|
||||
#endif
|
||||
#endif
|
||||
if (PyModule_AddIntConstant(mod, "DUPLEX_HALF", DUPLEX_HALF)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "DUPLEX_FULL", DUPLEX_FULL)) INITERR;
|
||||
if (PyModule_AddIntConstant(mod, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN)) INITERR;
|
||||
|
||||
if (mod == NULL)
|
||||
INITERR;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return mod;
|
||||
#endif
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,680 +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.
|
||||
*
|
||||
* 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 <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
|
||||
|
||||
#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
|
||||
|
||||
#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(pid_t 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
|
||||
}
|
||||
|
||||
ret = kill(pid , 0);
|
||||
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.
|
||||
*/
|
||||
void
|
||||
psutil_raise_for_pid(long pid, char *syscall) {
|
||||
if (errno != 0) // unlikely
|
||||
PyErr_SetFromOSErrnoWithSyscall(syscall);
|
||||
else if (psutil_pid_exists(pid) == 0)
|
||||
NoSuchProcess(syscall);
|
||||
else
|
||||
PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a PID return process priority as a Python integer.
|
||||
*/
|
||||
static PyObject *
|
||||
psutil_posix_getpriority(PyObject *self, PyObject *args) {
|
||||
pid_t pid;
|
||||
int priority;
|
||||
errno = 0;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &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) {
|
||||
pid_t pid;
|
||||
int priority;
|
||||
int retval;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &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) {
|
||||
char buf[NI_MAXHOST];
|
||||
int err;
|
||||
int addrlen;
|
||||
size_t n;
|
||||
size_t len;
|
||||
const char *data;
|
||||
char *ptr;
|
||||
|
||||
if (addr == NULL) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
else if (family == AF_INET || family == AF_INET6) {
|
||||
if (family == AF_INET)
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
else
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
err = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (err != 0) {
|
||||
// XXX we get here on FreeBSD when processing 'lo' / AF_INET6
|
||||
// broadcast. Not sure what to do other than returning None.
|
||||
// ifconfig does not show anything BTW.
|
||||
// PyErr_Format(PyExc_RuntimeError, gai_strerror(err));
|
||||
// return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
else {
|
||||
return Py_BuildValue("s", buf);
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
#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
|
||||
else {
|
||||
// unknown family
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
// AF_PACKET or AF_LINK
|
||||
if (len > 0) {
|
||||
ptr = buf;
|
||||
for (n = 0; n < len; ++n) {
|
||||
sprintf(ptr, "%02x:", data[n] & 0xff);
|
||||
ptr += 3;
|
||||
}
|
||||
*--ptr = '\0';
|
||||
return Py_BuildValue("s", buf);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (!ifa->ifa_addr)
|
||||
continue;
|
||||
family = ifa->ifa_addr->sa_family;
|
||||
py_address = psutil_convert_ipaddr(ifa->ifa_addr, family);
|
||||
// If the primary address can't be determined just skip it.
|
||||
// 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;
|
||||
|
||||
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(
|
||||
"(siOOOO)",
|
||||
ifa->ifa_name,
|
||||
family,
|
||||
py_address,
|
||||
py_netmask,
|
||||
py_broadcast,
|
||||
py_ptp
|
||||
);
|
||||
|
||||
if (! py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
Py_CLEAR(py_address);
|
||||
Py_CLEAR(py_netmask);
|
||||
Py_CLEAR(py_broadcast);
|
||||
Py_CLEAR(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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return NIC MTU. References:
|
||||
* http://www.i-scream.org/libstatgrab/
|
||||
*/
|
||||
static PyObject *
|
||||
psutil_net_if_mtu(PyObject *self, PyObject *args) {
|
||||
char *nic_name;
|
||||
int sock = -1;
|
||||
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 != -1)
|
||||
close(sock);
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 = -1;
|
||||
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 != -1)
|
||||
close(sock);
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* net_if_stats() macOS/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)) {
|
||||
#if defined(IFM_HPNA_1) && ((!defined(IFM_10G_LR)) \
|
||||
|| (IFM_10G_LR != IFM_HPNA_1))
|
||||
// HomePNA 1.0 (1Mb/s)
|
||||
case(IFM_HPNA_1):
|
||||
return 1;
|
||||
#endif
|
||||
// 10 Mbit
|
||||
case(IFM_10_T): // 10BaseT - RJ45
|
||||
case(IFM_10_2): // 10Base2 - Thinnet
|
||||
case(IFM_10_5): // 10Base5 - AUI
|
||||
case(IFM_10_STP): // 10BaseT over shielded TP
|
||||
case(IFM_10_FL): // 10baseFL - Fiber
|
||||
return 10;
|
||||
// 100 Mbit
|
||||
case(IFM_100_TX): // 100BaseTX - RJ45
|
||||
case(IFM_100_FX): // 100BaseFX - Fiber
|
||||
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(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
|
||||
return 1000;
|
||||
#if defined(IFM_10G_SR) || defined(IFM_10G_LR) || defined(IFM_10G_CX4) \
|
||||
|| defined(IFM_10G_T)
|
||||
#ifdef IFM_10G_SR
|
||||
case(IFM_10G_SR):
|
||||
#endif
|
||||
#ifdef IFM_10G_LR
|
||||
case(IFM_10G_LR):
|
||||
#endif
|
||||
#ifdef IFM_10G_CX4
|
||||
case(IFM_10G_CX4):
|
||||
#endif
|
||||
#ifdef IFM_10G_TWINAX
|
||||
case(IFM_10G_TWINAX):
|
||||
#endif
|
||||
#ifdef IFM_10G_TWINAX_LONG
|
||||
case(IFM_10G_TWINAX_LONG):
|
||||
#endif
|
||||
#ifdef IFM_10G_T
|
||||
case(IFM_10G_T):
|
||||
#endif
|
||||
return 10000;
|
||||
#endif
|
||||
#if defined(IFM_2500_SX)
|
||||
#ifdef IFM_2500_SX
|
||||
case(IFM_2500_SX):
|
||||
#endif
|
||||
return 2500;
|
||||
#endif // any 2.5GBit stuff...
|
||||
// We don't know what it is
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef IFM_TOKEN
|
||||
case IFM_TOKEN:
|
||||
switch(IFM_SUBTYPE(ifm_active)) {
|
||||
case IFM_TOK_STP4: // Shielded twisted pair 4m - DB9
|
||||
case IFM_TOK_UTP4: // Unshielded twisted pair 4m - RJ45
|
||||
return 4;
|
||||
case IFM_TOK_STP16: // Shielded twisted pair 16m - DB9
|
||||
case IFM_TOK_UTP16: // Unshielded twisted pair 16m - RJ45
|
||||
return 16;
|
||||
#if defined(IFM_TOK_STP100) || defined(IFM_TOK_UTP100)
|
||||
#ifdef IFM_TOK_STP100
|
||||
case IFM_TOK_STP100: // Shielded twisted pair 100m - DB9
|
||||
#endif
|
||||
#ifdef IFM_TOK_UTP100
|
||||
case IFM_TOK_UTP100: // Unshielded twisted pair 100m - RJ45
|
||||
#endif
|
||||
return 100;
|
||||
#endif
|
||||
// We don't know what it is
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef IFM_FDDI
|
||||
case IFM_FDDI:
|
||||
switch(IFM_SUBTYPE(ifm_active)) {
|
||||
// We don't know what it is
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IFM_IEEE80211:
|
||||
switch(IFM_SUBTYPE(ifm_active)) {
|
||||
case IFM_IEEE80211_FH1: // Frequency Hopping 1Mbps
|
||||
case IFM_IEEE80211_DS1: // Direct Sequence 1Mbps
|
||||
return 1;
|
||||
case IFM_IEEE80211_FH2: // Frequency Hopping 2Mbps
|
||||
case IFM_IEEE80211_DS2: // Direct Sequence 2Mbps
|
||||
return 2;
|
||||
case IFM_IEEE80211_DS5: // Direct Sequence 5Mbps
|
||||
return 5;
|
||||
case IFM_IEEE80211_DS11: // Direct Sequence 11Mbps
|
||||
return 11;
|
||||
case IFM_IEEE80211_DS22: // Direct Sequence 22Mbps
|
||||
return 22;
|
||||
// We don't know what it is
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return stats about a particular network interface.
|
||||
* References:
|
||||
* http://www.i-scream.org/libstatgrab/
|
||||
*/
|
||||
static PyObject *
|
||||
psutil_net_if_duplex_speed(PyObject *self, PyObject *args) {
|
||||
char *nic_name;
|
||||
int sock = -1;
|
||||
int ret;
|
||||
int duplex;
|
||||
int speed;
|
||||
struct ifreq ifr;
|
||||
struct ifmediareq ifmed;
|
||||
|
||||
if (! PyArg_ParseTuple(args, "s", &nic_name))
|
||||
return NULL;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == -1)
|
||||
return PyErr_SetFromErrno(PyExc_OSError);
|
||||
strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
|
||||
|
||||
// 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;
|
||||
}
|
||||
else {
|
||||
speed = psutil_get_nic_speed(ifmed.ifm_active);
|
||||
if ((ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active)
|
||||
duplex = 2;
|
||||
else if ((ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active)
|
||||
duplex = 1;
|
||||
else
|
||||
duplex = 0;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
return Py_BuildValue("[ii]", duplex, speed);
|
||||
}
|
||||
#endif // net_if_stats() macOS/BSD implementation
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* define the psutil C module methods and initialize the module.
|
||||
*/
|
||||
static PyMethodDef mod_methods[] = {
|
||||
{"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"},
|
||||
{"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}
|
||||
};
|
||||
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define INITERR return NULL
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_psutil_posix",
|
||||
NULL,
|
||||
-1,
|
||||
mod_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyObject *PyInit__psutil_posix(void)
|
||||
#else /* PY_MAJOR_VERSION */
|
||||
#define INITERR return
|
||||
|
||||
void init_psutil_posix(void)
|
||||
#endif /* PY_MAJOR_VERSION */
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *mod = PyModule_Create(&moduledef);
|
||||
#else
|
||||
PyObject *mod = Py_InitModule("_psutil_posix", mod_methods);
|
||||
#endif
|
||||
if (mod == NULL)
|
||||
INITERR;
|
||||
|
||||
#if defined(PSUTIL_BSD) || \
|
||||
defined(PSUTIL_OSX) || \
|
||||
defined(PSUTIL_SUNOS) || \
|
||||
defined(PSUTIL_AIX)
|
||||
if (PyModule_AddIntConstant(mod, "AF_LINK", AF_LINK)) INITERR;
|
||||
#endif
|
||||
|
||||
if (mod == NULL)
|
||||
INITERR;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return mod;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,8 +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.
|
||||
*/
|
||||
|
||||
int psutil_pid_exists(pid_t pid);
|
||||
void psutil_raise_for_pid(pid_t pid, char *msg);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* 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 <sys/mbuf_macro.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 /* __64BIT__ */
|
||||
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 /* __64BIT__ */
|
|
@ -1,371 +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.
|
||||
*
|
||||
* 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.
|
||||
pid_t 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, _Py_PARSE_PID "OO", &pid,
|
||||
&py_af_filter, &py_type_filter))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
|
||||
PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
|
||||
goto error;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
freep = kinfo_getfile(pid, &cnt);
|
||||
if (freep == NULL) {
|
||||
psutil_raise_for_pid(pid, "kinfo_getfile()");
|
||||
goto error;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,9 +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>
|
||||
|
||||
PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,34 +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;
|
||||
|
||||
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);
|
||||
PyObject* psutil_sensors_cpu_temperature(PyObject* self, PyObject* args);
|
||||
PyObject* psutil_cpu_freq(PyObject* self, PyObject* args);
|
||||
#endif
|
|
@ -1,363 +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.
|
||||
*
|
||||
* 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(
|
||||
"iiiNNi" _Py_PARSE_PID,
|
||||
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_CLEAR(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;
|
||||
}
|
|
@ -1,10 +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>
|
||||
|
||||
PyObject* psutil_net_connections(PyObject* self, PyObject* args);
|
|
@ -1,447 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* 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 *);
|
|
@ -1,688 +0,0 @@
|
|||
/*
|
||||
* 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/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 "../../_psutil_common.h"
|
||||
#include "../../_psutil_posix.h"
|
||||
#include "specific.h"
|
||||
|
||||
|
||||
#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
|
||||
#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// 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("sysctl (size = 0)");
|
||||
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;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
psutil_proc_cwd(PyObject *self, PyObject *args) {
|
||||
long pid;
|
||||
|
||||
char path[MAXPATHLEN];
|
||||
size_t pathlen = sizeof path;
|
||||
|
||||
if (! PyArg_ParseTuple(args, "l", &pid))
|
||||
return NULL;
|
||||
|
||||
#ifdef KERN_PROC_CWD
|
||||
int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD};
|
||||
if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) {
|
||||
if (errno == ENOENT)
|
||||
NoSuchProcess("");
|
||||
else
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
char *buf;
|
||||
if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ssize_t len = readlink(buf, path, sizeof(path) - 1);
|
||||
free(buf);
|
||||
if (len == -1) {
|
||||
if (errno == ENOENT)
|
||||
NoSuchProcess("readlink (ENOENT)");
|
||||
else
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
path[len] = '\0';
|
||||
#endif
|
||||
|
||||
return PyUnicode_DecodeFSDefault(path);
|
||||
}
|
||||
|
||||
|
||||
// XXX: This is no longer used as per
|
||||
// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820
|
||||
// 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("psutil_pid_exists");
|
||||
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("sysctl (size = 0)");
|
||||
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("sysctl (size = 0)");
|
||||
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 1;
|
||||
}
|
||||
|
||||
result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt);
|
||||
if (result == NULL) {
|
||||
PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed");
|
||||
kvm_close(kd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*procCount = (size_t)cnt;
|
||||
|
||||
size_t mlen = cnt * sizeof(kinfo_proc);
|
||||
|
||||
if ((*procList = malloc(mlen)) == NULL) {
|
||||
PyErr_NoMemory();
|
||||
kvm_close(kd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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];
|
||||
int st;
|
||||
size_t len;
|
||||
char *procargs;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC_ARGS;
|
||||
mib[2] = pid;
|
||||
mib[3] = KERN_PROC_ARGV;
|
||||
len = 0;
|
||||
|
||||
st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0);
|
||||
if (st == -1) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
procargs = (char *)malloc(len);
|
||||
if (procargs == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
st = sysctl(mib, __arraycount(mib), procargs, &len, NULL, 0);
|
||||
if (st == -1) {
|
||||
free(procargs);
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*argsize = len;
|
||||
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
|
||||
);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
PyObject *psutil_proc_cwd(PyObject *self, PyObject *args);
|
|
@ -1,800 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
// ============================================================================
|
||||
|
||||
|
||||
static void
|
||||
convert_kvm_err(const char *syscall, char *errbuf) {
|
||||
char fullmsg[8192];
|
||||
|
||||
sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf);
|
||||
if (strstr(errbuf, "Permission denied") != NULL)
|
||||
AccessDenied(fullmsg);
|
||||
else if (strstr(errbuf, "Operation not permitted") != NULL)
|
||||
AccessDenied(fullmsg);
|
||||
else
|
||||
PyErr_Format(PyExc_RuntimeError, fullmsg);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
|
||||
// Fills a kinfo_proc struct based on process pid.
|
||||
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("sysctl (size = 0)");
|
||||
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_FILE;
|
||||
mib[2] = KERN_FILE_BYPID;
|
||||
mib[3] = 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) {
|
||||
convert_kvm_err("kvm_openfiles", errbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt);
|
||||
if (result == NULL) {
|
||||
PyErr_Format(PyExc_RuntimeError, "kvm_getprocs syscall failed");
|
||||
kvm_close(kd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*procCount = (size_t)cnt;
|
||||
|
||||
size_t mlen = cnt * sizeof(struct kinfo_proc);
|
||||
|
||||
if ((*procList = malloc(mlen)) == NULL) {
|
||||
PyErr_NoMemory();
|
||||
kvm_close(kd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(*procList, result, mlen);
|
||||
assert(*procList != NULL);
|
||||
kvm_close(kd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char **
|
||||
_psutil_get_argv(pid_t pid) {
|
||||
static char **argv;
|
||||
int argv_mib[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
|
||||
size_t argv_size = 128;
|
||||
// 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(pid_t 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.
|
||||
pid_t 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, _Py_PARSE_PID, &pid))
|
||||
goto error;
|
||||
|
||||
kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
|
||||
if (! kd) {
|
||||
convert_kvm_err("kvm_openfiles()", errbuf);
|
||||
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("kvm_getprocs");
|
||||
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(
|
||||
_Py_PARSE_PID "dd",
|
||||
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) {
|
||||
pid_t pid;
|
||||
int cnt;
|
||||
|
||||
struct kinfo_file *freep;
|
||||
struct kinfo_proc kipp;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
|
||||
return NULL;
|
||||
|
||||
if (psutil_kinfo_proc(pid, &kipp) == -1)
|
||||
return NULL;
|
||||
|
||||
freep = kinfo_getfile(pid, &cnt);
|
||||
if (freep == NULL)
|
||||
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
|
||||
pid_t pid;
|
||||
struct kinfo_proc kp;
|
||||
char path[MAXPATHLEN];
|
||||
size_t pathlen = sizeof path;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &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) {
|
||||
pid_t 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, _Py_PARSE_PID "OO", &pid, &py_af_filter,
|
||||
&py_type_filter))
|
||||
goto error;
|
||||
if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
|
||||
PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
|
||||
goto error;
|
||||
}
|
||||
|
||||
freep = kinfo_getfile(pid, &cnt);
|
||||
if (freep == NULL) {
|
||||
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
|
||||
);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* 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(pid_t pid, int* cnt);
|
||||
int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
|
||||
char **_psutil_get_argv(pid_t pid);
|
||||
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_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);
|
|
@ -1,382 +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_osx module methods.
|
||||
*/
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h> // for INT_MAX
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <libproc.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
#include "../../_psutil_posix.h"
|
||||
#include "process_info.h"
|
||||
|
||||
/*
|
||||
* Returns a list of all BSD processes on the system. This routine
|
||||
* 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) {
|
||||
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;
|
||||
|
||||
/*
|
||||
* We start by calling sysctl with ptr == NULL and size == 0.
|
||||
* That will succeed, and set size to the appropriate length.
|
||||
* We then allocate a buffer of at least that size and call
|
||||
* sysctl with that buffer. If that succeeds, we're done.
|
||||
* If that call fails with ENOMEM, we throw the buffer away
|
||||
* and try again.
|
||||
* Note that the loop calls sysctl with NULL again. This is
|
||||
* is necessary because the ENOMEM failure case sets size to
|
||||
* the amount of data returned, not the amount of data that
|
||||
* could have been returned.
|
||||
*/
|
||||
while (lim-- > 0) {
|
||||
size = 0;
|
||||
if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
|
||||
return 1;
|
||||
}
|
||||
size2 = size + (size >> 3); // add some
|
||||
if (size2 > size) {
|
||||
ptr = malloc(size2);
|
||||
if (ptr == NULL)
|
||||
ptr = malloc(size);
|
||||
else
|
||||
size = size2;
|
||||
}
|
||||
else {
|
||||
ptr = malloc(size);
|
||||
}
|
||||
if (ptr == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) {
|
||||
err = errno;
|
||||
free(ptr);
|
||||
if (err != ENOMEM) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*procList = (kinfo_proc *)ptr;
|
||||
*procCount = size / sizeof(kinfo_proc);
|
||||
if (procCount <= 0) {
|
||||
PyErr_Format(PyExc_RuntimeError, "no PIDs found");
|
||||
return 1;
|
||||
}
|
||||
return 0; // success
|
||||
}
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_RuntimeError, "couldn't collect PIDs list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Read the maximum argument size for processes
|
||||
int
|
||||
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;
|
||||
PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_ARGMAX)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Return 1 if pid refers to a zombie process else 0.
|
||||
int
|
||||
psutil_is_zombie(pid_t pid) {
|
||||
struct kinfo_proc kp;
|
||||
|
||||
if (psutil_get_kinfo_proc(pid, &kp) == -1)
|
||||
return 0;
|
||||
return (kp.kp_proc.p_stat == SZOMB) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// return process args as a python list
|
||||
PyObject *
|
||||
psutil_get_cmdline(pid_t pid) {
|
||||
int mib[3];
|
||||
int nargs;
|
||||
size_t len;
|
||||
char *procargs = NULL;
|
||||
char *arg_ptr;
|
||||
char *arg_end;
|
||||
char *curr_arg;
|
||||
size_t argmax;
|
||||
|
||||
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)
|
||||
goto error;
|
||||
|
||||
procargs = (char *)malloc(argmax);
|
||||
if (NULL == procargs) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read argument space
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
mib[2] = pid;
|
||||
if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
|
||||
// In case of zombie process we'll get EINVAL. We translate it
|
||||
// to NSP and _psosx.py will translate it to ZP.
|
||||
if ((errno == EINVAL) && (psutil_pid_exists(pid)))
|
||||
NoSuchProcess("sysctl");
|
||||
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);
|
||||
len = strlen(arg_ptr);
|
||||
arg_ptr += len + 1;
|
||||
|
||||
if (arg_ptr == arg_end) {
|
||||
free(procargs);
|
||||
return Py_BuildValue("[]");
|
||||
}
|
||||
|
||||
// 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;
|
||||
py_retlist = Py_BuildValue("[]");
|
||||
if (!py_retlist)
|
||||
goto error;
|
||||
while (arg_ptr < arg_end && nargs > 0) {
|
||||
if (*arg_ptr++ == '\0') {
|
||||
py_arg = PyUnicode_DecodeFSDefault(curr_arg);
|
||||
if (! py_arg)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_arg))
|
||||
goto error;
|
||||
Py_DECREF(py_arg);
|
||||
// iterate to next arg and decrement # of args
|
||||
curr_arg = arg_ptr;
|
||||
nargs--;
|
||||
}
|
||||
}
|
||||
|
||||
free(procargs);
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
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(pid_t 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)
|
||||
goto error;
|
||||
|
||||
procargs = (char *)malloc(argmax);
|
||||
if (NULL == procargs) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read argument space
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
mib[2] = pid;
|
||||
if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
|
||||
// In case of zombie process we'll get EINVAL. We translate it
|
||||
// to NSP and _psosx.py will translate it to ZP.
|
||||
if ((errno == EINVAL) && (psutil_pid_exists(pid)))
|
||||
NoSuchProcess("sysctl");
|
||||
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) {
|
||||
int mib[4];
|
||||
size_t len;
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = 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("sysctl (len == 0)");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A wrapper around proc_pidinfo().
|
||||
* Returns 0 on failure (and Python exception gets already set).
|
||||
*/
|
||||
int
|
||||
psutil_proc_pidinfo(pid_t pid, int flavor, uint64_t arg, void *pti, int size) {
|
||||
errno = 0;
|
||||
int ret = proc_pidinfo(pid, flavor, arg, pti, size);
|
||||
if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
|
||||
psutil_raise_for_pid(pid, "proc_pidinfo()");
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -1,18 +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;
|
||||
|
||||
int psutil_get_argmax(void);
|
||||
int psutil_is_zombie(pid_t pid);
|
||||
int psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp);
|
||||
int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
|
||||
int psutil_proc_pidinfo(
|
||||
pid_t pid, int flavor, uint64_t arg, void *pti, int size);
|
||||
PyObject* psutil_get_cmdline(pid_t pid);
|
||||
PyObject* psutil_get_environ(pid_t pid);
|
|
@ -1,404 +0,0 @@
|
|||
/*
|
||||
* 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 _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);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,125 +0,0 @@
|
|||
/* 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);
|
||||
freeifaddrs(*ifap);
|
||||
return (-1);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/* 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
|
|
@ -1,415 +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>
|
||||
#include <windows.h>
|
||||
#include <PowrProf.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of logical, active CPUs. Return 0 if undetermined.
|
||||
* See discussion at: https://bugs.python.org/issue33166#msg314631
|
||||
*/
|
||||
static unsigned int
|
||||
psutil_get_num_cpus(int fail_on_err) {
|
||||
unsigned int ncpus = 0;
|
||||
|
||||
// Minimum requirement: Windows 7
|
||||
if (GetActiveProcessorCount != NULL) {
|
||||
ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
if ((ncpus == 0) && (fail_on_err == 1)) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
psutil_debug("GetActiveProcessorCount() not available; "
|
||||
"using GetSystemInfo()");
|
||||
ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors;
|
||||
if ((ncpus <= 0) && (fail_on_err == 1)) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"GetSystemInfo() failed to retrieve CPU count");
|
||||
}
|
||||
}
|
||||
return ncpus;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Retrieves system CPU timing information as a (user, system, idle)
|
||||
* tuple. On a multiprocessor system, the values returned are the
|
||||
* sum of the designated times across all processors.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_cpu_times(PyObject *self, PyObject *args) {
|
||||
double idle, kernel, user, system;
|
||||
FILETIME idle_time, kernel_time, user_time;
|
||||
|
||||
if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idle = (double)((HI_T * idle_time.dwHighDateTime) + \
|
||||
(LO_T * idle_time.dwLowDateTime));
|
||||
user = (double)((HI_T * user_time.dwHighDateTime) + \
|
||||
(LO_T * user_time.dwLowDateTime));
|
||||
kernel = (double)((HI_T * kernel_time.dwHighDateTime) + \
|
||||
(LO_T * kernel_time.dwLowDateTime));
|
||||
|
||||
// Kernel time includes idle time.
|
||||
// We return only busy kernel time subtracting idle time from
|
||||
// kernel time.
|
||||
system = (kernel - idle);
|
||||
return Py_BuildValue("(ddd)", user, system, idle);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Same as above but for all system CPUs.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_per_cpu_times(PyObject *self, PyObject *args) {
|
||||
double idle, kernel, systemt, user, interrupt, dpc;
|
||||
NTSTATUS status;
|
||||
_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
|
||||
UINT i;
|
||||
unsigned int ncpus;
|
||||
PyObject *py_tuple = NULL;
|
||||
PyObject *py_retlist = PyList_New(0);
|
||||
|
||||
if (py_retlist == NULL)
|
||||
return NULL;
|
||||
|
||||
// retrieves number of processors
|
||||
ncpus = psutil_get_num_cpus(1);
|
||||
if (ncpus == 0)
|
||||
goto error;
|
||||
|
||||
// allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
|
||||
// structures, one per processor
|
||||
sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
|
||||
malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
|
||||
if (sppi == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
// gets cpu time informations
|
||||
status = NtQuerySystemInformation(
|
||||
SystemProcessorPerformanceInformation,
|
||||
sppi,
|
||||
ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
|
||||
NULL);
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status,
|
||||
"NtQuerySystemInformation(SystemProcessorPerformanceInformation)"
|
||||
);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// computes system global times summing each
|
||||
// processor value
|
||||
idle = user = kernel = interrupt = dpc = 0;
|
||||
for (i = 0; i < ncpus; i++) {
|
||||
py_tuple = NULL;
|
||||
user = (double)((HI_T * sppi[i].UserTime.HighPart) +
|
||||
(LO_T * sppi[i].UserTime.LowPart));
|
||||
idle = (double)((HI_T * sppi[i].IdleTime.HighPart) +
|
||||
(LO_T * sppi[i].IdleTime.LowPart));
|
||||
kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) +
|
||||
(LO_T * sppi[i].KernelTime.LowPart));
|
||||
interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) +
|
||||
(LO_T * sppi[i].InterruptTime.LowPart));
|
||||
dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) +
|
||||
(LO_T * sppi[i].DpcTime.LowPart));
|
||||
|
||||
// kernel time includes idle time on windows
|
||||
// we return only busy kernel time subtracting
|
||||
// idle time from kernel time
|
||||
systemt = kernel - idle;
|
||||
py_tuple = Py_BuildValue(
|
||||
"(ddddd)",
|
||||
user,
|
||||
systemt,
|
||||
idle,
|
||||
interrupt,
|
||||
dpc
|
||||
);
|
||||
if (!py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
}
|
||||
|
||||
free(sppi);
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_tuple);
|
||||
Py_DECREF(py_retlist);
|
||||
if (sppi)
|
||||
free(sppi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of active, logical CPUs.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_cpu_count_logical(PyObject *self, PyObject *args) {
|
||||
unsigned int ncpus;
|
||||
|
||||
ncpus = psutil_get_num_cpus(0);
|
||||
if (ncpus != 0)
|
||||
return Py_BuildValue("I", ncpus);
|
||||
else
|
||||
Py_RETURN_NONE; // mimick os.cpu_count()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of physical CPU cores (hyper-thread CPUs count
|
||||
* is excluded).
|
||||
*/
|
||||
PyObject *
|
||||
psutil_cpu_count_phys(PyObject *self, PyObject *args) {
|
||||
DWORD rc;
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
|
||||
DWORD length = 0;
|
||||
DWORD offset = 0;
|
||||
DWORD ncpus = 0;
|
||||
DWORD prev_processor_info_size = 0;
|
||||
|
||||
// GetLogicalProcessorInformationEx() is available from Windows 7
|
||||
// onward. Differently from GetLogicalProcessorInformation()
|
||||
// it supports process groups, meaning this is able to report more
|
||||
// than 64 CPUs. See:
|
||||
// https://bugs.python.org/issue33166
|
||||
if (GetLogicalProcessorInformationEx == NULL) {
|
||||
psutil_debug("Win < 7; cpu_count_phys() forced to None");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
rc = GetLogicalProcessorInformationEx(
|
||||
RelationAll, buffer, &length);
|
||||
if (rc == FALSE) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (buffer) {
|
||||
free(buffer);
|
||||
}
|
||||
buffer = \
|
||||
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length);
|
||||
if (NULL == buffer) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
psutil_debug("GetLogicalProcessorInformationEx() returned ",
|
||||
GetLastError());
|
||||
goto return_none;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr = buffer;
|
||||
while (offset < length) {
|
||||
// Advance ptr by the size of the previous
|
||||
// SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.
|
||||
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) \
|
||||
(((char*)ptr) + prev_processor_info_size);
|
||||
|
||||
if (ptr->Relationship == RelationProcessorCore) {
|
||||
ncpus += 1;
|
||||
}
|
||||
|
||||
// When offset == length, we've reached the last processor
|
||||
// info struct in the buffer.
|
||||
offset += ptr->Size;
|
||||
prev_processor_info_size = ptr->Size;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
if (ncpus != 0) {
|
||||
return Py_BuildValue("I", ncpus);
|
||||
}
|
||||
else {
|
||||
psutil_debug("GetLogicalProcessorInformationEx() count was 0");
|
||||
Py_RETURN_NONE; // mimick os.cpu_count()
|
||||
}
|
||||
|
||||
return_none:
|
||||
if (buffer != NULL)
|
||||
free(buffer);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return CPU statistics.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_cpu_stats(PyObject *self, PyObject *args) {
|
||||
NTSTATUS status;
|
||||
_SYSTEM_PERFORMANCE_INFORMATION *spi = NULL;
|
||||
_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
|
||||
_SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL;
|
||||
unsigned int ncpus;
|
||||
UINT i;
|
||||
ULONG64 dpcs = 0;
|
||||
ULONG interrupts = 0;
|
||||
|
||||
// retrieves number of processors
|
||||
ncpus = psutil_get_num_cpus(1);
|
||||
if (ncpus == 0)
|
||||
goto error;
|
||||
|
||||
// get syscalls / ctx switches
|
||||
spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \
|
||||
malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION));
|
||||
if (spi == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
status = NtQuerySystemInformation(
|
||||
SystemPerformanceInformation,
|
||||
spi,
|
||||
ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION),
|
||||
NULL);
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQuerySystemInformation(SystemPerformanceInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// get DPCs
|
||||
InterruptInformation = \
|
||||
malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus);
|
||||
if (InterruptInformation == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = NtQuerySystemInformation(
|
||||
SystemInterruptInformation,
|
||||
InterruptInformation,
|
||||
ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION),
|
||||
NULL);
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQuerySystemInformation(SystemInterruptInformation)");
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i < ncpus; i++) {
|
||||
dpcs += InterruptInformation[i].DpcCount;
|
||||
}
|
||||
|
||||
// get interrupts
|
||||
sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
|
||||
malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
|
||||
if (sppi == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = NtQuerySystemInformation(
|
||||
SystemProcessorPerformanceInformation,
|
||||
sppi,
|
||||
ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
|
||||
NULL);
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status,
|
||||
"NtQuerySystemInformation(SystemProcessorPerformanceInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < ncpus; i++) {
|
||||
interrupts += sppi[i].InterruptCount;
|
||||
}
|
||||
|
||||
// done
|
||||
free(spi);
|
||||
free(InterruptInformation);
|
||||
free(sppi);
|
||||
return Py_BuildValue(
|
||||
"kkkk",
|
||||
spi->ContextSwitches,
|
||||
interrupts,
|
||||
(unsigned long)dpcs,
|
||||
spi->SystemCalls
|
||||
);
|
||||
|
||||
error:
|
||||
if (spi)
|
||||
free(spi);
|
||||
if (InterruptInformation)
|
||||
free(InterruptInformation);
|
||||
if (sppi)
|
||||
free(sppi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return CPU frequency.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_cpu_freq(PyObject *self, PyObject *args) {
|
||||
PROCESSOR_POWER_INFORMATION *ppi;
|
||||
NTSTATUS ret;
|
||||
ULONG size;
|
||||
LPBYTE pBuffer = NULL;
|
||||
ULONG current;
|
||||
ULONG max;
|
||||
unsigned int ncpus;
|
||||
|
||||
// Get the number of CPUs.
|
||||
ncpus = psutil_get_num_cpus(1);
|
||||
if (ncpus == 0)
|
||||
return NULL;
|
||||
|
||||
// Allocate size.
|
||||
size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION);
|
||||
pBuffer = (BYTE*)LocalAlloc(LPTR, size);
|
||||
if (! pBuffer) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Syscall.
|
||||
ret = CallNtPowerInformation(
|
||||
ProcessorInformation, NULL, 0, pBuffer, size);
|
||||
if (ret != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"CallNtPowerInformation syscall failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Results.
|
||||
ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer;
|
||||
max = ppi->MaxMhz;
|
||||
current = ppi->CurrentMhz;
|
||||
LocalFree(pBuffer);
|
||||
|
||||
return Py_BuildValue("kk", current, max);
|
||||
|
||||
error:
|
||||
if (pBuffer != NULL)
|
||||
LocalFree(pBuffer);
|
||||
return NULL;
|
||||
}
|
|
@ -1,14 +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>
|
||||
|
||||
PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_cpu_count_phys(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
|
|
@ -1,373 +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>
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
|
||||
|
||||
#ifndef _ARRAYSIZE
|
||||
#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
static char *psutil_get_drive_type(int type) {
|
||||
switch (type) {
|
||||
case DRIVE_FIXED:
|
||||
return "fixed";
|
||||
case DRIVE_CDROM:
|
||||
return "cdrom";
|
||||
case DRIVE_REMOVABLE:
|
||||
return "removable";
|
||||
case DRIVE_UNKNOWN:
|
||||
return "unknown";
|
||||
case DRIVE_NO_ROOT_DIR:
|
||||
return "unmounted";
|
||||
case DRIVE_REMOTE:
|
||||
return "remote";
|
||||
case DRIVE_RAMDISK:
|
||||
return "ramdisk";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return path's disk total and free as a Python tuple.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_disk_usage(PyObject *self, PyObject *args) {
|
||||
BOOL retval;
|
||||
ULARGE_INTEGER _, total, free;
|
||||
char *path;
|
||||
|
||||
if (PyArg_ParseTuple(args, "u", &path)) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free);
|
||||
Py_END_ALLOW_THREADS
|
||||
goto return_;
|
||||
}
|
||||
|
||||
// on Python 2 we also want to accept plain strings other
|
||||
// than Unicode
|
||||
#if PY_MAJOR_VERSION <= 2
|
||||
PyErr_Clear(); // drop the argument parsing error
|
||||
if (PyArg_ParseTuple(args, "s", &path)) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
retval = GetDiskFreeSpaceEx(path, &_, &total, &free);
|
||||
Py_END_ALLOW_THREADS
|
||||
goto return_;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
|
||||
return_:
|
||||
if (retval == 0)
|
||||
return PyErr_SetFromWindowsErrWithFilename(0, path);
|
||||
else
|
||||
return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a Python dict of tuples for disk I/O information. This may
|
||||
* require running "diskperf -y" command first.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_disk_io_counters(PyObject *self, PyObject *args) {
|
||||
DISK_PERFORMANCE diskPerformance;
|
||||
DWORD dwSize;
|
||||
HANDLE hDevice = NULL;
|
||||
char szDevice[MAX_PATH];
|
||||
char szDeviceDisplay[MAX_PATH];
|
||||
int devNum;
|
||||
int i;
|
||||
DWORD ioctrlSize;
|
||||
BOOL ret;
|
||||
PyObject *py_retdict = PyDict_New();
|
||||
PyObject *py_tuple = NULL;
|
||||
|
||||
if (py_retdict == NULL)
|
||||
return NULL;
|
||||
// Apparently there's no way to figure out how many times we have
|
||||
// to iterate in order to find valid drives.
|
||||
// Let's assume 32, which is higher than 26, the number of letters
|
||||
// in the alphabet (from A:\ to Z:\).
|
||||
for (devNum = 0; devNum <= 32; ++devNum) {
|
||||
py_tuple = NULL;
|
||||
sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum);
|
||||
hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (hDevice == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
// DeviceIoControl() sucks!
|
||||
i = 0;
|
||||
ioctrlSize = sizeof(diskPerformance);
|
||||
while (1) {
|
||||
i += 1;
|
||||
ret = DeviceIoControl(
|
||||
hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance,
|
||||
ioctrlSize, &dwSize, NULL);
|
||||
if (ret != 0)
|
||||
break; // OK!
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// Retry with a bigger buffer (+ limit for retries).
|
||||
if (i <= 1024) {
|
||||
ioctrlSize *= 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (GetLastError() == ERROR_INVALID_FUNCTION) {
|
||||
// This happens on AppVeyor:
|
||||
// https://ci.appveyor.com/project/giampaolo/psutil/build/
|
||||
// 1364/job/ascpdi271b06jle3
|
||||
// Assume it means we're dealing with some exotic disk
|
||||
// and go on.
|
||||
psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; "
|
||||
"ignore PhysicalDrive%i", devNum);
|
||||
goto next;
|
||||
}
|
||||
else if (GetLastError() == ERROR_NOT_SUPPORTED) {
|
||||
// Again, let's assume we're dealing with some exotic disk.
|
||||
psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; "
|
||||
"ignore PhysicalDrive%i", devNum);
|
||||
goto next;
|
||||
}
|
||||
// XXX: it seems we should also catch ERROR_INVALID_PARAMETER:
|
||||
// https://sites.ualberta.ca/dept/aict/uts/software/openbsd/
|
||||
// ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/
|
||||
// openafs-1.4.14/src/usd/usd_nt.c
|
||||
|
||||
// XXX: we can also bump into ERROR_MORE_DATA in which case
|
||||
// (quoting doc) we're supposed to retry with a bigger buffer
|
||||
// and specify a new "starting point", whatever it means.
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum);
|
||||
py_tuple = Py_BuildValue(
|
||||
"(IILLKK)",
|
||||
diskPerformance.ReadCount,
|
||||
diskPerformance.WriteCount,
|
||||
diskPerformance.BytesRead,
|
||||
diskPerformance.BytesWritten,
|
||||
// convert to ms:
|
||||
// https://github.com/giampaolo/psutil/issues/1012
|
||||
(unsigned long long)
|
||||
(diskPerformance.ReadTime.QuadPart) / 10000000,
|
||||
(unsigned long long)
|
||||
(diskPerformance.WriteTime.QuadPart) / 10000000);
|
||||
if (!py_tuple)
|
||||
goto error;
|
||||
if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
|
||||
next:
|
||||
CloseHandle(hDevice);
|
||||
}
|
||||
|
||||
return py_retdict;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_tuple);
|
||||
Py_DECREF(py_retdict);
|
||||
if (hDevice != NULL)
|
||||
CloseHandle(hDevice);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return disk partitions as a list of tuples such as
|
||||
* (drive_letter, drive_letter, type, "")
|
||||
*/
|
||||
PyObject *
|
||||
psutil_disk_partitions(PyObject *self, PyObject *args) {
|
||||
DWORD num_bytes;
|
||||
char drive_strings[255];
|
||||
char *drive_letter = drive_strings;
|
||||
char mp_buf[MAX_PATH];
|
||||
char mp_path[MAX_PATH];
|
||||
int all;
|
||||
int type;
|
||||
int ret;
|
||||
unsigned int old_mode = 0;
|
||||
char opts[20];
|
||||
HANDLE mp_h;
|
||||
BOOL mp_flag= TRUE;
|
||||
LPTSTR fs_type[MAX_PATH + 1] = { 0 };
|
||||
DWORD pflags = 0;
|
||||
PyObject *py_all;
|
||||
PyObject *py_retlist = PyList_New(0);
|
||||
PyObject *py_tuple = NULL;
|
||||
|
||||
if (py_retlist == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// avoid to visualize a message box in case something goes wrong
|
||||
// see https://github.com/giampaolo/psutil/issues/264
|
||||
old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
|
||||
if (! PyArg_ParseTuple(args, "O", &py_all))
|
||||
goto error;
|
||||
all = PyObject_IsTrue(py_all);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
num_bytes = GetLogicalDriveStrings(254, drive_letter);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (num_bytes == 0) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (*drive_letter != 0) {
|
||||
py_tuple = NULL;
|
||||
opts[0] = 0;
|
||||
fs_type[0] = 0;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
type = GetDriveType(drive_letter);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
// by default we only show hard drives and cd-roms
|
||||
if (all == 0) {
|
||||
if ((type == DRIVE_UNKNOWN) ||
|
||||
(type == DRIVE_NO_ROOT_DIR) ||
|
||||
(type == DRIVE_REMOTE) ||
|
||||
(type == DRIVE_RAMDISK)) {
|
||||
goto next;
|
||||
}
|
||||
// floppy disk: skip it by default as it introduces a
|
||||
// considerable slowdown.
|
||||
if ((type == DRIVE_REMOVABLE) &&
|
||||
(strcmp(drive_letter, "A:\\") == 0)) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
ret = GetVolumeInformation(
|
||||
(LPCTSTR)drive_letter, NULL, _ARRAYSIZE(drive_letter),
|
||||
NULL, NULL, &pflags, (LPTSTR)fs_type, _ARRAYSIZE(fs_type));
|
||||
if (ret == 0) {
|
||||
// We might get here in case of a floppy hard drive, in
|
||||
// which case the error is (21, "device not ready").
|
||||
// Let's pretend it didn't happen as we already have
|
||||
// the drive name and type ('removable').
|
||||
strcat_s(opts, _countof(opts), "");
|
||||
SetLastError(0);
|
||||
}
|
||||
else {
|
||||
if (pflags & FILE_READ_ONLY_VOLUME)
|
||||
strcat_s(opts, _countof(opts), "ro");
|
||||
else
|
||||
strcat_s(opts, _countof(opts), "rw");
|
||||
if (pflags & FILE_VOLUME_IS_COMPRESSED)
|
||||
strcat_s(opts, _countof(opts), ",compressed");
|
||||
|
||||
// Check for mount points on this volume and add/get info
|
||||
// (checks first to know if we can even have mount points)
|
||||
if (pflags & FILE_SUPPORTS_REPARSE_POINTS) {
|
||||
mp_h = FindFirstVolumeMountPoint(
|
||||
drive_letter, mp_buf, MAX_PATH);
|
||||
if (mp_h != INVALID_HANDLE_VALUE) {
|
||||
while (mp_flag) {
|
||||
|
||||
// Append full mount path with drive letter
|
||||
strcpy_s(mp_path, _countof(mp_path), drive_letter);
|
||||
strcat_s(mp_path, _countof(mp_path), mp_buf);
|
||||
|
||||
py_tuple = Py_BuildValue(
|
||||
"(ssss)",
|
||||
drive_letter,
|
||||
mp_path,
|
||||
fs_type, // Typically NTFS
|
||||
opts);
|
||||
|
||||
if (!py_tuple ||
|
||||
PyList_Append(py_retlist, py_tuple) == -1) {
|
||||
FindVolumeMountPointClose(mp_h);
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_CLEAR(py_tuple);
|
||||
|
||||
// Continue looking for more mount points
|
||||
mp_flag = FindNextVolumeMountPoint(
|
||||
mp_h, mp_buf, MAX_PATH);
|
||||
}
|
||||
FindVolumeMountPointClose(mp_h);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(opts) > 0)
|
||||
strcat_s(opts, _countof(opts), ",");
|
||||
strcat_s(opts, _countof(opts), psutil_get_drive_type(type));
|
||||
|
||||
py_tuple = Py_BuildValue(
|
||||
"(ssss)",
|
||||
drive_letter,
|
||||
drive_letter,
|
||||
fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS
|
||||
opts);
|
||||
if (!py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
goto next;
|
||||
|
||||
next:
|
||||
drive_letter = strchr(drive_letter, 0) + 1;
|
||||
}
|
||||
|
||||
SetErrorMode(old_mode);
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
SetErrorMode(old_mode);
|
||||
Py_XDECREF(py_tuple);
|
||||
Py_DECREF(py_retlist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Accept a filename's drive in native format like "\Device\HarddiskVolume1\"
|
||||
and return the corresponding drive letter (e.g. "C:\\").
|
||||
If no match is found return an empty string.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) {
|
||||
LPCTSTR lpDevicePath;
|
||||
TCHAR d = TEXT('A');
|
||||
TCHAR szBuff[5];
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &lpDevicePath))
|
||||
return NULL;
|
||||
|
||||
while (d <= TEXT('Z')) {
|
||||
TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')};
|
||||
TCHAR szTarget[512] = {0};
|
||||
if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) {
|
||||
if (_tcscmp(lpDevicePath, szTarget) == 0) {
|
||||
_stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d);
|
||||
return Py_BuildValue("s", szBuff);
|
||||
}
|
||||
}
|
||||
d++;
|
||||
}
|
||||
return Py_BuildValue("s", "");
|
||||
}
|
|
@ -1,12 +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>
|
||||
|
||||
PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_disk_usage(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args);
|
|
@ -1,443 +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.
|
||||
*/
|
||||
|
||||
// Fixes clash between winsock2.h and windows.h
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <Python.h>
|
||||
#include <windows.h>
|
||||
#include <wchar.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
|
||||
|
||||
static PIP_ADAPTER_ADDRESSES
|
||||
psutil_get_nic_addresses() {
|
||||
// allocate a 15 KB buffer to start with
|
||||
int outBufLen = 15000;
|
||||
DWORD dwRetVal = 0;
|
||||
ULONG attempts = 0;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
||||
|
||||
do {
|
||||
pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
|
||||
if (pAddresses == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dwRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses,
|
||||
&outBufLen);
|
||||
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
|
||||
free(pAddresses);
|
||||
pAddresses = NULL;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3));
|
||||
|
||||
if (dwRetVal != NO_ERROR) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError, "GetAdaptersAddresses() syscall failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pAddresses;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a Python list of named tuples with overall network I/O information
|
||||
*/
|
||||
PyObject *
|
||||
psutil_net_io_counters(PyObject *self, PyObject *args) {
|
||||
DWORD dwRetVal = 0;
|
||||
MIB_IF_ROW2 *pIfRow = NULL;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
|
||||
PyObject *py_retdict = PyDict_New();
|
||||
PyObject *py_nic_info = NULL;
|
||||
PyObject *py_nic_name = NULL;
|
||||
|
||||
if (py_retdict == NULL)
|
||||
return NULL;
|
||||
pAddresses = psutil_get_nic_addresses();
|
||||
if (pAddresses == NULL)
|
||||
goto error;
|
||||
pCurrAddresses = pAddresses;
|
||||
|
||||
while (pCurrAddresses) {
|
||||
py_nic_name = NULL;
|
||||
py_nic_info = NULL;
|
||||
|
||||
pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2));
|
||||
if (pIfRow == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2));
|
||||
pIfRow->InterfaceIndex = pCurrAddresses->IfIndex;
|
||||
dwRetVal = GetIfEntry2(pIfRow);
|
||||
if (dwRetVal != NO_ERROR) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"GetIfEntry() or GetIfEntry2() syscalls failed.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
py_nic_info = Py_BuildValue(
|
||||
"(KKKKKKKK)",
|
||||
pIfRow->OutOctets,
|
||||
pIfRow->InOctets,
|
||||
(pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts),
|
||||
(pIfRow->InUcastPkts + pIfRow->InNUcastPkts),
|
||||
pIfRow->InErrors,
|
||||
pIfRow->OutErrors,
|
||||
pIfRow->InDiscards,
|
||||
pIfRow->OutDiscards);
|
||||
if (!py_nic_info)
|
||||
goto error;
|
||||
|
||||
py_nic_name = PyUnicode_FromWideChar(
|
||||
pCurrAddresses->FriendlyName,
|
||||
wcslen(pCurrAddresses->FriendlyName));
|
||||
|
||||
if (py_nic_name == NULL)
|
||||
goto error;
|
||||
if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info))
|
||||
goto error;
|
||||
Py_CLEAR(py_nic_name);
|
||||
Py_CLEAR(py_nic_info);
|
||||
|
||||
free(pIfRow);
|
||||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
|
||||
free(pAddresses);
|
||||
return py_retdict;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_nic_name);
|
||||
Py_XDECREF(py_nic_info);
|
||||
Py_DECREF(py_retdict);
|
||||
if (pAddresses != NULL)
|
||||
free(pAddresses);
|
||||
if (pIfRow != NULL)
|
||||
free(pIfRow);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return NICs addresses.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_net_if_addrs(PyObject *self, PyObject *args) {
|
||||
unsigned int i = 0;
|
||||
ULONG family;
|
||||
PCTSTR intRet;
|
||||
PCTSTR netmaskIntRet;
|
||||
char *ptr;
|
||||
char buff_addr[1024];
|
||||
char buff_macaddr[1024];
|
||||
char buff_netmask[1024];
|
||||
DWORD dwRetVal = 0;
|
||||
ULONG converted_netmask;
|
||||
UINT netmask_bits;
|
||||
struct in_addr in_netmask;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
|
||||
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
|
||||
|
||||
PyObject *py_retlist = PyList_New(0);
|
||||
PyObject *py_tuple = NULL;
|
||||
PyObject *py_address = NULL;
|
||||
PyObject *py_mac_address = NULL;
|
||||
PyObject *py_nic_name = NULL;
|
||||
PyObject *py_netmask = NULL;
|
||||
|
||||
if (py_retlist == NULL)
|
||||
return NULL;
|
||||
|
||||
pAddresses = psutil_get_nic_addresses();
|
||||
if (pAddresses == NULL)
|
||||
goto error;
|
||||
pCurrAddresses = pAddresses;
|
||||
|
||||
while (pCurrAddresses) {
|
||||
pUnicast = pCurrAddresses->FirstUnicastAddress;
|
||||
|
||||
netmaskIntRet = NULL;
|
||||
py_nic_name = NULL;
|
||||
py_nic_name = PyUnicode_FromWideChar(
|
||||
pCurrAddresses->FriendlyName,
|
||||
wcslen(pCurrAddresses->FriendlyName));
|
||||
if (py_nic_name == NULL)
|
||||
goto error;
|
||||
|
||||
// MAC address
|
||||
if (pCurrAddresses->PhysicalAddressLength != 0) {
|
||||
ptr = buff_macaddr;
|
||||
*ptr = '\0';
|
||||
for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) {
|
||||
if (i == (pCurrAddresses->PhysicalAddressLength - 1)) {
|
||||
sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n",
|
||||
(int)pCurrAddresses->PhysicalAddress[i]);
|
||||
}
|
||||
else {
|
||||
sprintf_s(ptr, _countof(buff_macaddr), "%.2X-",
|
||||
(int)pCurrAddresses->PhysicalAddress[i]);
|
||||
}
|
||||
ptr += 3;
|
||||
}
|
||||
*--ptr = '\0';
|
||||
|
||||
py_mac_address = Py_BuildValue("s", buff_macaddr);
|
||||
if (py_mac_address == NULL)
|
||||
goto error;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
Py_INCREF(Py_None);
|
||||
Py_INCREF(Py_None);
|
||||
py_tuple = Py_BuildValue(
|
||||
"(OiOOOO)",
|
||||
py_nic_name,
|
||||
-1, // this will be converted later to AF_LINK
|
||||
py_mac_address,
|
||||
Py_None, // netmask (not supported)
|
||||
Py_None, // broadcast (not supported)
|
||||
Py_None // ptp (not supported on Windows)
|
||||
);
|
||||
if (! py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
Py_CLEAR(py_mac_address);
|
||||
}
|
||||
|
||||
// find out the IP address associated with the NIC
|
||||
if (pUnicast != NULL) {
|
||||
for (i = 0; pUnicast != NULL; i++) {
|
||||
family = pUnicast->Address.lpSockaddr->sa_family;
|
||||
if (family == AF_INET) {
|
||||
struct sockaddr_in *sa_in = (struct sockaddr_in *)
|
||||
pUnicast->Address.lpSockaddr;
|
||||
intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff_addr,
|
||||
sizeof(buff_addr));
|
||||
if (!intRet)
|
||||
goto error;
|
||||
netmask_bits = pUnicast->OnLinkPrefixLength;
|
||||
dwRetVal = ConvertLengthToIpv4Mask(
|
||||
netmask_bits, &converted_netmask);
|
||||
if (dwRetVal == NO_ERROR) {
|
||||
in_netmask.s_addr = converted_netmask;
|
||||
netmaskIntRet = inet_ntop(
|
||||
AF_INET, &in_netmask, buff_netmask,
|
||||
sizeof(buff_netmask));
|
||||
if (!netmaskIntRet)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (family == AF_INET6) {
|
||||
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
|
||||
pUnicast->Address.lpSockaddr;
|
||||
intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr),
|
||||
buff_addr, sizeof(buff_addr));
|
||||
if (!intRet)
|
||||
goto error;
|
||||
}
|
||||
else {
|
||||
// we should never get here
|
||||
pUnicast = pUnicast->Next;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
py_address = PyUnicode_FromString(buff_addr);
|
||||
#else
|
||||
py_address = PyString_FromString(buff_addr);
|
||||
#endif
|
||||
if (py_address == NULL)
|
||||
goto error;
|
||||
|
||||
if (netmaskIntRet != NULL) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
py_netmask = PyUnicode_FromString(buff_netmask);
|
||||
#else
|
||||
py_netmask = PyString_FromString(buff_netmask);
|
||||
#endif
|
||||
} else {
|
||||
Py_INCREF(Py_None);
|
||||
py_netmask = Py_None;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
Py_INCREF(Py_None);
|
||||
py_tuple = Py_BuildValue(
|
||||
"(OiOOOO)",
|
||||
py_nic_name,
|
||||
family,
|
||||
py_address,
|
||||
py_netmask,
|
||||
Py_None, // broadcast (not supported)
|
||||
Py_None // ptp (not supported on Windows)
|
||||
);
|
||||
|
||||
if (! py_tuple)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_tuple))
|
||||
goto error;
|
||||
Py_CLEAR(py_tuple);
|
||||
Py_CLEAR(py_address);
|
||||
Py_CLEAR(py_netmask);
|
||||
|
||||
pUnicast = pUnicast->Next;
|
||||
}
|
||||
}
|
||||
Py_CLEAR(py_nic_name);
|
||||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
|
||||
free(pAddresses);
|
||||
return py_retlist;
|
||||
|
||||
error:
|
||||
if (pAddresses)
|
||||
free(pAddresses);
|
||||
Py_DECREF(py_retlist);
|
||||
Py_XDECREF(py_tuple);
|
||||
Py_XDECREF(py_address);
|
||||
Py_XDECREF(py_nic_name);
|
||||
Py_XDECREF(py_netmask);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Provides stats about NIC interfaces installed on the system.
|
||||
* TODO: get 'duplex' (currently it's hard coded to '2', aka
|
||||
'full duplex')
|
||||
*/
|
||||
PyObject *
|
||||
psutil_net_if_stats(PyObject *self, PyObject *args) {
|
||||
int i;
|
||||
DWORD dwSize = 0;
|
||||
DWORD dwRetVal = 0;
|
||||
MIB_IFTABLE *pIfTable;
|
||||
MIB_IFROW *pIfRow;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
|
||||
char descr[MAX_PATH];
|
||||
int ifname_found;
|
||||
|
||||
PyObject *py_nic_name = NULL;
|
||||
PyObject *py_retdict = PyDict_New();
|
||||
PyObject *py_ifc_info = NULL;
|
||||
PyObject *py_is_up = NULL;
|
||||
|
||||
if (py_retdict == NULL)
|
||||
return NULL;
|
||||
|
||||
pAddresses = psutil_get_nic_addresses();
|
||||
if (pAddresses == NULL)
|
||||
goto error;
|
||||
|
||||
pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE));
|
||||
if (pIfTable == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
dwSize = sizeof(MIB_IFTABLE);
|
||||
if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) {
|
||||
free(pIfTable);
|
||||
pIfTable = (MIB_IFTABLE *) malloc(dwSize);
|
||||
if (pIfTable == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
// Make a second call to GetIfTable to get the actual
|
||||
// data we want.
|
||||
if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "GetIfTable() syscall failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < (int) pIfTable->dwNumEntries; i++) {
|
||||
pIfRow = (MIB_IFROW *) & pIfTable->table[i];
|
||||
|
||||
// GetIfTable is not able to give us NIC with "friendly names"
|
||||
// so we determine them via GetAdapterAddresses() which
|
||||
// provides friendly names *and* descriptions and find the
|
||||
// ones that match.
|
||||
ifname_found = 0;
|
||||
pCurrAddresses = pAddresses;
|
||||
while (pCurrAddresses) {
|
||||
sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description);
|
||||
if (lstrcmp(descr, pIfRow->bDescr) == 0) {
|
||||
py_nic_name = PyUnicode_FromWideChar(
|
||||
pCurrAddresses->FriendlyName,
|
||||
wcslen(pCurrAddresses->FriendlyName));
|
||||
if (py_nic_name == NULL)
|
||||
goto error;
|
||||
ifname_found = 1;
|
||||
break;
|
||||
}
|
||||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
if (ifname_found == 0) {
|
||||
// Name not found means GetAdapterAddresses() doesn't list
|
||||
// this NIC, only GetIfTable, meaning it's not really a NIC
|
||||
// interface so we skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// is up?
|
||||
if ((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED ||
|
||||
pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) &&
|
||||
pIfRow->dwAdminStatus == 1 ) {
|
||||
py_is_up = Py_True;
|
||||
}
|
||||
else {
|
||||
py_is_up = Py_False;
|
||||
}
|
||||
Py_INCREF(py_is_up);
|
||||
|
||||
py_ifc_info = Py_BuildValue(
|
||||
"(Oikk)",
|
||||
py_is_up,
|
||||
2, // there's no way to know duplex so let's assume 'full'
|
||||
pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb
|
||||
pIfRow->dwMtu
|
||||
);
|
||||
if (!py_ifc_info)
|
||||
goto error;
|
||||
if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info))
|
||||
goto error;
|
||||
Py_CLEAR(py_nic_name);
|
||||
Py_CLEAR(py_ifc_info);
|
||||
}
|
||||
|
||||
free(pIfTable);
|
||||
free(pAddresses);
|
||||
return py_retdict;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_is_up);
|
||||
Py_XDECREF(py_ifc_info);
|
||||
Py_XDECREF(py_nic_name);
|
||||
Py_DECREF(py_retdict);
|
||||
if (pIfTable != NULL)
|
||||
free(pIfTable);
|
||||
if (pAddresses != NULL)
|
||||
free(pAddresses);
|
||||
return NULL;
|
||||
}
|
|
@ -1,11 +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>
|
||||
|
||||
PyObject *psutil_net_if_addrs(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_net_if_stats(PyObject *self, PyObject *args);
|
||||
PyObject *psutil_net_io_counters(PyObject *self, PyObject *args);
|
|
@ -1,603 +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.
|
||||
* Define Windows structs and constants which are considered private.
|
||||
*/
|
||||
|
||||
#if !defined(__NTEXTAPI_H__)
|
||||
#define __NTEXTAPI_H__
|
||||
#include <winternl.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
typedef LONG NTSTATUS;
|
||||
|
||||
// https://github.com/ajkhoury/TestDll/blob/master/nt_ddk.h
|
||||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
|
||||
// ================================================================
|
||||
// Enums
|
||||
// ================================================================
|
||||
|
||||
#undef SystemExtendedHandleInformation
|
||||
#define SystemExtendedHandleInformation 64
|
||||
#undef MemoryWorkingSetInformation
|
||||
#define MemoryWorkingSetInformation 0x1
|
||||
#undef ObjectNameInformation
|
||||
#define ObjectNameInformation 1
|
||||
#undef ProcessIoPriority
|
||||
#define ProcessIoPriority 33
|
||||
#undef ProcessWow64Information
|
||||
#define ProcessWow64Information 26
|
||||
#undef SystemProcessIdInformation
|
||||
#define SystemProcessIdInformation 88
|
||||
|
||||
// process suspend() / resume()
|
||||
typedef enum _KTHREAD_STATE {
|
||||
Initialized,
|
||||
Ready,
|
||||
Running,
|
||||
Standby,
|
||||
Terminated,
|
||||
Waiting,
|
||||
Transition,
|
||||
DeferredReady,
|
||||
GateWait,
|
||||
MaximumThreadState
|
||||
} KTHREAD_STATE, *PKTHREAD_STATE;
|
||||
|
||||
typedef enum _KWAIT_REASON {
|
||||
Executive,
|
||||
FreePage,
|
||||
PageIn,
|
||||
PoolAllocation,
|
||||
DelayExecution,
|
||||
Suspended,
|
||||
UserRequest,
|
||||
WrExecutive,
|
||||
WrFreePage,
|
||||
WrPageIn,
|
||||
WrPoolAllocation,
|
||||
WrDelayExecution,
|
||||
WrSuspended,
|
||||
WrUserRequest,
|
||||
WrEventPair,
|
||||
WrQueue,
|
||||
WrLpcReceive,
|
||||
WrLpcReply,
|
||||
WrVirtualMemory,
|
||||
WrPageOut,
|
||||
WrRendezvous,
|
||||
WrKeyedEvent,
|
||||
WrTerminated,
|
||||
WrProcessInSwap,
|
||||
WrCpuRateControl,
|
||||
WrCalloutStack,
|
||||
WrKernel,
|
||||
WrResource,
|
||||
WrPushLock,
|
||||
WrMutex,
|
||||
WrQuantumEnd,
|
||||
WrDispatchInt,
|
||||
WrPreempted,
|
||||
WrYieldExecution,
|
||||
WrFastMutex,
|
||||
WrGuardedMutex,
|
||||
WrRundown,
|
||||
WrAlertByThreadId,
|
||||
WrDeferredPreempt,
|
||||
MaximumWaitReason
|
||||
} KWAIT_REASON, *PKWAIT_REASON;
|
||||
|
||||
// ================================================================
|
||||
// Structs.
|
||||
// ================================================================
|
||||
|
||||
// cpu_stats(), per_cpu_times()
|
||||
typedef struct {
|
||||
LARGE_INTEGER IdleTime;
|
||||
LARGE_INTEGER KernelTime;
|
||||
LARGE_INTEGER UserTime;
|
||||
LARGE_INTEGER DpcTime;
|
||||
LARGE_INTEGER InterruptTime;
|
||||
ULONG InterruptCount;
|
||||
} _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
|
||||
|
||||
// cpu_stats()
|
||||
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;
|
||||
|
||||
// cpu_stats()
|
||||
typedef struct {
|
||||
ULONG ContextSwitches;
|
||||
ULONG DpcCount;
|
||||
ULONG DpcRate;
|
||||
ULONG TimeIncrement;
|
||||
ULONG DpcBypassCount;
|
||||
ULONG ApcBypassCount;
|
||||
} _SYSTEM_INTERRUPT_INFORMATION;
|
||||
|
||||
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 {
|
||||
ULONG_PTR NumberOfHandles;
|
||||
ULONG_PTR Reserved;
|
||||
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
|
||||
} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
|
||||
|
||||
typedef struct _CLIENT_ID2 {
|
||||
HANDLE UniqueProcess;
|
||||
HANDLE UniqueThread;
|
||||
} CLIENT_ID2, *PCLIENT_ID2;
|
||||
|
||||
#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_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;
|
||||
|
||||
#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2
|
||||
#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2
|
||||
|
||||
typedef struct _SYSTEM_PROCESS_INFORMATION2 {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG NumberOfThreads;
|
||||
LARGE_INTEGER SpareLi1;
|
||||
LARGE_INTEGER SpareLi2;
|
||||
LARGE_INTEGER SpareLi3;
|
||||
LARGE_INTEGER CreateTime;
|
||||
LARGE_INTEGER UserTime;
|
||||
LARGE_INTEGER KernelTime;
|
||||
UNICODE_STRING ImageName;
|
||||
LONG BasePriority;
|
||||
HANDLE UniqueProcessId;
|
||||
HANDLE InheritedFromUniqueProcessId;
|
||||
ULONG HandleCount;
|
||||
ULONG SessionId;
|
||||
ULONG_PTR PageDirectoryBase;
|
||||
SIZE_T PeakVirtualSize;
|
||||
SIZE_T VirtualSize;
|
||||
DWORD PageFaultCount;
|
||||
SIZE_T PeakWorkingSetSize;
|
||||
SIZE_T WorkingSetSize;
|
||||
SIZE_T QuotaPeakPagedPoolUsage;
|
||||
SIZE_T QuotaPagedPoolUsage;
|
||||
SIZE_T QuotaPeakNonPagedPoolUsage;
|
||||
SIZE_T QuotaNonPagedPoolUsage;
|
||||
SIZE_T PagefileUsage;
|
||||
SIZE_T PeakPagefileUsage;
|
||||
SIZE_T PrivatePageCount;
|
||||
LARGE_INTEGER ReadOperationCount;
|
||||
LARGE_INTEGER WriteOperationCount;
|
||||
LARGE_INTEGER OtherOperationCount;
|
||||
LARGE_INTEGER ReadTransferCount;
|
||||
LARGE_INTEGER WriteTransferCount;
|
||||
LARGE_INTEGER OtherTransferCount;
|
||||
SYSTEM_THREAD_INFORMATION Threads[1];
|
||||
} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2;
|
||||
|
||||
#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2
|
||||
#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2
|
||||
|
||||
// cpu_freq()
|
||||
typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
ULONG Number;
|
||||
ULONG MaxMhz;
|
||||
ULONG CurrentMhz;
|
||||
ULONG MhzLimit;
|
||||
ULONG MaxIdleState;
|
||||
ULONG CurrentIdleState;
|
||||
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
|
||||
|
||||
#ifndef __IPHLPAPI_H__
|
||||
typedef struct in6_addr {
|
||||
union {
|
||||
UCHAR Byte[16];
|
||||
USHORT Word[8];
|
||||
} u;
|
||||
} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR;
|
||||
#endif
|
||||
|
||||
// PEB / cmdline(), cwd(), environ()
|
||||
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_;
|
||||
|
||||
// users()
|
||||
typedef struct _WINSTATION_INFO {
|
||||
BYTE Reserved1[72];
|
||||
ULONG SessionId;
|
||||
BYTE Reserved2[4];
|
||||
FILETIME ConnectTime;
|
||||
FILETIME DisconnectTime;
|
||||
FILETIME LastInputTime;
|
||||
FILETIME LoginTime;
|
||||
BYTE Reserved3[1096];
|
||||
FILETIME CurrentTime;
|
||||
} WINSTATION_INFO, *PWINSTATION_INFO;
|
||||
|
||||
// cpu_count_phys()
|
||||
#if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP)
|
||||
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
|
||||
LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
|
||||
DWORD Size;
|
||||
_ANONYMOUS_UNION
|
||||
union {
|
||||
PROCESSOR_RELATIONSHIP Processor;
|
||||
NUMA_NODE_RELATIONSHIP NumaNode;
|
||||
CACHE_RELATIONSHIP Cache;
|
||||
GROUP_RELATIONSHIP Group;
|
||||
} DUMMYUNIONNAME;
|
||||
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, \
|
||||
*PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
|
||||
#endif
|
||||
|
||||
// memory_uss()
|
||||
typedef struct _MEMORY_WORKING_SET_BLOCK {
|
||||
ULONG_PTR Protection : 5;
|
||||
ULONG_PTR ShareCount : 3;
|
||||
ULONG_PTR Shared : 1;
|
||||
ULONG_PTR Node : 3;
|
||||
#ifdef _WIN64
|
||||
ULONG_PTR VirtualPage : 52;
|
||||
#else
|
||||
ULONG VirtualPage : 20;
|
||||
#endif
|
||||
} MEMORY_WORKING_SET_BLOCK, *PMEMORY_WORKING_SET_BLOCK;
|
||||
|
||||
// memory_uss()
|
||||
typedef struct _MEMORY_WORKING_SET_INFORMATION {
|
||||
ULONG_PTR NumberOfEntries;
|
||||
MEMORY_WORKING_SET_BLOCK WorkingSetInfo[1];
|
||||
} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;
|
||||
|
||||
// memory_uss()
|
||||
typedef struct _PSUTIL_PROCESS_WS_COUNTERS {
|
||||
SIZE_T NumberOfPages;
|
||||
SIZE_T NumberOfPrivatePages;
|
||||
SIZE_T NumberOfSharedPages;
|
||||
SIZE_T NumberOfShareablePages;
|
||||
} PSUTIL_PROCESS_WS_COUNTERS, *PPSUTIL_PROCESS_WS_COUNTERS;
|
||||
|
||||
// exe()
|
||||
typedef struct _SYSTEM_PROCESS_ID_INFORMATION {
|
||||
HANDLE ProcessId;
|
||||
UNICODE_STRING ImageName;
|
||||
} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION;
|
||||
|
||||
// ====================================================================
|
||||
// PEB structs for cmdline(), cwd(), environ()
|
||||
// ====================================================================
|
||||
|
||||
#ifdef _WIN64
|
||||
typedef struct {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[21];
|
||||
PVOID LoaderData;
|
||||
PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
|
||||
// more fields...
|
||||
} PEB_;
|
||||
|
||||
// When we are a 64 bit process accessing a 32 bit (WoW64)
|
||||
// process we need to use the 32 bit structure layout.
|
||||
typedef struct {
|
||||
USHORT Length;
|
||||
USHORT MaxLength;
|
||||
DWORD Buffer;
|
||||
} UNICODE_STRING32;
|
||||
|
||||
typedef struct {
|
||||
BYTE Reserved1[16];
|
||||
DWORD Reserved2[5];
|
||||
UNICODE_STRING32 CurrentDirectoryPath;
|
||||
DWORD CurrentDirectoryHandle;
|
||||
UNICODE_STRING32 DllPath;
|
||||
UNICODE_STRING32 ImagePathName;
|
||||
UNICODE_STRING32 CommandLine;
|
||||
DWORD env;
|
||||
} RTL_USER_PROCESS_PARAMETERS32;
|
||||
|
||||
typedef struct {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[1];
|
||||
DWORD Reserved3[2];
|
||||
DWORD Ldr;
|
||||
DWORD ProcessParameters;
|
||||
// more fields...
|
||||
} PEB32;
|
||||
#else // ! _WIN64
|
||||
typedef struct {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[1];
|
||||
PVOID Reserved3[2];
|
||||
PVOID Ldr;
|
||||
PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
|
||||
// more fields...
|
||||
} PEB_;
|
||||
|
||||
// When we are a 32 bit (WoW64) process accessing a 64 bit process
|
||||
// we need to use the 64 bit structure layout and a special function
|
||||
// to read its memory.
|
||||
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID64 BaseAddress,
|
||||
PVOID Buffer,
|
||||
ULONG64 Size,
|
||||
PULONG64 NumberOfBytesRead);
|
||||
|
||||
typedef struct {
|
||||
PVOID Reserved1[2];
|
||||
PVOID64 PebBaseAddress;
|
||||
PVOID Reserved2[4];
|
||||
PVOID UniqueProcessId[2];
|
||||
PVOID Reserved3[2];
|
||||
} PROCESS_BASIC_INFORMATION64;
|
||||
|
||||
typedef struct {
|
||||
USHORT Length;
|
||||
USHORT MaxLength;
|
||||
PVOID64 Buffer;
|
||||
} UNICODE_STRING64;
|
||||
|
||||
typedef struct {
|
||||
BYTE Reserved1[16];
|
||||
PVOID64 Reserved2[5];
|
||||
UNICODE_STRING64 CurrentDirectoryPath;
|
||||
PVOID64 CurrentDirectoryHandle;
|
||||
UNICODE_STRING64 DllPath;
|
||||
UNICODE_STRING64 ImagePathName;
|
||||
UNICODE_STRING64 CommandLine;
|
||||
PVOID64 env;
|
||||
} RTL_USER_PROCESS_PARAMETERS64;
|
||||
|
||||
typedef struct {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[21];
|
||||
PVOID64 LoaderData;
|
||||
PVOID64 ProcessParameters;
|
||||
// more fields...
|
||||
} PEB64;
|
||||
#endif // _WIN64
|
||||
|
||||
// ================================================================
|
||||
// Type defs for modules loaded at runtime.
|
||||
// ================================================================
|
||||
|
||||
BOOL (WINAPI *_GetLogicalProcessorInformationEx) (
|
||||
LOGICAL_PROCESSOR_RELATIONSHIP relationship,
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
|
||||
PDWORD ReturnLength);
|
||||
|
||||
#define GetLogicalProcessorInformationEx _GetLogicalProcessorInformationEx
|
||||
|
||||
BOOLEAN (WINAPI * _WinStationQueryInformationW) (
|
||||
HANDLE ServerHandle,
|
||||
ULONG SessionId,
|
||||
WINSTATIONINFOCLASS WinStationInformationClass,
|
||||
PVOID pWinStationInformation,
|
||||
ULONG WinStationInformationLength,
|
||||
PULONG pReturnLength);
|
||||
|
||||
#define WinStationQueryInformationW _WinStationQueryInformationW
|
||||
|
||||
NTSTATUS (NTAPI *_NtQueryInformationProcess) (
|
||||
HANDLE ProcessHandle,
|
||||
DWORD ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
DWORD ProcessInformationLength,
|
||||
PDWORD ReturnLength);
|
||||
|
||||
#define NtQueryInformationProcess _NtQueryInformationProcess
|
||||
|
||||
NTSTATUS (NTAPI *_NtQuerySystemInformation) (
|
||||
ULONG SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength);
|
||||
|
||||
#define NtQuerySystemInformation _NtQuerySystemInformation
|
||||
|
||||
NTSTATUS (NTAPI *_NtSetInformationProcess) (
|
||||
HANDLE ProcessHandle,
|
||||
DWORD ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
DWORD ProcessInformationLength);
|
||||
|
||||
#define NtSetInformationProcess _NtSetInformationProcess
|
||||
|
||||
PSTR (NTAPI * _RtlIpv4AddressToStringA) (
|
||||
struct in_addr *Addr,
|
||||
PSTR S);
|
||||
|
||||
#define RtlIpv4AddressToStringA _RtlIpv4AddressToStringA
|
||||
|
||||
PSTR (NTAPI * _RtlIpv6AddressToStringA) (
|
||||
struct in6_addr *Addr,
|
||||
PSTR P);
|
||||
|
||||
#define RtlIpv6AddressToStringA _RtlIpv6AddressToStringA
|
||||
|
||||
DWORD (WINAPI * _GetExtendedTcpTable) (
|
||||
PVOID pTcpTable,
|
||||
PDWORD pdwSize,
|
||||
BOOL bOrder,
|
||||
ULONG ulAf,
|
||||
TCP_TABLE_CLASS TableClass,
|
||||
ULONG Reserved);
|
||||
|
||||
#define GetExtendedTcpTable _GetExtendedTcpTable
|
||||
|
||||
DWORD (WINAPI * _GetExtendedUdpTable) (
|
||||
PVOID pUdpTable,
|
||||
PDWORD pdwSize,
|
||||
BOOL bOrder,
|
||||
ULONG ulAf,
|
||||
UDP_TABLE_CLASS TableClass,
|
||||
ULONG Reserved);
|
||||
|
||||
#define GetExtendedUdpTable _GetExtendedUdpTable
|
||||
|
||||
DWORD (CALLBACK *_GetActiveProcessorCount) (
|
||||
WORD GroupNumber);
|
||||
|
||||
#define GetActiveProcessorCount _GetActiveProcessorCount
|
||||
|
||||
ULONGLONG (CALLBACK *_GetTickCount64) (
|
||||
void);
|
||||
|
||||
#define GetTickCount64 _GetTickCount64
|
||||
|
||||
NTSTATUS (NTAPI *_NtQueryObject) (
|
||||
HANDLE Handle,
|
||||
OBJECT_INFORMATION_CLASS ObjectInformationClass,
|
||||
PVOID ObjectInformation,
|
||||
ULONG ObjectInformationLength,
|
||||
PULONG ReturnLength);
|
||||
|
||||
#define NtQueryObject _NtQueryObject
|
||||
|
||||
NTSTATUS (WINAPI *_RtlGetVersion) (
|
||||
PRTL_OSVERSIONINFOW lpVersionInformation
|
||||
);
|
||||
|
||||
#define RtlGetVersion _RtlGetVersion
|
||||
|
||||
NTSTATUS (WINAPI *_NtResumeProcess) (
|
||||
HANDLE hProcess
|
||||
);
|
||||
|
||||
#define NtResumeProcess _NtResumeProcess
|
||||
|
||||
NTSTATUS (WINAPI *_NtSuspendProcess) (
|
||||
HANDLE hProcess
|
||||
);
|
||||
|
||||
#define NtSuspendProcess _NtSuspendProcess
|
||||
|
||||
NTSTATUS (NTAPI *_NtQueryVirtualMemory) (
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
int MemoryInformationClass,
|
||||
PVOID MemoryInformation,
|
||||
SIZE_T MemoryInformationLength,
|
||||
PSIZE_T ReturnLength
|
||||
);
|
||||
|
||||
#define NtQueryVirtualMemory _NtQueryVirtualMemory
|
||||
|
||||
ULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) (
|
||||
NTSTATUS status
|
||||
);
|
||||
|
||||
#define RtlNtStatusToDosErrorNoTeb _RtlNtStatusToDosErrorNoTeb
|
||||
|
||||
#endif // __NTEXTAPI_H__
|
|
@ -1,292 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module retrieves handles opened by a process.
|
||||
* We use NtQuerySystemInformation to enumerate them and NtQueryObject
|
||||
* to obtain the corresponding file name.
|
||||
* Since NtQueryObject hangs for certain handle types we call it in a
|
||||
* separate thread which gets killed if it doesn't complete within 100ms.
|
||||
* This is a limitation of the Windows API and ProcessHacker uses the
|
||||
* same trick: https://github.com/giampaolo/psutil/pull/597
|
||||
*
|
||||
* CREDITS: original implementation was written by Jeff Tang.
|
||||
* It was then rewritten by Giampaolo Rodola many years later.
|
||||
* Utility functions for getting the file handles and names were re-adapted
|
||||
* from the excellent ProcessHacker.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <Python.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
#include "process_utils.h"
|
||||
|
||||
|
||||
#define THREAD_TIMEOUT 100 // ms
|
||||
// Global object shared between the 2 threads.
|
||||
PUNICODE_STRING globalFileName = NULL;
|
||||
|
||||
|
||||
static int
|
||||
psutil_enum_handles(PSYSTEM_HANDLE_INFORMATION_EX *handles) {
|
||||
static ULONG initialBufferSize = 0x10000;
|
||||
NTSTATUS status;
|
||||
PVOID buffer;
|
||||
ULONG bufferSize;
|
||||
|
||||
bufferSize = initialBufferSize;
|
||||
buffer = MALLOC_ZERO(bufferSize);
|
||||
if (buffer == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((status = NtQuerySystemInformation(
|
||||
SystemExtendedHandleInformation,
|
||||
buffer,
|
||||
bufferSize,
|
||||
NULL
|
||||
)) == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
FREE(buffer);
|
||||
bufferSize *= 2;
|
||||
|
||||
// Fail if we're resizing the buffer to something very large.
|
||||
if (bufferSize > 256 * 1024 * 1024) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"SystemExtendedHandleInformation buffer too big");
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer = MALLOC_ZERO(bufferSize);
|
||||
if (buffer == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
|
||||
FREE(buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
psutil_get_filename(LPVOID lpvParam) {
|
||||
HANDLE hFile = *((HANDLE*)lpvParam);
|
||||
NTSTATUS status;
|
||||
ULONG bufferSize;
|
||||
ULONG attempts = 8;
|
||||
|
||||
bufferSize = 0x200;
|
||||
globalFileName = MALLOC_ZERO(bufferSize);
|
||||
if (globalFileName == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
// Note: also this is supposed to hang, hence why we do it in here.
|
||||
if (GetFileType(hFile) != FILE_TYPE_DISK) {
|
||||
SetLastError(0);
|
||||
globalFileName->Length = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A loop is needed because the I/O subsystem likes to give us the
|
||||
// wrong return lengths...
|
||||
do {
|
||||
status = NtQueryObject(
|
||||
hFile,
|
||||
ObjectNameInformation,
|
||||
globalFileName,
|
||||
bufferSize,
|
||||
&bufferSize
|
||||
);
|
||||
if (status == STATUS_BUFFER_OVERFLOW ||
|
||||
status == STATUS_INFO_LENGTH_MISMATCH ||
|
||||
status == STATUS_BUFFER_TOO_SMALL)
|
||||
{
|
||||
FREE(globalFileName);
|
||||
globalFileName = MALLOC_ZERO(bufferSize);
|
||||
if (globalFileName == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
} while (--attempts);
|
||||
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
|
||||
FREE(globalFileName);
|
||||
globalFileName = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (globalFileName != NULL) {
|
||||
FREE(globalFileName);
|
||||
globalFileName = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static DWORD
|
||||
psutil_threaded_get_filename(HANDLE hFile) {
|
||||
DWORD dwWait;
|
||||
HANDLE hThread;
|
||||
DWORD threadRetValue;
|
||||
|
||||
hThread = CreateThread(
|
||||
NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL);
|
||||
if (hThread == NULL) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("CreateThread");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Wait for the worker thread to finish.
|
||||
dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT);
|
||||
|
||||
// If the thread hangs, kill it and cleanup.
|
||||
if (dwWait == WAIT_TIMEOUT) {
|
||||
psutil_debug(
|
||||
"get handle name thread timed out after %i ms", THREAD_TIMEOUT);
|
||||
if (TerminateThread(hThread, 0) == 0) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("TerminateThread");
|
||||
CloseHandle(hThread);
|
||||
return 1;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dwWait == WAIT_FAILED) {
|
||||
psutil_debug("WaitForSingleObject -> WAIT_FAILED");
|
||||
if (TerminateThread(hThread, 0) == 0) {
|
||||
PyErr_SetFromOSErrnoWithSyscall(
|
||||
"WaitForSingleObject -> WAIT_FAILED -> TerminateThread");
|
||||
CloseHandle(hThread);
|
||||
return 1;
|
||||
}
|
||||
PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
|
||||
CloseHandle(hThread);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (GetExitCodeThread(hThread, &threadRetValue) == 0) {
|
||||
if (TerminateThread(hThread, 0) == 0) {
|
||||
PyErr_SetFromOSErrnoWithSyscall(
|
||||
"GetExitCodeThread (failed) -> TerminateThread");
|
||||
CloseHandle(hThread);
|
||||
return 1;
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread");
|
||||
return 1;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
return threadRetValue;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
|
||||
PSYSTEM_HANDLE_INFORMATION_EX handlesList = NULL;
|
||||
PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL;
|
||||
HANDLE hFile = NULL;
|
||||
ULONG i = 0;
|
||||
BOOLEAN errorOccurred = FALSE;
|
||||
PyObject* py_path = NULL;
|
||||
PyObject* py_retlist = PyList_New(0);;
|
||||
|
||||
if (!py_retlist)
|
||||
return NULL;
|
||||
|
||||
// Due to the use of global variables, ensure only 1 call
|
||||
// to psutil_get_open_files() is running.
|
||||
EnterCriticalSection(&PSUTIL_CRITICAL_SECTION);
|
||||
|
||||
if (psutil_enum_handles(&handlesList) != 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < handlesList->NumberOfHandles; i++) {
|
||||
hHandle = &handlesList->Handles[i];
|
||||
if ((ULONG_PTR)hHandle->UniqueProcessId != dwPid)
|
||||
continue;
|
||||
if (! DuplicateHandle(
|
||||
hProcess,
|
||||
hHandle->HandleValue,
|
||||
GetCurrentProcess(),
|
||||
&hFile,
|
||||
0,
|
||||
TRUE,
|
||||
DUPLICATE_SAME_ACCESS))
|
||||
{
|
||||
// Will fail if not a regular file; just skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// This will set *globalFileName* global variable.
|
||||
if (psutil_threaded_get_filename(hFile) != 0)
|
||||
goto error;
|
||||
|
||||
if (globalFileName->Length > 0) {
|
||||
py_path = PyUnicode_FromWideChar(globalFileName->Buffer,
|
||||
wcslen(globalFileName->Buffer));
|
||||
if (! py_path)
|
||||
goto error;
|
||||
if (PyList_Append(py_retlist, py_path))
|
||||
goto error;
|
||||
Py_CLEAR(py_path); // also sets to NULL
|
||||
}
|
||||
|
||||
// Loop cleanup section.
|
||||
if (globalFileName != NULL) {
|
||||
FREE(globalFileName);
|
||||
globalFileName = NULL;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
hFile = NULL;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
error:
|
||||
Py_XDECREF(py_retlist);
|
||||
errorOccurred = TRUE;
|
||||
goto exit;
|
||||
|
||||
exit:
|
||||
if (hFile != NULL)
|
||||
CloseHandle(hFile);
|
||||
if (globalFileName != NULL) {
|
||||
FREE(globalFileName);
|
||||
globalFileName = NULL;
|
||||
}
|
||||
if (py_path != NULL)
|
||||
Py_DECREF(py_path);
|
||||
if (handlesList != NULL)
|
||||
FREE(handlesList);
|
||||
|
||||
LeaveCriticalSection(&PSUTIL_CRITICAL_SECTION);
|
||||
if (errorOccurred == TRUE)
|
||||
return NULL;
|
||||
return py_retlist;
|
||||
}
|
|
@ -1,10 +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>
|
||||
|
||||
PyObject* psutil_get_open_files(DWORD pid, HANDLE hProcess);
|
|
@ -1,733 +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_windows module methods.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "../../_psutil_common.h"
|
||||
#include "process_info.h"
|
||||
#include "process_utils.h"
|
||||
|
||||
|
||||
#ifndef _WIN64
|
||||
typedef NTSTATUS (NTAPI *__NtQueryInformationProcess)(
|
||||
HANDLE ProcessHandle,
|
||||
DWORD ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
DWORD ProcessInformationLength,
|
||||
PDWORD ReturnLength);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (!VirtualQueryEx(hProcess, src, &info, sizeof(info))) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("VirtualQueryEx");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*psize = info.RegionSize - ((char*)src - (char*)info.BaseAddress);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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(DWORD 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
|
||||
*/
|
||||
SIZE_T size = 0;
|
||||
#ifndef _WIN64
|
||||
static __NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL;
|
||||
static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL;
|
||||
#endif
|
||||
HANDLE hProcess = NULL;
|
||||
LPCVOID src;
|
||||
WCHAR *buffer = NULL;
|
||||
#ifdef _WIN64
|
||||
LPVOID ppeb32 = NULL;
|
||||
#else
|
||||
PVOID64 src64;
|
||||
BOOL weAreWow64;
|
||||
BOOL theyAreWow64;
|
||||
#endif
|
||||
DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
|
||||
NTSTATUS status;
|
||||
|
||||
hProcess = psutil_handle_from_pid(pid, access);
|
||||
if (hProcess == NULL)
|
||||
return -1;
|
||||
|
||||
#ifdef _WIN64
|
||||
/* 64 bit case. Check if the target is a 32 bit process running in WoW64
|
||||
* mode. */
|
||||
status = NtQueryInformationProcess(
|
||||
hProcess,
|
||||
ProcessWow64Information,
|
||||
&ppeb32,
|
||||
sizeof(LPVOID),
|
||||
NULL);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQueryInformationProcess(ProcessWow64Information)");
|
||||
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)) {
|
||||
// May fail with ERROR_PARTIAL_COPY, see:
|
||||
// https://github.com/giampaolo/psutil/issues/875
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read process parameters
|
||||
if (!ReadProcessMemory(hProcess,
|
||||
UlongToPtr(peb32.ProcessParameters),
|
||||
&procParameters32,
|
||||
sizeof(procParameters32),
|
||||
NULL))
|
||||
{
|
||||
// May fail with ERROR_PARTIAL_COPY, see:
|
||||
// https://github.com/giampaolo/psutil/issues/875
|
||||
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_SetFromOSErrnoWithSyscall("IsWow64Process");
|
||||
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 = \
|
||||
psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "NtWow64QueryInformationProcess64");
|
||||
if (NtWow64QueryInformationProcess64 == NULL) {
|
||||
PyErr_Clear();
|
||||
AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (NtWow64ReadVirtualMemory64 == NULL) {
|
||||
NtWow64ReadVirtualMemory64 = \
|
||||
psutil_GetProcAddressFromLib(
|
||||
"ntdll.dll", "NtWow64ReadVirtualMemory64");
|
||||
if (NtWow64ReadVirtualMemory64 == NULL) {
|
||||
PyErr_Clear();
|
||||
AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
status = NtWow64QueryInformationProcess64(
|
||||
hProcess,
|
||||
ProcessBasicInformation,
|
||||
&pbi64,
|
||||
sizeof(pbi64),
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status,
|
||||
"NtWow64QueryInformationProcess64(ProcessBasicInformation)"
|
||||
);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read peb
|
||||
status = NtWow64ReadVirtualMemory64(
|
||||
hProcess,
|
||||
pbi64.PebBaseAddress,
|
||||
&peb64,
|
||||
sizeof(peb64),
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(status, "NtWow64ReadVirtualMemory64");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read process parameters
|
||||
status = NtWow64ReadVirtualMemory64(
|
||||
hProcess,
|
||||
peb64.ProcessParameters,
|
||||
&procParameters64,
|
||||
sizeof(procParameters64),
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status,
|
||||
"NtWow64ReadVirtualMemory64(ProcessParameters)"
|
||||
);
|
||||
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;
|
||||
|
||||
status = NtQueryInformationProcess(
|
||||
hProcess,
|
||||
ProcessBasicInformation,
|
||||
&pbi,
|
||||
sizeof(pbi),
|
||||
NULL);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQueryInformationProcess(ProcessBasicInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
// read peb
|
||||
if (!ReadProcessMemory(hProcess,
|
||||
pbi.PebBaseAddress,
|
||||
&peb,
|
||||
sizeof(peb),
|
||||
NULL))
|
||||
{
|
||||
// May fail with ERROR_PARTIAL_COPY, see:
|
||||
// https://github.com/giampaolo/psutil/issues/875
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// read process parameters
|
||||
if (!ReadProcessMemory(hProcess,
|
||||
peb.ProcessParameters,
|
||||
&procParameters,
|
||||
sizeof(procParameters),
|
||||
NULL))
|
||||
{
|
||||
// May fail with ERROR_PARTIAL_COPY, see:
|
||||
// https://github.com/giampaolo/psutil/issues/875
|
||||
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) {
|
||||
AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
|
||||
goto error;
|
||||
}
|
||||
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) {
|
||||
status = NtWow64ReadVirtualMemory64(
|
||||
hProcess,
|
||||
src64,
|
||||
buffer,
|
||||
size,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(status, "NtWow64ReadVirtualMemory64");
|
||||
goto error;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (!ReadProcessMemory(hProcess, src, buffer, size, NULL)) {
|
||||
// May fail with ERROR_PARTIAL_COPY, see:
|
||||
// https://github.com/giampaolo/psutil/issues/875
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get process cmdline by using NtQueryInformationProcess. This is a
|
||||
* method alternative to PEB which is less likely to result in
|
||||
* AccessDenied. Requires Windows 8.1+.
|
||||
*/
|
||||
static int
|
||||
psutil_cmdline_query_proc(DWORD pid, WCHAR **pdata, SIZE_T *psize) {
|
||||
HANDLE hProcess = NULL;
|
||||
ULONG bufLen = 0;
|
||||
NTSTATUS status;
|
||||
char * buffer = NULL;
|
||||
WCHAR * bufWchar = NULL;
|
||||
PUNICODE_STRING tmp = NULL;
|
||||
size_t size;
|
||||
int ProcessCommandLineInformation = 60;
|
||||
|
||||
if (PSUTIL_WINVER < PSUTIL_WINDOWS_8_1) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError, "requires Windows 8.1+");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
|
||||
if (hProcess == NULL)
|
||||
goto error;
|
||||
|
||||
// get the right buf size
|
||||
status = NtQueryInformationProcess(
|
||||
hProcess,
|
||||
ProcessCommandLineInformation,
|
||||
NULL,
|
||||
0,
|
||||
&bufLen);
|
||||
|
||||
// https://github.com/giampaolo/psutil/issues/1501
|
||||
if (status == STATUS_NOT_FOUND) {
|
||||
AccessDenied("NtQueryInformationProcess(ProcessBasicInformation) -> "
|
||||
"STATUS_NOT_FOUND translated into PermissionError");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (status != STATUS_BUFFER_OVERFLOW && \
|
||||
status != STATUS_BUFFER_TOO_SMALL && \
|
||||
status != STATUS_INFO_LENGTH_MISMATCH) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQueryInformationProcess(ProcessBasicInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// allocate memory
|
||||
buffer = calloc(bufLen, 1);
|
||||
if (buffer == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
// get the cmdline
|
||||
status = NtQueryInformationProcess(
|
||||
hProcess,
|
||||
ProcessCommandLineInformation,
|
||||
buffer,
|
||||
bufLen,
|
||||
&bufLen
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQueryInformationProcess(ProcessCommandLineInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// build the string
|
||||
tmp = (PUNICODE_STRING)buffer;
|
||||
size = wcslen(tmp->Buffer) + 1;
|
||||
bufWchar = (WCHAR *)calloc(size, sizeof(WCHAR));
|
||||
if (bufWchar == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
wcscpy_s(bufWchar, size, tmp->Buffer);
|
||||
*pdata = bufWchar;
|
||||
*psize = size * sizeof(WCHAR);
|
||||
free(buffer);
|
||||
CloseHandle(hProcess);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (buffer != NULL)
|
||||
free(buffer);
|
||||
if (hProcess != NULL)
|
||||
CloseHandle(hProcess);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a Python list representing the arguments for the process
|
||||
* with given pid or NULL on error.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_get_cmdline(DWORD pid, int use_peb) {
|
||||
PyObject *ret = NULL;
|
||||
WCHAR *data = NULL;
|
||||
SIZE_T size;
|
||||
PyObject *py_retlist = NULL;
|
||||
PyObject *py_unicode = NULL;
|
||||
LPWSTR *szArglist = NULL;
|
||||
int nArgs, i;
|
||||
int func_ret;
|
||||
|
||||
/*
|
||||
Reading the PEB to get the cmdline seem to be the best method if
|
||||
somebody has tampered with the parameters after creating the process.
|
||||
For instance, create a process as suspended, patch the command line
|
||||
in its PEB and unfreeze it. It requires more privileges than
|
||||
NtQueryInformationProcess though (the fallback):
|
||||
- https://github.com/giampaolo/psutil/pull/1398
|
||||
- https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/
|
||||
*/
|
||||
if (use_peb == 1)
|
||||
func_ret = psutil_get_process_data(pid, KIND_CMDLINE, &data, &size);
|
||||
else
|
||||
func_ret = psutil_cmdline_query_proc(pid, &data, &size);
|
||||
if (func_ret != 0)
|
||||
goto out;
|
||||
|
||||
// attempt to parse the command line using Win32 API
|
||||
szArglist = CommandLineToArgvW(data, &nArgs);
|
||||
if (szArglist == NULL) {
|
||||
PyErr_SetFromOSErrnoWithSyscall("CommandLineToArgvW");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
ret = py_retlist;
|
||||
py_retlist = NULL;
|
||||
|
||||
out:
|
||||
if (szArglist != NULL)
|
||||
LocalFree(szArglist);
|
||||
if (data != NULL)
|
||||
free(data);
|
||||
Py_XDECREF(py_unicode);
|
||||
Py_XDECREF(py_retlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
psutil_get_cwd(DWORD pid) {
|
||||
PyObject *ret = NULL;
|
||||
WCHAR *data = NULL;
|
||||
SIZE_T size;
|
||||
|
||||
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:
|
||||
if (data != NULL)
|
||||
free(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* returns a Python string containing the environment variable data for the
|
||||
* process with given pid or NULL on error.
|
||||
*/
|
||||
PyObject *
|
||||
psutil_get_environ(DWORD 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:
|
||||
if (data != NULL)
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure
|
||||
* fills the structure with various process information in one shot
|
||||
* by using NtQuerySystemInformation.
|
||||
* We use this as a fallback when faster functions fail with access
|
||||
* denied. This is slower because it iterates over all processes
|
||||
* but it doesn't require any privilege (also work for PID 0).
|
||||
* On success return 1, else 0 with Python exception already set.
|
||||
*/
|
||||
int
|
||||
psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
|
||||
PVOID *retBuffer) {
|
||||
static ULONG initialBufferSize = 0x4000;
|
||||
NTSTATUS status;
|
||||
PVOID buffer;
|
||||
ULONG bufferSize;
|
||||
PSYSTEM_PROCESS_INFORMATION process;
|
||||
|
||||
bufferSize = initialBufferSize;
|
||||
buffer = malloc(bufferSize);
|
||||
if (buffer == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (TRUE) {
|
||||
status = NtQuerySystemInformation(
|
||||
SystemProcessInformation,
|
||||
buffer,
|
||||
bufferSize,
|
||||
&bufferSize);
|
||||
if (status == STATUS_BUFFER_TOO_SMALL ||
|
||||
status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
free(buffer);
|
||||
buffer = malloc(bufferSize);
|
||||
if (buffer == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! NT_SUCCESS(status)) {
|
||||
psutil_SetFromNTStatusErr(
|
||||
status, "NtQuerySystemInformation(SystemProcessInformation)");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bufferSize <= 0x20000)
|
||||
initialBufferSize = bufferSize;
|
||||
|
||||
process = PSUTIL_FIRST_PROCESS(buffer);
|
||||
do {
|
||||
if ((ULONG_PTR)process->UniqueProcessId == pid) {
|
||||
*retProcess = process;
|
||||
*retBuffer = buffer;
|
||||
return 1;
|
||||
}
|
||||
} while ((process = PSUTIL_NEXT_PROCESS(process)));
|
||||
|
||||
NoSuchProcess("NtQuerySystemInformation (no PID found)");
|
||||
goto error;
|
||||
|
||||
error:
|
||||
if (buffer != NULL)
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get various process information by using NtQuerySystemInformation.
|
||||
* We use this as a fallback when faster functions fail with access
|
||||
* denied. This is slower because it iterates over all processes.
|
||||
* Returned tuple includes the following process info:
|
||||
*
|
||||
* - num_threads()
|
||||
* - ctx_switches()
|
||||
* - num_handles() (fallback)
|
||||
* - cpu_times() (fallback)
|
||||
* - create_time() (fallback)
|
||||
* - io_counters() (fallback)
|
||||
* - memory_info() (fallback)
|
||||
*/
|
||||
PyObject *
|
||||
psutil_proc_info(PyObject *self, PyObject *args) {
|
||||
DWORD pid;
|
||||
PSYSTEM_PROCESS_INFORMATION process;
|
||||
PVOID buffer;
|
||||
ULONG i;
|
||||
ULONG ctx_switches = 0;
|
||||
double user_time;
|
||||
double kernel_time;
|
||||
double create_time;
|
||||
PyObject *py_retlist;
|
||||
|
||||
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
|
||||
return NULL;
|
||||
if (! psutil_get_proc_info(pid, &process, &buffer))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < process->NumberOfThreads; i++)
|
||||
ctx_switches += process->Threads[i].ContextSwitches;
|
||||
user_time = (double)process->UserTime.HighPart * HI_T + \
|
||||
(double)process->UserTime.LowPart * LO_T;
|
||||
kernel_time = (double)process->KernelTime.HighPart * HI_T + \
|
||||
(double)process->KernelTime.LowPart * LO_T;
|
||||
|
||||
// Convert the LARGE_INTEGER union to a Unix time.
|
||||
// It's the best I could find by googling and borrowing code here
|
||||
// and there. The time returned has a precision of 1 second.
|
||||
if (0 == pid || 4 == pid) {
|
||||
// the python module will translate this into BOOT_TIME later
|
||||
create_time = 0;
|
||||
}
|
||||
else {
|
||||
create_time = psutil_LargeIntegerToUnixTime(process->CreateTime);
|
||||
}
|
||||
|
||||
py_retlist = Py_BuildValue(
|
||||
#if defined(_WIN64)
|
||||
"kkdddiKKKKKK" "kKKKKKKKKK",
|
||||
#else
|
||||
"kkdddiKKKKKK" "kIIIIIIIII",
|
||||
#endif
|
||||
process->HandleCount, // num handles
|
||||
ctx_switches, // num ctx switches
|
||||
user_time, // cpu user time
|
||||
kernel_time, // cpu kernel time
|
||||
create_time, // create time
|
||||
(int)process->NumberOfThreads, // num threads
|
||||
// IO counters
|
||||
process->ReadOperationCount.QuadPart, // io rcount
|
||||
process->WriteOperationCount.QuadPart, // io wcount
|
||||
process->ReadTransferCount.QuadPart, // io rbytes
|
||||
process->WriteTransferCount.QuadPart, // io wbytes
|
||||
process->OtherOperationCount.QuadPart, // io others count
|
||||
process->OtherTransferCount.QuadPart, // io others bytes
|
||||
// memory
|
||||
process->PageFaultCount, // num page faults
|
||||
process->PeakWorkingSetSize, // peak wset
|
||||
process->WorkingSetSize, // wset
|
||||
process->QuotaPeakPagedPoolUsage, // peak paged pool
|
||||
process->QuotaPagedPoolUsage, // paged pool
|
||||
process->QuotaPeakNonPagedPoolUsage, // peak non paged pool
|
||||
process->QuotaNonPagedPoolUsage, // non paged pool
|
||||
process->PagefileUsage, // pagefile
|
||||
process->PeakPagefileUsage, // peak pagefile
|
||||
process->PrivatePageCount // private
|
||||
);
|
||||
|
||||
free(buffer);
|
||||
return py_retlist;
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче