3
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
4
# Use of this source code is governed by a BSD-style license that can be
5
# found in the LICENSE file.
7
"""A clone of iotop (http://guichaz.free.fr/iotop/) showing real time
10
It works on Linux only (FreeBSD and macOS are missing support for IO
12
It doesn't work on Windows as curses module is required.
16
$ python3 scripts/iotop.py
17
Total DISK READ: 0.00 B/s | Total DISK WRITE: 472.00 K/s
18
PID USER DISK READ DISK WRITE COMMAND
19
13155 giampao 0.00 B/s 428.00 K/s /usr/bin/google-chrome-beta
20
3260 giampao 0.00 B/s 0.00 B/s bash
21
3779 giampao 0.00 B/s 0.00 B/s gnome-session --session=ubuntu
22
3830 giampao 0.00 B/s 0.00 B/s /usr/bin/dbus-launch
23
3831 giampao 0.00 B/s 0.00 B/s //bin/dbus-daemon --fork --print-pid 5
24
3841 giampao 0.00 B/s 0.00 B/s /usr/lib/at-spi-bus-launcher
25
3845 giampao 0.00 B/s 0.00 B/s /bin/dbus-daemon
26
3848 giampao 0.00 B/s 0.00 B/s /usr/lib/at-spi2-core/at-spi2-registryd
27
3862 giampao 0.00 B/s 0.00 B/s /usr/lib/gnome-settings-daemon
29
Author: Giampaolo Rodola' <g.rodola@gmail.com>
39
sys.exit('platform not supported')
42
from psutil._common import bytes2human
49
def printl(line, highlight=False):
50
"""A thin wrapper around curses's addstr()."""
54
line += " " * (win.getmaxyx()[1] - len(line))
55
win.addstr(lineno, 0, line, curses.A_REVERSE)
57
win.addstr(lineno, 0, line, 0)
67
"""Calculate IO usage by comparing IO statistics before and
69
Return a tuple including all currently running processes
70
sorted by IO activity and total disks I/O activity.
72
# first get a list of all processes and disk io counters
73
procs = list(psutil.process_iter())
76
p._before = p.io_counters()
80
disks_before = psutil.disk_io_counters()
85
# then retrieve the same info again
89
p._after = p.io_counters()
90
p._cmdline = ' '.join(p.cmdline())
93
p._username = p.username()
94
except (psutil.NoSuchProcess, psutil.ZombieProcess):
96
disks_after = psutil.disk_io_counters()
98
# finally calculate results by comparing data before and
101
p._read_per_sec = p._after.read_bytes - p._before.read_bytes
102
p._write_per_sec = p._after.write_bytes - p._before.write_bytes
103
p._total = p._read_per_sec + p._write_per_sec
105
disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes
106
disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes
108
# sort processes by total disk IO so that the more intensive
109
# ones get listed first
110
processes = sorted(procs, key=lambda p: p._total, reverse=True)
112
return (processes, disks_read_per_sec, disks_write_per_sec)
115
def refresh_window(procs, disks_read, disks_write):
116
"""Print results on screen by using curses."""
118
templ = "%-5s %-7s %11s %11s %s"
121
disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" % (
122
bytes2human(disks_read),
123
bytes2human(disks_write),
127
header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND")
128
printl(header, highlight=True)
134
bytes2human(p._read_per_sec),
135
bytes2human(p._write_per_sec),
147
curses.use_default_colors()
148
for i in range(curses.COLORS):
149
curses.init_pair(i + 1, i, -1)
167
if win.getch() == ord('q'):
169
args = poll(interval)
170
refresh_window(*args)
174
except (KeyboardInterrupt, SystemExit):
180
if __name__ == '__main__':