diff options
Diffstat (limited to 'python/psutil/examples/iotop.py')
-rwxr-xr-x | python/psutil/examples/iotop.py | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/python/psutil/examples/iotop.py b/python/psutil/examples/iotop.py new file mode 100755 index 000000000..16ac7fbf6 --- /dev/null +++ b/python/psutil/examples/iotop.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +A clone of iotop (http://guichaz.free.fr/iotop/) showing real time +disk I/O statistics. + +It works on Linux only (FreeBSD and OSX are missing support for IO +counters). +It doesn't work on Windows as curses module is required. + +Example output: + +$ python examples/iotop.py +Total DISK READ: 0.00 B/s | Total DISK WRITE: 472.00 K/s +PID USER DISK READ DISK WRITE COMMAND +13155 giampao 0.00 B/s 428.00 K/s /usr/bin/google-chrome-beta +3260 giampao 0.00 B/s 0.00 B/s bash +3779 giampao 0.00 B/s 0.00 B/s gnome-session --session=ubuntu +3830 giampao 0.00 B/s 0.00 B/s /usr/bin/dbus-launch +3831 giampao 0.00 B/s 0.00 B/s //bin/dbus-daemon --fork --print-pid 5 +3841 giampao 0.00 B/s 0.00 B/s /usr/lib/at-spi-bus-launcher +3845 giampao 0.00 B/s 0.00 B/s /bin/dbus-daemon +3848 giampao 0.00 B/s 0.00 B/s /usr/lib/at-spi2-core/at-spi2-registryd +3862 giampao 0.00 B/s 0.00 B/s /usr/lib/gnome-settings-daemon + +Author: Giampaolo Rodola' <g.rodola@gmail.com> +""" + +import atexit +import time +import sys +try: + import curses +except ImportError: + sys.exit('platform not supported') + +import psutil + + +# --- curses stuff +def tear_down(): + win.keypad(0) + curses.nocbreak() + curses.echo() + curses.endwin() + +win = curses.initscr() +atexit.register(tear_down) +curses.endwin() +lineno = 0 + + +def print_line(line, highlight=False): + """A thin wrapper around curses's addstr().""" + global lineno + try: + if highlight: + line += " " * (win.getmaxyx()[1] - len(line)) + win.addstr(lineno, 0, line, curses.A_REVERSE) + else: + win.addstr(lineno, 0, line, 0) + except curses.error: + lineno = 0 + win.refresh() + raise + else: + lineno += 1 +# --- /curses stuff + + +def bytes2human(n): + """ + >>> bytes2human(10000) + '9.8 K/s' + >>> bytes2human(100001221) + '95.4 M/s' + """ + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + prefix = {} + for i, s in enumerate(symbols): + prefix[s] = 1 << (i + 1) * 10 + for s in reversed(symbols): + if n >= prefix[s]: + value = float(n) / prefix[s] + return '%.2f %s/s' % (value, s) + return '%.2f B/s' % (n) + + +def poll(interval): + """Calculate IO usage by comparing IO statics before and + after the interval. + Return a tuple including all currently running processes + sorted by IO activity and total disks I/O activity. + """ + # first get a list of all processes and disk io counters + procs = [p for p in psutil.process_iter()] + for p in procs[:]: + try: + p._before = p.io_counters() + except psutil.Error: + procs.remove(p) + continue + disks_before = psutil.disk_io_counters() + + # sleep some time + time.sleep(interval) + + # then retrieve the same info again + for p in procs[:]: + try: + p._after = p.io_counters() + p._cmdline = ' '.join(p.cmdline()) + if not p._cmdline: + p._cmdline = p.name() + p._username = p.username() + except (psutil.NoSuchProcess, psutil.ZombieProcess): + procs.remove(p) + disks_after = psutil.disk_io_counters() + + # finally calculate results by comparing data before and + # after the interval + for p in procs: + p._read_per_sec = p._after.read_bytes - p._before.read_bytes + p._write_per_sec = p._after.write_bytes - p._before.write_bytes + p._total = p._read_per_sec + p._write_per_sec + + disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes + disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes + + # sort processes by total disk IO so that the more intensive + # ones get listed first + processes = sorted(procs, key=lambda p: p._total, reverse=True) + + return (processes, disks_read_per_sec, disks_write_per_sec) + + +def refresh_window(procs, disks_read, disks_write): + """Print results on screen by using curses.""" + curses.endwin() + templ = "%-5s %-7s %11s %11s %s" + win.erase() + + disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \ + % (bytes2human(disks_read), bytes2human(disks_write)) + print_line(disks_tot) + + header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND") + print_line(header, highlight=True) + + for p in procs: + line = templ % ( + p.pid, + p._username[:7], + bytes2human(p._read_per_sec), + bytes2human(p._write_per_sec), + p._cmdline) + try: + print_line(line) + except curses.error: + break + win.refresh() + + +def main(): + try: + interval = 0 + while True: + args = poll(interval) + refresh_window(*args) + interval = 1 + except (KeyboardInterrupt, SystemExit): + pass + +if __name__ == '__main__': + main() |