1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/psutil/examples/iotop.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,159 @@ 1.4 +#!/usr/bin/env python 1.5 + 1.6 +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. 1.7 +# Use of this source code is governed by a BSD-style license that can be 1.8 +# found in the LICENSE file. 1.9 + 1.10 +""" 1.11 +A clone of iotop (http://guichaz.free.fr/iotop/) showing real time 1.12 +disk I/O statistics. 1.13 + 1.14 +It works on Linux only (FreeBSD and OSX are missing support for IO 1.15 +counters). 1.16 +It doesn't work on Windows as curses module is required. 1.17 + 1.18 +Author: Giampaolo Rodola' <g.rodola@gmail.com> 1.19 +""" 1.20 + 1.21 +import os 1.22 +import sys 1.23 +import psutil 1.24 +if not hasattr(psutil.Process, 'get_io_counters') or os.name != 'posix': 1.25 + sys.exit('platform not supported') 1.26 +import time 1.27 +import curses 1.28 +import atexit 1.29 + 1.30 + 1.31 +# --- curses stuff 1.32 +def tear_down(): 1.33 + win.keypad(0) 1.34 + curses.nocbreak() 1.35 + curses.echo() 1.36 + curses.endwin() 1.37 + 1.38 +win = curses.initscr() 1.39 +atexit.register(tear_down) 1.40 +curses.endwin() 1.41 +lineno = 0 1.42 + 1.43 +def print_line(line, highlight=False): 1.44 + """A thin wrapper around curses's addstr().""" 1.45 + global lineno 1.46 + try: 1.47 + if highlight: 1.48 + line += " " * (win.getmaxyx()[1] - len(line)) 1.49 + win.addstr(lineno, 0, line, curses.A_REVERSE) 1.50 + else: 1.51 + win.addstr(lineno, 0, line, 0) 1.52 + except curses.error: 1.53 + lineno = 0 1.54 + win.refresh() 1.55 + raise 1.56 + else: 1.57 + lineno += 1 1.58 +# --- /curses stuff 1.59 + 1.60 + 1.61 +def bytes2human(n): 1.62 + """ 1.63 + >>> bytes2human(10000) 1.64 + '9.8 K/s' 1.65 + >>> bytes2human(100001221) 1.66 + '95.4 M/s' 1.67 + """ 1.68 + symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') 1.69 + prefix = {} 1.70 + for i, s in enumerate(symbols): 1.71 + prefix[s] = 1 << (i+1)*10 1.72 + for s in reversed(symbols): 1.73 + if n >= prefix[s]: 1.74 + value = float(n) / prefix[s] 1.75 + return '%.2f %s/s' % (value, s) 1.76 + return '%.2f B/s' % (n) 1.77 + 1.78 +def poll(interval): 1.79 + """Calculate IO usage by comparing IO statics before and 1.80 + after the interval. 1.81 + Return a tuple including all currently running processes 1.82 + sorted by IO activity and total disks I/O activity. 1.83 + """ 1.84 + # first get a list of all processes and disk io counters 1.85 + procs = [p for p in psutil.process_iter()] 1.86 + for p in procs[:]: 1.87 + try: 1.88 + p._before = p.get_io_counters() 1.89 + except psutil.Error: 1.90 + procs.remove(p) 1.91 + continue 1.92 + disks_before = psutil.disk_io_counters() 1.93 + 1.94 + # sleep some time 1.95 + time.sleep(interval) 1.96 + 1.97 + # then retrieve the same info again 1.98 + for p in procs[:]: 1.99 + try: 1.100 + p._after = p.get_io_counters() 1.101 + p._cmdline = ' '.join(p.cmdline) 1.102 + if not p._cmdline: 1.103 + p._cmdline = p.name 1.104 + p._username = p.username 1.105 + except psutil.NoSuchProcess: 1.106 + procs.remove(p) 1.107 + disks_after = psutil.disk_io_counters() 1.108 + 1.109 + # finally calculate results by comparing data before and 1.110 + # after the interval 1.111 + for p in procs: 1.112 + p._read_per_sec = p._after.read_bytes - p._before.read_bytes 1.113 + p._write_per_sec = p._after.write_bytes - p._before.write_bytes 1.114 + p._total = p._read_per_sec + p._write_per_sec 1.115 + 1.116 + disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes 1.117 + disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes 1.118 + 1.119 + # sort processes by total disk IO so that the more intensive 1.120 + # ones get listed first 1.121 + processes = sorted(procs, key=lambda p: p._total, reverse=True) 1.122 + 1.123 + return (processes, disks_read_per_sec, disks_write_per_sec) 1.124 + 1.125 + 1.126 +def refresh_window(procs, disks_read, disks_write): 1.127 + """Print results on screen by using curses.""" 1.128 + curses.endwin() 1.129 + templ = "%-5s %-7s %11s %11s %s" 1.130 + win.erase() 1.131 + 1.132 + disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \ 1.133 + % (bytes2human(disks_read), bytes2human(disks_write)) 1.134 + print_line(disks_tot) 1.135 + 1.136 + header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND") 1.137 + print_line(header, highlight=True) 1.138 + 1.139 + for p in procs: 1.140 + line = templ % (p.pid, 1.141 + p._username[:7], 1.142 + bytes2human(p._read_per_sec), 1.143 + bytes2human(p._write_per_sec), 1.144 + p._cmdline) 1.145 + try: 1.146 + print_line(line) 1.147 + except curses.error: 1.148 + break 1.149 + win.refresh() 1.150 + 1.151 +def main(): 1.152 + try: 1.153 + interval = 0 1.154 + while 1: 1.155 + args = poll(interval) 1.156 + refresh_window(*args) 1.157 + interval = 1 1.158 + except (KeyboardInterrupt, SystemExit): 1.159 + pass 1.160 + 1.161 +if __name__ == '__main__': 1.162 + main()