diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmatop | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmatop')
-rw-r--r-- | src/pmatop/GNUmakefile | 28 | ||||
-rw-r--r-- | src/pmatop/pmatop.py | 917 |
2 files changed, 945 insertions, 0 deletions
diff --git a/src/pmatop/GNUmakefile b/src/pmatop/GNUmakefile new file mode 100644 index 0000000..4676b93 --- /dev/null +++ b/src/pmatop/GNUmakefile @@ -0,0 +1,28 @@ +# +# Copyright (c) 2013 Red Hat. +# +# 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. +# + +TOPDIR = ../.. +include $(TOPDIR)/src/include/builddefs + +TARGET = pmatop +PYFILES = $(TARGET).py + +default default_pcp: $(PYFILES) + +include $(BUILDRULES) + +install install_pcp: $(PYFILES) +ifneq "$(PYTHON)" "" + $(INSTALL) -m 755 $(PYFILES) $(PCP_BIN_DIR)/$(TARGET) +endif diff --git a/src/pmatop/pmatop.py b/src/pmatop/pmatop.py new file mode 100644 index 0000000..b15ae83 --- /dev/null +++ b/src/pmatop/pmatop.py @@ -0,0 +1,917 @@ +#!/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) |