| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # $Id: iotop.py 1143 2011-10-05 19:11:59Z g.rodola $ | 3 # $Id: iotop.py 1213 2011-10-29 03:30:41Z g.rodola $ |
| 4 # | 4 # |
| 5 # Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. | 5 # Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 6 # Use of this source code is governed by a BSD-style license that can be | 6 # Use of this source code is governed by a BSD-style license that can be |
| 7 # found in the LICENSE file. | 7 # found in the LICENSE file. |
| 8 | 8 |
| 9 """ | 9 """ |
| 10 A clone of iotop (http://guichaz.free.fr/iotop/) showing real time | 10 A clone of iotop (http://guichaz.free.fr/iotop/) showing real time |
| 11 disk I/O statistics. | 11 disk I/O statistics. |
| 12 | 12 |
| 13 It works on UNIX only as curses module is not available on Windows. | 13 It works on Linux only (FreeBSD and OSX are missing support for IO |
| 14 counters). |
| 15 It doesn't work on Windows as curses module is required. |
| 14 | 16 |
| 15 Author: Giampaolo Rodola' <g.rodola@gmail.com> | 17 Author: Giampaolo Rodola' <g.rodola@gmail.com> |
| 16 """ | 18 """ |
| 17 | 19 |
| 20 import os |
| 21 import sys |
| 22 import psutil |
| 23 if not hasattr(psutil.Process, 'get_io_counters') or os.name != 'posix': |
| 24 sys.exit('platform not supported') |
| 18 import time | 25 import time |
| 19 import curses | 26 import curses |
| 20 import atexit | 27 import atexit |
| 21 | 28 |
| 22 import psutil | 29 |
| 30 # --- curses stuff |
| 31 def tear_down(): |
| 32 win.keypad(0) |
| 33 curses.nocbreak() |
| 34 curses.echo() |
| 35 curses.endwin() |
| 23 | 36 |
| 24 win = curses.initscr() | 37 win = curses.initscr() |
| 38 atexit.register(tear_down) |
| 39 curses.endwin() |
| 40 lineno = 0 |
| 41 |
| 42 def print_line(line, highlight=False): |
| 43 """A thin wrapper around curses's addstr().""" |
| 44 global lineno |
| 45 try: |
| 46 if highlight: |
| 47 line += " " * (win.getmaxyx()[1] - len(line)) |
| 48 win.addstr(lineno, 0, line, curses.A_REVERSE) |
| 49 else: |
| 50 win.addstr(lineno, 0, line, 0) |
| 51 except curses.error: |
| 52 lineno = 0 |
| 53 win.refresh() |
| 54 raise |
| 55 else: |
| 56 lineno += 1 |
| 57 # --- /curses stuff |
| 58 |
| 25 | 59 |
| 26 def bytes2human(n): | 60 def bytes2human(n): |
| 27 """ | 61 """ |
| 28 >>> bytes2human(10000) | 62 >>> bytes2human(10000) |
| 29 '9.8 K/s' | 63 '9.8 K/s' |
| 30 >>> bytes2human(100001221) | 64 >>> bytes2human(100001221) |
| 31 '95.4 M/s' | 65 '95.4 M/s' |
| 32 """ | 66 """ |
| 33 symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') | 67 symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') |
| 34 prefix = {} | 68 prefix = {} |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 80 | 114 |
| 81 disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes | 115 disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes |
| 82 disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes | 116 disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes |
| 83 | 117 |
| 84 # sort processes by total disk IO so that the more intensive | 118 # sort processes by total disk IO so that the more intensive |
| 85 # ones get listed first | 119 # ones get listed first |
| 86 processes = sorted(procs, key=lambda p: p._total, reverse=True) | 120 processes = sorted(procs, key=lambda p: p._total, reverse=True) |
| 87 | 121 |
| 88 return (processes, disks_read_per_sec, disks_write_per_sec) | 122 return (processes, disks_read_per_sec, disks_write_per_sec) |
| 89 | 123 |
| 90 def run(win): | 124 |
| 125 def refresh_window(procs, disks_read, disks_write): |
| 91 """Print results on screen by using curses.""" | 126 """Print results on screen by using curses.""" |
| 92 curses.endwin() | 127 curses.endwin() |
| 93 templ = "%-5s %-7s %11s %11s %s" | 128 templ = "%-5s %-7s %11s %11s %s" |
| 94 interval = 0 | 129 win.erase() |
| 95 while 1: | |
| 96 procs, disks_read, disks_write = poll(interval) | |
| 97 win.erase() | |
| 98 | 130 |
| 99 disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \ | 131 disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \ |
| 100 % (bytes2human(disks_read), bytes2human(disks_write)) | 132 % (bytes2human(disks_read), bytes2human(disks_write)) |
| 101 win.addstr(0, 0, disks_tot) | 133 print_line(disks_tot) |
| 102 | 134 |
| 103 header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND") | 135 header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND") |
| 104 header += " " * (win.getmaxyx()[1] - len(header)) | 136 print_line(header, highlight=True) |
| 105 win.addstr(1, 0, header, curses.A_REVERSE) | |
| 106 | 137 |
| 107 lineno = 2 | 138 for p in procs: |
| 108 for p in procs: | 139 line = templ % (p.pid, |
| 109 line = templ % (p.pid, | 140 p._username[:7], |
| 110 p._username[:7], | 141 bytes2human(p._read_per_sec), |
| 111 bytes2human(p._read_per_sec), | 142 bytes2human(p._write_per_sec), |
| 112 bytes2human(p._write_per_sec), | 143 p._cmdline) |
| 113 p._cmdline) | 144 try: |
| 114 try: | 145 print_line(line) |
| 115 win.addstr(lineno, 0, line) | 146 except curses.error: |
| 116 except curses.error: | 147 break |
| 117 break | 148 win.refresh() |
| 118 win.refresh() | |
| 119 lineno += 1 | |
| 120 interval = 1 | |
| 121 | 149 |
| 122 def main(): | 150 def main(): |
| 123 def tear_down(): | |
| 124 win.keypad(0) | |
| 125 curses.nocbreak() | |
| 126 curses.echo() | |
| 127 curses.endwin() | |
| 128 | |
| 129 atexit.register(tear_down) | |
| 130 try: | 151 try: |
| 131 run(win) | 152 interval = 0 |
| 153 while 1: |
| 154 args = poll(interval) |
| 155 refresh_window(*args) |
| 156 interval = 1 |
| 132 except (KeyboardInterrupt, SystemExit): | 157 except (KeyboardInterrupt, SystemExit): |
| 133 pass | 158 print |
| 134 | 159 |
| 135 if __name__ == '__main__': | 160 if __name__ == '__main__': |
| 136 main() | 161 main() |
| OLD | NEW |