diff --git a/.travis.yml b/.travis.yml
index 7d2db52..a1b4b62 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,10 +6,11 @@ os:
env:
matrix:
- - PYTHON_VERSION=2.7
- - PYTHON_VERSION=3.3
- - PYTHON_VERSION=3.4
- - PYTHON_VERSION=3.5
+ - PYTHON_VERSION=2.7 PSUTIL_VERSION=1
+ - PYTHON_VERSION=2.7 PSUTIL_VERSION=2
+ - PYTHON_VERSION=3.3 PSUTIL_VERSION=3
+ - PYTHON_VERSION=3.4 PSUTIL_VERSION=4
+ - PYTHON_VERSION=3.5 PSUTIL_VERSION=5
global:
- CONDA_DEPENDENCIES='numpy matplotlib psutil'
- PIP_DEPENDENCIES='coveralls pytest-cov'
@@ -17,10 +18,11 @@ env:
before_install:
- git clone git://github.com/astropy/ci-helpers.git
- - source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh
+ - source ci-helpers/travis/setup_conda.sh
script:
- py.test --cov psrecord psrecord
+ - python setup.py check --restructuredtext
after_success:
- coveralls
diff --git a/README.md b/README.rst
similarity index 50%
rename from README.md
rename to README.rst
index 6c5ce99..4a94ff3 100644
--- a/README.md
+++ b/README.rst
@@ -1,36 +1,31 @@
-[![Build Status](https://travis-ci.org/astrofrog/psrecord.svg?branch=master)](https://travis-ci.org/astrofrog/psrecord) [![Coverage Status](https://coveralls.io/repos/astrofrog/psrecord/badge.svg)](https://coveralls.io/r/astrofrog/psrecord)
+|Build Status| |Coverage Status|
About
=====
``psrecord`` is a small utility that uses the
-[psutil](https://code.google.com/p/psutil/) library to record the CPU and
-memory activity of a process. The package is still under development and is
-therefore experimental.
+`psutil `__ library to record the CPU
+and memory activity of a process. The package is still under development
+and is therefore experimental.
-The code is released under a Simplified BSD License, which is given in the
-``LICENSE`` file.
+The code is released under a Simplified BSD License, which is given in
+the ``LICENSE`` file.
Requirements
============
-* Python 2.x or 3.x
-* [psutil](https://code.google.com/p/psutil/)
-* [matplotlib](http://www.matplotlib.org) (optional, used for plotting)
+- Python 2.7 or 3.3 and higher
+- `psutil `__
+- `matplotlib `__ (optional, used for
+ plotting)
Installation
============
-To install:
+To install, simply do::
pip install psrecord
-or if you want the latest developer version:
-
- git clone https://github.com/astrofrog/psrecord
- cd psrecord
- python setup.py install
-
Usage
=====
@@ -39,11 +34,15 @@ Basics
To record the CPU and memory activity of an existing process to a file:
+::
+
psrecord 1330 --log activity.txt
-where ``1330`` is an example of a process ID which you can find with ``ps`` or
-``top``. You can also use ``psrecord`` to start up a process by specifying the
-command in quotes:
+where ``1330`` is an example of a process ID which you can find with
+``ps`` or ``top``. You can also use ``psrecord`` to start up a process
+by specifying the command in quotes:
+
+::
psrecord "hyperion model.rtin model.rtout" --log activity.txt
@@ -52,27 +51,35 @@ Plotting
To make a plot of the activity:
+::
+
psrecord 1330 --plot plot.png
This will produce a plot such as:
-![screenshot](screenshot.png)
+.. image:: https://github.com/astrofrog/psrecord/raw/master/screenshot.png
-You can combine these options to write the activity to a file and make a plot
-at the same time:
+You can combine these options to write the activity to a file and make a
+plot at the same time:
+
+::
psrecord 1330 --log activity.txt --plot plot.png
Duration and intervals
----------------------
-By default, the monitoring will continue until the process is stopped. You can
-also specify a maximum duration in seconds:
+By default, the monitoring will continue until the process is stopped.
+You can also specify a maximum duration in seconds:
+
+::
psrecord 1330 --log activity.txt --duration 10
-Finally, the process is polled as often as possible by default, but it is
-possible to set the time between samples in seconds:
+Finally, the process is polled as often as possible by default, but it
+is possible to set the time between samples in seconds:
+
+::
psrecord 1330 --log activity.txt --interval 2
@@ -81,10 +88,17 @@ Subprocesses
To include sub-processes in the CPU and memory stats, use:
+::
+
psrecord 1330 --log activity.txt --include-children
Reporting issues
================
-Please report any issues in the
-[issue tracker](https://github.com/astrofrog/psrecord/issues).
+Please report any issues in the `issue
+tracker `__.
+
+.. |Build Status| image:: https://travis-ci.org/astrofrog/psrecord.svg?branch=master
+ :target: https://travis-ci.org/astrofrog/psrecord
+.. |Coverage Status| image:: https://coveralls.io/repos/astrofrog/psrecord/badge.svg
+ :target: https://coveralls.io/r/astrofrog/psrecord
diff --git a/psrecord/main.py b/psrecord/main.py
index a194d77..b0b9678 100644
--- a/psrecord/main.py
+++ b/psrecord/main.py
@@ -47,10 +47,14 @@ def get_memory(process):
def all_children(pr):
processes = []
+ children = []
try:
+ children = pr.children()
+ except AttributeError:
children = pr.get_children()
- except:
- children = []
+ except Exception:
+ pass
+
for child in children:
processes.append(child)
processes += all_children(child)
@@ -143,6 +147,8 @@ def monitor(pid, logfile=None, plot=None, duration=None, interval=None,
pr_status = pr.status()
except TypeError: # psutil < 2.0
pr_status = pr.status
+ except psutil.NoSuchProcess:
+ break
# Check if process status indicates we should exit
if pr_status in [psutil.STATUS_ZOMBIE, psutil.STATUS_DEAD]:
@@ -167,18 +173,19 @@ def monitor(pid, logfile=None, plot=None, duration=None, interval=None,
if include_children:
for child in all_children(pr):
try:
- current_cpu += child.get_cpu_percent()
- current_mem = child.get_memory_info()
+ current_cpu += get_percent(child)
+ current_mem = get_memory(child)
except:
continue
current_mem_real += current_mem.rss / 1024. ** 2
current_mem_virtual += current_mem.vms / 1024. ** 2
if logfile:
- f.write("{0:12.3f} {1:12.3f} {2:12.3f} {3:12.3f}\n".format(current_time - start_time,
- current_cpu,
- current_mem_real,
- current_mem_virtual))
+ f.write("{0:12.3f} {1:12.3f} {2:12.3f} {3:12.3f}\n".format(
+ current_time - start_time,
+ current_cpu,
+ current_mem_real,
+ current_mem_virtual))
f.flush()
if interval is not None:
diff --git a/psrecord/tests/test_main.py b/psrecord/tests/test_main.py
index 8e7e999..9973349 100644
--- a/psrecord/tests/test_main.py
+++ b/psrecord/tests/test_main.py
@@ -2,7 +2,32 @@
import sys
import subprocess
-from ..main import main, monitor
+import psutil
+from ..main import main, monitor, all_children
+
+TEST_CODE = """
+import subprocess
+p = subprocess.Popen('sleep 5'.split())
+p.wait()
+"""
+
+
+def test_all_children(tmpdir):
+
+ filename = tmpdir.join('test.py').strpath
+
+ with open(filename, 'w') as f:
+ f.write(TEST_CODE)
+
+ p = subprocess.Popen('{0} {1}'.format(sys.executable, filename).split())
+
+ import time
+ time.sleep(1)
+
+ pr = psutil.Process(p.pid)
+ children = all_children(pr)
+ assert len(children) > 0
+ p.kill()
class TestMonitor(object):
@@ -19,6 +44,10 @@ def test_simple(self):
def test_simple_with_interval(self):
monitor(self.p.pid, duration=3, interval=0.1)
+ def test_with_children(self, tmpdir):
+ # Test with current process since it has a subprocess (self.p)
+ monitor(os.getpid(), duration=3, include_children=True)
+
def test_logfile(self, tmpdir):
filename = tmpdir.join('test_logfile').strpath
monitor(self.p.pid, logfile=filename, duration=3)