michael@0: #!/usr/bin/env python michael@0: michael@0: # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: """ michael@0: A clone of top / htop. michael@0: michael@0: Author: Giampaolo Rodola' michael@0: """ michael@0: michael@0: import os michael@0: import sys michael@0: if os.name != 'posix': michael@0: sys.exit('platform not supported') michael@0: import time michael@0: import curses michael@0: import atexit michael@0: from datetime import datetime, timedelta michael@0: michael@0: import psutil michael@0: michael@0: michael@0: # --- curses stuff michael@0: def tear_down(): michael@0: win.keypad(0) michael@0: curses.nocbreak() michael@0: curses.echo() michael@0: curses.endwin() michael@0: michael@0: win = curses.initscr() michael@0: atexit.register(tear_down) michael@0: curses.endwin() michael@0: lineno = 0 michael@0: michael@0: def print_line(line, highlight=False): michael@0: """A thin wrapper around curses's addstr().""" michael@0: global lineno michael@0: try: michael@0: if highlight: michael@0: line += " " * (win.getmaxyx()[1] - len(line)) michael@0: win.addstr(lineno, 0, line, curses.A_REVERSE) michael@0: else: michael@0: win.addstr(lineno, 0, line, 0) michael@0: except curses.error: michael@0: lineno = 0 michael@0: win.refresh() michael@0: raise michael@0: else: michael@0: lineno += 1 michael@0: # --- /curses stuff michael@0: michael@0: michael@0: def bytes2human(n): michael@0: """ michael@0: >>> bytes2human(10000) michael@0: '9K' michael@0: >>> bytes2human(100001221) michael@0: '95M' michael@0: """ michael@0: symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') michael@0: prefix = {} michael@0: for i, s in enumerate(symbols): michael@0: prefix[s] = 1 << (i+1)*10 michael@0: for s in reversed(symbols): michael@0: if n >= prefix[s]: michael@0: value = int(float(n) / prefix[s]) michael@0: return '%s%s' % (value, s) michael@0: return "%sB" % n michael@0: michael@0: def poll(interval): michael@0: # sleep some time michael@0: time.sleep(interval) michael@0: procs = [] michael@0: procs_status = {} michael@0: for p in psutil.process_iter(): michael@0: try: michael@0: p.dict = p.as_dict(['username', 'get_nice', 'get_memory_info', michael@0: 'get_memory_percent', 'get_cpu_percent', michael@0: 'get_cpu_times', 'name', 'status']) michael@0: try: michael@0: procs_status[str(p.dict['status'])] += 1 michael@0: except KeyError: michael@0: procs_status[str(p.dict['status'])] = 1 michael@0: except psutil.NoSuchProcess: michael@0: pass michael@0: else: michael@0: procs.append(p) michael@0: michael@0: # return processes sorted by CPU percent usage michael@0: processes = sorted(procs, key=lambda p: p.dict['cpu_percent'], reverse=True) michael@0: return (processes, procs_status) michael@0: michael@0: def print_header(procs_status, num_procs): michael@0: """Print system-related info, above the process list.""" michael@0: michael@0: def get_dashes(perc): michael@0: dashes = "|" * int((float(perc) / 10 * 4)) michael@0: empty_dashes = " " * (40 - len(dashes)) michael@0: return dashes, empty_dashes michael@0: michael@0: # cpu usage michael@0: for cpu_num, perc in enumerate(psutil.cpu_percent(interval=0, percpu=True)): michael@0: dashes, empty_dashes = get_dashes(perc) michael@0: print_line(" CPU%-2s [%s%s] %5s%%" % (cpu_num, dashes, empty_dashes, michael@0: perc)) michael@0: mem = psutil.virtual_memory() michael@0: dashes, empty_dashes = get_dashes(mem.percent) michael@0: used = mem.total - mem.available michael@0: line = " Mem [%s%s] %5s%% %6s/%s" % ( michael@0: dashes, empty_dashes, michael@0: mem.percent, michael@0: str(int(used / 1024 / 1024)) + "M", michael@0: str(int(mem.total / 1024 / 1024)) + "M" michael@0: ) michael@0: print_line(line) michael@0: michael@0: # swap usage michael@0: swap = psutil.swap_memory() michael@0: dashes, empty_dashes = get_dashes(swap.percent) michael@0: line = " Swap [%s%s] %5s%% %6s/%s" % ( michael@0: dashes, empty_dashes, michael@0: swap.percent, michael@0: str(int(swap.used / 1024 / 1024)) + "M", michael@0: str(int(swap.total / 1024 / 1024)) + "M" michael@0: ) michael@0: print_line(line) michael@0: michael@0: # processes number and status michael@0: st = [] michael@0: for x, y in procs_status.items(): michael@0: if y: michael@0: st.append("%s=%s" % (x, y)) michael@0: st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1) michael@0: print_line(" Processes: %s (%s)" % (num_procs, ' '.join(st))) michael@0: # load average, uptime michael@0: uptime = datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME) michael@0: av1, av2, av3 = os.getloadavg() michael@0: line = " Load average: %.2f %.2f %.2f Uptime: %s" \ michael@0: % (av1, av2, av3, str(uptime).split('.')[0]) michael@0: print_line(line) michael@0: michael@0: def refresh_window(procs, procs_status): michael@0: """Print results on screen by using curses.""" michael@0: curses.endwin() michael@0: templ = "%-6s %-8s %4s %5s %5s %6s %4s %9s %2s" michael@0: win.erase() michael@0: header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%", michael@0: "TIME+", "NAME") michael@0: print_header(procs_status, len(procs)) michael@0: print_line("") michael@0: print_line(header, highlight=True) michael@0: for p in procs: michael@0: # TIME+ column shows process CPU cumulative time and it michael@0: # is expressed as: "mm:ss.ms" michael@0: if p.dict['cpu_times'] != None: michael@0: ctime = timedelta(seconds=sum(p.dict['cpu_times'])) michael@0: ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60, michael@0: str((ctime.seconds % 60)).zfill(2), michael@0: str(ctime.microseconds)[:2]) michael@0: else: michael@0: ctime = '' michael@0: if p.dict['memory_percent'] is not None: michael@0: p.dict['memory_percent'] = round(p.dict['memory_percent'], 1) michael@0: else: michael@0: p.dict['memory_percent'] = '' michael@0: if p.dict['cpu_percent'] is None: michael@0: p.dict['cpu_percent'] = '' michael@0: if p.dict['username']: michael@0: username = p.dict['username'][:8] michael@0: else: michael@0: username = "" michael@0: line = templ % (p.pid, michael@0: username, michael@0: p.dict['nice'], michael@0: bytes2human(getattr(p.dict['memory_info'], 'vms', 0)), michael@0: bytes2human(getattr(p.dict['memory_info'], 'rss', 0)), michael@0: p.dict['cpu_percent'], michael@0: p.dict['memory_percent'], michael@0: ctime, michael@0: p.dict['name'] or '', michael@0: ) michael@0: try: michael@0: print_line(line) michael@0: except curses.error: michael@0: break michael@0: win.refresh() michael@0: michael@0: michael@0: def main(): michael@0: try: michael@0: interval = 0 michael@0: while 1: michael@0: args = poll(interval) michael@0: refresh_window(*args) michael@0: interval = 1 michael@0: except (KeyboardInterrupt, SystemExit): michael@0: pass michael@0: michael@0: if __name__ == '__main__': michael@0: main()