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 |