summaryrefslogtreecommitdiff
path: root/src/pmatop
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmatop
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmatop')
-rw-r--r--src/pmatop/GNUmakefile28
-rw-r--r--src/pmatop/pmatop.py917
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)