python/psutil/examples/iotop.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rwxr-xr-x

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 #!/usr/bin/env python
     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 """
     8 A clone of iotop (http://guichaz.free.fr/iotop/) showing real time
     9 disk I/O statistics.
    11 It works on Linux only (FreeBSD and OSX are missing support for IO
    12 counters).
    13 It doesn't work on Windows as curses module is required.
    15 Author: Giampaolo Rodola' <g.rodola@gmail.com>
    16 """
    18 import os
    19 import sys
    20 import psutil
    21 if not hasattr(psutil.Process, 'get_io_counters') or os.name != 'posix':
    22     sys.exit('platform not supported')
    23 import time
    24 import curses
    25 import atexit
    28 # --- curses stuff
    29 def tear_down():
    30     win.keypad(0)
    31     curses.nocbreak()
    32     curses.echo()
    33     curses.endwin()
    35 win = curses.initscr()
    36 atexit.register(tear_down)
    37 curses.endwin()
    38 lineno = 0
    40 def print_line(line, highlight=False):
    41     """A thin wrapper around curses's addstr()."""
    42     global lineno
    43     try:
    44         if highlight:
    45             line += " " * (win.getmaxyx()[1] - len(line))
    46             win.addstr(lineno, 0, line, curses.A_REVERSE)
    47         else:
    48             win.addstr(lineno, 0, line, 0)
    49     except curses.error:
    50         lineno = 0
    51         win.refresh()
    52         raise
    53     else:
    54         lineno += 1
    55 # --- /curses stuff
    58 def bytes2human(n):
    59     """
    60     >>> bytes2human(10000)
    61     '9.8 K/s'
    62     >>> bytes2human(100001221)
    63     '95.4 M/s'
    64     """
    65     symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
    66     prefix = {}
    67     for i, s in enumerate(symbols):
    68         prefix[s] = 1 << (i+1)*10
    69     for s in reversed(symbols):
    70         if n >= prefix[s]:
    71             value = float(n) / prefix[s]
    72             return '%.2f %s/s' % (value, s)
    73     return '%.2f B/s' % (n)
    75 def poll(interval):
    76     """Calculate IO usage by comparing IO statics before and
    77     after the interval.
    78     Return a tuple including all currently running processes
    79     sorted by IO activity and total disks I/O activity.
    80     """
    81     # first get a list of all processes and disk io counters
    82     procs = [p for p in psutil.process_iter()]
    83     for p in procs[:]:
    84         try:
    85             p._before = p.get_io_counters()
    86         except psutil.Error:
    87             procs.remove(p)
    88             continue
    89     disks_before = psutil.disk_io_counters()
    91     # sleep some time
    92     time.sleep(interval)
    94     # then retrieve the same info again
    95     for p in procs[:]:
    96         try:
    97             p._after = p.get_io_counters()
    98             p._cmdline = ' '.join(p.cmdline)
    99             if not p._cmdline:
   100                 p._cmdline = p.name
   101             p._username = p.username
   102         except psutil.NoSuchProcess:
   103             procs.remove(p)
   104     disks_after = psutil.disk_io_counters()
   106     # finally calculate results by comparing data before and
   107     # after the interval
   108     for p in procs:
   109         p._read_per_sec = p._after.read_bytes - p._before.read_bytes
   110         p._write_per_sec = p._after.write_bytes - p._before.write_bytes
   111         p._total = p._read_per_sec + p._write_per_sec
   113     disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes
   114     disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes
   116     # sort processes by total disk IO so that the more intensive
   117     # ones get listed first
   118     processes = sorted(procs, key=lambda p: p._total, reverse=True)
   120     return (processes, disks_read_per_sec, disks_write_per_sec)
   123 def refresh_window(procs, disks_read, disks_write):
   124     """Print results on screen by using curses."""
   125     curses.endwin()
   126     templ = "%-5s %-7s %11s %11s  %s"
   127     win.erase()
   129     disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \
   130                 % (bytes2human(disks_read), bytes2human(disks_write))
   131     print_line(disks_tot)
   133     header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND")
   134     print_line(header, highlight=True)
   136     for p in procs:
   137         line = templ % (p.pid,
   138                         p._username[:7],
   139                         bytes2human(p._read_per_sec),
   140                         bytes2human(p._write_per_sec),
   141                         p._cmdline)
   142         try:
   143             print_line(line)
   144         except curses.error:
   145             break
   146     win.refresh()
   148 def main():
   149     try:
   150         interval = 0
   151         while 1:
   152             args = poll(interval)
   153             refresh_window(*args)
   154             interval = 1
   155     except (KeyboardInterrupt, SystemExit):
   156         pass
   158 if __name__ == '__main__':
   159     main()

mercurial