#!/usr/bin/python # # pmatop.py # # Copyright (C) 2013, 2014 Red Hat Inc. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # """Advanced System & Process Monitor using the libpcp Wrapper module Additional Information: Performance Co-Pilot Web Site http://www.performancecopilot.org """ # ignore line too long, missing docstring, method could be a function, # too many public methods # pylint: disable=C0301 # pylint: disable=C0111 # pylint: disable=R0201 # pylint: disable=R0904 ############################################################################## # # imports # import os import datetime import time import sys import select import signal import cpmapi as c_api import cpmgui as c_gui from pcp import pmapi, pmgui from pcp.pmsubsys import Subsystem try: import curses except ImportError as e: print(e) print("pmatop requires curses.py") sys.exit(0) ME = "pmatop" def debug(mssg): import logging logging.basicConfig(filename='pmatop.log',level=logging.DEBUG) if type(mssg) == type(""): logging.debug(mssg) else: logging.debug(str(mssg) + "\n") # scale ------------------------------------------------------------- def scale(value, magnitude): return value / magnitude # record --------------------------------------------------------------- def record(context, config, duration, path, host): # -f saves the metrics in a directory if os.path.exists(path): return "playback directory %s already exists\n" % path try: # Non-graphical application using libpcp_gui services - never want # to see popup dialogs from pmlogger(1) here, so force the issue. os.environ['PCP_XCONFIRM_PROG'] = '/bin/true' interval = pmapi.timeval.fromInterval(str(duration) + " seconds") context.pmRecordSetup(path, ME, 0) # pylint: disable=W0621 context.pmRecordAddHost(host, 1, config) deadhand = "-T" + str(interval) + "seconds" context.pmRecordControl(0, c_gui.PM_REC_SETARG, deadhand) context.pmRecordControl(0, c_gui.PM_REC_ON, "") interval.sleep() context.pmRecordControl(0, c_gui.PM_REC_OFF, "") # Note: pmlogger has a deadhand timer that will make it stop of its # own accord once -T limit is reached; but we send an OFF-recording # message anyway for cleanliness, just prior to pmcollectl exiting. except pmapi.pmErr as e: return "Cannot create PCP archive: " + path + " " + str(e) return "" # record_add_creator ------------------------------------------------------ def record_add_creator(path): fdesc = open(path, "a+") args = "" for i in sys.argv: args = args + i + " " fdesc.write("# Created by " + args) fdesc.write("\n#\n") fdesc.close() # minutes_seconds ---------------------------------------------------------- def minutes_seconds(milli): milli = abs(milli) sec, milli = divmod(milli, 1000) tenth, milli = divmod(milli, 100) milli = milli / 10 minute, sec = divmod(sec, 60) hour, minute = divmod(minute, 60) day, hour = divmod(hour, 24) if day > 0: return "%dd" % (day) elif hour > 0: return "%dh%dm" % (hour, minute) elif minute > 0: return "%dm%ds" % (minute, sec) else: return "%d.%d%1ds" % (sec, tenth, milli) # _StandardOutput -------------------------------------------------- class _StandardOutput(object): def width_write(self, value): self._width = value width = property(None, width_write, None, None) def __init__(self, out): if out == sys.stdout: self._width = 80 self.stdout = True else: self.stdout = False self.so_stdscr = out def addstr(self, str, clrtoeol=False): if self.stdout: sys.stdout.write(str) else: self.so_stdscr.addstr(str) if clrtoeol: self.so_stdscr.clrtoeol() def clear(self): if not self.stdout: self.so_stdscr.clear() def move(self, y, x): if not self.stdout: self.so_stdscr.move(y, x) def getyx(self): if self.stdout: return (0, 0) else: return self.so_stdscr.getyx() def getmaxyx(self): if self.stdout: return (1000, self._width) else: return self.so_stdscr.getmaxyx() def nodelay(self, tf): if not self.stdout: self.so_stdscr.nodelay(tf) def timeout(self, milliseconds): if not self.stdout: self.so_stdscr.timeout(milliseconds) def refresh(self): if not self.stdout: self.so_stdscr.refresh() def clrtobot(self): if not self.stdout: self.so_stdscr.clrtobot() def clear(self): if not self.stdout: self.so_stdscr.clear() def getch(self): if not self.stdout: return self.so_stdscr.getch() else: while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: char = sys.stdin.read(1) if len(char) == 0: return -1 else: return ord(char) return -1 # _AtopPrint -------------------------------------------------- class _AtopPrint(object): def __init__(self, ss, a_stdscr): self.ss = ss self.p_stdscr = a_stdscr self.command_line = self.p_stdscr.getyx()[0] self.apyx = a_stdscr.getmaxyx() self.ONEKBYTE = 1024 self.ONEMBYTE = 1048576 self.ONEGBYTE = 1073741824 self.ONETBYTE = 1099511627776 self.MAXBYTE = 1024 self.MAXKBYTE = self.ONEKBYTE*99999 self.MAXMBYTE = self.ONEMBYTE*999 self.MAXGBYTE = self.ONEGBYTE*999 self.ANYFORMAT = 0 self.KBFORMAT = 1 self.MBFORMAT = 2 self.GBFORMAT = 3 self.TBFORMAT = 4 self.OVFORMAT = 9 def end_of_screen(self): return self.p_stdscr.getyx()[0] >= self.apyx[0]-1 def set_line(self): self.command_line = self.p_stdscr.getyx()[0] self.p_stdscr.addstr('\n') def next_line(self): if self.p_stdscr.stdout: print('') return line = self.p_stdscr.getyx() apy = line[0] if line[1] > 0: apy += 1 self.p_stdscr.addstr(' ' * (self.apyx[1] - line[1])) self.p_stdscr.move(apy, 0) def valstr(self, value, width, avg_secs=0): ''' Function valstr() converts 'value' to a string of 'width' fixed number of positions. If 'value' does not fit, it will be formatted to exponent-notation. ''' maxval = 0 remain = 0 exp = 0 suffix = "" strvalue = "" if avg_secs: value = (value + (avg_secs/2)) / avg_secs width = width - 2 suffix = "/s" maxval = pow(10.0, width) - 1 if value < 0: sign = -1 value = abs(value) else: sign = 1 if value == 0: strvalue = "%*d" % (width, value) elif abs(value) >= 1: # exponent, if needed, will be positive maxval = pow(10.0, width) - 1 if value > maxval: # convert to E format: canonical form, fit width maxval = pow(10.0, width-2) - 1 while value > maxval: exp += 1 remain = value % 10 value /= 10 width -= 2 if remain >= 5: value += 1 strvalue = "%*de%d%s" % (width, value * sign, exp, suffix) else: # E format not needed: split int and fraction, fit width intval = str(int(value * sign)) fractional = str(value%1)[1:width-len(intval)+1] if fractional == ".": fractional = "" prval = "%s%s" % (intval, fractional) strvalue = "%*s%s" % (width, prval, suffix) else: # exponent, if needed, will be negative if value < 0.01: # convert to E format: canonical form, fit width width -= 3 while value < 1: exp += 1 value *= 10 fractional = str(value%1)[1:width] if fractional == ".": fractional = "" prval = "%d%s" % (value * sign, fractional) strvalue = "%*se-%d%s" % (width, prval, exp, suffix) else: # E format not needed: reduce precision, remove trailing 0s svalue = str(value * sign).replace("0.",".")[0:width] strvalue = "%*s" % (width, svalue.rstrip('0')) return strvalue def memstr(self, value, width=6, pformat=-1, avg_secs=0): ''' Function memstr() converts 'value' to a string of 'width' fixed number of positions and a memory size unit specifier which may optionally be specified 'pformat'; otherwise it is deduced. ''' if pformat == -1: pformat = self.ANYFORMAT aformat = "" verifyval = 0 suffix = "" strvalue = "" if value < 0: verifyval = -value * 10 else: verifyval = value if avg_secs: value /= avg_secs verifyval *= 100 width -= 2 suffix = "/s" if verifyval <= self.MAXBYTE: # bytes ? aformat = self.ANYFORMAT elif verifyval <= self.MAXKBYTE: # kbytes ? aformat = self.KBFORMAT elif verifyval <= self.MAXMBYTE: # mbytes ? aformat = self.MBFORMAT elif verifyval <= self.MAXGBYTE: # mbytes ? aformat = self.GBFORMAT else: aformat = self.TBFORMAT if aformat <= pformat: aformat = pformat if aformat == self.ANYFORMAT: strvalue = "%s%s" % (self.valstr((value), width), suffix) elif aformat == self.KBFORMAT: strvalue = "%sK%s" % (self.valstr((value/self.ONEKBYTE), width-1), suffix) elif aformat == self.MBFORMAT: strvalue = "%sM%s" % (self.valstr((value/self.ONEMBYTE), width-1), suffix) elif aformat == self.GBFORMAT: strvalue = "%sG%s" % (self.valstr((value/self.ONEGBYTE), width-1), suffix) elif aformat == self.TBFORMAT: strvalue = "%sT%s" % (self.valstr((value/self.ONETBYTE), width-1), suffix) else: strvalue = "*****" return strvalue # _ProcessorPrint -------------------------------------------------- class _ProcessorPrint(_AtopPrint): # Missing: #trun (total # running threads) # Missing: #exit (requires accounting) # Substitutions: proc.runq.sleeping for #tslpi (threads sleeping) # Substitutions: proc.runq.blocked for #tslpu (threads uninterrupt sleep) def prc(self): self.p_stdscr.addstr('PRC |') self.p_stdscr.addstr(' sys %8s |' % (minutes_seconds(self.ss.get_metric_value('kernel.all.cpu.sys')))) self.p_stdscr.addstr(' user %7s |' % (minutes_seconds(self.ss.get_metric_value('kernel.all.cpu.user')))) self.p_stdscr.addstr(' #proc %6d |' % (self.ss.get_metric_value('kernel.all.nprocs'))) if self.apyx[1] >= 95: self.p_stdscr.addstr(' #tslpi %s |' % self.valstr(self.ss.get_metric_value('proc.runq.sleeping'), 5)) if self.apyx[1] >= 110: self.p_stdscr.addstr(' #tslpu %s |' % self.valstr(self.ss.get_metric_value('proc.runq.blocked'), 5)) self.p_stdscr.addstr(' #zombie %s' % self.valstr(self.ss.get_metric_value('proc.runq.defunct'), 4)) self.next_line() # Missing: curscal (current current scaling percentage) def cpu(self): self.ss.get_total() self.p_stdscr.addstr('CPU |') self.p_stdscr.addstr(' sys %s%% |' % self.valstr(100 * self.ss.get_metric_value('kernel.all.cpu.sys') / self.ss.cpu_total, 7)) self.p_stdscr.addstr(' user %s%% |' % self.valstr(100 * self.ss.get_metric_value('kernel.all.cpu.user') / self.ss.cpu_total, 6)) self.p_stdscr.addstr(' irq %7d%% |' % ( 100 * self.ss.get_metric_value('kernel.all.cpu.irq.hard') / self.ss.cpu_total + 100 * self.ss.get_metric_value('kernel.all.cpu.irq.soft') / self.ss.cpu_total)) self.p_stdscr.addstr(' idle %s%% |' % self.valstr(100 * self.ss.get_metric_value('kernel.all.cpu.idle') / self.ss.cpu_total, 6)) self.p_stdscr.addstr(' wait %s%% |' % self.valstr(100 * self.ss.get_metric_value('kernel.all.cpu.wait.total') / self.ss.cpu_total, 6)) self.next_line() ncpu = self.ss.get_metric_value('hinv.ncpu') max_display_cpus = self.apyx[0] / 4 for k in range(ncpu): percpu_sys = (100 * self.ss.get_scalar_value('kernel.percpu.cpu.sys', k) / self.ss.cpu_total) percpu_user = (100 * self.ss.get_scalar_value('kernel.percpu.cpu.user', k) / self.ss.cpu_total) if percpu_sys == 0 and percpu_user == 0: continue self.p_stdscr.addstr('cpu |') self.p_stdscr.addstr(' sys %7d%% |' % percpu_sys) self.p_stdscr.addstr(' user %6d%% |' % percpu_user) self.p_stdscr.addstr(' irq %7d%% |' % ( 100 * self.ss.get_scalar_value('kernel.percpu.cpu.irq.hard', k) / self.ss.cpu_total + 100 * self.ss.get_scalar_value('kernel.percpu.cpu.irq.soft', k) / self.ss.cpu_total)) self.p_stdscr.addstr(' idle %6d%% |' % (100 * self.ss.get_scalar_value('kernel.percpu.cpu.idle', k) / self.ss.cpu_total)) self.p_stdscr.addstr(' cpu%02d %5d%% |' % (k, 100 * self.ss.get_scalar_value('kernel.percpu.cpu.wait.total', k) / self.ss.cpu_total)) if self.apyx[1] >= 95: self.p_stdscr.addstr(' curf %sMHz |' % (self.valstr(scale(self.ss.get_scalar_value('hinv.cpu.clock', k), 1000), 4))) self.next_line() if ncpu > max_display_cpus and k >= max_display_cpus: break self.p_stdscr.addstr('CPL |') self.p_stdscr.addstr(' avg1 %s |' % self.valstr(self.ss.get_scalar_value('kernel.all.load', 0), 7)) self.p_stdscr.addstr(' avg5 %s |' % self.valstr(self.ss.get_scalar_value('kernel.all.load', 1), 7)) self.p_stdscr.addstr(' avg15 %s |' % self.valstr(self.ss.get_scalar_value('kernel.all.load', 2), 6)) self.p_stdscr.addstr(' csw %s |' % self.valstr(self.ss.get_metric_value('kernel.all.pswitch'), 8)) self.p_stdscr.addstr(' intr %s |' % self.valstr(self.ss.get_metric_value('kernel.all.intr'), 7)) if self.apyx[1] >= 110: self.p_stdscr.addstr(' |') if self.apyx[1] >= 95: self.p_stdscr.addstr(' numcpu %2d |' % (self.ss.get_metric_value('hinv.ncpu'))) self.next_line() # _InterruptPrint -------------------------------------------------- class _InterruptPrint(_AtopPrint): pass # _DiskPrint -------------------------------------------------- class _DiskPrint(_AtopPrint): def interval_write(self, value): self._interval = value interval = property(None, interval_write, None, None) def replay_archive_write(self, value): self._replay_archive = value replay_archive = property(None, replay_archive_write, None, None) def disk(self, context): try: (inst, iname) = context.pmGetInDom(self.ss.metric_descs[self.ss.metrics_dict['disk.partitions.read']]) except pmapi.pmErr as e: iname = iname = "X" # Missing: LVM avq (average queue depth) lvms = dict(map(lambda x: (os.path.realpath("/dev/mapper/" + x)[5:], x), (os.listdir("/dev/mapper")))) for j in xrange(self.ss.get_len(self.ss.get_metric_value('disk.partitions.read'))): if self._replay_archive == True: if iname[j][:2] != "dm": continue lvm = iname[j] else: if iname[j] not in lvms: continue lvm = lvms[iname[j]] partitions_read = self.ss.get_scalar_value('disk.partitions.read', j) partitions_write = self.ss.get_scalar_value('disk.partitions.write', j) if partitions_read == 0 and partitions_write == 0: continue self.p_stdscr.addstr('LVM |') self.p_stdscr.addstr(' %-12s |' % (lvm[len(lvm)-12:])) self.p_stdscr.addstr(' |') self.p_stdscr.addstr(' read %s |' % self.valstr(partitions_read, 7)) self.p_stdscr.addstr(' write %s |' % self.valstr(partitions_write, 6)) if self.apyx[1] >= 95: val = (float(self.ss.get_scalar_value('disk.partitions.blkread', j)) / float(self._interval * 1000)) * 100 self.p_stdscr.addstr(' MBr/s %s |' % self.valstr(val, 6)) if self.apyx[1] >= 110: val = (float(self.ss.get_scalar_value('disk.partitions.blkwrite', j)) / float(self._interval * 1000)) * 100 self.p_stdscr.addstr(' MBw/s %s |' % self.valstr(val, 6)) if self.end_of_screen(): break self.next_line() try: (inst, iname) = context.pmGetInDom(self.ss.metric_descs[self.ss.metrics_dict['disk.dev.read']]) except pmapi.pmErr as e: iname = iname = "X" for j in xrange(self.ss.get_len(self.ss.get_metric_value('disk.dev.read_bytes'))): self.p_stdscr.addstr('DSK |') self.p_stdscr.addstr(' %-12s |' % (iname[j])) busy = (float(self.ss.get_scalar_value('disk.dev.avactive', j)) / float(self._interval * 1000)) * 100 if busy > 100: busy = 0 self.p_stdscr.addstr(' busy %6d%% |' % (busy)) val = self.ss.get_scalar_value('disk.dev.read', j) self.p_stdscr.addstr(' read %s |' % self.valstr(val, 7)) self.p_stdscr.addstr(' write %s |' % self.valstr(self.ss.get_scalar_value('disk.dev.write', j), 6)) if self.apyx[1] >= 95: val = (float(self.ss.get_scalar_value('disk.partitions.blkread', j)) / float(self._interval * 1000)) * 100 self.p_stdscr.addstr(' MBr/s %s |' % self.valstr(val, 6)) if self.apyx[1] >= 110: val = (float(self.ss.get_scalar_value('disk.partitions.blkwrite', j)) / float(self._interval * 1000)) * 100 self.p_stdscr.addstr(' MBw/s %s |' % self.valstr(val, 6)) try: avio = (float(self.ss.get_scalar_value('disk.dev.avactive', j)) / float(self.ss.get_scalar_value('disk.dev.total', j))) except ZeroDivisionError: avio = 0 self.p_stdscr.addstr(' avio %4.2g ms |' % (avio)) if self.end_of_screen(): break self.next_line() # _MemoryPrint -------------------------------------------------- class _MemoryPrint(_AtopPrint): # Missing: shrss (resident shared memory size) def mem(self): self.p_stdscr.addstr('MEM |') self.p_stdscr.addstr(' tot %s |' % (self.memstr(self.ss.get_metric_value('mem.physmem') * self.ONEKBYTE, 8))) self.p_stdscr.addstr(' free %s |' % (self.memstr(self.ss.get_metric_value('mem.freemem') * self.ONEKBYTE, 7))) self.p_stdscr.addstr(' cache %s |' % (self.memstr(self.ss.get_metric_value('mem.util.cached') * self.ONEKBYTE, 6))) self.p_stdscr.addstr(' buff %s |' % (self.memstr(self.ss.get_metric_value('mem.util.bufmem') * self.ONEKBYTE, 7))) self.p_stdscr.addstr(' slab %s |' % (self.memstr(self.ss.get_metric_value('mem.util.slab') * self.ONEKBYTE, 7))) if self.apyx[1] >= 95: self.p_stdscr.addstr(' #shmem %s |' % (self.memstr(self.ss.get_metric_value('mem.util.shmem') * self.ONEKBYTE, 5))) self.next_line() self.p_stdscr.addstr('SWP |') self.p_stdscr.addstr(' tot %s |' % (self.memstr(self.ss.get_metric_value('mem.util.swapTotal') * self.ONEKBYTE, 8))) self.p_stdscr.addstr(' free %s |' % (self.memstr(self.ss.get_metric_value('mem.util.swapFree') * self.ONEKBYTE, 7))) self.p_stdscr.addstr(' |') self.p_stdscr.addstr(' vmcom %s |' % (self.memstr(self.ss.get_metric_value('mem.util.committed_AS') * self.ONEKBYTE, 6))) self.p_stdscr.addstr(' vmlim %s |' % (self.memstr(self.ss.get_metric_value('mem.util.commitLimit') * self.ONEKBYTE, 6))) self.next_line() self.p_stdscr.addstr('PAG |') self.p_stdscr.addstr(' scan %s |' % (self.valstr(self.ss.get_metric_value('mem.vmstat.slabs_scanned'), 7))) self.p_stdscr.addstr(' steal %s |' % (self.valstr(self.ss.get_metric_value('mem.vmstat.pginodesteal'), 6))) self.p_stdscr.addstr(' stall %s |' % (self.valstr(self.ss.get_metric_value('mem.vmstat.allocstall'), 6))) self.p_stdscr.addstr(' swin %s |' % (self.valstr(self.ss.get_metric_value('mem.vmstat.pswpin'), 7))) self.p_stdscr.addstr(' swout %s |' % (self.valstr(self.ss.get_metric_value('mem.vmstat.pswpout'), 6))) self.next_line() # _NetPrint -------------------------------------------------- class _NetPrint(_AtopPrint): def net(self, context): if self.end_of_screen(): return self.p_stdscr.addstr('NET | transport |') self.p_stdscr.addstr(' tcpi %sM |' % self.valstr(self.ss.get_metric_value('network.tcp.insegs'), 6)) self.p_stdscr.addstr(' tcpo %sM |' % self.valstr(self.ss.get_metric_value('network.tcp.outsegs'), 6)) self.p_stdscr.addstr(' udpi %sM |' % self.valstr(self.ss.get_metric_value('network.udp.indatagrams'), 6)) self.p_stdscr.addstr(' udpo %sM |' % self.valstr(self.ss.get_metric_value('network.udp.outdatagrams'), 6)) if self.apyx[1] >= 95: self.p_stdscr.addstr(' tcpao %sM |' % self.valstr(self.ss.get_metric_value('network.tcp.activeopens'), 5)) if self.apyx[1] >= 110: self.p_stdscr.addstr(' tcppo %sM |' % self.valstr(self.ss.get_metric_value('network.tcp.passiveopens'), 5)) self.next_line() # Missing: icmpi (internet control message protocol received datagrams) # Missing: icmpo (internet control message protocol transmitted datagrams) self.p_stdscr.addstr('NET | network |') self.p_stdscr.addstr(' ipi %sM |' % self.valstr(self.ss.get_metric_value('network.ip.inreceives'), 7)) self.p_stdscr.addstr(' ipo %sM |' % self.valstr(self.ss.get_metric_value('network.ip.outrequests'), 7)) self.p_stdscr.addstr(' ipfrw %sM |' % self.valstr(self.ss.get_metric_value('network.ip.forwdatagrams'), 5)) self.p_stdscr.addstr(' deliv %sM |' % self.valstr(self.ss.get_metric_value('network.ip.indelivers'), 5)) if self.apyx[1] >= 95: self.p_stdscr.addstr(' icmpi %s |' % self.valstr(self.ss.get_metric_value('network.icmp.inmsgs'), 6)) if self.apyx[1] >= 110: self.p_stdscr.addstr(' icmpo %s |' % self.valstr(self.ss.get_metric_value('network.icmp.outmsgs'), 6)) self.next_line() try: (inst, iname) = context.pmGetInDom(self.ss.metric_descs[self.ss.metrics_dict['network.interface.in.bytes']]) except pmapi.pmErr as e: iname = iname = "X" net_metric = self.ss.get_metric_value('network.interface.in.bytes') if type(net_metric) == type([]): for j in xrange(len(self.ss.get_metric_value('network.interface.in.bytes'))): pcki = self.ss.get_scalar_value('network.interface.in.packets', j) pcko = self.ss.get_scalar_value('network.interface.out.packets', j) if pcki == 0 and pcko == 0: continue self.p_stdscr.addstr('NET |') self.p_stdscr.addstr(' %-12s |' % (iname[j])) self.p_stdscr.addstr(' pcki %sM |' % self.valstr(pcki, 6)) self.p_stdscr.addstr(' pcko %sM |' % self.valstr(pcko, 6)) self.p_stdscr.addstr(' si %s Kbps |' % self.valstr(scale(self.ss.get_scalar_value('network.interface.in.bytes', j), 100000000), 4)) self.p_stdscr.addstr(' so %s Kpbs |' % self.valstr(scale(self.ss.get_scalar_value('network.interface.out.bytes', j), 100000000), 4)) if self.apyx[1] >= 95: self.p_stdscr.addstr(' erri %sM |' % self.valstr(self.ss.get_scalar_value('network.interface.in.errors', j), 6)) if self.apyx[1] >= 110: self.p_stdscr.addstr(' erro %sM |' % self.valstr(self.ss.get_scalar_value('network.interface.out.errors', j), 6)) if self.end_of_screen(): break self.next_line() # _ProcPrint -------------------------------------------------- class _ProcPrint(_AtopPrint): def type_write(self, value): self._output_type = value output_type = property(None, type_write, None, None) @staticmethod def sort_l(l1, l2): if l1[1] < l2[1]: return -1 elif l1[1] > l2[1]: return 1 else: return 0 def proc(self): if self._output_type in ['g']: self.p_stdscr.addstr(' PID SYSCPU USRCPU VGROW RGROW RUID THR ST EXC S CPU CMD') elif self._output_type in ['m']: self.p_stdscr.addstr('PID ') if self.apyx[1] >= 110: self.p_stdscr.addstr('MINFLT MAJFLT ') else: self.p_stdscr.addstr(' ') if self.apyx[1] >= 95: self.p_stdscr.addstr('VSTEXT VSLIBS ') self.p_stdscr.addstr('VDATA VSTACK VGROW RGROW VSIZE RSIZE MEM CMD') self.next_line() # TODO Remember this state for Next/Previous Page cpu_time_sorted = list() for j in xrange(self.ss.get_metric_value('proc.nprocs')): cpu_time_sorted.append((j, self.ss.get_scalar_value('proc.psinfo.utime', j) + self.ss.get_scalar_value('proc.psinfo.stime', j))) cpu_time_sorted.sort(self.sort_l, reverse=True) for i in xrange(len(cpu_time_sorted)): j = cpu_time_sorted[i][0] if self._output_type in ['g', 'm']: self.p_stdscr.addstr('%5d ' % (self.ss.get_scalar_value('proc.psinfo.pid', j))) if self._output_type in ['g']: self.p_stdscr.addstr('%6s ' % minutes_seconds(self.ss.get_scalar_value('proc.psinfo.stime', j))) self.p_stdscr.addstr(' %6s ' % minutes_seconds(self.ss.get_scalar_value('proc.psinfo.utime', j))) self.p_stdscr.addstr('%s ' % self.memstr(self.ss.get_scalar_value('proc.psinfo.vsize', j), 5)) self.p_stdscr.addstr('%s ' % self.memstr(self.ss.get_scalar_value('proc.psinfo.rss', j), 5)) self.p_stdscr.addstr('%6s ' % (self.ss.get_scalar_value('proc.id.uid_nm', j)[0:6])) self.p_stdscr.addstr('%4d ' % self.ss.get_scalar_value('proc.psinfo.threads', j)) self.p_stdscr.addstr('%3s ' % '--') state = self.ss.get_scalar_value('proc.psinfo.sname', j) self.p_stdscr.addstr(' %2s ' % '-') if state not in ('D', 'R', 'S', 'T', 'W', 'X', 'Z'): state = 'S' self.p_stdscr.addstr('%2s ' % (state)) cpu_total = float(self.ss.cpu_total - self.ss.get_metric_value('kernel.all.cpu.idle')) proc_cpu_total = (self.ss.get_scalar_value('proc.psinfo.utime', j) + self.ss.get_scalar_value('proc.psinfo.stime', j)) if proc_cpu_total > cpu_total: proc_percent = 0 else: proc_percent = (100 * proc_cpu_total / cpu_total) self.p_stdscr.addstr('%2d%% ' % proc_percent) self.p_stdscr.addstr('%-15s ' % (self.ss.get_scalar_value('proc.psinfo.cmd', j))) if self._output_type in ['m']: # Missing: SWAPSZ, proc.psinfo.nswap frequently returns -1 if self.apyx[1] >= 110: minf = self.ss.get_scalar_value('proc.psinfo.minflt', j) majf = self.ss.get_scalar_value('proc.psinfo.maj_flt', j) if minf < 0: minf = 0 if majf < 0: majf = 0 self.p_stdscr.addstr("%s " % self.valstr(minf, 3)) self.p_stdscr.addstr("%s " % self.valstr(majf, 3)) if self.apyx[1] >= 95: self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.memory.textrss', j) * self.ONEKBYTE, 7)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.memory.librss', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.memory.datrss', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.memory.vmstack', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.psinfo.vsize', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.psinfo.rss', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.psinfo.vsize', j) * self.ONEKBYTE, 6)) self.p_stdscr.addstr("%s " % self.memstr(self.ss.get_scalar_value('proc.psinfo.rss', j) * self.ONEKBYTE, 6)) val = float(self.ss.get_old_scalar_value('proc.psinfo.rss', j)) / float(self.ss.get_metric_value('mem.physmem')) * 100 if val > 100: val = 0 self.p_stdscr.addstr('%2d%% ' % val) self.p_stdscr.addstr('%-15s' % (self.ss.get_scalar_value('proc.psinfo.cmd', j))) if self.end_of_screen(): break self.next_line() class _Options(object): def __init__(self): self.input_file = "" self.output_file = "" self.output_type = "g" self.host = "local:" self.create_archive = False self.replay_archive = False self.have_interval_arg = False self.interval_arg = 5 self.width = 0 self.n_samples = 0 self.opts = self.setup() def setup(self): """ Setup default command line argument option handling """ opts = pmapi.pmOptions() opts.pmSetOptionCallback(self.option_callback) opts.pmSetOverrideCallback(self.override) # leading - returns args that are not options with leading ^A opts.pmSetShortOptions("-gmw:r:L:h:V?") opts.pmSetLongOptionHeader("Options") opts.pmSetLongOption("generic", 0, 'g', '', "Display generic metrics") opts.pmSetLongOption("memory", 0, 'm', '', "Display memory metrics") opts.pmSetLongOption("write", 1, 'w', 'FILENAME', "Write metric data to file") opts.pmSetLongOption("read", 1, 'r', 'FILENAME', "Read metric data from file") opts.pmSetLongOption("width", 1, 'L', 'WIDTH', "Width of the output") opts.pmSetShortUsage("[options]\nInteractive: [-g|-m] [-L linelen] [-h host] [ interval [ samples ]]\nWrite raw logfile: pmatop -w rawfile [ interval [ samples ]]\nRead raw logfile: pmatop -r [ rawfile ] [-g|-m] [-L linelen] [-h host]") opts.pmSetLongOptionHost() opts.pmSetLongOptionVersion() opts.pmSetLongOptionHelp() return opts def override(self, opt): """ Override a few standard PCP options to match free(1) """ # pylint: disable=R0201 if opt == 'g': return 1 return 0 def option_callback(self, opt, optarg, index): """ Perform setup for an individual command line option """ # pylint: disable=W0613 if opt == "g": self.output_type = "g" elif opt == "m": self.output_type = "m" elif opt == "w": self.output_file = optarg self.create_archive = True elif opt == "r": self.opts.pmSetOptionArchiveFolio(optarg) self.input_file = optarg self.replay_archive = True elif opt == "L": self.width = int(optarg) elif opt == 'h': self.host = optarg elif opt == "": if self.have_interval_arg == False: self.interval_arg = optarg self.have_interval_arg = True else: self.n_samples = int(optarg) # main ---------------------------------------------------------------------- def main(stdscr_p): global stdscr stdscr = _StandardOutput(stdscr_p) sort = "" duration = 0.0 i = 1 ss = Subsystem() ss.init_processor_metrics() ss.init_memory_metrics() ss.init_disk_metrics() ss.init_network_metrics() ss.init_process_metrics() cpu = _ProcessorPrint(ss, stdscr) mem = _MemoryPrint(ss, stdscr) disk = _DiskPrint(ss, stdscr) net = _NetPrint(ss, stdscr) proc = _ProcPrint(ss, stdscr) proc.output_type = opts.output_type stdscr.width = opts.width pmc = pmapi.pmContext.fromOptions(opts.opts, sys.argv) if pmc.type == c_api.PM_CONTEXT_ARCHIVE: pmc.pmSetMode(c_api.PM_MODE_FORW, pmapi.timeval(0, 0), 0) host = pmc.pmGetContextHostName() (delta, errmsg) = pmc.pmParseInterval(str(opts.interval_arg) + " seconds") ss.setup_metrics(pmc) if opts.create_archive: delta_seconds = c_api.pmtimevalToReal(delta.tv_sec, delta.tv_usec) msec = str(int(1000.0 * delta_seconds)) configuration = "log mandatory on every " + msec + " milliseconds { " configuration += ss.dump_metrics() configuration += "}" if opts.n_samples != 0: duration = float(opts.n_samples) * delta_seconds else: duration = float(10) * delta_seconds status = record(pmgui.GuiClient(), configuration, duration, opts.output_file, host) if status != "": return status record_add_creator(opts.output_file) sys.exit(0) i_samples = 0 disk.interval = delta.tv_sec disk.replay_archive = opts.replay_archive try: elapsed = ss.get_metric_value('kernel.all.uptime') while (i_samples < opts.n_samples) or (opts.n_samples == 0): ss.get_stats(pmc) stdscr.move(0, 0) stdscr.addstr('ATOP - %s\t\t%s elapsed\n\n' % ( time.strftime("%c"), datetime.timedelta(0, elapsed))) elapsed = delta.tv_sec stdscr.move(2, 0) try: cpu.prc() cpu.cpu() mem.mem() disk.disk(pmc) net.net(pmc) proc.set_line() proc.proc() except pmapi.pmErr as e: return str(e) + " while processing " + str(ssx[0]) except Exception as e: # catch all errors, pcp or python or other pass stdscr.move(proc.command_line, 0) stdscr.refresh() stdscr.timeout(delta.tv_sec * 1000) char = stdscr.getch() if char != -1: # user typed a command try: cmd = chr(char) except ValueError: cmd = None if cmd == "q": raise KeyboardInterrupt elif cmd == " ": stdscr.clear() stdscr.refresh() elif cmd == "z": stdscr.timeout(-1) # currently it just does "hit any key to continue" char = stdscr.getch() elif cmd == "h" or cmd == "?": stdscr.clear () stdscr.move (0, 0) stdscr.addstr ('\nOptions shown for active processes:\n') stdscr.addstr ( "'g' - generic info (default)\n") stdscr.addstr ( "'m' - memory details\n") stdscr.addstr ( "Miscellaneous commands:\n") stdscr.addstr ("'z' - pause-button to freeze current sample (toggle)\n") stdscr.addstr ("^L - redraw the screen\n") stdscr.addstr ("hit any key to continue\n") stdscr.timeout(-1) char = stdscr.getch() stdscr.clear() elif cmd in ['g', 'm']: stdscr.clear() proc.output_type = cmd # TODO Next/Previous Page else: stdscr.move(proc.command_line, 0) stdscr.addstr("Invalid command %s\n" % (cmd), True) stdscr.addstr("Type 'h' to see a list of valid commands", True) stdscr.refresh() time.sleep(2) i_samples += 1 except KeyboardInterrupt: pass stdscr.refresh() time.sleep(1) return "" def sigwinch_handler(n, frame): global stdscr curses.endwin() curses.initscr() # consume any subsequent characters awhile while 1: char = stdscr.getch() if char == -1: break if __name__ == '__main__': global opts opts = _Options() if c_api.pmGetOptionsFromList(sys.argv) != 0: c_api.pmUsageMessage() sys.exit(1) if sys.stdout.isatty(): signal.signal(signal.SIGWINCH, sigwinch_handler) try: status = curses.wrapper(main) # pylint: disable-msg=C0103 except curses.error as e: status = "Error in the curses module. Try running " + ME + " in a larger window." else: # Output is piped or redirected status = main(sys.stdout) if status != "": print(status)