OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # $Id: top.py 1211 2011-10-28 19:58:18Z g.rodola $ |
| 4 # |
| 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 |
| 7 # found in the LICENSE file. |
| 8 |
| 9 """ |
| 10 A clone of top / htop. |
| 11 |
| 12 Author: Giampaolo Rodola' <g.rodola@gmail.com> |
| 13 """ |
| 14 |
| 15 import os |
| 16 import sys |
| 17 if os.name != 'posix': |
| 18 sys.exit('platform not supported') |
| 19 import time |
| 20 import curses |
| 21 import atexit |
| 22 from datetime import datetime, timedelta |
| 23 |
| 24 import psutil |
| 25 |
| 26 |
| 27 # --- curses stuff |
| 28 def tear_down(): |
| 29 win.keypad(0) |
| 30 curses.nocbreak() |
| 31 curses.echo() |
| 32 curses.endwin() |
| 33 |
| 34 win = curses.initscr() |
| 35 atexit.register(tear_down) |
| 36 curses.endwin() |
| 37 lineno = 0 |
| 38 |
| 39 def print_line(line, highlight=False): |
| 40 """A thin wrapper around curses's addstr().""" |
| 41 global lineno |
| 42 try: |
| 43 if highlight: |
| 44 line += " " * (win.getmaxyx()[1] - len(line)) |
| 45 win.addstr(lineno, 0, line, curses.A_REVERSE) |
| 46 else: |
| 47 win.addstr(lineno, 0, line, 0) |
| 48 except curses.error: |
| 49 lineno = 0 |
| 50 win.refresh() |
| 51 raise |
| 52 else: |
| 53 lineno += 1 |
| 54 # --- /curses stuff |
| 55 |
| 56 |
| 57 def bytes2human(n): |
| 58 """ |
| 59 >>> bytes2human(10000) |
| 60 '9.8 K/s' |
| 61 >>> bytes2human(100001221) |
| 62 '95.4 M/s' |
| 63 """ |
| 64 symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') |
| 65 prefix = {} |
| 66 for i, s in enumerate(symbols): |
| 67 prefix[s] = 1 << (i+1)*10 |
| 68 for s in reversed(symbols): |
| 69 if n >= prefix[s]: |
| 70 value = int(float(n) / prefix[s]) |
| 71 return '%s%s' % (value, s) |
| 72 return "0B" |
| 73 |
| 74 procs = [p for p in psutil.process_iter()] # the current process list |
| 75 |
| 76 def poll(interval): |
| 77 # add new processes to procs list; processes which have gone |
| 78 # in meantime will be removed from the list later |
| 79 cpids = [p.pid for p in procs] |
| 80 for p in psutil.process_iter(): |
| 81 if p.pid not in cpids: |
| 82 procs.append(p) |
| 83 |
| 84 # sleep some time |
| 85 time.sleep(interval) |
| 86 |
| 87 procs_status = {} |
| 88 # then retrieve the same info again |
| 89 for p in procs[:]: |
| 90 try: |
| 91 p._username = p.username |
| 92 p._nice = p.nice |
| 93 p._meminfo = p.get_memory_info() |
| 94 p._mempercent = p.get_memory_percent() |
| 95 p._cpu_percent = p.get_cpu_percent(interval=0) |
| 96 p._cpu_times = p.get_cpu_times() |
| 97 p._name = p.name |
| 98 try: |
| 99 procs_status[str(p.status)] += 1 |
| 100 except KeyError: |
| 101 procs_status[str(p.status)] = 1 |
| 102 except psutil.NoSuchProcess: |
| 103 procs.remove(p) |
| 104 |
| 105 # return processes sorted by CPU percent usage |
| 106 processes = sorted(procs, key=lambda p: p._cpu_percent, reverse=True) |
| 107 return (processes, procs_status) |
| 108 |
| 109 def print_header(procs_status): |
| 110 """Print system-related info, above the process list.""" |
| 111 |
| 112 def get_dashes(perc): |
| 113 dashes = "|" * int((float(perc) / 10 * 4)) |
| 114 empty_dashes = " " * (40 - len(dashes)) |
| 115 return dashes, empty_dashes |
| 116 |
| 117 # cpu usage |
| 118 for lineno, perc in enumerate(psutil.cpu_percent(interval=0, percpu=True)): |
| 119 dashes, empty_dashes = get_dashes(perc) |
| 120 print_line(" CPU%-2s [%s%s] %5s%%" % (lineno, dashes, empty_dashes, perc
)) |
| 121 |
| 122 # physmem usage |
| 123 phymem = psutil.phymem_usage() |
| 124 dashes, empty_dashes = get_dashes(phymem.percent) |
| 125 buffers = getattr(psutil, 'phymem_buffers', lambda: 0)() |
| 126 cached = getattr(psutil, 'cached_phymem', lambda: 0)() |
| 127 used = phymem.total - (phymem.free + buffers + cached) |
| 128 line = " Mem [%s%s] %5s%% %6s/%s" % (dashes, empty_dashes, |
| 129 phymem.percent, |
| 130 str(used / 1024 / 1024) + "M", |
| 131 str(phymem.total / 1024 / 1024) + "M"
) |
| 132 print_line(line) |
| 133 |
| 134 # swap usage |
| 135 vmem = psutil.virtmem_usage() |
| 136 dashes, empty_dashes = get_dashes(vmem.percent) |
| 137 line = " Swap [%s%s] %5s%% %6s/%s" % (dashes, empty_dashes, |
| 138 vmem.percent, |
| 139 str(vmem.used / 1024 / 1024) + "M", |
| 140 str(vmem.total / 1024 / 1024) + "M") |
| 141 print_line(line) |
| 142 |
| 143 # procesess number and status |
| 144 st = [] |
| 145 for x, y in procs_status.iteritems(): |
| 146 if y: |
| 147 st.append("%s=%s" % (x, y)) |
| 148 st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1) |
| 149 print_line(" Processes: %s (%s)" % (len(procs), ' '.join(st))) |
| 150 # load average, uptime |
| 151 uptime = datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME) |
| 152 av1, av2, av3 = os.getloadavg() |
| 153 line = " Load average: %.2f %.2f %.2f Uptime: %s" \ |
| 154 % (av1, av2, av3, str(uptime).split('.')[0]) |
| 155 print_line(line) |
| 156 |
| 157 def refresh_window(procs, procs_status): |
| 158 """Print results on screen by using curses.""" |
| 159 curses.endwin() |
| 160 templ = "%-6s %-8s %4s %5s %5s %6s %4s %9s %2s" |
| 161 win.erase() |
| 162 header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%", |
| 163 "TIME+", "NAME") |
| 164 print_header(procs_status) |
| 165 print_line("") |
| 166 print_line(header, highlight=True) |
| 167 for p in procs: |
| 168 # TIME+ column shows process CPU cumulative time and |
| 169 # is expressed as: mm:ss.ms |
| 170 ctime = timedelta(seconds=sum(p._cpu_times)) |
| 171 ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60, |
| 172 str((ctime.seconds % 60)).zfill(2), |
| 173 str(ctime.microseconds)[:2]) |
| 174 line = templ % (p.pid, |
| 175 p._username[:8], |
| 176 p._nice, |
| 177 bytes2human(p._meminfo.vms), |
| 178 bytes2human(p._meminfo.rss), |
| 179 p._cpu_percent, |
| 180 round(p._mempercent, 1), |
| 181 ctime, |
| 182 p._name, |
| 183 ) |
| 184 try: |
| 185 print_line(line) |
| 186 except curses.error: |
| 187 break |
| 188 win.refresh() |
| 189 |
| 190 |
| 191 def main(): |
| 192 try: |
| 193 interval = 0 |
| 194 while 1: |
| 195 args = poll(interval) |
| 196 refresh_window(*args) |
| 197 interval = 1 |
| 198 except (KeyboardInterrupt, SystemExit): |
| 199 print |
| 200 |
| 201 if __name__ == '__main__': |
| 202 main() |
OLD | NEW |